aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml4
-rw-r--r--.editorconfig2
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md3
-rw-r--r--.github/workflows/check-whitespace.yml2
-rw-r--r--.github/workflows/coverity.yml6
-rw-r--r--.github/workflows/l10n.yml6
-rw-r--r--.github/workflows/main.yml62
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml53
-rw-r--r--.mailmap1
-rw-r--r--CODE_OF_CONDUCT.md4
-rw-r--r--Documentation/CodingGuidelines209
-rw-r--r--Documentation/MyFirstContribution.txt7
-rw-r--r--Documentation/MyFirstObjectWalk.txt37
-rw-r--r--Documentation/RelNotes/1.6.2.txt2
-rw-r--r--Documentation/RelNotes/1.6.3.txt2
-rw-r--r--Documentation/RelNotes/1.6.4.txt2
-rw-r--r--Documentation/RelNotes/1.6.5.txt2
-rw-r--r--Documentation/RelNotes/1.6.6.txt2
-rw-r--r--Documentation/RelNotes/2.43.1.txt82
-rw-r--r--Documentation/RelNotes/2.43.2.txt37
-rw-r--r--Documentation/RelNotes/2.43.3.txt12
-rw-r--r--Documentation/RelNotes/2.44.0.txt295
-rw-r--r--Documentation/RelNotes/2.45.0.txt433
-rw-r--r--Documentation/SubmittingPatches45
-rw-r--r--Documentation/config.txt54
-rw-r--r--Documentation/config/advice.txt192
-rw-r--r--Documentation/config/clean.txt4
-rw-r--r--Documentation/config/clone.txt20
-rw-r--r--Documentation/config/core.txt17
-rw-r--r--Documentation/config/diff.txt12
-rw-r--r--Documentation/config/extensions.txt23
-rw-r--r--Documentation/config/feature.txt3
-rw-r--r--Documentation/config/fetch.txt6
-rw-r--r--Documentation/config/format.txt2
-rw-r--r--Documentation/config/grep.txt2
-rw-r--r--Documentation/config/init.txt11
-rw-r--r--Documentation/config/interactive.txt4
-rw-r--r--Documentation/config/mergetool.txt19
-rw-r--r--Documentation/config/rebase.txt2
-rw-r--r--Documentation/config/sendemail.txt12
-rw-r--r--Documentation/config/status.txt2
-rw-r--r--Documentation/config/transfer.txt4
-rw-r--r--Documentation/diff-options.txt7
-rw-r--r--Documentation/fetch-options.txt7
-rw-r--r--Documentation/git-add.txt23
-rw-r--r--Documentation/git-am.txt23
-rw-r--r--Documentation/git-bisect.txt16
-rw-r--r--Documentation/git-blame.txt2
-rw-r--r--Documentation/git-branch.txt3
-rw-r--r--Documentation/git-bugreport.txt10
-rw-r--r--Documentation/git-checkout.txt6
-rw-r--r--Documentation/git-cherry-pick.txt30
-rw-r--r--Documentation/git-clean.txt6
-rw-r--r--Documentation/git-clone.txt152
-rw-r--r--Documentation/git-commit-graph.txt2
-rw-r--r--Documentation/git-commit.txt2
-rw-r--r--Documentation/git-config.txt31
-rw-r--r--Documentation/git-cvsimport.txt4
-rw-r--r--Documentation/git-cvsserver.txt4
-rw-r--r--Documentation/git-daemon.txt10
-rw-r--r--Documentation/git-diagnose.txt2
-rw-r--r--Documentation/git-diff.txt2
-rw-r--r--Documentation/git-difftool.txt3
-rw-r--r--Documentation/git-fast-export.txt2
-rw-r--r--Documentation/git-fast-import.txt39
-rw-r--r--Documentation/git-fetch.txt4
-rw-r--r--Documentation/git-filter-branch.txt6
-rw-r--r--Documentation/git-for-each-ref.txt13
-rw-r--r--Documentation/git-format-patch.txt24
-rw-r--r--Documentation/git-grep.txt36
-rw-r--r--Documentation/git-imap-send.txt2
-rw-r--r--Documentation/git-index-pack.txt26
-rw-r--r--Documentation/git-init.txt91
-rw-r--r--Documentation/git-interpret-trailers.txt6
-rw-r--r--Documentation/git-ls-files.txt23
-rw-r--r--Documentation/git-merge-file.txt6
-rw-r--r--Documentation/git-merge-tree.txt5
-rw-r--r--Documentation/git-merge.txt72
-rw-r--r--Documentation/git-mv.txt2
-rw-r--r--Documentation/git-notes.txt2
-rw-r--r--Documentation/git-pack-refs.txt15
-rw-r--r--Documentation/git-pull.txt4
-rw-r--r--Documentation/git-rebase.txt56
-rw-r--r--Documentation/git-reflog.txt3
-rw-r--r--Documentation/git-remote.txt2
-rw-r--r--Documentation/git-replace.txt6
-rw-r--r--Documentation/git-replay.txt127
-rw-r--r--Documentation/git-rev-parse.txt39
-rw-r--r--Documentation/git-revert.txt4
-rw-r--r--Documentation/git-send-email.txt30
-rw-r--r--Documentation/git-status.txt8
-rw-r--r--Documentation/git-submodule.txt4
-rw-r--r--Documentation/git-svn.txt18
-rw-r--r--Documentation/git-switch.txt11
-rw-r--r--Documentation/git-tag.txt2
-rw-r--r--Documentation/git-update-ref.txt58
-rw-r--r--Documentation/git-worktree.txt4
-rw-r--r--Documentation/git.txt53
-rw-r--r--Documentation/gitattributes.txt24
-rw-r--r--Documentation/gitcli.txt3
-rw-r--r--Documentation/gitcore-tutorial.txt2
-rw-r--r--Documentation/gitdiffcore.txt8
-rw-r--r--Documentation/gitformat-index.txt4
-rw-r--r--Documentation/githooks.txt24
-rw-r--r--Documentation/gitk.txt4
-rw-r--r--Documentation/gitprotocol-capabilities.txt2
-rw-r--r--Documentation/gitprotocol-http.txt18
-rw-r--r--Documentation/gitprotocol-v2.txt14
-rw-r--r--Documentation/gitremote-helpers.txt15
-rw-r--r--Documentation/gitrepository-layout.txt4
-rw-r--r--Documentation/gitsubmodules.txt4
-rw-r--r--Documentation/gitweb.conf.txt14
-rw-r--r--Documentation/gitweb.txt4
-rw-r--r--Documentation/glossary-content.txt34
-rw-r--r--Documentation/howto/keep-canonical-history-correct.txt2
-rw-r--r--Documentation/howto/update-hook-example.txt4
-rw-r--r--Documentation/merge-options.txt2
-rw-r--r--Documentation/mergetools/vimdiff.txt3
-rw-r--r--Documentation/pretty-formats.txt12
-rw-r--r--Documentation/ref-storage-format.txt3
-rw-r--r--Documentation/rev-list-options.txt25
-rw-r--r--Documentation/signoff-option.txt2
-rw-r--r--Documentation/technical/repository-version.txt6
-rw-r--r--Documentation/trace2-target-values.txt2
-rw-r--r--Documentation/urls.txt52
-rw-r--r--Documentation/user-manual.txt42
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--INSTALL4
-rw-r--r--Makefile153
-rw-r--r--README.md6
l---------RelNotes2
-rw-r--r--add-patch.c44
-rw-r--r--advice.c117
-rw-r--r--advice.h18
-rw-r--r--apply.c51
-rw-r--r--archive-tar.c3
-rw-r--r--archive-zip.c1
-rw-r--r--archive.c7
-rw-r--r--archive.h1
-rw-r--r--attr.c95
-rw-r--r--bisect.c36
-rw-r--r--blame.c2
-rw-r--r--blame.h3
-rw-r--r--blob.c1
-rw-r--r--bloom.c1
-rw-r--r--branch.c27
-rw-r--r--branch.h14
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c34
-rw-r--r--builtin/am.c26
-rw-r--r--builtin/apply.c1
-rw-r--r--builtin/archive.c1
-rw-r--r--builtin/bisect.c18
-rw-r--r--builtin/blame.c7
-rw-r--r--builtin/branch.c39
-rw-r--r--builtin/bugreport.c10
-rw-r--r--builtin/cat-file.c39
-rw-r--r--builtin/checkout-index.c1
-rw-r--r--builtin/checkout.c192
-rw-r--r--builtin/clean.c22
-rw-r--r--builtin/clone.c138
-rw-r--r--builtin/column.c4
-rw-r--r--builtin/commit-graph.c5
-rw-r--r--builtin/commit-tree.c3
-rw-r--r--builtin/commit.c114
-rw-r--r--builtin/config.c44
-rw-r--r--builtin/credential-cache--daemon.c2
-rw-r--r--builtin/credential-cache.c5
-rw-r--r--builtin/describe.c2
-rw-r--r--builtin/diff-files.c1
-rw-r--r--builtin/diff-index.c2
-rw-r--r--builtin/diff-tree.c1
-rw-r--r--builtin/diff.c2
-rw-r--r--builtin/difftool.c1
-rw-r--r--builtin/fast-export.c4
-rw-r--r--builtin/fast-import.c189
-rw-r--r--builtin/fetch.c41
-rw-r--r--builtin/for-each-ref.c13
-rw-r--r--builtin/fsck.c7
-rw-r--r--builtin/fsmonitor--daemon.c5
-rw-r--r--builtin/gc.c117
-rw-r--r--builtin/get-tar-commit-id.c1
-rw-r--r--builtin/grep.c14
-rw-r--r--builtin/hash-object.c1
-rw-r--r--builtin/hook.c1
-rw-r--r--builtin/index-pack.c26
-rw-r--r--builtin/init-db.c17
-rw-r--r--builtin/interpret-trailers.c103
-rw-r--r--builtin/log.c41
-rw-r--r--builtin/ls-files.c14
-rw-r--r--builtin/ls-remote.c1
-rw-r--r--builtin/ls-tree.c17
-rw-r--r--builtin/mailinfo.c1
-rw-r--r--builtin/merge-base.c26
-rw-r--r--builtin/merge-file.c28
-rw-r--r--builtin/merge-recursive.c3
-rw-r--r--builtin/merge-tree.c59
-rw-r--r--builtin/merge.c77
-rw-r--r--builtin/mktag.c1
-rw-r--r--builtin/mv.c1
-rw-r--r--builtin/name-rev.c39
-rw-r--r--builtin/notes.c20
-rw-r--r--builtin/pack-objects.c17
-rw-r--r--builtin/pack-refs.c31
-rw-r--r--builtin/pull.c14
-rw-r--r--builtin/push.c48
-rw-r--r--builtin/range-diff.c1
-rw-r--r--builtin/read-tree.c7
-rw-r--r--builtin/rebase.c71
-rw-r--r--builtin/receive-pack.c66
-rw-r--r--builtin/reflog.c44
-rw-r--r--builtin/remote.c2
-rw-r--r--builtin/repack.c20
-rw-r--r--builtin/replay.c446
-rw-r--r--builtin/rerere.c1
-rw-r--r--builtin/reset.c11
-rw-r--r--builtin/rev-list.c21
-rw-r--r--builtin/rev-parse.c43
-rw-r--r--builtin/revert.c40
-rw-r--r--builtin/rm.c1
-rw-r--r--builtin/send-pack.c35
-rw-r--r--builtin/shortlog.c1
-rw-r--r--builtin/show-ref.c11
-rw-r--r--builtin/sparse-checkout.c15
-rw-r--r--builtin/stash.c14
-rw-r--r--builtin/stripspace.c4
-rw-r--r--builtin/submodule--helper.c3
-rw-r--r--builtin/tag.c63
-rw-r--r--builtin/unpack-objects.c12
-rw-r--r--builtin/update-ref.c27
-rw-r--r--builtin/upload-pack.c2
-rw-r--r--builtin/var.c2
-rw-r--r--builtin/verify-commit.c2
-rw-r--r--builtin/verify-tag.c1
-rw-r--r--builtin/worktree.c96
-rw-r--r--bulk-checkin.c1
-rw-r--r--bundle-uri.c1
-rw-r--r--cache-tree.c9
-rwxr-xr-xci/install-dependencies.sh10
-rwxr-xr-xci/install-docker-dependencies.sh7
-rwxr-xr-xci/lib.sh14
-rwxr-xr-xci/print-test-failures.sh2
-rwxr-xr-xci/run-build-and-minimal-fuzzers.sh19
-rwxr-xr-xci/run-build-and-tests.sh3
-rw-r--r--column.c4
-rw-r--r--combine-diff.c3
-rw-r--r--command-list.txt1
-rw-r--r--commit-graph.c27
-rw-r--r--commit-reach.c210
-rw-r--r--commit-reach.h26
-rw-r--r--commit.c237
-rw-r--r--commit.h5
-rw-r--r--compat/compiler.h1
-rw-r--r--compat/disk.h1
-rw-r--r--compat/fsmonitor/fsm-health-win32.c1
-rw-r--r--compat/fsmonitor/fsm-listen-darwin.c1
-rw-r--r--compat/fsmonitor/fsm-listen-win32.c1
-rw-r--r--compat/mingw.c110
-rw-r--r--compat/mingw.h6
-rw-r--r--compat/simple-ipc/ipc-shared.c3
-rw-r--r--compat/simple-ipc/ipc-unix-socket.c1
-rw-r--r--config.c154
-rw-r--r--config.h9
-rw-r--r--config.mak.uname175
-rw-r--r--contrib/coccinelle/xstrncmpz.cocci28
-rw-r--r--contrib/completion/git-completion.bash423
-rw-r--r--contrib/completion/git-prompt.sh35
-rwxr-xr-xcontrib/coverage-diff.sh9
-rw-r--r--contrib/credential/libsecret/git-credential-libsecret.c3
-rw-r--r--contrib/credential/osxkeychain/Makefile3
-rw-r--r--contrib/credential/osxkeychain/git-credential-osxkeychain.c376
-rw-r--r--contrib/credential/wincred/git-credential-wincred.c46
-rwxr-xr-xcontrib/hg-to-git/hg-to-git.py254
-rw-r--r--contrib/hg-to-git/hg-to-git.txt21
-rwxr-xr-xcontrib/subtree/git-subtree.sh30
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh42
-rwxr-xr-xcontrib/vscode/init.sh1
-rwxr-xr-xcontrib/workdir/git-new-workdir2
-rw-r--r--convert.c2
-rw-r--r--convert.h2
-rw-r--r--date.c50
-rw-r--r--date.h6
-rw-r--r--delta-islands.c7
-rw-r--r--diff-lib.c27
-rw-r--r--diff-no-index.c3
-rw-r--r--diff.c52
-rw-r--r--diffcore-break.c1
-rw-r--r--diffcore-delta.c5
-rw-r--r--dir-iterator.c105
-rw-r--r--dir-iterator.h3
-rw-r--r--dir.c21
-rw-r--r--dir.h7
-rw-r--r--editor.c7
-rw-r--r--entry.c1
-rw-r--r--environment.c7
-rw-r--r--environment.h3
-rw-r--r--exec-cmd.c1
-rw-r--r--fetch-pack.c16
-rw-r--r--fetch-pack.h1
-rw-r--r--fmt-merge-msg.c8
-rw-r--r--fsck.c148
-rw-r--r--fsmonitor--daemon.h4
-rw-r--r--fsmonitor-ipc.c1
-rw-r--r--fsmonitor.c312
-rw-r--r--gettext.c2
-rw-r--r--git-compat-util.h55
-rw-r--r--git-curl-compat.h9
-rwxr-xr-xgit-cvsimport.perl2
-rwxr-xr-xgit-difftool--helper.sh13
-rw-r--r--git-gui/.gitattributes1
-rw-r--r--git-gui/Makefile16
-rwxr-xr-xgit-gui/git-gui.sh6
-rw-r--r--git-gui/lib/encoding.tcl2
-rw-r--r--git-gui/po/README2
-rwxr-xr-xgit-instaweb.sh4
-rwxr-xr-xgit-p4.py29
-rwxr-xr-xgit-quiltimport.sh2
-rw-r--r--git.c13
-rw-r--r--gitk-git/Makefile4
-rwxr-xr-xgitk-git/gitk4
-rw-r--r--gitweb/INSTALL2
-rwxr-xr-xgitweb/gitweb.perl14
-rw-r--r--gitweb/static/gitweb.css2
-rw-r--r--gitweb/static/js/lib/common-lib.js10
-rw-r--r--gpg-interface.c24
-rw-r--r--gpg-interface.h2
-rw-r--r--graph.h4
-rw-r--r--grep.c5
-rw-r--r--hash-ll.h1
-rw-r--r--hash.h9
-rw-r--r--help.c5
-rw-r--r--http-backend.c13
-rw-r--r--http-fetch.c2
-rw-r--r--http-push.c12
-rw-r--r--http-walker.c1
-rw-r--r--http.c5
-rw-r--r--http.h1
-rw-r--r--imap-send.c31
-rw-r--r--json-writer.h4
-rw-r--r--kwset.c2
-rw-r--r--kwset.h2
-rw-r--r--line-log.c4
-rw-r--r--line-log.h2
-rw-r--r--line-range.c1
-rw-r--r--list-objects-filter-options.c5
-rw-r--r--list-objects-filter.c16
-rw-r--r--list-objects.c2
-rw-r--r--list.h2
-rw-r--r--lockfile.h6
-rw-r--r--log-tree.c30
-rw-r--r--log-tree.h2
-rw-r--r--loose.c259
-rw-r--r--loose.h22
-rw-r--r--ls-refs.c1
-rw-r--r--mailinfo.c18
-rw-r--r--match-trees.c4
-rw-r--r--mem-pool.c51
-rw-r--r--mem-pool.h5
-rw-r--r--merge-blobs.c2
-rw-r--r--merge-ll.c27
-rw-r--r--merge-ll.h5
-rw-r--r--merge-ort.c149
-rw-r--r--merge-recursive.c92
-rw-r--r--merge-recursive.h1
-rw-r--r--merge.c11
-rw-r--r--mergetools/vimdiff12
-rw-r--r--midx-write.c1525
-rw-r--r--midx.c1558
-rw-r--r--midx.h20
-rw-r--r--name-hash.c9
-rw-r--r--name-hash.h7
-rw-r--r--negotiator/noop.c1
-rw-r--r--notes-merge.c3
-rw-r--r--notes-utils.c3
-rw-r--r--notes.c2
-rw-r--r--object-file-convert.c277
-rw-r--r--object-file-convert.h24
-rw-r--r--object-file.c220
-rw-r--r--object-name.c64
-rw-r--r--object-name.h3
-rw-r--r--object-store-ll.h7
-rw-r--r--object.c19
-rw-r--r--object.h19
-rw-r--r--oid-array.c12
-rw-r--r--oidset.c10
-rw-r--r--oidset.h6
-rw-r--r--oss-fuzz/.gitignore2
-rw-r--r--oss-fuzz/dummy-cmd-main.c14
-rw-r--r--oss-fuzz/fuzz-config.c33
-rw-r--r--oss-fuzz/fuzz-date.c49
-rw-r--r--pack-bitmap-write.c5
-rw-r--r--pack-bitmap.c7
-rw-r--r--pack-check.c1
-rw-r--r--pack-write.c1
-rw-r--r--packfile.c4
-rw-r--r--packfile.h2
-rw-r--r--parse-options.c154
-rw-r--r--patch-ids.c1
-rw-r--r--path.c21
-rw-r--r--path.h9
-rw-r--r--perl/FromCPAN/Error.pm2
-rw-r--r--perl/Git/SVN.pm2
-rw-r--r--pkt-line.c37
-rw-r--r--pkt-line.h5
-rw-r--r--po/bg.po391
-rw-r--r--po/ca.po466
-rw-r--r--po/de.po392
-rw-r--r--po/fr.po427
-rw-r--r--po/id.po485
-rw-r--r--po/sv.po358
-rw-r--r--po/tr.po382
-rw-r--r--po/uk.po429
-rw-r--r--po/zh_CN.po538
-rw-r--r--po/zh_TW.po531
-rw-r--r--pretty.c65
-rw-r--r--pretty.h13
-rw-r--r--protocol-caps.c1
-rw-r--r--protocol.h2
-rw-r--r--reachable.c51
-rw-r--r--read-cache-ll.h12
-rw-r--r--read-cache.c37
-rw-r--r--rebase-interactive.c10
-rw-r--r--ref-filter.c45
-rw-r--r--ref-filter.h8
-rw-r--r--reflog-walk.c4
-rw-r--r--reflog-walk.h4
-rw-r--r--reflog.c3
-rw-r--r--refs.c267
-rw-r--r--refs.h64
-rw-r--r--refs/debug.c23
-rw-r--r--refs/files-backend.c271
-rw-r--r--refs/iterator.c69
-rw-r--r--refs/packed-backend.c55
-rw-r--r--refs/ref-cache.c3
-rw-r--r--refs/refs-internal.h42
-rw-r--r--refs/reftable-backend.c2242
-rw-r--r--reftable/basics.c22
-rw-r--r--reftable/basics.h24
-rw-r--r--reftable/basics_test.c55
-rw-r--r--reftable/block.c329
-rw-r--r--reftable/block.h51
-rw-r--r--reftable/block_test.c16
-rw-r--r--reftable/blocksource.c41
-rw-r--r--reftable/dump.c2
-rw-r--r--reftable/error.c4
-rw-r--r--reftable/generic.c1
-rw-r--r--reftable/iter.c10
-rw-r--r--reftable/iter.h12
-rw-r--r--reftable/merged.c220
-rw-r--r--reftable/merged.h11
-rw-r--r--reftable/merged_test.c86
-rw-r--r--reftable/pq.c37
-rw-r--r--reftable/pq.h16
-rw-r--r--reftable/pq_test.c41
-rw-r--r--reftable/publicbasics.c3
-rw-r--r--reftable/reader.c246
-rw-r--r--reftable/readwrite_test.c247
-rw-r--r--reftable/record.c358
-rw-r--r--reftable/record.h49
-rw-r--r--reftable/record_test.c80
-rw-r--r--reftable/refname.c57
-rw-r--r--reftable/refname_test.c3
-rw-r--r--reftable/reftable-error.h5
-rw-r--r--reftable/reftable-merged.h2
-rw-r--r--reftable/reftable-record.h18
-rw-r--r--reftable/reftable-writer.h4
-rw-r--r--reftable/stack.c892
-rw-r--r--reftable/stack.h11
-rw-r--r--reftable/stack_test.c293
-rw-r--r--reftable/system.h2
-rw-r--r--reftable/test_framework.c6
-rw-r--r--reftable/test_framework.h60
-rw-r--r--reftable/tree.c4
-rw-r--r--reftable/tree_test.c2
-rw-r--r--reftable/writer.c92
-rw-r--r--reftable/writer.h1
-rw-r--r--remote-curl.c36
-rw-r--r--remote.c34
-rw-r--r--remote.h3
-rw-r--r--repo-settings.c2
-rw-r--r--repository.c22
-rw-r--r--repository.h15
-rw-r--r--rerere.c5
-rw-r--r--reset.c6
-rw-r--r--revision.c150
-rw-r--r--revision.h1
-rw-r--r--run-command.c2
-rw-r--r--send-pack.c2
-rw-r--r--sequencer.c331
-rw-r--r--sequencer.h5
-rw-r--r--serve.c14
-rw-r--r--setup.c256
-rw-r--r--setup.h15
-rw-r--r--sh-i18n--envsubst.c4
-rw-r--r--sha1dc/sha1.c2
-rw-r--r--shallow.c22
-rw-r--r--shared.mak8
-rw-r--r--shell.c1
-rw-r--r--sideband.c9
-rw-r--r--sparse-index.c4
-rw-r--r--statinfo.c27
-rw-r--r--statinfo.h8
-rw-r--r--strbuf.c67
-rw-r--r--strbuf.h14
-rw-r--r--strvec.h8
-rw-r--r--submodule-config.c146
-rw-r--r--submodule-config.h5
-rw-r--r--submodule.c31
-rw-r--r--t/Makefile24
-rw-r--r--t/README34
-rw-r--r--t/annotate-tests.sh2
-rw-r--r--t/chainlint/blank-line-before-esac.expect8
-rw-r--r--t/chainlint/blank-line.expect4
-rw-r--r--t/chainlint/block.expect4
-rw-r--r--t/chainlint/chain-break-background.expect4
-rw-r--r--t/chainlint/chain-break-return-exit.expect14
-rw-r--r--t/chainlint/chain-break-status.expect4
-rw-r--r--t/chainlint/chained-subshell.expect4
-rw-r--r--t/chainlint/command-substitution-subsubshell.expect2
-rw-r--r--t/chainlint/dqstring-line-splice.expect6
-rw-r--r--t/chainlint/dqstring-no-interpolate.expect5
-rw-r--r--t/chainlint/empty-here-doc.expect4
-rw-r--r--t/chainlint/exclamation.expect2
-rw-r--r--t/chainlint/for-loop-abbreviated.expect2
-rw-r--r--t/chainlint/for-loop.expect1
-rw-r--r--t/chainlint/function.expect4
-rw-r--r--t/chainlint/here-doc.expect4
-rw-r--r--t/chainlint/loop-detect-status.expect20
-rw-r--r--t/chainlint/nested-cuddled-subshell.expect6
-rw-r--r--t/chainlint/nested-loop-detect-failure.expect24
-rw-r--r--t/chainlint/nested-subshell.expect1
-rw-r--r--t/chainlint/pipe.expect2
-rw-r--r--t/chainlint/subshell-here-doc.expect4
-rw-r--r--t/chainlint/subshell-one-liner.expect5
-rw-r--r--t/chainlint/t7900-subtree.expect1
-rw-r--r--t/chainlint/token-pasting.expect14
-rw-r--r--t/chainlint/while-loop.expect1
-rwxr-xr-xt/check-non-portable-shell.pl2
-rw-r--r--t/helper/test-bundle-uri.c2
-rw-r--r--t/helper/test-ctype.c70
-rw-r--r--t/helper/test-date.c2
-rw-r--r--t/helper/test-delete-gpgsig.c62
-rw-r--r--t/helper/test-fast-rebase.c241
-rw-r--r--t/helper/test-pkt-line.c60
-rw-r--r--t/helper/test-prio-queue.c51
-rw-r--r--t/helper/test-reach.c13
-rw-r--r--t/helper/test-ref-store.c38
-rw-r--r--t/helper/test-regex.c2
-rw-r--r--t/helper/test-repository.c2
-rw-r--r--t/helper/test-simple-ipc.c1
-rw-r--r--t/helper/test-submodule.c53
-rw-r--r--t/helper/test-tool.c4
-rw-r--r--t/helper/test-tool.h4
-rw-r--r--t/helper/test-trace2.c55
-rw-r--r--t/lib-credential.sh1
-rw-r--r--t/lib-cvs.sh4
-rw-r--r--t/lib-gpg.sh2
-rw-r--r--t/lib-parallel-checkout.sh2
-rw-r--r--t/oid-info/hash-info12
-rw-r--r--t/perf/perf-lib.sh2
-rwxr-xr-xt/perf/repos/inflate-repo.sh2
-rwxr-xr-xt/t0001-init.sh70
-rwxr-xr-xt/t0002-gitfile.sh2
-rwxr-xr-xt/t0003-attributes.sh76
-rwxr-xr-xt/t0006-date.sh9
-rwxr-xr-xt/t0009-prio-queue.sh66
-rwxr-xr-xt/t0010-racy-git.sh31
-rwxr-xr-xt/t0011-hashmap.sh2
-rwxr-xr-xt/t0018-advice.sh1
-rwxr-xr-xt/t0024-crlf-archive.sh13
-rwxr-xr-xt/t0028-working-tree-encoding.sh4
-rwxr-xr-xt/t0030-stripspace.sh15
-rwxr-xr-xt/t0035-safe-bare-repository.sh34
-rwxr-xr-xt/t0040-parse-options.sh24
-rwxr-xr-xt/t0070-fundamental.sh62
-rwxr-xr-xt/t0080-unit-test-output.sh1
-rwxr-xr-xt/t0091-bugreport.sh4
-rwxr-xr-xt/t0204-gettext-reencode-sanity.sh2
-rwxr-xr-xt/t0210-trace2-normal.sh20
-rwxr-xr-xt/t0211-trace2-perf.sh252
-rwxr-xr-xt/t0212-trace2-event.sh40
-rwxr-xr-xt/t0301-credential-cache.sh8
-rwxr-xr-xt/t0303-credential-external.sh26
-rwxr-xr-xt/t0410-partial-clone.sh19
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh4
-rwxr-xr-xt/t0600-reffiles-backend.sh475
-rwxr-xr-xt/t0601-reffiles-pack-refs.sh (renamed from t/t3210-pack-refs.sh)86
-rwxr-xr-xt/t0610-reftable-basics.sh1032
-rwxr-xr-xt/t0611-reftable-httpd.sh26
-rwxr-xr-xt/t1006-cat-file.sh417
-rwxr-xr-xt/t1007-hash-object.sh6
-rwxr-xr-xt/t1016-compatObjectFormat.sh281
-rwxr-xr-xt/t1016/gpg2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh10
-rwxr-xr-xt/t1300-config.sh214
-rwxr-xr-xt/t1301-shared-repo.sh31
-rwxr-xr-xt/t1302-repo-version.sh23
-rwxr-xr-xt/t1400-update-ref.sh129
-rwxr-xr-xt/t1401-symbolic-ref.sh9
-rwxr-xr-xt/t1403-show-ref.sh38
-rwxr-xr-xt/t1404-update-ref-errors.sh274
-rwxr-xr-xt/t1405-main-ref-store.sh24
-rwxr-xr-xt/t1406-submodule-ref-store.sh8
-rwxr-xr-xt/t1407-worktree-ref-store.sh37
-rwxr-xr-xt/t1409-avoid-packing-refs.sh6
-rwxr-xr-xt/t1410-reflog.sh154
-rwxr-xr-xt/t1414-reflog-walk.sh11
-rwxr-xr-xt/t1415-worktree-refs.sh11
-rwxr-xr-xt/t1417-reflog-updateref.sh10
-rwxr-xr-xt/t1419-exclude-refs.sh6
-rwxr-xr-xt/t1500-rev-parse.sh17
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh11
-rwxr-xr-xt/t1503-rev-parse-verify.sh5
-rwxr-xr-xt/t1509/prepare-chroot.sh2
-rwxr-xr-xt/t2011-checkout-invalid-head.sh17
-rwxr-xr-xt/t2016-checkout-patch.sh46
-rwxr-xr-xt/t2017-checkout-orphan.sh2
-rwxr-xr-xt/t2020-checkout-detach.sh17
-rwxr-xr-xt/t2024-checkout-dwim.sh2
-rwxr-xr-xt/t2060-switch.sh2
-rwxr-xr-xt/t2070-restore.sh1
-rwxr-xr-xt/t2071-restore-patch.sh47
-rwxr-xr-xt/t2072-restore-pathspec-file.sh1
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh30
-rwxr-xr-xt/t2200-add-update.sh10
-rwxr-xr-xt/t2400-worktree-add.sh38
-rwxr-xr-xt/t3200-branch.sh160
-rwxr-xr-xt/t3202-show-branch.sh49
-rwxr-xr-xt/t3310-notes-merge-manual-resolve.sh6
-rwxr-xr-xt/t3321-notes-stripspace.sh8
-rwxr-xr-xt/t3400-rebase.sh10
-rwxr-xr-xt/t3404-rebase-interactive.sh12
-rwxr-xr-xt/t3407-rebase-abort.sh17
-rwxr-xr-xt/t3420-rebase-autostash.sh10
-rwxr-xr-xt/t3424-rebase-empty.sh55
-rwxr-xr-xt/t3428-rebase-signoff.sh67
-rwxr-xr-xt/t3438-rebase-broken-files.sh9
-rwxr-xr-xt/t3501-revert-cherry-pick.sh15
-rwxr-xr-xt/t3505-cherry-pick-empty.sh51
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh2
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh32
-rwxr-xr-xt/t3650-replay-basics.sh198
-rwxr-xr-xt/t3700-add.sh47
-rwxr-xr-xt/t3701-add-interactive.sh22
-rwxr-xr-xt/t3903-stash.sh64
-rwxr-xr-xt/t3904-stash-patch.sh6
-rwxr-xr-xt/t3920-crlf-messages.sh4
-rwxr-xr-xt/t4001-diff-rename.sh24
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4011-diff-symlink.sh4
-rwxr-xr-xt/t4013-diff-various.sh88
-rw-r--r--t/t4018/csharp-exclude-assignments20
-rw-r--r--t/t4018/csharp-exclude-control-statements34
-rw-r--r--t/t4018/csharp-exclude-exceptions29
-rw-r--r--t/t4018/csharp-exclude-generic-method-calls12
-rw-r--r--t/t4018/csharp-exclude-init-dispose22
-rw-r--r--t/t4018/csharp-exclude-iterations26
-rw-r--r--t/t4018/csharp-exclude-method-calls20
-rw-r--r--t/t4018/csharp-exclude-other18
-rw-r--r--t/t4018/csharp-method10
-rw-r--r--t/t4018/csharp-method-array10
-rw-r--r--t/t4018/csharp-method-explicit12
-rw-r--r--t/t4018/csharp-method-generics11
-rw-r--r--t/t4018/csharp-method-generics-alternate-spaces11
-rw-r--r--t/t4018/csharp-method-modifiers13
-rw-r--r--t/t4018/csharp-method-multiline10
-rw-r--r--t/t4018/csharp-method-params10
-rw-r--r--t/t4018/csharp-method-special-chars11
-rw-r--r--t/t4018/csharp-method-with-spacing10
-rw-r--r--t/t4018/csharp-property11
-rw-r--r--t/t4018/csharp-property-braces-same-line10
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4042-diff-textconv-caching.sh22
-rwxr-xr-xt/t4053-diff-no-index.sh12
-rwxr-xr-xt/t4126-apply-empty.sh24
-rwxr-xr-xt/t4129-apply-samemode.sh33
-rwxr-xr-xt/t4150-am.sh8
-rwxr-xr-xt/t4201-shortlog.sh32
-rwxr-xr-xt/t4202-log.sh17
-rwxr-xr-xt/t4205-log-pretty-formats.sh40
-rwxr-xr-xt/t4210-log-i18n.sh4
-rwxr-xr-xt/t4254-am-corrupt.sh2
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh55
-rwxr-xr-xt/t5000-tar-tree.sh10
-rwxr-xr-xt/t5003-archive-zip.sh34
-rwxr-xr-xt/t5100-mailinfo.sh24
-rw-r--r--t/t5100/comment.expect2
-rw-r--r--t/t5100/comment.in2
-rwxr-xr-xt/t5300-pack-object.sh41
-rwxr-xr-xt/t5312-prune-corruption.sh26
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh2
-rwxr-xr-xt/t5318-commit-graph.sh8
-rwxr-xr-xt/t5332-multi-pack-reuse.sh86
-rwxr-xr-xt/t5401-update-hooks.sh8
-rwxr-xr-xt/t5510-fetch.sh4
-rwxr-xr-xt/t5514-fetch-multiple.sh161
-rwxr-xr-xt/t5526-fetch-submodules.sh2
-rwxr-xr-xt/t5534-push-signed.sh2
-rwxr-xr-xt/t5541-http-push-smart.sh18
-rwxr-xr-xt/t5550-http-fetch-dumb.sh4
-rwxr-xr-xt/t5551-http-fetch-smart.sh22
-rwxr-xr-xt/t5555-http-smart-common.sh1
-rwxr-xr-xt/t5558-clone-bundle-uri.sh18
-rwxr-xr-xt/t5574-fetch-output.sh89
-rwxr-xr-xt/t5601-clone.sh29
-rwxr-xr-xt/t5606-clone-options.sh8
-rwxr-xr-xt/t5701-git-serve.sh24
-rwxr-xr-xt/t5702-protocol-v2.sh19
-rwxr-xr-xt/t5801/git-remote-testgit9
-rwxr-xr-xt/t6005-rev-list-count.sh18
-rwxr-xr-xt/t6009-rev-list-parent.sh11
-rwxr-xr-xt/t6018-rev-list-glob.sh6
-rwxr-xr-xt/t6021-rev-list-exclude-hidden.sh4
-rwxr-xr-xt/t6022-rev-list-missing.sh81
-rwxr-xr-xt/t6030-bisect-porcelain.sh10
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh2
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh3
-rwxr-xr-xt/t6135-pathspec-with-attrs.sh27
-rwxr-xr-xt/t6300-for-each-ref.sh64
-rwxr-xr-xt/t6301-for-each-ref-errors.sh13
-rwxr-xr-xt/t6302-for-each-ref-filter.sh31
-rwxr-xr-xt/t6403-merge-file.sh124
-rwxr-xr-xt/t6406-merge-attr.sh16
-rwxr-xr-xt/t6413-merge-crlf.sh4
-rwxr-xr-xt/t6418-merge-text-auto.sh1
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh45
-rwxr-xr-xt/t6437-submodule-merge.sh14
-rwxr-xr-xt/t6500-gc.sh30
-rwxr-xr-xt/t7003-filter-branch.sh5
-rwxr-xr-xt/t7004-tag.sh4
-rwxr-xr-xt/t7102-reset.sh8
-rwxr-xr-xt/t7105-reset-patch.sh38
-rwxr-xr-xt/t7106-reset-unborn-branch.sh2
-rwxr-xr-xt/t7201-co.sh66
-rwxr-xr-xt/t7300-clean.sh7
-rwxr-xr-xt/t7301-clean-interactive.sh490
-rwxr-xr-xt/t7400-submodule-basic.sh3
-rwxr-xr-xt/t7402-submodule-rebase.sh2
-rwxr-xr-xt/t7450-bad-git-dotfiles.sh26
-rwxr-xr-xt/t7501-commit-basic-functionality.sh84
-rwxr-xr-xt/t7502-commit-porcelain.sh23
-rwxr-xr-xt/t7507-commit-verbose.sh10
-rwxr-xr-xt/t7508-status.sh36
-rwxr-xr-xt/t7512-status-help.sh28
-rwxr-xr-xt/t7513-interpret-trailers.sh33
-rwxr-xr-xt/t7514-commit-patch.sh6
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh225
-rwxr-xr-xt/t7700-repack.sh4
-rwxr-xr-xt/t7704-repack-cruft.sh2
-rwxr-xr-xt/t7800-difftool.sh113
-rwxr-xr-xt/t7900-maintenance.sh45
-rwxr-xr-xt/t8010-cat-file-filters.sh2
-rwxr-xr-xt/t8013-blame-ignore-revs.sh28
-rwxr-xr-xt/t9002-column.sh12
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh2
-rwxr-xr-xt/t9117-git-svn-init-clone.sh16
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh2
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh56
-rwxr-xr-xt/t9210-scalar.sh9
-rwxr-xr-xt/t9300-fast-import.sh644
-rwxr-xr-xt/t9350-fast-export.sh14
-rwxr-xr-xt/t9400-git-cvsserver-server.sh35
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh5
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh29
-rwxr-xr-xt/t9801-git-p4-branch.sh2
-rwxr-xr-xt/t9802-git-p4-filetype.sh2
-rwxr-xr-xt/t9807-git-p4-submit.sh2
-rwxr-xr-xt/t9816-git-p4-locked.sh2
-rwxr-xr-xt/t9824-git-p4-git-lfs.sh4
-rwxr-xr-xt/t9902-completion.sh263
-rw-r--r--t/test-lib-functions.sh41
-rw-r--r--t/test-lib-github-workflow-markup.sh6
-rw-r--r--t/test-lib-junit.sh2
-rw-r--r--t/test-lib.sh25
-rw-r--r--t/unit-tests/t-ctype.c53
-rw-r--r--t/unit-tests/t-mem-pool.c31
-rw-r--r--t/unit-tests/t-prio-queue.c91
-rw-r--r--t/unit-tests/test-lib.c61
-rw-r--r--tempfile.c21
-rw-r--r--tempfile.h2
-rwxr-xr-xtemplates/hooks--pre-commit.sample2
-rw-r--r--tmp-objdir.c1
-rw-r--r--trace.c2
-rw-r--r--trace2.c138
-rw-r--r--trace2.h4
-rw-r--r--trace2/tr2_ctr.c1
-rw-r--r--trace2/tr2_sysenv.c2
-rw-r--r--trace2/tr2_tgt_normal.c1
-rw-r--r--trace2/tr2_tls.c1
-rw-r--r--trace2/tr2_tls.h1
-rw-r--r--trace2/tr2_tmr.c1
-rw-r--r--trailer.c325
-rw-r--r--trailer.h52
-rw-r--r--transport-helper.c47
-rw-r--r--transport.c4
-rw-r--r--tree-walk.c60
-rw-r--r--tree-walk.h7
-rw-r--r--tree.c5
-rw-r--r--upload-pack.c183
-rw-r--r--usage.c5
-rw-r--r--userdiff.c55
-rw-r--r--utf8.c2
-rw-r--r--utf8.h6
-rw-r--r--walker.c2
-rw-r--r--worktree.c62
-rw-r--r--worktree.h12
-rw-r--r--wrapper.c1
-rw-r--r--write-or-die.c27
-rw-r--r--wt-status.c79
-rw-r--r--wt-status.h7
-rw-r--r--xdiff-interface.c38
-rw-r--r--xdiff-interface.h1
813 files changed, 27049 insertions, 11915 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index b6280692d2..77346a4929 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,7 +1,7 @@
env:
CIRRUS_CLONE_DEPTH: 1
-freebsd_12_task:
+freebsd_task:
env:
GIT_PROVE_OPTS: "--timer --jobs 10"
GIT_TEST_OPTS: "--no-chain-lint --no-bin-wrappers"
@@ -9,7 +9,7 @@ freebsd_12_task:
DEFAULT_TEST_TARGET: prove
DEVELOPER: 1
freebsd_instance:
- image_family: freebsd-12-3
+ image_family: freebsd-13-2
memory: 2G
install_script:
pkg install -y gettext gmake perl5
diff --git a/.editorconfig b/.editorconfig
index f9d819623d..15d6cbeab1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,7 +4,7 @@ insert_final_newline = true
# The settings for C (*.c and *.h) files are mirrored in .clang-format. Keep
# them in sync.
-[*.{c,h,sh,perl,pl,pm,txt}]
+[{*.{c,h,sh,perl,pl,pm,txt},config.mak.*,Makefile}]
indent_style = tab
tab_width = 8
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 952c7c3a2a..37654cdfd7 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,4 +4,7 @@ a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
bug reports. Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/)
to conveniently send your Pull Requests commits to our mailing list.
+For a single-commit pull request, please *leave the pull request description
+empty*: your commit message itself should describe your changes.
+
Please read the "guidelines for contributing" linked above!
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index a58e2dc8ad..a241a63428 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -19,7 +19,7 @@ jobs:
check-whitespace:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index e5532d381b..53cf12fe04 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -38,7 +38,7 @@ jobs:
COVERITY_LANGUAGE: cxx
COVERITY_PLATFORM: overridden-below
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: install minimal Git for Windows SDK
if: contains(matrix.os, 'windows')
uses: git-for-windows/setup-git-for-windows-sdk@v1
@@ -98,7 +98,7 @@ jobs:
# A cache miss will add ~30s to create, but a cache hit will save minutes.
- name: restore the Coverity Build Tool
id: cache
- uses: actions/cache/restore@v3
+ uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}/cov-analysis
key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
@@ -141,7 +141,7 @@ jobs:
esac
- name: cache the Coverity Build Tool
if: steps.cache.outputs.cache-hit != 'true'
- uses: actions/cache/save@v3
+ uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}/cov-analysis
key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
index 6c3849658a..e2c3dbdcb5 100644
--- a/.github/workflows/l10n.yml
+++ b/.github/workflows/l10n.yml
@@ -63,9 +63,10 @@ jobs:
origin \
${{ github.ref }} \
$args
- - uses: actions/setup-go@v2
+ - uses: actions/setup-go@v5
with:
go-version: '>=1.16'
+ cache: false
- name: Install git-po-helper
run: go install github.com/git-l10n/git-po-helper@main
- name: Install other dependencies
@@ -91,14 +92,13 @@ jobs:
cat git-po-helper.out
exit $exit_code
- name: Create comment in pull request for report
- uses: mshick/add-pr-comment@v1
+ uses: mshick/add-pr-comment@v2
if: >-
always() &&
github.event_name == 'pull_request_target' &&
env.COMMENT_BODY != ''
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- repo-token-user-login: 'github-actions[bot]'
message: >
${{ steps.check-commits.outcome == 'failure' && 'Errors and warnings' || 'Warnings' }}
found by [git-po-helper](https://github.com/git-l10n/git-po-helper#readme) in workflow
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9fdbd54028..3428773b09 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -63,7 +63,7 @@ jobs:
echo "skip_concurrent=$skip_concurrent" >>$GITHUB_OUTPUT
- name: skip if the commit or tree was already tested
id: skip-if-redundant
- uses: actions/github-script@v6
+ uses: actions/github-script@v7
if: steps.check-ref.outputs.enabled == 'yes'
with:
github-token: ${{secrets.GITHUB_TOKEN}}
@@ -112,7 +112,7 @@ jobs:
group: windows-build-${{ github.ref }}
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: build
shell: bash
@@ -123,7 +123,7 @@ jobs:
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: windows-artifacts
path: artifacts
@@ -140,7 +140,7 @@ jobs:
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- name: download tracked files and build artifacts
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: windows-artifacts
path: ${{github.workspace}}
@@ -157,9 +157,9 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: failed-tests-windows
+ name: failed-tests-windows-${{ matrix.nr }}
path: ${{env.FAILED_TEST_ARTIFACTS}}
vs-build:
name: win+VS build
@@ -173,10 +173,10 @@ jobs:
group: vs-build-${{ github.ref }}
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: initialize vcpkg
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
repository: 'microsoft/vcpkg'
path: 'compat/vcbuild/vcpkg'
@@ -212,7 +212,7 @@ jobs:
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: vs-artifacts
path: artifacts
@@ -230,7 +230,7 @@ jobs:
steps:
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: download tracked files and build artifacts
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
name: vs-artifacts
path: ${{github.workspace}}
@@ -248,9 +248,9 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: failed-tests-windows
+ name: failed-tests-windows-vs-${{ matrix.nr }}
path: ${{env.FAILED_TEST_ARTIFACTS}}
regular:
name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
@@ -266,6 +266,9 @@ jobs:
- jobname: linux-sha256
cc: clang
pool: ubuntu-latest
+ - jobname: linux-reftable
+ cc: clang
+ pool: ubuntu-latest
- jobname: linux-gcc
cc: gcc
cc_package: gcc-8
@@ -277,6 +280,9 @@ jobs:
- jobname: osx-clang
cc: clang
pool: macos-13
+ - jobname: osx-reftable
+ cc: clang
+ pool: macos-13
- jobname: osx-gcc
cc: gcc
cc_package: gcc-13
@@ -287,6 +293,9 @@ jobs:
- jobname: linux-leaks
cc: gcc
pool: ubuntu-latest
+ - jobname: linux-reftable-leaks
+ cc: gcc
+ pool: ubuntu-latest
- jobname: linux-asan-ubsan
cc: clang
pool: ubuntu-latest
@@ -297,7 +306,7 @@ jobs:
runs_on_pool: ${{matrix.vector.pool}}
runs-on: ${{matrix.vector.pool}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: ci/install-dependencies.sh
- run: ci/run-build-and-tests.sh
- name: print test failures
@@ -305,10 +314,21 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
+ fuzz-smoke-test:
+ name: fuzz smoke test
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ env:
+ CC: clang
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - run: ci/install-dependencies.sh
+ - run: ci/run-build-and-minimal-fuzzers.sh
dockerized:
name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
needs: ci-config
@@ -331,9 +351,9 @@ jobs:
runs-on: ubuntu-latest
container: ${{matrix.vector.image}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
if: matrix.vector.jobname != 'linux32'
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
if: matrix.vector.jobname == 'linux32'
- run: ci/install-docker-dependencies.sh
- run: ci/run-build-and-tests.sh
@@ -342,13 +362,13 @@ jobs:
run: ci/print-test-failures.sh
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname != 'linux32'
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != '' && matrix.vector.jobname == 'linux32'
- uses: actions/upload-artifact@v1
+ uses: actions/upload-artifact@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -362,7 +382,7 @@ jobs:
group: static-analysis-${{ github.ref }}
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: ci/install-dependencies.sh
- run: ci/run-static-analysis.sh
- run: ci/check-directional-formatting.bash
@@ -385,7 +405,7 @@ jobs:
artifact: sparse-20.04
- name: Install the current `sparse` package
run: sudo dpkg -i sparse-20.04/sparse_*.deb
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install other dependencies
run: ci/install-dependencies.sh
- run: make sparse
@@ -400,6 +420,6 @@ jobs:
jobname: Documentation
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: ci/install-dependencies.sh
- run: ci/test-documentation.sh
diff --git a/.gitignore b/.gitignore
index 5e56e471b3..612c0f6a0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,6 +135,7 @@
/git-remote-ext
/git-repack
/git-replace
+/git-replay
/git-request-pull
/git-rerere
/git-reset
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cd98bcb18a..c0fa2fe90b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,7 +7,7 @@ workflow:
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_REF_PROTECTED == "true"
-test:
+test:linux:
image: $image
before_script:
- ./ci/install-docker-dependencies.sh
@@ -26,6 +26,9 @@ test:
- jobname: linux-sha256
image: ubuntu:latest
CC: clang
+ - jobname: linux-reftable
+ image: ubuntu:latest
+ CC: clang
- jobname: linux-gcc
image: ubuntu:20.04
CC: gcc
@@ -40,6 +43,9 @@ test:
- jobname: linux-leaks
image: ubuntu:latest
CC: gcc
+ - jobname: linux-reftable-leaks
+ image: ubuntu:latest
+ CC: gcc
- jobname: linux-asan-ubsan
image: ubuntu:latest
CC: clang
@@ -51,3 +57,48 @@ test:
paths:
- t/failed-test-artifacts
when: on_failure
+
+test:osx:
+ image: $image
+ tags:
+ - saas-macos-medium-m1
+ variables:
+ TEST_OUTPUT_DIRECTORY: "/Volumes/RAMDisk"
+ before_script:
+ # Create a 4GB RAM disk that we use to store test output on. This small hack
+ # significantly speeds up tests by more than a factor of 2 because the
+ # macOS runners use network-attached storage as disks, which is _really_
+ # slow with the many small writes that our tests do.
+ - sudo diskutil apfs create $(hdiutil attach -nomount ram://8192000) RAMDisk
+ - ./ci/install-dependencies.sh
+ script:
+ - ./ci/run-build-and-tests.sh
+ after_script:
+ - |
+ if test "$CI_JOB_STATUS" != 'success'
+ then
+ ./ci/print-test-failures.sh
+ mv "$TEST_OUTPUT_DIRECTORY"/failed-test-artifacts t/
+ fi
+ parallel:
+ matrix:
+ - jobname: osx-clang
+ image: macos-13-xcode-14
+ CC: clang
+ - jobname: osx-reftable
+ image: macos-13-xcode-14
+ CC: clang
+ artifacts:
+ paths:
+ - t/failed-test-artifacts
+ when: on_failure
+
+static-analysis:
+ image: ubuntu:22.04
+ variables:
+ jobname: StaticAnalysis
+ before_script:
+ - ./ci/install-docker-dependencies.sh
+ script:
+ - ./ci/run-static-analysis.sh
+ - ./ci/check-directional-formatting.bash
diff --git a/.mailmap b/.mailmap
index 82129be449..18128a1250 100644
--- a/.mailmap
+++ b/.mailmap
@@ -152,6 +152,7 @@ Lars Doelle <lars.doelle@on-line ! de>
Lars Doelle <lars.doelle@on-line.de>
Lars Noschinski <lars@public.noschinski.de> <lars.noschinski@rwth-aachen.de>
Li Hong <leehong@pku.edu.cn>
+Linus Arver <linus@ucla.edu> <linusa@google.com>
Linus Torvalds <torvalds@linux-foundation.org> <torvalds@evo.osdl.org>
Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 0215b1fd4c..e58917c50a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
-Community Impact Guidelines were inspired by
+Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 39b9b7260f..1d92b2da03 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -188,6 +188,22 @@ For shell scripts specifically (not exhaustive):
hopefully nobody starts using "local" before they are reimplemented
in C ;-)
+ - Some versions of shell do not understand "export variable=value",
+ so we write "variable=value" and then "export variable" on two
+ separate lines.
+
+ - Some versions of dash have broken variable assignment when prefixed
+ with "local", "export", and "readonly", in that the value to be
+ assigned goes through field splitting at $IFS unless quoted.
+
+ (incorrect)
+ local variable=$value
+ local variable=$(command args)
+
+ (correct)
+ local variable="$value"
+ local variable="$(command args)"
+
- 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.
@@ -446,12 +462,41 @@ For C programs:
detail.
- The first #include in C files, except in platform specific compat/
- implementations and sha1dc/, must be either "git-compat-util.h" or
- one of the approved headers that includes it first for you. (The
- approved headers currently include "builtin.h",
- "t/helper/test-tool.h", "xdiff/xinclude.h", or
- "reftable/system.h"). You do not have to include more than one of
- these.
+ implementations and sha1dc/, must be <git-compat-util.h>. This
+ header file insulates other header files and source files from
+ platform differences, like which system header files must be
+ included in what order, and what C preprocessor feature macros must
+ be defined to trigger certain features we expect out of the system.
+ A collorary to this is that C files should not directly include
+ system header files themselves.
+
+ There are some exceptions, because certain group of files that
+ implement an API all have to include the same header file that
+ defines the API and it is convenient to include <git-compat-util.h>
+ there. Namely:
+
+ - the implementation of the built-in commands in the "builtin/"
+ directory that include "builtin.h" for the cmd_foo() prototype
+ definition,
+
+ - the test helper programs in the "t/helper/" directory that include
+ "t/helper/test-tool.h" for the cmd__foo() prototype definition,
+
+ - the xdiff implementation in the "xdiff/" directory that includes
+ "xdiff/xinclude.h" for the xdiff machinery internals,
+
+ - the unit test programs in "t/unit-tests/" directory that include
+ "t/unit-tests/test-lib.h" that gives them the unit-tests
+ framework, and
+
+ - the source files that implement reftable in the "reftable/"
+ directory that include "reftable/system.h" for the reftable
+ internals,
+
+ are allowed to assume that they do not have to include
+ <git-compat-util.h> themselves, as it is included as the first
+ '#include' in these header files. These headers must be the first
+ header file to be "#include"d in them, though.
- A C file must directly include the header files that declare the
functions and the types it uses, except for the functions and types
@@ -518,7 +563,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.
@@ -578,7 +623,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.
@@ -612,15 +657,15 @@ Writing Documentation:
- Prefer succinctness and matter-of-factly describing functionality
in the abstract. E.g.
- --short:: Emit output in the short-format.
+ `--short`:: Emit output in the short-format.
and avoid something like these overly verbose alternatives:
- --short:: Use this to emit output in the short-format.
- --short:: You can use this to get output in the short-format.
- --short:: A user who prefers shorter output could....
- --short:: Should a person and/or program want shorter output, he
- she/they/it can...
+ `--short`:: Use this to emit output in the short-format.
+ `--short`:: You can use this to get output in the short-format.
+ `--short`:: A user who prefers shorter output could....
+ `--short`:: Should a person and/or program want shorter output, he
+ she/they/it can...
This practice often eliminates the need to involve human actors in
your description, but it is a good practice regardless of the
@@ -630,12 +675,12 @@ Writing Documentation:
addressing the hypothetical user, and possibly "we" when
discussing how the program might react to the user. E.g.
- You can use this option instead of --xyz, but we might remove
+ You can use this option instead of `--xyz`, but we might remove
support for it in future versions.
while keeping in mind that you can probably be less verbose, e.g.
- Use this instead of --xyz. This option might be removed in future
+ Use this instead of `--xyz`. This option might be removed in future
versions.
- If you still need to refer to an example person that is
@@ -653,63 +698,118 @@ Writing Documentation:
The same general rule as for code applies -- imitate the existing
conventions.
- A few commented examples follow to provide reference when writing or
- modifying command usage strings and synopsis sections in the manual
- pages:
- Placeholders are spelled in lowercase and enclosed in angle brackets:
- <file>
- --sort=<key>
- --abbrev[=<n>]
+Markup:
+
+ Literal parts (e.g. use of command-line options, command names,
+ branch names, URLs, pathnames (files and directories), configuration and
+ environment variables) must be typeset as verbatim (i.e. wrapped with
+ backticks):
+ `--pretty=oneline`
+ `git rev-list`
+ `remote.pushDefault`
+ `http://git.example.com`
+ `.git/config`
+ `GIT_DIR`
+ `HEAD`
+ `umask`(2)
+
+ An environment variable must be prefixed with "$" only when referring to its
+ value and not when referring to the variable itself, in this case there is
+ nothing to add except the backticks:
+ `GIT_DIR` is specified
+ `$GIT_DIR/hooks/pre-receive`
+
+ Word phrases enclosed in `backtick characters` are rendered literally
+ and will not be further expanded. The use of `backticks` to achieve the
+ previous rule means that literal examples should not use AsciiDoc
+ escapes.
+ Correct:
+ `--pretty=oneline`
+ Incorrect:
+ `\--pretty=oneline`
+
+ Placeholders are spelled in lowercase and enclosed in
+ angle brackets surrounded by underscores:
+ _<file>_
+ _<commit>_
If a placeholder has multiple words, they are separated by dashes:
- <new-branch-name>
- --template=<template-directory>
+ _<new-branch-name>_
+ _<template-directory>_
+
+ A placeholder is not enclosed in backticks, as it is not a literal.
+
+ When needed, use a distinctive identifier for placeholders, usually
+ made of a qualification and a type:
+ _<git-dir>_
+ _<key-id>_
+
+ When literal and placeholders are mixed, each markup is applied for
+ each sub-entity. If they are stuck, a special markup, called
+ unconstrained formatting is required.
+ Unconstrained formating for placeholders is __<like-this>__
+ Unconstrained formatting for literal formatting is ++like this++
+ `--jobs` _<n>_
+ ++--sort=++__<key>__
+ __<directory>__++/.git++
+ ++remote.++__<name>__++.mirror++
+
+ caveat: ++ unconstrained format is not verbatim and may expand
+ content. Use Asciidoc escapes inside them.
+
+Synopsis Syntax
+
+ Syntax grammar is formatted neither as literal nor as placeholder.
+
+ A few commented examples follow to provide reference when writing or
+ modifying command usage strings and synopsis sections in the manual
+ pages:
Possibility of multiple occurrences is indicated by three dots:
- <file>...
+ _<file>_...
(One or more of <file>.)
Optional parts are enclosed in square brackets:
- [<file>...]
+ [_<file>_...]
(Zero or more of <file>.)
- --exec-path[=<path>]
+ ++--exec-path++[++=++__<path>__]
(Option with an optional argument. Note that the "=" is inside the
brackets.)
- [<patch>...]
+ [_<patch>_...]
(Zero or more of <patch>. Note that the dots are inside, not
outside the brackets.)
Multiple alternatives are indicated with vertical bars:
- [-q | --quiet]
- [--utf8 | --no-utf8]
+ [`-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]
+ 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)]
+ Do: ++--track++[++=++(`direct`|`inherit`)]`
+ Don't: ++--track++[++=++(`direct` | `inherit`)]
Parentheses are used for grouping:
- [(<rev> | <range>)...]
+ [(_<rev>_ | _<range>_)...]
(Any number of either <rev> or <range>. Parens are needed to make
it clear that "..." pertains to both <rev> and <range>.)
- [(-p <parent>)...]
+ [(`-p` _<parent>_)...]
(Any number of option -p, each with one <parent> argument.)
- git remote set-head <name> (-a | -d | <branch>)
+ `git remote set-head` _<name>_ (`-a` | `-d` | _<branch>_)
(One and only one of "-a", "-d" or "<branch>" _must_ (no square
brackets) be provided.)
And a somewhat more contrived example:
- --diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]
+ `--diff-filter=[(A|C|D|M|R|T|U|X|B)...[*]]`
Here "=" is outside the brackets, because "--diff-filter=" is a
valid usage. "*" has its own pair of brackets, because it can
(optionally) be specified only when one or more of the letters is
@@ -720,37 +820,6 @@ Writing Documentation:
the user would type into a shell and use 'Git' (uppercase first letter)
when talking about the version control system and its properties.
- A few commented examples follow to provide reference when writing or
- modifying paragraphs or option/command explanations that contain options
- or commands:
-
- Literal examples (e.g. use of command-line options, command names,
- branch names, URLs, pathnames (files and directories), configuration and
- environment variables) must be typeset in monospace (i.e. wrapped with
- backticks):
- `--pretty=oneline`
- `git rev-list`
- `remote.pushDefault`
- `http://git.example.com`
- `.git/config`
- `GIT_DIR`
- `HEAD`
-
- An environment variable must be prefixed with "$" only when referring to its
- value and not when referring to the variable itself, in this case there is
- nothing to add except the backticks:
- `GIT_DIR` is specified
- `$GIT_DIR/hooks/pre-receive`
-
- Word phrases enclosed in `backtick characters` are rendered literally
- and will not be further expanded. The use of `backticks` to achieve the
- previous rule means that literal examples should not use AsciiDoc
- escapes.
- Correct:
- `--pretty=oneline`
- Incorrect:
- `\--pretty=oneline`
-
If some place in the documentation needs to typeset a command usage
example with inline substitutions, it is fine to use +monospaced and
inline substituted text+ instead of `monospaced literal text`, and with
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 7cfed60c2e..f06563e981 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -35,8 +35,9 @@ announcements, design discussions, and more take place. Those interested in
contributing are welcome to post questions here. The Git list requires
plain-text-only emails and prefers inline and bottom-posting when replying to
mail; you will be CC'd in all replies to you. Optionally, you can subscribe to
-the list by sending an email to majordomo@vger.kernel.org with "subscribe git"
-in the body. The https://lore.kernel.org/git[archive] of this mailing list is
+the list by sending an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details).
+The https://lore.kernel.org/git[archive] of this mailing list is
available to view in a browser.
==== https://groups.google.com/forum/#!forum/git-mentoring[git-mentoring@googlegroups.com]
@@ -833,7 +834,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
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index c68cdb11b9..dec8afe5b1 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -210,13 +210,14 @@ We'll also need to include the `config.h` header:
...
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
/*
* For now, we don't have any custom configuration, so fall back to
* the default config.
*/
- return git_default_config(var, value, cb);
+ return git_default_config(var, value, ctx, cb);
}
----
@@ -389,10 +390,11 @@ modifying `rev_info.grep_filter`, which is a `struct grep_opt`.
First some setup. Add `grep_config()` to `git_walken_config()`:
----
-static int git_walken_config(const char *var, const char *value, void *cb)
+static int git_walken_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
{
- grep_config(var, value, cb);
- return git_default_config(var, value, cb);
+ grep_config(var, value, ctx, cb);
+ return git_default_config(var, value, ctx, cb);
}
----
@@ -523,7 +525,7 @@ about each one.
We can base our work on an example. `git pack-objects` prepares all kinds of
objects for packing into a bitmap or packfile. The work we are interested in
-resides in `builtins/pack-objects.c:get_object_list()`; examination of that
+resides in `builtin/pack-objects.c:get_object_list()`; examination of that
function shows that the all-object walk is being performed by
`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two
functions reside in `list-objects.c`; examining the source shows that, despite
@@ -732,8 +734,8 @@ walk we've just performed:
} else {
trace_printf(
_("Filtered object walk with filterspec 'tree:1'.\n"));
- CALLOC_ARRAY(rev->filter, 1);
- parse_list_objects_filter(rev->filter, "tree:1");
+
+ parse_list_objects_filter(&rev->filter, "tree:1");
}
traverse_commit_list(rev, walken_show_commit,
walken_show_object, NULL);
@@ -752,10 +754,12 @@ points to the same tree object as its grandparent.)
=== Counting Omitted Objects
We also have the capability to enumerate all objects which were omitted by a
-filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking
-`traverse_commit_list_filtered()` to populate the `omitted` list means that our
-object walk does not perform any better than an unfiltered object walk; all
-reachable objects are walked in order to populate the list.
+filter, like with `git log --filter=<spec> --filter-print-omitted`. To do this,
+change `traverse_commit_list()` to `traverse_commit_list_filtered()`, which is
+able to populate an `omitted` list. Asking for this list of filtered objects
+may cause performance degradations, however, because in this case, despite
+filtering objects, the possibly much larger set of all reachable objects must
+be processed in order to populate that list.
First, add the `struct oidset` and related items we will use to iterate it:
@@ -776,8 +780,9 @@ static void walken_object_walk(
...
----
-Modify the call to `traverse_commit_list_filtered()` to include your `omitted`
-object:
+Replace the call to `traverse_commit_list()` with
+`traverse_commit_list_filtered()` and pass a pointer to the `omitted` oidset
+defined and initialized above:
----
...
@@ -843,7 +848,7 @@ those lines without having to recompile.
With only that change, run again (but save yourself some scrollback):
----
-$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | head -n 10
----
Take a look at the top commit with `git show` and the object ID you printed; it
@@ -871,7 +876,7 @@ of the first handful:
----
$ make
-$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10
+$ GIT_TRACE=1 ./bin-wrappers/git walken 2>&1 | tail -n 10
----
The last commit object given should have the same OID as the one we saw at the
diff --git a/Documentation/RelNotes/1.6.2.txt b/Documentation/RelNotes/1.6.2.txt
index 980adfb315..166d73c60f 100644
--- a/Documentation/RelNotes/1.6.2.txt
+++ b/Documentation/RelNotes/1.6.2.txt
@@ -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
diff --git a/Documentation/RelNotes/1.6.3.txt b/Documentation/RelNotes/1.6.3.txt
index 4bcff945e0..bbf177fc3c 100644
--- a/Documentation/RelNotes/1.6.3.txt
+++ b/Documentation/RelNotes/1.6.3.txt
@@ -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
diff --git a/Documentation/RelNotes/1.6.4.txt b/Documentation/RelNotes/1.6.4.txt
index a2a34b43a7..0fccfb0bf0 100644
--- a/Documentation/RelNotes/1.6.4.txt
+++ b/Documentation/RelNotes/1.6.4.txt
@@ -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
diff --git a/Documentation/RelNotes/1.6.5.txt b/Documentation/RelNotes/1.6.5.txt
index 6c7f7da7eb..79cb1b2b6d 100644
--- a/Documentation/RelNotes/1.6.5.txt
+++ b/Documentation/RelNotes/1.6.5.txt
@@ -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
diff --git a/Documentation/RelNotes/1.6.6.txt b/Documentation/RelNotes/1.6.6.txt
index 3ed1e01433..88b86a827e 100644
--- a/Documentation/RelNotes/1.6.6.txt
+++ b/Documentation/RelNotes/1.6.6.txt
@@ -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
diff --git a/Documentation/RelNotes/2.43.1.txt b/Documentation/RelNotes/2.43.1.txt
new file mode 100644
index 0000000000..20e96f2dfa
--- /dev/null
+++ b/Documentation/RelNotes/2.43.1.txt
@@ -0,0 +1,82 @@
+Git 2.43.1 Release Notes
+========================
+
+There is nothing exciting to see here. Relative to Git 2.43, this
+release contains the fixes that have already been merged to the
+'master' branch of the development towards the next major release.
+
+Fixes since Git 2.43.0
+----------------------
+
+ * The way CI testing used "prove" could lead to running the test
+ suite twice needlessly, which has been corrected.
+
+ * Newer versions of Getopt::Long started giving warnings against our
+ (ab)use of it in "git send-email". Bump the minimum version
+ requirement for Perl to 5.8.1 (from September 2002) to allow
+ simplifying our implementation.
+
+ * Earlier we stopped relying on commit-graph that (still) records
+ information about commits that are lost from the object store,
+ which has negative performance implications. The default has been
+ flipped to disable this pessimization.
+
+ * Stale URLs have been updated to their current counterparts (or
+ archive.org) and HTTP links are replaced with working HTTPS links.
+
+ * trace2 streams used to record the URLs that potentially embed
+ authentication material, which has been corrected.
+
+ * The sample pre-commit hook that tries to catch introduction of new
+ paths that use potentially non-portable characters did not notice
+ an existing path getting renamed to such a problematic path, when
+ rename detection was enabled.
+
+ * The command line parser for the "log" family of commands was too
+ loose when parsing certain numbers, e.g., silently ignoring the
+ extra 'q' in "git log -n 1q" without complaining, which has been
+ tightened up.
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+ to interpret "--rev" as a rev, and "--path" as a path. This was
+ fixed for many programs like "reset" and "checkout".
+
+ * "git bisect reset" has been taught to clean up state files and refs
+ even when BISECT_START file is gone.
+
+ * Some codepaths did not correctly parse configuration variables
+ specified with valueless "true", which has been corrected.
+
+ * Code clean-up for sanity checking of command line options for "git
+ show-ref".
+
+ * The code to parse the From e-mail header has been updated to avoid
+ recursion.
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+ which has been corrected.
+
+ * Command line completion script (in contrib/) learned to work better
+ with the reftable backend.
+
+ * "git status" is taught to show both the branch being bisected and
+ being rebased when both are in effect at the same time.
+ cf. <xmqqil76kyov.fsf@gitster.g>
+
+ * "git archive --list extra garbage" silently ignored excess command
+ line parameters, which has been corrected.
+
+ * "git sparse-checkout set" added default patterns even when the
+ patterns are being fed from the standard input, which has been
+ corrected.
+
+ * Unlike other environment variables that took the usual
+ true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+ which has been corrected.
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+ --recurse-submodules" with commit graph enabled) made in-core
+ commit object in an inconsistent state by discarding the necessary
+ data from commit-graph too early, which has been corrected.
+
+Also contains various documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.2.txt b/Documentation/RelNotes/2.43.2.txt
new file mode 100644
index 0000000000..5895e23a54
--- /dev/null
+++ b/Documentation/RelNotes/2.43.2.txt
@@ -0,0 +1,37 @@
+Git 2.43.2 Release Notes
+========================
+
+Relative to Git 2.43.1, this release has two important fixes to allow
+"git imap-send" to be built with NO_CURL defined, and to restore the
+forced flushing behaviour when GIT_FLUSH=1 is set. It also contains
+other, unexciting, fixes that have already been merged to the 'master'
+branch of the development towards the next major release.
+
+Fixes since Git 2.43.1
+----------------------
+
+ * Update to a new feature recently added, "git show-ref --exists".
+
+ * Rename detection logic ignored the final line of a file if it is an
+ incomplete line.
+
+ * "git diff --no-rename A B" did not disable rename detection but did
+ not trigger an error from the command line parser.
+
+ * "git diff --no-index file1 file2" segfaulted while invoking the
+ external diff driver, which has been corrected.
+
+ * Rewrite //-comments to /* comments */ in files whose comments
+ prevalently use the latter.
+
+ * A failed "git tag -s" did not necessarily result in an error
+ depending on the crypto backend, which has been corrected.
+
+ * "git stash" sometimes was silent even when it failed due to
+ unwritable index file, which has been corrected.
+
+ * Recent conversion to allow more than 0/1 in GIT_FLUSH broke the
+ mechanism by flipping what yes/no means by mistake, which has been
+ corrected.
+
+Also contains documentation updates, code clean-ups and minor fixups.
diff --git a/Documentation/RelNotes/2.43.3.txt b/Documentation/RelNotes/2.43.3.txt
new file mode 100644
index 0000000000..924f20594f
--- /dev/null
+++ b/Documentation/RelNotes/2.43.3.txt
@@ -0,0 +1,12 @@
+Git 2.43.3 Release Notes
+========================
+
+Relative to Git 2.43.2, this release fixes one regression that
+manifests while running "git commit -v --trailer".
+
+Fixes since Git 2.43.2
+----------------------
+
+ * "git commit -v --trailer=..." was broken with recent update and
+ placed the trailer _after_ the divider line, which has been
+ corrected.
diff --git a/Documentation/RelNotes/2.44.0.txt b/Documentation/RelNotes/2.44.0.txt
index 6a2bd93c91..14f9ce8226 100644
--- a/Documentation/RelNotes/2.44.0.txt
+++ b/Documentation/RelNotes/2.44.0.txt
@@ -1,6 +1,13 @@
Git v2.44 Release Notes
=======================
+Backward Compatibility Notes
+
+ * "git checkout -B <branch>" used to allow switching to a branch that
+ is in use on another worktree, but this was by mistake. The users
+ need to use "--ignore-other-worktrees" option.
+
+
UI, Workflows & Features
* "git add" and "git stash" learned to support the ":(attr:...)"
@@ -9,6 +16,92 @@ UI, Workflows & Features
* "git rebase --autosquash" is now enabled for non-interactive rebase,
but it is still incompatible with the apply backend.
+ * Introduce "git replay", a tool meant on the server side without
+ working tree to recreate a history.
+
+ * "git merge-file" learned to take the "--diff-algorithm" option to
+ use algorithm different from the default "myers" diff.
+
+ * Command line completion (in contrib/) learned to complete path
+ arguments to the "add/set" subcommands of "git sparse-checkout"
+ better.
+
+ * "git checkout -B <branch> [<start-point>]" allowed a branch that is
+ in use in another worktree to be updated and checked out, which
+ might be a bit unexpected. The rule has been tightened, which is a
+ breaking change. "--ignore-other-worktrees" option is required to
+ unbreak you, if you are used to the current behaviour that "-B"
+ overrides the safety.
+
+ * The builtin_objectmode attribute is populated for each path
+ without adding anything in .gitattributes files, which would be
+ useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)"
+ to limit to executables.
+
+ * "git fetch" learned to pay attention to "fetch.all" configuration
+ variable, which pretends as if "--all" was passed from the command
+ line when no remote parameter was given.
+
+ * In addition to (rather cryptic) Security Identifiers, show username
+ and domain in the error message when we barf on mismatch between
+ the Git directory and the current user on Windows.
+
+ * The error message given when "git branch -d branch" fails due to
+ commits unique to the branch has been split into an error and a new
+ conditional advice message.
+
+ * When given an existing but unreadable file as a configuration file,
+ gitweb behaved as if the file did not exist at all, but now it
+ errors out. This is a change that may break backward compatibility.
+
+ * When $HOME/.gitconfig is missing but XDG config file is available, we
+ should write into the latter, not former. "git gc" and "git
+ maintenance" wrote into a wrong "global config" file, which have
+ been corrected.
+
+ * Define "special ref" as a very narrow set that consists of
+ FETCH_HEAD and MERGE_HEAD, and clarify everything else that used to
+ be classified as such are actually just pseudorefs.
+
+ * All conditional "advice" messages show how to turn them off, which
+ becomes repetitive. Setting advice.* configuration explicitly on
+ now omits the instruction part.
+
+ * The "disable repository discovery of a bare repository" check,
+ triggered by setting safe.bareRepository configuration variable to
+ 'explicit', has been loosened to exclude the ".git/" directory inside
+ a non-bare repository from the check. So you can do "cd .git &&
+ git cmd" to run a Git command that works on a bare repository without
+ explicitly specifying $GIT_DIR now.
+
+ * The completion script (in contrib/) learned more options that can
+ be used with "git log".
+
+ * The labels on conflict markers for the common ancestor, our version,
+ and the other version are available to custom 3-way merge driver
+ via %S, %X, and %Y placeholders.
+
+ * The write codepath for the reftable data learned to honor
+ core.fsync configuration.
+
+ * The "--fsck-objects" option of "git index-pack" now can take the
+ optional parameter to tweak severity of different fsck errors.
+
+ * The wincred credential backend has been taught to support oauth
+ refresh token the same way as credential-cache and
+ credential-libsecret backends.
+
+ * Command line completion support (in contrib/) has been
+ updated for "git bisect".
+
+ * "git branch" and friends learned to use the formatted text as
+ sorting key, not the underlying timestamp value, when the --sort
+ option is used with author or committer timestamp with a format
+ specifier (e.g., "--sort=creatordate:format:%H:%M:%S").
+
+ * The command line completion script (in contrib/) learned to
+ complete configuration variable names better.
+
Performance, Internal Implementation, Development Support etc.
@@ -21,13 +114,72 @@ Performance, Internal Implementation, Development Support etc.
in an unspecified order, to allow certain optimizations to take
advantage of.
+ * Simplify API implementation to delete references by eliminating
+ duplication.
+
+ * Subject approxidate() and show_date() machinery to OSS-Fuzz.
+
+ * A new helper to let us pretend that we called lstat() when we know
+ our cache_entry is up-to-date via fsmonitor.
+
+ * The optimization based on fsmonitor in the "diff --cached"
+ codepath is resurrected with the "fake-lstat" introduced earlier.
+
+ * Test balloon to use C99 "bool" type from <stdbool.h> has been
+ added.
+
+ * "git clone" has been prepared to allow cloning a repository with
+ non-default hash function into a repository that uses the reftable
+ backend.
+
+ * Streaming spans of packfile data used to be done only from a
+ single, primary, pack in a repository with multiple packfiles. It
+ has been extended to allow reuse from other packfiles, too.
+
+ * Comment updates to help developers not to attempt to modify
+ messages from plumbing commands that must stay constant.
+
+ It might make sense to reassess the plumbing needs every few years,
+ but that should be done as a separate effort.
+
+ * Move test-ctype helper to the unit-test framework.
+
+ * Instead of manually creating refs/ hierarchy on disk upon a
+ creation of a secondary worktree, which is only usable via the
+ files backend, use the refs API to populate it.
+
+ * CI for GitLab learned to drive macOS jobs.
+
+ * A few tests to "git commit -o <pathspec>" and "git commit -i
+ <pathspec>" has been added.
+
+ * Tests on ref API are moved around to prepare for reftable.
+
+ * The Makefile often had to say "-L$(path) -R$(path)" that repeats
+ the path to the same library directory for link time and runtime.
+ A Makefile template is used to reduce such repetition.
+
+ * The priority queue test has been migrated to the unit testing
+ framework.
+
+ * Setting `feature.experimental` opts the user into multi-pack reuse
+ experiment
+
+ * Squelch node.js 16 deprecation warnings from GitHub Actions CI
+ by updating actions/github-script and actions/checkout that use
+ node.js 20.
+
+ * The mechanism to report the filename in the source code, used by
+ the unit-test machinery, assumed that the compiler expanded __FILE__
+ to the path to the source given to the $(CC), but some compilers
+ give full path, breaking the output. This has been corrected.
+
Fixes since v2.43
-----------------
* The way CI testing used "prove" could lead to running the test
suite twice needlessly, which has been corrected.
- (merge e7e03ef995 js/ci-discard-prove-state later to maint).
* Update ref-related tests.
@@ -38,6 +190,145 @@ Fixes since v2.43
(ab)use of it in "git send-email". Bump the minimum version
requirement for Perl to 5.8.1 (from September 2002) to allow
simplifying our implementation.
- (merge 6ff658cc78 tz/send-email-negatable-options later to maint).
+
+ * Earlier we stopped relying on commit-graph that (still) records
+ information about commits that are lost from the object store,
+ which has negative performance implications. The default has been
+ flipped to disable this pessimization.
+
+ * Stale URLs have been updated to their current counterparts (or
+ archive.org) and HTTP links are replaced with working HTTPS links.
+
+ * trace2 streams used to record the URLs that potentially embed
+ authentication material, which has been corrected.
+
+ * The sample pre-commit hook that tries to catch introduction of new
+ paths that use potentially non-portable characters did not notice
+ an existing path getting renamed to such a problematic path, when
+ rename detection was enabled.
+
+ * The command line parser for the "log" family of commands was too
+ loose when parsing certain numbers, e.g., silently ignoring the
+ extra 'q' in "git log -n 1q" without complaining, which has been
+ tightened up.
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+ to interpret "--rev" as a rev, and "--path" as a path. This was
+ fixed for many programs like "reset" and "checkout".
+
+ * "git bisect reset" has been taught to clean up state files and refs
+ even when BISECT_START file is gone.
+
+ * Some codepaths did not correctly parse configuration variables
+ specified with valueless "true", which has been corrected.
+
+ * Code clean-up for sanity checking of command line options for "git
+ show-ref".
+
+ * The code to parse the From e-mail header has been updated to avoid
+ recursion.
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+ which has been corrected.
+
+ * Command line completion script (in contrib/) learned to work better
+ with the reftable backend.
+
+ * "git status" is taught to show both the branch being bisected and
+ being rebased when both are in effect at the same time.
+
+ * "git archive --list extra garbage" silently ignored excess command
+ line parameters, which has been corrected.
+
+ * "git sparse-checkout set" added default patterns even when the
+ patterns are being fed from the standard input, which has been
+ corrected.
+
+ * "git sparse-checkout (add|set) --[no-]cone --end-of-options" did
+ not handle "--end-of-options" correctly after a recent update.
+
+ * Unlike other environment variables that took the usual
+ true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+ which has been corrected.
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+ --recurse-submodules" with commit graph enabled) made in-core
+ commit object in an inconsistent state by discarding the necessary
+ data from commit-graph too early, which has been corrected.
+
+ * Update to a new feature recently added, "git show-ref --exists".
+
+ * oss-fuzz tests are built and run in CI.
+ (merge c4a9cf1df3 js/oss-fuzz-build-in-ci later to maint).
+
+ * Rename detection logic ignored the final line of a file if it is an
+ incomplete line.
+
+ * GitHub CI update.
+ (merge 0188b2c8e0 pb/ci-github-skip-logs-for-broken-tests later to maint).
+
+ * "git diff --no-rename A B" did not disable rename detection but did
+ not trigger an error from the command line parser.
+
+ * "git archive --remote=<remote>" learned to talk over the smart
+ http (aka stateless) transport.
+ (merge 176cd68634 jx/remote-archive-over-smart-http later to maint).
+
+ * Fetching via protocol v0 over Smart HTTP transport sometimes failed
+ to correctly auto-follow tags.
+ (merge fba732c462 jk/fetch-auto-tag-following-fix later to maint).
+
+ * The documentation for the --exclude-per-directory option marked it
+ as deprecated, which confused readers into thinking there may be a
+ plan to remove it in the future, which was not our intention.
+ (merge 0009542cab jc/ls-files-doc-update later to maint).
+
+ * "git diff --no-index file1 file2" segfaulted while invoking the
+ external diff driver, which has been corrected.
+
+ * Rewrite //-comments to /* comments */ in files whose comments
+ prevalently use the latter.
+
+ * Cirrus CI jobs started breaking because we specified version of
+ FreeBSD that is no longer available, which has been corrected.
+ (merge 81fffb66d3 cb/use-freebsd-13-2-at-cirrus-ci later to maint).
+
+ * A caller called index_file_exists() that takes a string expressed
+ as <ptr, length> with a wrong length, which has been corrected.
+ (merge 156e28b36d jh/sparse-index-expand-to-path-fix later to maint).
+
+ * A failed "git tag -s" did not necessarily result in an error
+ depending on the crypto backend, which has been corrected.
+
+ * "git stash" sometimes was silent even when it failed due to
+ unwritable index file, which has been corrected.
+
+ * "git show-ref --verify" did not show things like "CHERRY_PICK_HEAD",
+ which has been corrected.
+
+ * Recent conversion to allow more than 0/1 in GIT_FLUSH broke the
+ mechanism by flipping what yes/no means by mistake, which has been
+ corrected.
+
+ * The sequencer machinery does not use the ref API and instead
+ records names of certain objects it needs for its correct operation
+ in temporary files, which makes these objects susceptible to loss
+ by garbage collection. These temporary files have been added as
+ starting points for reachability analysis to fix this.
+ (merge bc7f5db896 pw/gc-during-rebase later to maint).
+
+ * "git cherry-pick" invoked during "git rebase -i" session lost
+ the authorship information, which has been corrected.
+ (merge e4301f73ff vn/rebase-with-cherry-pick-authorship later to maint).
+
+ * The code paths that call repo_read_object_file() have been
+ tightened to react to errors.
+ (merge 568459bf5e js/check-null-from-read-object-file later to maint).
* Other code cleanup, docfix, build fix, etc.
+ (merge 5aea3955bc rj/clarify-branch-doc-m later to maint).
+ (merge 9cce3be2df bk/bisect-doc-fix later to maint).
+ (merge 8430b438f6 vd/fsck-submodule-url-test later to maint).
+ (merge 3cb4384683 jc/t0091-with-unknown-git later to maint).
+ (merge 020456cb74 rs/receive-pack-remove-find-header later to maint).
+ (merge bc47139f4f la/trailer-cleanups later to maint).
diff --git a/Documentation/RelNotes/2.45.0.txt b/Documentation/RelNotes/2.45.0.txt
new file mode 100644
index 0000000000..15704ff98f
--- /dev/null
+++ b/Documentation/RelNotes/2.45.0.txt
@@ -0,0 +1,433 @@
+Git v2.45 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+UI, Workflows & Features
+
+ * Integrate the reftable code into the refs framework as a backend.
+ With "git init --ref-format=reftable", hopefully it would be a lot
+ more efficient to manage a repository with many references.
+
+ * "git checkout -p" and friends learned that that "@" is a synonym
+ for "HEAD".
+
+ * Variants of vimdiff learned to honor mergetool.<variant>.layout
+ settings.
+
+ * "git reflog" learned a "list" subcommand that enumerates known reflogs.
+
+ * When a merge conflicted at a submodule, merge-ort backend used to
+ unconditionally give a lengthy message to suggest how to resolve
+ it. Now the message can be squelched as an advice message.
+
+ * "git for-each-ref" learned "--include-root-refs" option to show
+ even the stuff outside the 'refs/' hierarchy.
+
+ * "git rev-list --missing=print" has learned to optionally take
+ "--allow-missing-tips", which allows the objects at the starting
+ points to be missing.
+
+ * "git merge-tree" has learned that the three trees involved in the
+ 3-way merge only need to be trees, not necessarily commits.
+
+ * "git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
+ other kinds of *_HEAD pseudorefs.
+
+ * Platform specific tweaks for OS/390 has been added to
+ config.mak.uname.
+
+ * Users with safe.bareRepository=explicit can still work from within
+ $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/)
+ of the primary worktree without explicitly specifying the $GIT_DIR
+ environment variable or the --git-dir=<path> option.
+
+ * The output format for dates "iso-strict" has been tweaked to show
+ a time in the Zulu timezone with "Z" suffix, instead of "+00:00".
+
+ * "git diff" and friends learned two extra configuration variables,
+ diff.srcPrefix and diff.dstPrefix.
+
+ * The status.showUntrackedFiles configuration variable had a name
+ that tempts users to set a Boolean value expressed in our usual
+ "false", "off", and "0", but it only took "no". This has been
+ corrected so "true" and its synonyms are taken as "normal", while
+ "false" and its synonyms are taken as "no".
+
+ * Remove an ancient and not well maintained Hg-to-git migration
+ script from contrib/.
+
+ * Hints that suggest what to do after resolving conflicts can now be
+ squelched by disabling advice.mergeConflict.
+
+ * Allow git-cherry-pick(1) to automatically drop redundant commits via
+ a new `--empty` option, similar to the `--empty` options for
+ git-rebase(1) and git-am(1). Includes a soft deprecation of
+ `--keep-redundant-commits` as well as some related docs changes and
+ sequencer code cleanup.
+
+ * "git config" learned "--comment=<message>" option to leave a
+ comment immediately after the "variable = value" on the same line
+ in the configuration file.
+
+ * core.commentChar used to be limited to a single byte, but has been
+ updated to allow an arbitrary multi-byte sequence.
+
+ * "git add -p" and other "interactive hunk selection" UI has learned to
+ skip showing the hunk immediately after it has already been shown, and
+ an additional action to explicitly ask to reshow the current hunk.
+
+ * "git pack-refs" learned the "--auto" option, which defers the decision of
+ whether and how to pack to the ref backend. This is used by the reftable
+ backend to avoid repacking of an already-optimal ref database. The new mode
+ is triggered from "git gc --auto".
+
+ * "git add -u <pathspec>" and "git commit [-i] <pathspec>" did not
+ diagnose a pathspec element that did not match any files in certain
+ situations, unlike "git add <pathspec>" did.
+
+ * The userdiff patterns for C# has been updated.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The code to iterate over refs with the reftable backend has seen
+ some optimization.
+
+ * More tests that are marked as "ref-files only" have been updated to
+ improve test coverage of reftable backend.
+
+ * Some parts of command line completion script (in contrib/) have
+ been micro-optimized.
+
+ * The way placeholders are to be marked-up in documentation have been
+ specified; use "_<placeholder>_" to typeset the word inside a pair
+ of <angle-brackets> emphasized.
+
+ * "git --no-lazy-fetch cmd" allows to run "cmd" while disabling lazy
+ fetching of objects from the promisor remote, which may be handy
+ for debugging.
+
+ * The implementation in "git clean" that makes "-n" and "-i" ignore
+ clean.requireForce has been simplified, together with the
+ documentation.
+
+ * Uses of xwrite() helper have been audited and updated for better
+ error checking and simpler code.
+
+ * Some trace2 events that lacked def_param have learned to show it,
+ enriching the output.
+
+ * The parse-options code that deals with abbreviated long option
+ names have been cleaned up.
+
+ * The code in reftable backend that creates new table files works
+ better with the tempfile framework to avoid leaving cruft after a
+ failure.
+
+ * The reftable code has its own custom binary search function whose
+ comparison callback has an unusual interface, which caused the
+ binary search to degenerate into a linear search, which has been
+ corrected.
+
+ * The code to iterate over reflogs in the reftable has been optimized
+ to reduce memory allocation and deallocation.
+
+ * Work to support a repository that work with both SHA-1 and SHA-256
+ hash algorithms has started.
+
+ * A new fuzz target that exercises config parsing code has been
+ added.
+
+ * Fix the way recently added tests interpolate variables defined
+ outside them, and document the best practice to help future
+ developers.
+
+ * Introduce an experimental protocol for contributors to propose the
+ topic description to be used in the "What's cooking" report, the
+ merge commit message for the topic, and in the release notes and
+ document it in the SubmittingPatches document.
+
+ * The t/README file now gives a hint on running individual tests in
+ the "t/" directory with "make t<num>-*.sh t<num>-*.sh".
+ (merge 8d383806fc pb/test-scripts-are-build-targets later to maint).
+
+ * The "hint:" messages given by the advice mechanism, when given a
+ message with a blank line, left a line with trailing whitespace,
+ which has been cleansed.
+
+ * Documentation rules has been explicitly described how to mark-up
+ literal parts and a few manual pages have been updated as examples.
+
+ * The .editorconfig file has been taught that a Makefile uses HT
+ indentation.
+
+ * t-prio-queue test has been cleaned up by using C99 compound
+ literals; this is meant to also serve as a weather-balloon to smoke
+ out folks with compilers who have trouble compiling code that uses
+ the feature.
+
+ * Windows binary used to decide the use of unix-domain socket at
+ build time, but it learned to make the decision at runtime instead.
+
+ * The "shared repository" test in the t0610 reftable test failed
+ under restrictive umask setting (e.g. 007), which has been
+ corrected.
+
+ * Document and apply workaround for a buggy version of dash that
+ mishandles "local var=val" construct.
+
+ * The codepaths that reach date_mode_from_type() have been updated to
+ pass "struct date_mode" by value to make them thread safe.
+
+ * The strategy to compact multiple tables of reftables after many
+ operations accumulate many entries has been improved to avoid
+ accumulating too many tables uncollected.
+
+
+Fixes since v2.44
+-----------------
+
+ * "git apply" on a filesystem without filemode support have learned
+ to take a hint from what is in the index for the path, even when
+ not working with the "--index" or "--cached" option, when checking
+ the executable bit match what is required by the preimage in the
+ patch.
+ (merge 45b625142d cp/apply-core-filemode later to maint).
+
+ * "git column" has been taught to reject negative padding value, as
+ it would lead to nonsense behaviour including division by zero.
+ (merge 76fb807faa kh/column-reject-negative-padding later to maint).
+
+ * "git am --help" now tells readers what actions are available in
+ "git am --whitespace=<action>", in addition to saying that the
+ option is passed through to the underlying "git apply".
+ (merge a171dac734 jc/am-whitespace-doc later to maint).
+
+ * "git tag --column" failed to check the exit status of its "git
+ column" invocation, which has been corrected.
+ (merge 92e66478fc rj/tag-column-fix later to maint).
+
+ * Credential helper based on libsecret (in contrib/) has been updated
+ to handle an empty password correctly.
+ (merge 8f1f2023b7 mh/libsecret-empty-password-fix later to maint).
+
+ * "git difftool --dir-diff" learned to honor the "--trust-exit-code"
+ option; it used to always exit with 0 and signalled success.
+ (merge eb84c8b6ce ps/difftool-dir-diff-exit-code later to maint).
+
+ * The code incorrectly attempted to use textconv cache when asked,
+ even when we are not running in a repository, which has been
+ corrected.
+ (merge affe355fe7 jk/textconv-cache-outside-repo-fix later to maint).
+
+ * Remove an empty file that shouldn't have been added in the first
+ place.
+ (merge 4f66942215 js/remove-cruft-files later to maint).
+
+ * The logic to access reflog entries by date and number had ugly
+ corner cases at the boundaries, which have been cleaned up.
+ (merge 5edd126720 jk/reflog-special-cases-fix later to maint).
+
+ * An error message from "git upload-pack", which responds to "git
+ fetch" requests, had a trialing NUL in it, which has been
+ corrected.
+ (merge 3f4c7a0805 sg/upload-pack-error-message-fix later to maint).
+
+ * Clarify wording in the CodingGuidelines that requires <git-compat-util.h>
+ to be the first header file.
+ (merge 4e89f0e07c jc/doc-compat-util later to maint).
+
+ * "git commit -v --cleanup=scissors" used to add the scissors line
+ twice in the log message buffer, which has been corrected.
+ (merge e90cc075cc jt/commit-redundant-scissors-fix later to maint).
+
+ * A custom remote helper no longer cannot access the newly created
+ repository during "git clone", which is a regression in Git 2.44.
+ This has been corrected.
+ (merge 199f44cb2e ps/remote-helper-repo-initialization-fix later to maint).
+
+ * Various parts of upload-pack have been updated to bound the resource
+ consumption relative to the size of the repository to protect from
+ abusive clients.
+ (merge 6cd05e768b jk/upload-pack-bounded-resources later to maint).
+
+ * The upload-pack program, when talking over v2, accepted the
+ packfile-uris protocol extension from the client, even if it did
+ not advertise the capability, which has been corrected.
+ (merge a922bfa3b5 jk/upload-pack-v2-capability-cleanup later to maint).
+
+ * Make sure failure return from merge_bases_many() is properly caught.
+ (merge 25fd20eb44 js/merge-base-with-missing-commit later to maint).
+
+ * FSMonitor client code was confused when FSEvents were given in a
+ different case on a case-insensitive filesystem, which has been
+ corrected.
+ (merge 29c139ce78 jh/fsmonitor-icase-corner-case-fix later to maint).
+
+ * The "core.commentChar" configuration variable only allows an ASCII
+ character, which was not clearly documented, which has been
+ corrected.
+ (merge fb7c556f58 kh/doc-commentchar-is-a-byte later to maint).
+
+ * With release 2.44 we got rid of all uses of test_i18ngrep and there
+ is no in-flight topic that adds a new use of it. Make a call to
+ test_i18ngrep a hard failure, so that we can remove it at the end
+ of this release cycle.
+ (merge 381a83dfa3 jc/test-i18ngrep later to maint).
+
+ * The command line completion script (in contrib/) learned to
+ complete "git reflog" better.
+ (merge 1284f9cc11 rj/complete-reflog later to maint).
+
+ * The logic to complete the command line arguments to "git worktree"
+ subcommand (in contrib/) has been updated to correctly honor things
+ like "git -C dir" etc.
+ (merge 3574816d98 rj/complete-worktree-paths-fix later to maint).
+
+ * When git refuses to create a branch because the proposed branch
+ name is not a valid refname, an advice message is given to refer
+ the user to exact naming rules.
+ (merge 8fbd903e58 kh/branch-ref-syntax-advice later to maint).
+
+ * Code simplification by getting rid of code that sets an environment
+ variable that is no longer used.
+ (merge 72a8d3f027 pw/rebase-i-ignore-cherry-pick-help-environment later to maint).
+
+ * The code to find the effective end of log messages can fall into an
+ endless loop, which has been corrected.
+ (merge 2541cba2d6 fs/find-end-of-log-message-fix later to maint).
+
+ * Mark-up used in the documentation has been improved for
+ consistency.
+ (merge 45d5ed3e50 ja/doc-markup-fixes later to maint).
+
+ * The status.showUntrackedFiles configuration variable was
+ incorrectly documented to accept "false", which has been corrected.
+
+ * Leaks from "git restore" have been plugged.
+ (merge 2f64da0790 rj/restore-plug-leaks later to maint).
+
+ * "git bugreport --no-suffix" was not supported and instead
+ segfaulted, which has been corrected.
+ (merge b3b57c69da js/bugreport-no-suffix-fix later to maint).
+
+ * The documentation for "%(trailers[:options])" placeholder in the
+ "--pretty" option of commands in the "git log" family has been
+ updated.
+ (merge bff85a338c bl/doc-key-val-sep-fix later to maint).
+
+ * "git checkout --conflict=bad" reported a bad conflictStyle as if it
+ were given to a configuration variable; it has been corrected to
+ report that the command line option is bad.
+ (merge 5a99c1ac1a pw/checkout-conflict-errorfix later to maint).
+
+ * Code clean-up in the "git log" machinery that implements custom log
+ message formatting.
+ (merge 1c10b8e5b0 jk/pretty-subject-cleanup later to maint).
+
+ * "git config" corrupted literal HT characters written in the
+ configuration file as part of a value, which has been corrected.
+ (merge e6895c3f97 ds/config-internal-whitespace-fix later to maint).
+
+ * A unit test for reftable code tried to enumerate all files in a
+ directory after reftable operations and expected to see nothing but
+ the files it wanted to leave there, but was fooled by .nfs* cruft
+ files left, which has been corrected.
+ (merge 0068aa7946 ps/reftable-unit-test-nfs-workaround later to maint).
+
+ * The implementation and documentation of "object-format" option
+ exchange between the Git itself and its remote helpers did not
+ quite match, which has been corrected.
+
+ * The "--pretty=<shortHand>" option of the commands in the "git log"
+ family, defined as "[pretty] shortHand = <expansion>" should have
+ been looked up case insensitively, but was not, which has been
+ corrected.
+ (merge f999d5188b bl/pretty-shorthand-config-fix later to maint).
+
+ * "git apply" failed to extract the filename the patch applied to,
+ when the change was about an empty file created in or deleted from
+ a directory whose name ends with a SP, which has been corrected.
+ (merge 776ffd1a30 jc/apply-parse-diff-git-header-names-fix later to maint).
+
+ * Update a more recent tutorial doc.
+ (merge 95ab557b4b dg/myfirstobjectwalk-updates later to maint).
+
+ * The test script had an incomplete and ineffective attempt to avoid
+ clobbering the testing user's real crontab (and its equivalents),
+ which has been completed.
+ (merge 73cb87773b es/test-cron-safety later to maint).
+
+ * Use advice_if_enabled() API to rewrite a simple pattern to
+ call advise() after checking advice_enabled().
+ (merge 6412d01527 rj/use-adv-if-enabled later to maint).
+
+ * Another "set -u" fix for the bash prompt (in contrib/) script.
+ (merge d7805bc743 vs/complete-with-set-u-fix later to maint).
+
+ * "git checkout/switch --detach foo", after switching to the detached
+ HEAD state, gave the tracking information for the 'foo' branch,
+ which was pointless.
+
+ * "git apply" has been updated to lift the hardcoded pathname length
+ limit, which in turn allowed a mksnpath() function that is no
+ longer used.
+ (merge 708f7e0590 rs/apply-lift-path-length-limit later to maint).
+
+ * A file descriptor leak in an error codepath, used when "git apply
+ --reject" fails to create the *.rej file, has been corrected.
+ (merge 2b1f456adf rs/apply-reject-fd-leakfix later to maint).
+
+ * A config parser callback function fell through instead of returning
+ after recognising and processing a variable, wasting cycles, which
+ has been corrected.
+ (merge a816ccd642 ds/fetch-config-parse-microfix later to maint).
+
+ * Fix was added to work around a regression in libcURL 8.7.0 (which has
+ already been fixed in their tip of the tree).
+ (merge 92a209bf24 jk/libcurl-8.7-regression-workaround later to maint).
+
+ * The variable that holds the value read from the core.excludefile
+ configuration variable used to leak, which has been corrected.
+ (merge 0e0fefb29f jc/unleak-core-excludesfile later to maint).
+
+ * vreportf(), which is used by error() and friends, has been taught
+ to give the error message printf-format string when its vsnprintf()
+ call fails, instead of showing nothing useful to identify the
+ nature of the error.
+ (merge c63adab961 rs/usage-fallback-to-show-message-format later to maint).
+
+ * Adjust to an upcoming changes to GNU make that breaks our Makefiles.
+ (merge 227b8fd902 tb/make-indent-conditional-with-non-spaces later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge f0e578c69c rs/use-xstrncmpz later to maint).
+ (merge 83e6eb7d7a ba/credential-test-clean-fix later to maint).
+ (merge 64562d784d jb/doc-interactive-singlekey-do-not-need-perl later to maint).
+ (merge c431a235e2 cp/t9146-use-test-path-helpers later to maint).
+ (merge 82d75402d5 ds/doc-send-email-capitalization later to maint).
+ (merge 41bff66e35 jc/doc-add-placeholder-fix later to maint).
+ (merge 6835f0efe9 jw/remote-doc-typofix later to maint).
+ (merge 244001aa20 hs/rebase-not-in-progress later to maint).
+ (merge 2ca6c07db2 jc/no-include-of-compat-util-from-headers later to maint).
+ (merge 87bd7fbb9c rs/fetch-simplify-with-starts-with later to maint).
+ (merge f39addd0d9 rs/name-rev-with-mempool later to maint).
+ (merge 9a97b43e03 rs/submodule-prefix-simplify later to maint).
+ (merge 40b8076462 ak/rebase-autosquash later to maint).
+ (merge 3223204456 eg/add-uflags later to maint).
+ (merge 5f78d52dce es/config-doc-sort-sections later to maint).
+ (merge 781fb7b4c2 as/option-names-in-messages later to maint).
+ (merge 51d41dc243 jk/doc-remote-helpers-markup-fix later to maint).
+ (merge e1aaf309db pb/ci-win-artifact-names-fix later to maint).
+ (merge ad538c61da jc/index-pack-fsck-levels later to maint).
+ (merge 67471bc704 ja/doc-formatting-fix later to maint).
+ (merge 86f9ce7dd6 bl/doc-config-fixes later to maint).
+ (merge 0d527842b7 az/grep-group-error-message-update later to maint).
+ (merge 7c43bdf07b rs/strbuf-expand-bad-format later to maint).
+ (merge 8b68b48d5c ds/typofix-core-config-doc later to maint).
+ (merge 39bb692152 rs/imap-send-use-xsnprintf later to maint).
+ (merge 8d320cec60 jc/t2104-style-fixes later to maint).
+ (merge b4454d5a7b pw/t3428-cleanup later to maint).
+ (merge 84a7c33a4b pf/commitish-committish later to maint).
+ (merge 8882ee9d68 la/mailmap-entry later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index bce7f97815..c647c7e1b4 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -355,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.
@@ -447,6 +459,18 @@ an explanation of changes between each iteration can be kept in
Git-notes and inserted automatically following the three-dash
line via `git format-patch --notes`.
+[[the-topic-summary]]
+*This is EXPERIMENTAL*.
+
+When sending a topic, you can propose a one-paragraph summary that
+should appear in the "What's cooking" report when it is picked up to
+explain the topic. If you choose to do so, please write a 2-5 line
+paragraph that will fit well in our release notes (see many bulleted
+entries in the Documentation/RelNotes/* files for examples), and make
+it the first paragraph of the cover letter. For a single-patch
+series, use the space between the three-dash line and the diffstat, as
+described earlier.
+
[[attachment]]
Do not attach the patch as a MIME attachment, compressed or not.
Do not let your e-mail client send quoted-printable. Do not let
@@ -570,7 +594,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]]
@@ -590,11 +614,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.
@@ -686,7 +711,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
diff --git a/Documentation/config.txt b/Documentation/config.txt
index e3a74dd1c1..70b448b132 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -22,9 +22,10 @@ multivalued.
Syntax
~~~~~~
-The syntax is fairly flexible and permissive; whitespaces are mostly
-ignored. The '#' and ';' characters begin comments to the end of line,
-blank lines are ignored.
+The syntax is fairly flexible and permissive. Whitespace characters,
+which in this context are the space character (SP) and the horizontal
+tabulation (HT), are mostly ignored. The '#' and ';' characters begin
+comments to the end of line. Blank lines are ignored.
The file consists of sections and variables. A section begins with
the name of the section in square brackets and continues until the next
@@ -63,16 +64,17 @@ the variable is the boolean "true").
The variable names are case-insensitive, allow only alphanumeric characters
and `-`, and must start with an alphabetic character.
-A line that defines a value can be continued to the next line by
-ending it with a `\`; the backslash and the end-of-line are
-stripped. Leading whitespaces after 'name =', the remainder of the
-line after the first comment character '#' or ';', and trailing
-whitespaces of the line are discarded unless they are enclosed in
-double quotes. Internal whitespaces within the value are retained
-verbatim.
+Whitespace characters surrounding `name`, `=` and `value` are discarded.
+Internal whitespace characters within 'value' are retained verbatim.
+Comments starting with either `#` or `;` and extending to the end of line
+are discarded. A line that defines a value can be continued to the next
+line by ending it with a backslash (`\`); the backslash and the end-of-line
+characters are discarded.
-Inside double quotes, double quote `"` and backslash `\` characters
-must be escaped: use `\"` for `"` and `\\` for `\`.
+If `value` needs to contain leading or trailing whitespace characters,
+it must be enclosed in double quotation marks (`"`). Inside double quotation
+marks, double quote (`"`) and backslash (`\`) characters must be escaped:
+use `\"` for `"` and `\\` for `\`.
The following escape sequences (beside `\"` and `\\`) are recognized:
`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB)
@@ -369,20 +371,18 @@ inventing new variables for use in your own tool, make sure their
names do not conflict with those that are used by Git itself and
other popular tools, and describe them in your documentation.
-include::config/advice.txt[]
-
-include::config/attr.txt[]
-
-include::config/core.txt[]
-
include::config/add.txt[]
+include::config/advice.txt[]
+
include::config/alias.txt[]
include::config/am.txt[]
include::config/apply.txt[]
+include::config/attr.txt[]
+
include::config/blame.txt[]
include::config/branch.txt[]
@@ -405,10 +405,12 @@ include::config/commit.txt[]
include::config/commitgraph.txt[]
-include::config/credential.txt[]
-
include::config/completion.txt[]
+include::config/core.txt[]
+
+include::config/credential.txt[]
+
include::config/diff.txt[]
include::config/difftool.txt[]
@@ -421,10 +423,10 @@ include::config/feature.txt[]
include::config/fetch.txt[]
-include::config/format.txt[]
-
include::config/filter.txt[]
+include::config/format.txt[]
+
include::config/fsck.txt[]
include::config/fsmonitor--daemon.txt[]
@@ -435,10 +437,10 @@ include::config/gitcvs.txt[]
include::config/gitweb.txt[]
-include::config/grep.txt[]
-
include::config/gpg.txt[]
+include::config/grep.txt[]
+
include::config/gui.txt[]
include::config/guitool.txt[]
@@ -519,10 +521,10 @@ include::config/splitindex.txt[]
include::config/ssh.txt[]
-include::config/status.txt[]
-
include::config/stash.txt[]
+include::config/status.txt[]
+
include::config/submodule.txt[]
include::config/tag.txt[]
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 2737381a11..0e35ae5240 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -1,30 +1,65 @@
advice.*::
These variables control various optional help messages designed to
- aid new users. All 'advice.*' variables default to 'true', and you
- can tell Git that you do not need help by setting these to 'false':
+ aid new users. When left unconfigured, Git will give the message
+ alongside instructions on how to squelch it. You can tell Git
+ that you do not need the help message by setting these to `false`:
+
--
+ addEmbeddedRepo::
+ Shown when the user accidentally adds one
+ git repo inside of another.
+ addEmptyPathspec::
+ Shown when the user runs `git add` without providing
+ the pathspec parameter.
+ addIgnoredFile::
+ Shown when the user attempts to add an ignored file to
+ the index.
+ amWorkDir::
+ Shown when linkgit:git-am[1] fails to apply a patch
+ file, to tell the user the location of the file.
ambiguousFetchRefspec::
- Advice shown when a fetch refspec for multiple remotes maps to
+ Shown when a fetch refspec for multiple remotes maps to
the same remote-tracking branch namespace and causes branch
tracking set-up to fail.
+ checkoutAmbiguousRemoteBranchName::
+ Shown when the argument to
+ linkgit:git-checkout[1] and linkgit:git-switch[1]
+ ambiguously resolves to a
+ remote tracking branch on more than one remote in
+ situations where an unambiguous argument would have
+ otherwise caused a remote-tracking branch to be
+ checked out. See the `checkout.defaultRemote`
+ configuration variable for how to set a given remote
+ to be used by default in some situations where this
+ advice would be printed.
+ commitBeforeMerge::
+ Shown when linkgit:git-merge[1] refuses to
+ merge to avoid overwriting local changes.
+ detachedHead::
+ Shown when the user uses
+ linkgit:git-switch[1] or linkgit:git-checkout[1]
+ to move to the detached HEAD state, to tell the user how
+ to create a local branch after the fact.
+ diverging::
+ Shown when a fast-forward is not possible.
fetchShowForcedUpdates::
- Advice shown when linkgit:git-fetch[1] takes a long time
+ Shown when linkgit:git-fetch[1] takes a long time
to calculate forced updates after ref updates, or to warn
that the check is disabled.
- pushUpdateRejected::
- Set this variable to 'false' if you want to disable
- 'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
- 'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
- simultaneously.
- pushNonFFCurrent::
- Advice shown when linkgit:git-push[1] fails due to a
- non-fast-forward update to the current branch.
- pushNonFFMatching::
- Advice shown when you ran linkgit:git-push[1] and pushed
- 'matching refs' explicitly (i.e. you used ':', or
- specified a refspec that isn't your current branch) and
- it resulted in a non-fast-forward error.
+ forceDeleteBranch::
+ Shown when the user tries to delete a not fully merged
+ branch without the force option set.
+ ignoredHook::
+ Shown when a hook is ignored because the hook is not
+ set as executable.
+ implicitIdentity::
+ Shown when the user's information is guessed from the
+ system username and domain name, to tell the user how to
+ set their identity configuration.
+ mergeConflict::
+ Shown when various commands stop because of conflicts.
+ nestedTag::
+ Shown when a user attempts to recursively tag a tag object.
pushAlreadyExists::
Shown when linkgit:git-push[1] rejects an update that
does not qualify for fast-forwarding (e.g., a tag.)
@@ -37,17 +72,45 @@ advice.*::
tries to overwrite a remote ref that points at an
object that is not a commit-ish, or make the remote
ref point at an object that is not a commit-ish.
+ pushNonFFCurrent::
+ Shown when linkgit:git-push[1] fails due to a
+ non-fast-forward update to the current branch.
+ pushNonFFMatching::
+ Shown when the user ran linkgit:git-push[1] and pushed
+ "matching refs" explicitly (i.e. used `:`, or
+ specified a refspec that isn't the current branch) and
+ it resulted in a non-fast-forward error.
+ pushRefNeedsUpdate::
+ Shown when linkgit:git-push[1] rejects a forced update of
+ a branch when its remote-tracking ref has updates that we
+ do not have locally.
pushUnqualifiedRefname::
Shown when linkgit:git-push[1] gives up trying to
guess based on the source and destination refs what
remote ref namespace the source belongs in, but where
we can still suggest that the user push to either
- refs/heads/* or refs/tags/* based on the type of the
+ `refs/heads/*` or `refs/tags/*` based on the type of the
source object.
- pushRefNeedsUpdate::
- Shown when linkgit:git-push[1] rejects a forced update of
- a branch when its remote-tracking ref has updates that we
- do not have locally.
+ pushUpdateRejected::
+ Set this variable to `false` if you want to disable
+ `pushNonFFCurrent`, `pushNonFFMatching`, `pushAlreadyExists`,
+ `pushFetchFirst`, `pushNeedsForce`, and `pushRefNeedsUpdate`
+ simultaneously.
+ refSyntax::
+ Shown when the user provides an illegal ref name, to
+ tell the user about the ref syntax documentation.
+ resetNoRefresh::
+ Shown when linkgit:git-reset[1] takes more than 2
+ seconds to refresh the index after reset, to tell the user
+ that they can use the `--no-refresh` option.
+ resolveConflict::
+ Shown by various commands when conflicts
+ prevent the operation from being performed.
+ rmHints::
+ Shown on failure in the output of linkgit:git-rm[1], to
+ give directions on how to proceed from the current state.
+ sequencerInUse::
+ Shown when a sequencer command is already in progress.
skippedCherryPicks::
Shown when linkgit:git-rebase[1] skips a commit that has already
been cherry-picked onto the upstream branch.
@@ -65,81 +128,30 @@ advice.*::
by linkgit:git-switch[1] or
linkgit:git-checkout[1] when switching branches.
statusUoption::
- Advise to consider using the `-u` option to linkgit:git-status[1]
- when the command takes more than 2 seconds to enumerate untracked
- files.
- commitBeforeMerge::
- Advice shown when linkgit:git-merge[1] refuses to
- merge to avoid overwriting local changes.
- resetNoRefresh::
- Advice to consider using the `--no-refresh` option to
- linkgit:git-reset[1] when the command takes more than 2 seconds
- to refresh the index after reset.
- resolveConflict::
- Advice shown by various commands when conflicts
- prevent the operation from being performed.
- sequencerInUse::
- Advice shown when a sequencer command is already in progress.
- implicitIdentity::
- Advice on how to set your identity configuration when
- your information is guessed from the system username and
- domain name.
- detachedHead::
- Advice shown when you used
- linkgit:git-switch[1] or linkgit:git-checkout[1]
- to move to the detached HEAD state, to instruct how to
- create a local branch after the fact.
- suggestDetachingHead::
- Advice shown when linkgit:git-switch[1] refuses to detach HEAD
- without the explicit `--detach` option.
- checkoutAmbiguousRemoteBranchName::
- Advice shown when the argument to
- linkgit:git-checkout[1] and linkgit:git-switch[1]
- ambiguously resolves to a
- remote tracking branch on more than one remote in
- situations where an unambiguous argument would have
- otherwise caused a remote-tracking branch to be
- checked out. See the `checkout.defaultRemote`
- configuration variable for how to set a given remote
- to be used by default in some situations where this
- advice would be printed.
- amWorkDir::
- Advice that shows the location of the patch file when
- linkgit:git-am[1] fails to apply it.
- rmHints::
- In case of failure in the output of linkgit:git-rm[1],
- show directions on how to proceed from the current state.
- addEmbeddedRepo::
- Advice on what to do when you've accidentally added one
- git repo inside of another.
- ignoredHook::
- Advice shown if a hook is ignored because the hook is not
- set as executable.
- waitingForEditor::
- Print a message to the terminal whenever Git is waiting for
- editor input from the user.
- nestedTag::
- Advice shown if a user attempts to recursively tag a tag object.
+ Shown when linkgit:git-status[1] takes more than 2
+ seconds to enumerate untracked files, to tell the user that
+ they can use the `-u` option.
submoduleAlternateErrorStrategyDie::
- Advice shown when a submodule.alternateErrorStrategy option
+ Shown when a submodule.alternateErrorStrategy option
configured to "die" causes a fatal error.
+ submoduleMergeConflict::
+ Advice shown when a non-trivial submodule merge conflict is
+ encountered.
submodulesNotUpdated::
- Advice shown when a user runs a submodule command that fails
+ Shown when a user runs a submodule command that fails
because `git submodule update --init` was not run.
- addIgnoredFile::
- Advice shown if a user attempts to add an ignored file to
- the index.
- addEmptyPathspec::
- Advice shown if a user runs the add command without providing
- the pathspec parameter.
+ suggestDetachingHead::
+ Shown when linkgit:git-switch[1] refuses to detach HEAD
+ without the explicit `--detach` option.
updateSparsePath::
- Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
+ Shown when either linkgit:git-add[1] or linkgit:git-rm[1]
is asked to update index entries outside the current sparse
checkout.
- diverging::
- Advice shown when a fast-forward is not possible.
+ waitingForEditor::
+ Shown when Git is waiting for editor input. Relevant
+ when e.g. the editor is not launched inside the terminal.
worktreeAddOrphan::
- Advice shown when a user tries to create a worktree from an
- invalid reference, to instruct how to create a new orphan
+ Shown when the user tries to create a worktree from an
+ invalid reference, to tell the user how to create a new unborn
branch instead.
--
diff --git a/Documentation/config/clean.txt b/Documentation/config/clean.txt
index f05b9403b5..c0188ead4e 100644
--- a/Documentation/config/clean.txt
+++ b/Documentation/config/clean.txt
@@ -1,3 +1,3 @@
clean.requireForce::
- A boolean to make git-clean do nothing unless given -f,
- -i, or -n. Defaults to true.
+ A boolean to make git-clean refuse to delete files unless -f
+ is given. Defaults to true.
diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
index d037b57f72..0a10efd174 100644
--- a/Documentation/config/clone.txt
+++ b/Documentation/config/clone.txt
@@ -1,13 +1,23 @@
-clone.defaultRemoteName::
+`clone.defaultRemoteName`::
The name of the remote to create when cloning a repository. Defaults to
- `origin`, and can be overridden by passing the `--origin` command-line
+ `origin`.
+ifdef::git-clone[]
+ It can be overridden by passing the `--origin` command-line
+ option.
+endif::[]
+ifndef::git-clone[]
+ It can be overridden by passing the `--origin` command-line
option to linkgit:git-clone[1].
+endif::[]
-clone.rejectShallow::
+`clone.rejectShallow`::
Reject cloning a repository if it is a shallow one; this can be overridden by
- passing the `--reject-shallow` option on the command line. See linkgit:git-clone[1]
+ passing the `--reject-shallow` option on the command line.
+ifndef::git-clone[]
+ See linkgit:git-clone[1].
+endif::[]
-clone.filterSubmodules::
+`clone.filterSubmodules`::
If a partial clone filter is provided (see `--filter` in
linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
the filter to submodules.
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 0e8c2832bf..93d65e1dfd 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -520,6 +520,7 @@ core.editor::
`GIT_EDITOR` is not set. See linkgit:git-var[1].
core.commentChar::
+core.commentString::
Commands such as `commit` and `tag` that let you edit
messages consider a line that begins with this character
commented, and removes them after the editor returns
@@ -527,6 +528,20 @@ core.commentChar::
+
If set to "auto", `git-commit` would select a character that is not
the beginning character of any line in existing commit messages.
++
+Note that these two variables are aliases of each other, and in modern
+versions of Git you are free to use a string (e.g., `//` or `â‘â•â‘`) with
+`commentChar`. Versions of Git prior to v2.45.0 will ignore
+`commentString` but will reject a value of `commentChar` that consists
+of more than a single ASCII byte. If you plan to use your config with
+older and newer versions of Git, you may want to specify both:
++
+ [core]
+ # single character for older versions
+ commentChar = "#"
+ # string for newer versions (which will override commentChar
+ # because it comes later in the file)
+ commentString = "//"
core.filesRefLockTimeout::
The length of time, in milliseconds, to retry when trying to
@@ -688,7 +703,7 @@ core.createObject::
will not overwrite existing objects.
+
On some file system/operating system combinations, this is unreliable.
-Set this config setting to 'rename' there; However, This will remove the
+Set this config setting to 'rename' there; however, this will remove the
check that makes sure that existing object files will not get overwritten.
core.notesRef::
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index bd5ae0c337..5ce7b91f1d 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -108,9 +108,15 @@ diff.mnemonicPrefix::
`git diff --no-index a b`;;
compares two non-git things (1) and (2).
-diff.noprefix::
+diff.noPrefix::
If set, 'git diff' does not show any source or destination prefix.
+diff.srcPrefix::
+ If set, 'git diff' uses this source prefix. Defaults to "a/".
+
+diff.dstPrefix::
+ If set, 'git diff' uses this destination prefix. Defaults to "b/".
+
diff.relative::
If set to 'true', 'git diff' does not show changes outside of the directory
and show pathnames relative to the current directory.
@@ -223,5 +229,5 @@ diff.colorMoved::
diff.colorMovedWS::
When moved lines are colored using e.g. the `diff.colorMoved` setting,
- this option controls the `<mode>` how spaces are treated
- for details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
+ this option controls the `<mode>` how spaces are treated.
+ For details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt
index bccaec7a96..38dce3df35 100644
--- a/Documentation/config/extensions.txt
+++ b/Documentation/config/extensions.txt
@@ -7,6 +7,29 @@ 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.compatObjectFormat::
+
+ Specify a compatitbility hash algorithm to use. The acceptable values
+ are `sha1` and `sha256`. The value specified must be different from the
+ value of extensions.objectFormat. This allows client level
+ interoperability between git repositories whose objectFormat matches
+ this compatObjectFormat. In particular when fully implemented the
+ pushes and pulls from a repository in whose objectFormat matches
+ compatObjectFormat. As well as being able to use oids encoded in
+ compatObjectFormat in addition to oids encoded with objectFormat to
+ locally specify objects.
+
+extensions.refStorage::
+ Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
extensions.worktreeConfig::
If enabled, then worktrees will load config settings from the
`$GIT_DIR/config.worktree` file in addition to the
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index bf9546fca4..f061b64b74 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -17,6 +17,9 @@ skipping more commits at a time, reducing the number of round trips.
+
* `pack.useBitmapBoundaryTraversal=true` may improve bitmap traversal times by
walking fewer objects.
++
+* `pack.allowPackReuse=multi` may improve the time it takes to create a pack by
+reusing objects from multiple packs instead of just one.
feature.manyFiles::
Enable config options that optimize for repos with many files in the
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index aea5b97477..d7dc461bd1 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -50,6 +50,12 @@ fetch.pruneTags::
refs. See also `remote.<name>.pruneTags` and the PRUNING
section of linkgit:git-fetch[1].
+fetch.all::
+ If true, fetch will attempt to update all available remotes.
+ This behavior can be overridden by passing `--no-all` or by
+ explicitly specifying one or more remote(s) to fetch from.
+ Defaults to false.
+
fetch.output::
Control how ref update status is printed. Valid values are
`full` and `compact`. Default value is `full`. See the
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index c98412b697..7410e930e5 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -119,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
diff --git a/Documentation/config/grep.txt b/Documentation/config/grep.txt
index e521f20390..10041f27b0 100644
--- a/Documentation/config/grep.txt
+++ b/Documentation/config/grep.txt
@@ -24,5 +24,5 @@ grep.fullName::
If set to true, enable `--full-name` option by default.
grep.fallbackToNoIndex::
- If set to true, fall back to git grep --no-index if git grep
+ If set to true, fall back to `git grep --no-index` if `git grep`
is executed outside of a git repository. Defaults to false.
diff --git a/Documentation/config/init.txt b/Documentation/config/init.txt
index 79c79d6617..af03acdbcb 100644
--- a/Documentation/config/init.txt
+++ b/Documentation/config/init.txt
@@ -1,7 +1,10 @@
-init.templateDir::
- Specify the directory from which templates will be copied.
- (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+:see-git-init:
+ifndef::git-init[]
+:see-git-init: (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+endif::[]
-init.defaultBranch::
+`init.templateDir`::
+ Specify the directory from which templates will be copied. {see-git-init}
+`init.defaultBranch`::
Allows overriding the default branch name e.g. when initializing
a new repository.
diff --git a/Documentation/config/interactive.txt b/Documentation/config/interactive.txt
index a2d3c7ec44..5cc26555f1 100644
--- a/Documentation/config/interactive.txt
+++ b/Documentation/config/interactive.txt
@@ -4,9 +4,7 @@ interactive.singleKey::
Currently this is used by the `--patch` mode of
linkgit:git-add[1], linkgit:git-checkout[1],
linkgit:git-restore[1], linkgit:git-commit[1],
- linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
- setting is silently ignored if portable keystroke input
- is not available; requires the Perl module Term::ReadKey.
+ linkgit:git-reset[1], and linkgit:git-stash[1].
interactive.diffFilter::
When an interactive command (such as `git add --patch`) shows
diff --git a/Documentation/config/mergetool.txt b/Documentation/config/mergetool.txt
index 294f61efd1..00bf665aa0 100644
--- a/Documentation/config/mergetool.txt
+++ b/Documentation/config/mergetool.txt
@@ -45,14 +45,21 @@ mergetool.meld.useAutoMerge::
value of `false` avoids using `--auto-merge` altogether, and is the
default value.
-mergetool.vimdiff.layout::
- The vimdiff backend uses this variable to control how its split
- windows appear. Applies even if you are using Neovim (`nvim`) or
- gVim (`gvim`) as the merge tool. See BACKEND SPECIFIC HINTS section
+mergetool.<vimdiff variant>.layout::
+ Configure the split window layout for vimdiff's `<variant>`, which is any of `vimdiff`,
+ `nvimdiff`, `gvimdiff`.
+ Upon launching `git mergetool` with `--tool=<variant>` (or without `--tool`
+ if `merge.tool` is configured as `<variant>`), Git will consult
+ `mergetool.<variant>.layout` to determine the tool's layout. If the
+ variant-specific configuration is not available, `vimdiff`'s is used as
+ fallback. If that too is not available, a default layout with 4 windows
+ will be used. To configure the layout, see the `BACKEND SPECIFIC HINTS`
+ifdef::git-mergetool[]
+ section.
+endif::[]
ifndef::git-mergetool[]
- in linkgit:git-mergetool[1].
+ section in linkgit:git-mergetool[1].
endif::[]
- for details.
mergetool.hideResolved::
During a merge, Git will automatically resolve as many conflicts as
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index d59576dbb2..c6187ab28b 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -40,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
diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt
index 7fc770ee9e..6a869d67eb 100644
--- a/Documentation/config/sendemail.txt
+++ b/Documentation/config/sendemail.txt
@@ -8,7 +8,7 @@ sendemail.smtpEncryption::
See linkgit:git-send-email[1] for description. Note that this
setting is not subject to the 'identity' mechanism.
-sendemail.smtpsslcertpath::
+sendemail.smtpSSLCertPath::
Path to ca-certificates (either a directory or a single file).
Set it to an empty string to disable certificate verification.
@@ -62,12 +62,12 @@ sendemail.chainReplyTo::
sendemail.envelopeSender::
sendemail.from::
sendemail.headerCmd::
-sendemail.signedoffbycc::
+sendemail.signedOffByCc::
sendemail.smtpPass::
-sendemail.suppresscc::
+sendemail.suppressCc::
sendemail.suppressFrom::
sendemail.to::
-sendemail.tocmd::
+sendemail.toCmd::
sendemail.smtpDomain::
sendemail.smtpServer::
sendemail.smtpServerPort::
@@ -81,8 +81,8 @@ sendemail.xmailer::
linkgit:git-send-email[1] command-line options. See its
documentation for details.
-sendemail.signedoffcc (deprecated)::
- Deprecated alias for `sendemail.signedoffbycc`.
+sendemail.signedOffCc (deprecated)::
+ Deprecated alias for `sendemail.signedOffByCc`.
sendemail.smtpBatchSize::
Number of messages to be sent per connection, after that a relogin
diff --git a/Documentation/config/status.txt b/Documentation/config/status.txt
index 2ff8237f8f..8caf90f51c 100644
--- a/Documentation/config/status.txt
+++ b/Documentation/config/status.txt
@@ -57,6 +57,8 @@ status.showUntrackedFiles::
--
+
If this variable is not specified, it defaults to 'normal'.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
This variable can be overridden with the -u|--untracked-files option
of linkgit:git-status[1] and linkgit:git-commit[1].
diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt
index a9cbdb88a1..f1ce50f4a6 100644
--- a/Documentation/config/transfer.txt
+++ b/Documentation/config/transfer.txt
@@ -121,3 +121,7 @@ transfer.bundleURI::
information from the remote server (if advertised) and download
bundles before continuing the clone through the Git protocol.
Defaults to `false`.
+
+transfer.advertiseObjectInfo::
+ When `true`, the `object-info` capability is advertised by
+ servers. Defaults to false.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 53ec3c9a34..0e9456957e 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -299,7 +299,7 @@ and accumulating child directory counts in the parent directories:
Synonym for --dirstat=cumulative
--dirstat-by-file[=<param1,param2>...]::
- Synonym for --dirstat=files,param1,param2...
+ Synonym for --dirstat=files,<param1>,<param2>...
--summary::
Output a condensed summary of extended header information
@@ -865,8 +865,9 @@ endif::git-format-patch[]
--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`.
+ This overrides configuration variables such as `diff.noprefix`,
+ `diff.srcPrefix`, `diff.dstPrefix`, and `diff.mnemonicPrefix`
+ (see `git-config`(1)).
--line-prefix=<prefix>::
Prepend an additional prefix to every line of output.
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index a1d6633a4f..e22b217fba 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -1,5 +1,6 @@
---all::
- Fetch all remotes.
+--[no-]all::
+ Fetch all remotes. This overrides the configuration variable
+ `fetch.all`.
-a::
--append::
@@ -201,7 +202,7 @@ endif::git-pull[]
destination of an explicit refspec; see `--prune`).
ifndef::git-pull[]
---recurse-submodules[=yes|on-demand|no]::
+--recurse-submodules[=(yes|on-demand|no)]::
This option controls if and under what conditions new commits of
submodules should be fetched too. When recursing through submodules,
`git fetch` always attempts to fetch "changed" submodules, that is, a
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ed44c1cb31..aceaa025e3 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -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>...]
@@ -63,7 +63,7 @@ OPTIONS
to ignore removed files; use `--no-all` option if you want
to add modified or new files but ignore removed ones.
+
-For more details about the <pathspec> syntax, see the 'pathspec' entry
+For more details about the _<pathspec>_ syntax, see the 'pathspec' entry
in linkgit:gitglossary[7].
-n::
@@ -119,10 +119,10 @@ apply to the index. See EDITING PATCHES below.
-u::
--update::
Update the index just where it already has an entry matching
- <pathspec>. This removes as well as modifies index entries to
+ _<pathspec>_. This removes as well as modifies index entries to
match the working tree, but adds no new files.
+
-If no <pathspec> is given when `-u` option is used, all
+If no _<pathspec>_ is given when `-u` option is used, all
tracked files in the entire working tree are updated (old versions
of Git used to limit the update to the current directory and its
subdirectories).
@@ -131,11 +131,11 @@ subdirectories).
--all::
--no-ignore-removal::
Update the index not only where the working tree has a file
- matching <pathspec> but also where the index already has an
+ matching _<pathspec>_ but also where the index already has an
entry. This adds, modifies, and removes index entries to
match the working tree.
+
-If no <pathspec> is given when `-A` option is used, all
+If no _<pathspec>_ is given when `-A` option is used, all
files in the entire working tree are updated (old versions
of Git used to limit the update to the current directory and its
subdirectories).
@@ -145,11 +145,11 @@ subdirectories).
Update the index by adding new files that are unknown to the
index and files modified in the working tree, but ignore
files that have been removed from the working tree. This
- option is a no-op when no <pathspec> is used.
+ option is a no-op when no _<pathspec>_ is used.
+
This option is primarily to help users who are used to older
-versions of Git, whose "git add <pathspec>..." was a synonym
-for "git add --no-all <pathspec>...", i.e. ignored removed files.
+versions of Git, whose "git add _<pathspec>_..." was a synonym
+for "git add --no-all _<pathspec>_...", i.e. ignored removed files.
-N::
--intent-to-add::
@@ -198,8 +198,8 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
unchanged.
--pathspec-from-file=<file>::
- Pathspec is passed in `<file>` instead of commandline args. If
- `<file>` is exactly `-` then standard input is used. Pathspec
+ Pathspec is passed in _<file>_ instead of commandline args. If
+ _<file>_ is exactly `-` then standard input is used. Pathspec
elements are separated by LF or CR/LF. Pathspec elements can be
quoted as explained for the configuration variable `core.quotePath`
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
@@ -348,6 +348,7 @@ patch::
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
+ p - print the current hunk
? - print help
+
After deciding the fate for all hunks, if there is any hunk
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index e080458d6c..624a6e6fe4 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -66,13 +66,19 @@ OPTIONS
--quoted-cr=<action>::
This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
---empty=(stop|drop|keep)::
- By default, or when the option is set to 'stop', the command
- errors out on an input e-mail message lacking a patch
- and stops 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.
+--empty=(drop|keep|stop)::
+ How to handle an e-mail message lacking a patch:
++
+--
+`drop`;;
+ The e-mail message will be skipped.
+`keep`;;
+ An empty commit will be created, with the contents of the e-mail
+ message as its log.
+`stop`;;
+ The command will fail, stopping in the middle of the current `am`
+ session. This is the default behavior.
+--
-m::
--message-id::
@@ -128,6 +134,9 @@ include::rerere-options.txt[]
These flags are passed to the 'git apply' (see linkgit:git-apply[1])
program that applies
the patch.
++
+Valid <action> for the `--whitespace` option are:
+`nowarn`, `warn`, `fix`, `error`, and `error-all`.
--patch-format::
By default the command will try to detect the patch format
diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt
index 191b4a42b6..82f944dc03 100644
--- a/Documentation/git-bisect.txt
+++ b/Documentation/git-bisect.txt
@@ -16,11 +16,11 @@ DESCRIPTION
The command takes various subcommands, and different options depending
on the subcommand:
- git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
- [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
+ git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
+ [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
- git bisect terms [--term-good | --term-bad]
+ git bisect terms [--term-(good|old) | --term-(bad|new)]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
@@ -165,8 +165,10 @@ To get a reminder of the currently used terms, use
git bisect terms
------------------------------------------------
-You can get just the old (respectively new) term with `git bisect terms
---term-old` or `git bisect terms --term-good`.
+You can get just the old term with `git bisect terms --term-old`
+or `git bisect terms --term-good`; `git bisect terms --term-new`
+and `git bisect terms --term-bad` can be used to learn how to call
+the commits more recent than the sought change.
If you would like to use your own terms instead of "bad"/"good" or
"new"/"old", you can choose any names you like (except existing bisect
@@ -299,7 +301,7 @@ Cutting down bisection by giving more parameters to bisect start
You can further cut down the number of trials, if you know what part of
the tree is involved in the problem you are tracking down, by specifying
-path parameters when issuing the `bisect start` command:
+pathspec parameters when issuing the `bisect start` command:
------------
$ git bisect start -- arch/i386 include/asm-i386
@@ -362,7 +364,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
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 5720d04ffe..b1d7fb539d 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -210,7 +210,7 @@ annotated.
. Each blame entry always starts with a line of:
- <40-byte hex sha1> <sourceline> <resultline> <num_lines>
+ <40-byte-hex-sha1> <sourceline> <resultline> <num-lines>
+
Line numbers count from 1.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4395aa9354..0b08442932 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -312,7 +312,8 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
option is omitted, the current HEAD will be used instead.
<oldbranch>::
- The name of an existing branch to rename.
+ The name of an existing branch. If this option is omitted,
+ the name of the current branch will be used instead.
<newbranch>::
The new name for an existing branch. The same restrictions as for
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
index 392d9eb6ae..112658b3c3 100644
--- a/Documentation/git-bugreport.txt
+++ b/Documentation/git-bugreport.txt
@@ -8,7 +8,8 @@ git-bugreport - Collect information for user to file a bug report
SYNOPSIS
--------
[verse]
-'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+'git bugreport' [(-o | --output-directory) <path>]
+ [(-s | --suffix) <format> | --no-suffix]
[--diagnose[=<mode>]]
DESCRIPTION
@@ -51,16 +52,19 @@ OPTIONS
-s <format>::
--suffix <format>::
+--no-suffix::
Specify an alternate suffix for the bugreport name, to create a file
- named 'git-bugreport-<formatted suffix>'. This should take the form of a
+ named 'git-bugreport-<formatted-suffix>'. This should take the form of a
strftime(3) format string; the current local time will be used.
+ `--no-suffix` disables the suffix and the file is just named
+ `git-bugreport` without any disambiguation measure.
--no-diagnose::
--diagnose[=<mode>]::
Create a zip archive of supplemental information about the user's
machine, Git client, and repository state. The archive is written to the
same output directory as the bug report and is named
- 'git-diagnostics-<formatted suffix>'.
+ 'git-diagnostics-<formatted-suffix>'.
+
Without `mode` specified, the diagnostic archive will contain the default set of
statistics reported by `git diagnose`. An optional `mode` value may be specified
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 240c54639e..8bdfa54ab0 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -63,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>::
@@ -215,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
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index fdcad3d200..81ace900fc 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -131,20 +131,36 @@ effect to your index in a row.
even without this option. Note also, that use of this option only
keeps commits that were initially empty (i.e. the commit recorded the
same tree as its parent). Commits which are made empty due to a
- previous commit are dropped. To force the inclusion of those commits
- use `--keep-redundant-commits`.
+ previous commit will cause the cherry-pick to fail. To force the
+ inclusion of those commits, use `--empty=keep`.
--allow-empty-message::
By default, cherry-picking a commit with an empty message will fail.
This option overrides that behavior, allowing commits with empty
messages to be cherry picked.
+--empty=(drop|keep|stop)::
+ How to handle commits being cherry-picked that are redundant with
+ changes already in the current history.
++
+--
+`drop`;;
+ The commit will be dropped.
+`keep`;;
+ The commit will be kept. Implies `--allow-empty`.
+`stop`;;
+ The cherry-pick will stop when the commit is applied, allowing
+ you to examine the commit. This is the default behavior.
+--
++
+Note that `--empty=drop` and `--empty=stop` only specify how to handle a
+commit that was not initially empty, but rather became empty due to a previous
+commit. Commits that were initially empty will still cause the cherry-pick to
+fail unless one of `--empty=keep` or `--allow-empty` are specified.
++
+
--keep-redundant-commits::
- If a commit being cherry picked duplicates a commit already in the
- current history, it will become empty. By default these
- redundant commits cause `cherry-pick` to stop so the user can
- examine the commit. This option overrides that behavior and
- creates an empty commit object. Implies `--allow-empty`.
+ Deprecated synonym for `--empty=keep`.
--strategy=<strategy>::
Use the given merge strategy. Should only be used once.
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 69331e3f05..fd17165416 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -37,7 +37,7 @@ OPTIONS
--force::
If the Git configuration variable clean.requireForce is not set
to false, 'git clean' will refuse to delete files or directories
- unless given -f or -i. Git will refuse to modify untracked
+ unless given -f. Git will refuse to modify untracked
nested git repositories (directories with a .git subdirectory)
unless a second -f is given.
@@ -45,10 +45,14 @@ OPTIONS
--interactive::
Show what would be done and clean files interactively. See
``Interactive mode'' for details.
+ Configuration variable `clean.requireForce` is ignored, as
+ this mode gives its own safety protection by going interactive.
-n::
--dry-run::
Don't actually remove anything, just show what would be done.
+ Configuration variable `clean.requireForce` is ignored, as
+ nothing will be deleted anyway.
-q::
--quiet::
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index c37c4a37f7..5de18de2ab 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,15 +9,15 @@ git-clone - Clone a repository into a new directory
SYNOPSIS
--------
[verse]
-'git clone' [--template=<template-directory>]
- [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
- [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
- [--dissociate] [--separate-git-dir <git-dir>]
- [--depth <depth>] [--[no-]single-branch] [--no-tags]
- [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
- [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
- [--filter=<filter> [--also-filter-submodules]] [--] <repository>
- [<directory>]
+`git clone` [++--template=++__<template-directory>__]
+ [`-l`] [`-s`] [`--no-hardlinks`] [`-q`] [`-n`] [`--bare`] [`--mirror`]
+ [`-o` _<name>_] [`-b` _<name>_] [`-u` _<upload-pack>_] [`--reference` _<repository>_]
+ [`--dissociate`] [`--separate-git-dir` _<git-dir>_]
+ [`--depth` _<depth>_] [`--`[`no-`]`single-branch`] [`--no-tags`]
+ [++--recurse-submodules++[++=++__<pathspec>__]] [`--`[`no-`]`shallow-submodules`]
+ [`--`[`no-`]`remote-submodules`] [`--jobs` _<n>_] [`--sparse`] [`--`[`no-`]`reject-shallow`]
+ [++--filter=++__<filter-spec>__] [`--also-filter-submodules`]] [`--`] _<repository>_
+ [_<directory>_]
DESCRIPTION
-----------
@@ -31,7 +31,7 @@ currently active branch.
After the clone, a plain `git fetch` without arguments will update
all the remote-tracking branches, and a `git pull` without
arguments will in addition merge the remote master branch into the
-current master branch, if any (this is untrue when "--single-branch"
+current master branch, if any (this is untrue when `--single-branch`
is given; see below).
This default configuration is achieved by creating references to
@@ -42,12 +42,12 @@ configuration variables.
OPTIONS
-------
--l::
---local::
+`-l`::
+`--local`::
When the repository to clone from is on a local machine,
this flag bypasses the normal "Git aware" transport
mechanism and clones the repository by making a copy of
- HEAD and everything under objects and refs directories.
+ `HEAD` and everything under objects and refs directories.
The files under `.git/objects/` directory are hardlinked
to save space when possible.
+
@@ -67,14 +67,14 @@ links.
source repository, similar to running `cp -r src dst` while modifying
`src`.
---no-hardlinks::
+`--no-hardlinks`::
Force the cloning process from a repository on a local
filesystem to copy the files under the `.git/objects`
directory instead of using hardlinks. This may be desirable
if you are trying to make a back-up of your repository.
--s::
---shared::
+`-s`::
+`--shared`::
When the repository to clone is on the local machine,
instead of using hard links, automatically setup
`.git/objects/info/alternates` to share the objects
@@ -101,10 +101,10 @@ If you want to break the dependency of a repository cloned with `--shared` on
its source repository, you can simply run `git repack -a` to copy all
objects from the source repository into a pack in the cloned repository.
---reference[-if-able] <repository>::
- If the reference repository is on the local machine,
+`--reference`[`-if-able`] _<repository>_::
+ If the reference _<repository>_ is on the local machine,
automatically setup `.git/objects/info/alternates` to
- obtain objects from the reference repository. Using
+ obtain objects from the reference _<repository>_. Using
an already existing repository as an alternate will
require fewer objects to be copied from the repository
being cloned, reducing network and local storage costs.
@@ -115,7 +115,7 @@ objects from the source repository into a pack in the cloned repository.
*NOTE*: see the NOTE for the `--shared` option, and also the
`--dissociate` option.
---dissociate::
+`--dissociate`::
Borrow the objects from reference repositories specified
with the `--reference` options only to reduce network
transfer, and stop borrowing from them after a clone is made
@@ -126,43 +126,43 @@ objects from the source repository into a pack in the cloned repository.
same repository, and this option can be used to stop the
borrowing.
--q::
---quiet::
+`-q`::
+`--quiet`::
Operate quietly. Progress is not reported to the standard
error stream.
--v::
---verbose::
+`-v`::
+`--verbose`::
Run verbosely. Does not affect the reporting of progress status
to the standard error stream.
---progress::
+`--progress`::
Progress status is reported on the standard error stream
by default when it is attached to a terminal, unless `--quiet`
is specified. This flag forces progress status even if the
standard error stream is not directed to a terminal.
---server-option=<option>::
+++--server-option=++__<option>__::
Transmit the given string to the server when communicating using
protocol version 2. The given string must not contain a NUL or LF
character. The server's handling of server options, including
unknown ones, is server-specific.
- When multiple `--server-option=<option>` are given, they are all
+ When multiple ++--server-option=++__<option>__ are given, they are all
sent to the other side in the order listed on the command line.
--n::
---no-checkout::
+`-n`::
+`--no-checkout`::
No checkout of HEAD is performed after the clone is complete.
---[no-]reject-shallow::
+`--`[`no-`]`reject-shallow`::
Fail if the source repository is a shallow repository.
- The 'clone.rejectShallow' configuration variable can be used to
+ The `clone.rejectShallow` configuration variable can be used to
specify the default.
---bare::
+`--bare`::
Make a 'bare' Git repository. That is, instead of
- creating `<directory>` and placing the administrative
- files in `<directory>/.git`, make the `<directory>`
+ creating _<directory>_ and placing the administrative
+ files in _<directory>_`/.git`, make the _<directory>_
itself the `$GIT_DIR`. This obviously implies the `--no-checkout`
because there is nowhere to check out the working tree.
Also the branch heads at the remote are copied directly
@@ -171,28 +171,28 @@ objects from the source repository into a pack in the cloned repository.
used, neither remote-tracking branches nor the related
configuration variables are created.
---sparse::
+`--sparse`::
Employ a sparse-checkout, with only files in the toplevel
directory initially being present. The
linkgit:git-sparse-checkout[1] command can be used to grow the
working directory as needed.
---filter=<filter-spec>::
+++--filter=++__<filter-spec>__::
Use the partial clone feature and request that the server sends
a subset of reachable objects according to a given object filter.
- When using `--filter`, the supplied `<filter-spec>` is used for
+ When using `--filter`, the supplied _<filter-spec>_ is used for
the partial clone filter. For example, `--filter=blob:none` will
filter out all blobs (file contents) until needed by Git. Also,
- `--filter=blob:limit=<size>` will filter out all blobs of size
- at least `<size>`. For more details on filter specifications, see
+ ++--filter=blob:limit=++__<size>__ will filter out all blobs of size
+ at least _<size>_. For more details on filter specifications, see
the `--filter` option in linkgit:git-rev-list[1].
---also-filter-submodules::
+`--also-filter-submodules`::
Also apply the partial clone filter to any submodules in the repository.
Requires `--filter` and `--recurse-submodules`. This can be turned on by
default by setting the `clone.filterSubmodules` config option.
---mirror::
+`--mirror`::
Set up a mirror of the source repository. This implies `--bare`.
Compared to `--bare`, `--mirror` not only maps local branches of the
source to local branches of the target, it maps all refs (including
@@ -200,37 +200,37 @@ objects from the source repository into a pack in the cloned repository.
that all these refs are overwritten by a `git remote update` in the
target repository.
--o <name>::
---origin <name>::
+`-o` _<name>_::
+`--origin` _<name>_::
Instead of using the remote name `origin` to keep track of the upstream
- repository, use `<name>`. Overrides `clone.defaultRemoteName` from the
+ repository, use _<name>_. Overrides `clone.defaultRemoteName` from the
config.
--b <name>::
---branch <name>::
+`-b` _<name>_::
+`--branch` _<name>_::
Instead of pointing the newly created HEAD to the branch pointed
- to by the cloned repository's HEAD, point to `<name>` branch
+ to by the cloned repository's HEAD, point to _<name>_ branch
instead. In a non-bare repository, this is the branch that will
be checked out.
`--branch` can also take tags and detaches the HEAD at that commit
in the resulting repository.
--u <upload-pack>::
---upload-pack <upload-pack>::
+`-u` _<upload-pack>_::
+`--upload-pack` _<upload-pack>_::
When given, and the repository to clone from is accessed
via ssh, this specifies a non-default path for the command
run on the other end.
---template=<template-directory>::
+++--template=++__<template-directory>__::
Specify the directory from which templates will be used;
(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
--c <key>=<value>::
---config <key>=<value>::
+`-c` __<key>__++=++__<value>__::
+`--config` __<key>__++=++__<value>__::
Set a configuration variable in the newly-created repository;
this takes effect immediately after the repository is
initialized, but before the remote history is fetched or any
- files checked out. The key is in the same format as expected by
+ files checked out. The _<key>_ is in the same format as expected by
linkgit:git-config[1] (e.g., `core.eol=true`). If multiple
values are given for the same key, each value will be written to
the config file. This makes it safe, for example, to add
@@ -239,35 +239,35 @@ objects from the source repository into a pack in the cloned repository.
Due to limitations of the current implementation, some configuration
variables do not take effect until after the initial fetch and checkout.
Configuration variables known to not take effect are:
-`remote.<name>.mirror` and `remote.<name>.tagOpt`. Use the
+++remote.++__<name>__++.mirror++ and ++remote.++__<name>__++.tagOpt++. Use the
corresponding `--mirror` and `--no-tags` options instead.
---depth <depth>::
+`--depth` _<depth>_::
Create a 'shallow' clone with a history truncated to the
specified number of commits. Implies `--single-branch` unless
`--no-single-branch` is given to fetch the histories near the
tips of all branches. If you want to clone submodules shallowly,
also pass `--shallow-submodules`.
---shallow-since=<date>::
+++--shallow-since=++__<date>__::
Create a shallow clone with a history after the specified time.
---shallow-exclude=<revision>::
+++--shallow-exclude=++__<revision>__::
Create a shallow clone with a history, excluding commits
reachable from a specified remote branch or tag. This option
can be specified multiple times.
---[no-]single-branch::
+`--`[`no-`]`single-branch`::
Clone only the history leading to the tip of a single branch,
either specified by the `--branch` option or the primary
branch remote's `HEAD` points at.
Further fetches into the resulting repository will only update the
remote-tracking branch for the branch this option was used for the
- initial cloning. If the HEAD at the remote did not point at any
+ initial cloning. If the `HEAD` at the remote did not point at any
branch when `--single-branch` clone was made, no remote-tracking
branch is created.
---no-tags::
+`--no-tags`::
Don't clone any tags, and set
`remote.<remote>.tagOpt=--no-tags` in the config, ensuring
that future `git pull` and `git fetch` operations won't follow
@@ -279,9 +279,9 @@ maintain a branch with no references other than a single cloned
branch. This is useful e.g. to maintain minimal clones of the default
branch of some repository for search indexing.
---recurse-submodules[=<pathspec>]::
+`--recurse-submodules`[`=`{empty}__<pathspec>__]::
After the clone is created, initialize and clone submodules
- within based on the provided pathspec. If no pathspec is
+ within based on the provided _<pathspec>_. If no _=<pathspec>_ is
provided, all submodules are initialized and cloned.
This option can be given multiple times for pathspecs consisting
of multiple entries. The resulting clone has `submodule.active` set to
@@ -295,42 +295,48 @@ the clone is finished. This option is ignored if the cloned repository does
not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`,
or `--mirror` is given)
---[no-]shallow-submodules::
+`--`[`no-`]`shallow-submodules`::
All submodules which are cloned will be shallow with a depth of 1.
---[no-]remote-submodules::
+`--`[`no-`]`remote-submodules`::
All submodules which are cloned will use the status of the submodule's
remote-tracking branch to update the submodule, rather than the
superproject's recorded SHA-1. Equivalent to passing `--remote` to
`git submodule update`.
---separate-git-dir=<git-dir>::
+`--separate-git-dir=`{empty}__<git-dir>__::
Instead of placing the cloned repository where it is supposed
to be, place the cloned repository at the specified directory,
then make a filesystem-agnostic Git symbolic link to there.
The result is Git repository can be separated from working
tree.
--j <n>::
---jobs <n>::
+`--ref-format=`{empty}__<ref-format>__::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
+`-j` _<n>_::
+`--jobs` _<n>_::
The number of submodules fetched at the same time.
Defaults to the `submodule.fetchJobs` option.
-<repository>::
- The (possibly remote) repository to clone from. See the
+_<repository>_::
+ The (possibly remote) _<repository>_ to clone from. See the
<<URLS,GIT URLS>> section below for more information on specifying
repositories.
-<directory>::
+_<directory>_::
The name of a new directory to clone into. The "humanish"
- part of the source repository is used if no directory is
+ part of the source repository is used if no _<directory>_ is
explicitly given (`repo` for `/path/to/repo.git` and `foo`
for `host.xz:foo/.git`). Cloning into an existing directory
is only allowed if the directory is empty.
---bundle-uri=<uri>::
+`--bundle-uri=`{empty}__<uri>__::
Before fetching from the remote, fetch a bundle from the given
- `<uri>` and unbundle the data into the local repository. The refs
+ _<uri>_ and unbundle the data into the local repository. The refs
in the bundle will be stored under the hidden `refs/bundle/*`
namespace. This option is incompatible with `--depth`,
`--shallow-since`, and `--shallow-exclude`.
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index c8dbceba01..903b16830e 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -13,7 +13,7 @@ SYNOPSIS
'git commit-graph write' [--object-dir <dir>] [--append]
[--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]
[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]
- <split options>
+ <split-options>
DESCRIPTION
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a6cef5d820..89ecfc63a8 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -347,6 +347,8 @@ The possible options are:
- 'normal' - Shows untracked files and directories
- 'all' - Also shows individual files in untracked directories.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
--
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index b1caac887a..ac61113fcc 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,9 +9,9 @@ git-config - Get and set repository or global options
SYNOPSIS
--------
[verse]
-'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
+'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
@@ -87,6 +87,18 @@ OPTIONS
values. This is the same as providing '^$' as the `value-pattern`
in `--replace-all`.
+--comment <message>::
+ Append a comment at the end of new or modified lines.
+
+ If _<message>_ begins with one or more whitespaces followed
+ by "#", it is used as-is. If it begins with "#", a space is
+ prepended before it is used. Otherwise, a string " # " (a
+ space followed by a hash followed by a space) is prepended
+ to it. And the resulting string is placed immediately after
+ the value defined for the variable. The _<message>_ must
+ not contain linefeed characters (no multi-line comments are
+ permitted).
+
--get::
Get the value for a given key (optionally filtered by a regex
matching the value). Returns error code 1 if the key was not
@@ -103,11 +115,11 @@ OPTIONS
names are not.
--get-urlmatch <name> <URL>::
- When given a two-part name section.key, the value for
- section.<URL>.key whose <URL> part matches the best to the
+ When given a two-part <name> as <section>.<key>, the value for
+ <section>.<URL>.<key> whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
- section.key is used as a fallback). When given just the
- section as name, do so for all the keys in the section and
+ <section>.<key> is used as a fallback). When given just the
+ <section> as name, do so for all the keys in the section and
list them. Returns error code 1 if no value is found.
--global::
@@ -275,7 +287,8 @@ Valid `<type>`'s include:
-e::
--edit::
Opens an editor to modify the specified config file; either
- `--system`, `--global`, or repository (default).
+ `--system`, `--global`, `--local` (default), `--worktree`, or
+ `--file <config-file>`.
--[no-]includes::
Respect `include.*` directives in config files when looking up
@@ -285,7 +298,7 @@ Valid `<type>`'s include:
--default <value>::
When using `--get`, and the requested variable is not found, behave as if
- <value> were the value assigned to the that variable.
+ <value> were the value assigned to that variable.
CONFIGURATION
-------------
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index b3f27671a0..90fdc2551a 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -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
---
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index cf4a5a283e..4c475efeab 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -197,7 +197,7 @@ allowing access over SSH.
5. Clients should now be able to check out the project. Use the CVS 'module'
name to indicate what Git 'head' you want to check out. This also sets the
name of your newly checked-out directory, unless you tell it otherwise with
- `-d <dir_name>`. For example, this checks out 'master' branch to the
+ `-d <dir-name>`. For example, this checks out 'master' branch to the
`project-master` directory:
+
------
@@ -224,7 +224,7 @@ the database to work reliably (otherwise you need to make sure
that the database is up to date any time 'git-cvsserver' is executed).
By default it uses SQLite databases in the Git directory, named
-`gitcvs.<module_name>.sqlite`. Note that the SQLite backend creates
+`gitcvs.<module-name>.sqlite`. Note that the SQLite backend creates
temporary files in the same directory as the database file on
write so it might not be enough to grant the users using
'git-cvsserver' write access to the database file without granting
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index e064f91c9e..ede7b935d6 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -18,7 +18,7 @@ SYNOPSIS
[--allow-override=<service>] [--forbid-override=<service>]
[--access-hook=<path>] [--[no-]informative-errors]
[--inetd |
- [--listen=<host_or_ipaddr>] [--port=<n>]
+ [--listen=<host-or-ipaddr>] [--port=<n>]
[--user=<user> [--group=<group>]]]
[--log-destination=(stderr|syslog|none)]
[<directory>...]
@@ -86,10 +86,10 @@ OPTIONS
Incompatible with --detach, --port, --listen, --user and --group
options.
---listen=<host_or_ipaddr>::
+--listen=<host-or-ipaddr>::
Listen on a specific IP address or hostname. IP addresses can
be either an IPv4 address or an IPv6 address if supported. If IPv6
- is not supported, then --listen=hostname is also not supported and
+ is not supported, then --listen=<hostname> is also not supported and
--listen must be given an IPv4 address.
Can be given more than once.
Incompatible with `--inetd` option.
@@ -141,8 +141,8 @@ otherwise `stderr`.
specified with no parameter, a request to
git://host/{tilde}alice/foo is taken as a request to access
'foo' repository in the home directory of user `alice`.
- If `--user-path=path` is specified, the same request is
- taken as a request to access `path/foo` repository in
+ If `--user-path=<path>` is specified, the same request is
+ taken as a request to access `<path>/foo` repository in
the home directory of user `alice`.
--verbose::
diff --git a/Documentation/git-diagnose.txt b/Documentation/git-diagnose.txt
index 3ec8cc7ad7..0711959e6f 100644
--- a/Documentation/git-diagnose.txt
+++ b/Documentation/git-diagnose.txt
@@ -45,7 +45,7 @@ OPTIONS
-s <format>::
--suffix <format>::
Specify an alternate suffix for the diagnostics archive name, to create
- a file named 'git-diagnostics-<formatted suffix>'. This should take the
+ a file named 'git-diagnostics-<formatted-suffix>'. This should take the
form of a strftime(3) format string; the current local time will be
used.
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 08087ffad5..c065f023ec 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -103,7 +103,7 @@ 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>. A tree of interest is the one pointed to
-by the special ref `AUTO_MERGE`, which is written by the 'ort' merge
+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).
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index 50cb080085..a616f8b2e6 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -90,7 +90,7 @@ instead. `--no-symlinks` is the default on Windows.
--extcmd=<command>::
Specify a custom command for viewing diffs.
'git-difftool' ignores the configured defaults and runs
- `$command $LOCAL $REMOTE` when this option is specified.
+ `<command> $LOCAL $REMOTE` when this option is specified.
Additionally, `$BASE` is set in the environment.
-g::
@@ -105,7 +105,6 @@ instead. `--no-symlinks` is the default on Windows.
`merge.tool` until a tool is found.
--[no-]trust-exit-code::
- 'git-difftool' invokes a diff tool individually on each file.
Errors reported by the diff tool are ignored by default.
Use `--trust-exit-code` to make 'git-difftool' exit when an
invoked diff tool returns a non-zero exit code.
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 4643ddbe68..752e4b9b01 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -48,7 +48,7 @@ When asking to 'abort' (which is the default), this program will die
when encountering such a tag. With 'drop' it will omit such tags from
the output. With 'rewrite', if the tagged object is a commit, it will
rewrite the tag to tag an ancestor commit (via parent rewriting; see
-linkgit:git-rev-list[1])
+linkgit:git-rev-list[1]).
-M::
-C::
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index bd7b1e0a2e..8b6dde45f1 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -630,18 +630,28 @@ in octal. Git only supports the following modes:
In both formats `<path>` is the complete path of the file to be added
(if not already existing) or modified (if already existing).
-A `<path>` string must use UNIX-style directory separators (forward
-slash `/`), may contain any byte other than `LF`, and must not
-start with double quote (`"`).
-
-A path can use C-style string quoting; this is accepted in all cases
-and mandatory if the filename starts with double quote or contains
-`LF`. In C-style quoting, the complete name should be surrounded with
-double quotes, and any `LF`, backslash, or double quote characters
-must be escaped by preceding them with a backslash (e.g.,
-`"path/with\n, \\ and \" in it"`).
-
-The value of `<path>` must be in canonical form. That is it must not:
+A `<path>` can be written as unquoted bytes or a C-style quoted string.
+
+When a `<path>` does not start with a double quote (`"`), it is an
+unquoted string and is parsed as literal bytes without any escape
+sequences. However, if the filename contains `LF` or starts with double
+quote, it cannot be represented as an unquoted string and must be
+quoted. Additionally, the source `<path>` in `filecopy` or `filerename`
+must be quoted if it contains SP.
+
+When a `<path>` starts with a double quote (`"`), it is a C-style quoted
+string, where the complete filename is enclosed in a pair of double
+quotes and escape sequences are used. Certain characters must be escaped
+by preceding them with a backslash: `LF` is written as `\n`, backslash
+as `\\`, and double quote as `\"`. Some characters may optionally be
+written with escape sequences: `\a` for bell, `\b` for backspace, `\f`
+for form feed, `\n` for line feed, `\r` for carriage return, `\t` for
+horizontal tab, and `\v` for vertical tab. Any byte can be written with
+3-digit octal codes (e.g., `\033`). All filenames can be represented as
+quoted strings.
+
+A `<path>` must use UNIX-style directory separators (forward slash `/`)
+and its value must be in canonical form. That is it must not:
* contain an empty directory component (e.g. `foo//bar` is invalid),
* end with a directory separator (e.g. `foo/` is invalid),
@@ -651,6 +661,7 @@ The value of `<path>` must be in canonical form. That is it must not:
The root of the tree can be represented by an empty string as `<path>`.
+`<path>` cannot contain NUL, either literally or escaped as `\000`.
It is recommended that `<path>` always be encoded using UTF-8.
`filedelete`
@@ -745,11 +756,11 @@ paths for a commit are encouraged to do so.
`notemodify`
^^^^^^^^^^^^
-Included in a `commit` `<notes_ref>` command to add a new note
+Included in a `commit` `<notes-ref>` command to add a new note
annotating a `<commit-ish>` or change this annotation contents.
Internally it is similar to filemodify 100644 on `<commit-ish>`
path (maybe split into subdirectories). It's not advised to
-use any other commands to write to the `<notes_ref>` tree except
+use any other commands to write to the `<notes-ref>` tree except
`filedeleteall` to delete all existing notes in this tree.
This command has two different means of specifying the content
of the note.
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index f123139c58..50900a50da 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -186,8 +186,8 @@ origin:
------------------------------------------------
$ git fetch origin --prune --prune-tags
$ git fetch origin --prune 'refs/tags/*:refs/tags/*'
-$ git fetch <url of origin> --prune --prune-tags
-$ git fetch <url of origin> --prune 'refs/tags/*:refs/tags/*'
+$ git fetch <url-of-origin> --prune --prune-tags
+$ git fetch <url-of-origin> --prune 'refs/tags/*:refs/tags/*'
------------------------------------------------
OUTPUT
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 62e482a95e..5a4f853785 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -14,7 +14,7 @@ SYNOPSIS
[--msg-filter <command>] [--commit-filter <command>]
[--tag-name-filter <command>] [--prune-empty]
[--original <namespace>] [-d <directory>] [-f | --force]
- [--state-branch <branch>] [--] [<rev-list options>...]
+ [--state-branch <branch>] [--] [<rev-list-options>...]
WARNING
-------
@@ -32,7 +32,7 @@ listed there as reasonably possible.
DESCRIPTION
-----------
Lets you rewrite Git revision history by rewriting the branches mentioned
-in the <rev-list options>, applying custom filters on each revision.
+in the <rev-list-options>, applying custom filters on each revision.
Those filters can modify each tree (e.g. removing a file or running
a perl rewrite on all files) or information about each commit.
Otherwise, all information (including original commit times or merge
@@ -624,7 +624,7 @@ with:
real backup; it dereferences tags first.)
** Running git-filter-branch with either --tags or --all in your
- <rev-list options>. In order to retain annotated tags as
+ <rev-list-options>. In order to retain annotated tags as
annotated, you must use --tag-name-filter (and must not have
restored from refs/original/ in a previously botched rewrite).
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index be9543f684..c1dd12b93c 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
[(--sort=<key>)...] [--format=<format>]
- [ --stdin | <pattern>... ]
+ [--include-root-refs] [ --stdin | <pattern>... ]
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
@@ -105,6 +105,9 @@ TAB %(refname)`.
any excluded pattern(s) are shown. Matching is done using the
same rules as `<pattern>` above.
+--include-root-refs::
+ List root refs (HEAD and pseudorefs) apart from regular refs.
+
FIELD NAMES
-----------
@@ -359,9 +362,11 @@ In any case, a field name that refers to a field inapplicable to
the object referred by the ref does not cause an error. It
returns an empty string instead.
-As a special case for the date-type fields, you may specify a format for
-the date by adding `:` followed by date format name (see the
-values the `--date` option to linkgit:git-rev-list[1] takes).
+As a special case for the date-type fields, you may specify a format for the
+date by adding `:` followed by date format name (see the values the `--date`
+option to linkgit:git-rev-list[1] takes). If this formatting is provided in
+a `--sort` key, references will be sorted according to the byte-value of the
+formatted string rather than the numeric value of the underlying timestamp.
Some atoms like %(align) and %(if) always require a matching %(end).
We call them "opening atoms" and sometimes denote them as %($open).
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index aaafce24be..728bb3821c 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -17,10 +17,10 @@ SYNOPSIS
[--signature-file=<file>]
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
- [--in-reply-to=<message id>] [--suffix=.<sfx>]
+ [--in-reply-to=<message-id>] [--suffix=.<sfx>]
[--ignore-if-in-upstream] [--always]
[--cover-from-description=<mode>]
- [--rfc] [--subject-prefix=<subject prefix>]
+ [--rfc] [--subject-prefix=<subject-prefix>]
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
@@ -30,8 +30,8 @@ SYNOPSIS
[--range-diff=<previous> [--creation-factor=<percent>]]
[--filename-max-length=<n>]
[--progress]
- [<common diff options>]
- [ <since> | <revision range> ]
+ [<common-diff-options>]
+ [ <since> | <revision-range> ]
DESCRIPTION
-----------
@@ -64,7 +64,7 @@ There are two ways to specify which commits to operate on.
to the tip of the current branch that are not in the history
that leads to the <since> to be output.
-2. Generic <revision range> expression (see "SPECIFYING
+2. Generic <revision-range> expression (see "SPECIFYING
REVISIONS" section in linkgit:gitrevisions[7]) means the
commits in the specified range.
@@ -179,9 +179,9 @@ Beware that the default for 'git send-email' is to thread emails
itself. If you want `git format-patch` to take care of threading, you
will want to ensure that threading is disabled for `git send-email`.
---in-reply-to=<message id>::
+--in-reply-to=<message-id>::
Make the first mail (or all the mails with `--no-thread`) appear as a
- reply to the given <message id>, which avoids breaking threads to
+ reply to the given <message-id>, which avoids breaking threads to
provide a new patch series.
--ignore-if-in-upstream::
@@ -219,9 +219,9 @@ populated with placeholder text.
Use the contents of <file> instead of the branch's description
for generating the cover letter.
---subject-prefix=<subject prefix>::
+--subject-prefix=<subject-prefix>::
Instead of the standard '[PATCH]' prefix in the subject
- line, instead use '[<subject prefix>]'. This can be used
+ line, instead use '[<subject-prefix>]'. This can be used
to name a patch series, and can be combined with the
`--numbered` option.
+
@@ -403,7 +403,7 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
`format.useAutoBase` configuration.
--root::
- Treat the revision argument as a <revision range>, even if it
+ Treat the revision argument as a <revision-range>, even if it
is just a single commit (that would normally be treated as a
<since>). Note that root commits included in the specified
range are always formatted as creation patches, independently
@@ -610,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.
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 0d0103c780..1e6d7b65c8 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -28,7 +28,7 @@ SYNOPSIS
[-f <file>] [-e] <pattern>
[--and|--or|--not|(|)|-e <pattern>...]
[--recurse-submodules] [--parent-basename <basename>]
- [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+ [ [--[no-]exclude-standard] [--cached | --untracked | --no-index] | <tree>...]
[--] [<pathspec>...]
DESCRIPTION
@@ -45,13 +45,21 @@ OPTIONS
Instead of searching tracked files in the working tree, search
blobs registered in the index file.
---no-index::
- Search files in the current directory that is not managed by Git.
-
--untracked::
In addition to searching in the tracked files in the working
tree, search also in untracked files.
+--no-index::
+ Search files in the current directory that is not managed by Git,
+ or by ignoring that the current directory is managed by Git. This
+ is rather similar to running the regular `grep(1)` utility with its
+ `-r` option specified, but with some additional benefits, such as
+ using pathspec patterns to limit paths; see the 'pathspec' entry
+ in linkgit:gitglossary[7] for more information.
++
+This option cannot be used together with `--cached` or `--untracked`.
+See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below.
+
--no-exclude-standard::
Also search in ignored files by not honoring the `.gitignore`
mechanism. Only useful with `--untracked`.
@@ -64,9 +72,9 @@ OPTIONS
--recurse-submodules::
Recursively search in each submodule that is active and
checked out in the repository. When used in combination with the
- <tree> option the prefix of all submodule output will be the name of
- the parent project's <tree> object. This option has no effect
- if `--no-index` is given.
+ _<tree>_ option the prefix of all submodule output will be the name of
+ the parent project's _<tree>_ object. This option cannot be used together
+ with `--untracked`, and it has no effect if `--no-index` is specified.
-a::
--text::
@@ -178,7 +186,7 @@ providing this option will cause it to die.
Use \0 as the delimiter for pathnames in the output, and print
them verbatim. Without this option, pathnames with "unusual"
characters are quoted as explained for the configuration
- variable core.quotePath (see linkgit:git-config[1]).
+ variable `core.quotePath` (see linkgit:git-config[1]).
-o::
--only-matching::
@@ -248,8 +256,8 @@ providing this option will cause it to die.
a non-zero status.
--threads <num>::
- Number of grep worker threads to use.
- See `grep.threads` in 'CONFIGURATION' for more information.
+ Number of `grep` worker threads to use. See 'NOTES ON THREADS'
+ and `grep.threads` in 'CONFIGURATION' for more information.
-f <file>::
Read patterns from <file>, one per line.
@@ -332,13 +340,13 @@ EXAMPLES
NOTES ON THREADS
----------------
-The `--threads` option (and the grep.threads configuration) will be ignored when
+The `--threads` option (and the `grep.threads` configuration) will be ignored when
`--open-files-in-pager` is used, forcing a single-threaded execution.
When grepping the object store (with `--cached` or giving tree objects), running
-with multiple threads might perform slower than single threaded if `--textconv`
-is given and there are too many text conversions. So if you experience low
-performance in this case, it might be desirable to use `--threads=1`.
+with multiple threads might perform slower than single-threaded if `--textconv`
+is given and there are too many text conversions. Thus, if low performance is
+experienced in this case, it might be desirable to use `--threads=1`.
CONFIGURATION
-------------
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index f7b1851514..c8a89d7243 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -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
--------
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 6486620c3d..5a20deefd5 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -79,8 +79,13 @@ OPTIONS
to force the version for the generated pack index, and to force
64-bit index entries on objects located above the given offset.
---strict::
- Die, if the pack contains broken objects or links.
+--strict[=<msg-id>=<severity>...]::
+ Die, if the pack contains broken objects or links. An optional
+ comma-separated list of `<msg-id>=<severity>` can be passed to change
+ the severity of some possible issues, e.g.,
+ `--strict="missingEmail=ignore,badTagName=error"`. See the entry for the
+ `fsck.<msg-id>` configuration options in linkgit:git-fsck[1] for more
+ information on the possible values of `<msg-id>` and `<severity>`.
--progress-title::
For internal use only.
@@ -91,13 +96,18 @@ default and "Indexing objects" when `--stdin` is specified.
--check-self-contained-and-connected::
Die if the pack contains broken links. For internal use only.
---fsck-objects::
- For internal use only.
+--fsck-objects[=<msg-id>=<severity>...]::
+ Die if the pack contains broken objects, but unlike `--strict`, don't
+ choke on broken links. If the pack contains a tree pointing to a
+ .gitmodules blob that does not exist, prints the hash of that blob
+ (for the caller to check) after the hash that goes into the name of the
+ pack/idx file (see "Notes").
+
-Die if the pack contains broken objects. If the pack contains a tree
-pointing to a .gitmodules blob that does not exist, prints the hash of
-that blob (for the caller to check) after the hash that goes into the
-name of the pack/idx file (see "Notes").
+An optional comma-separated list of `<msg-id>=<severity>` can be passed to
+change the severity of some possible issues, e.g.,
+`--fsck-objects="missingEmail=ignore,badTagName=ignore"`. See the entry for the
+`fsck.<msg-id>` configuration options in linkgit:git-fsck[1] for more
+information on the possible values of `<msg-id>` and `<severity>`.
--threads=<n>::
Specifies the number of threads to spawn when resolving
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 6f0d2973bf..daff93bd16 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -9,10 +9,11 @@ git-init - Create an empty Git repository or reinitialize an existing one
SYNOPSIS
--------
[verse]
-'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
- [--separate-git-dir <git-dir>] [--object-format=<format>]
- [-b <branch-name> | --initial-branch=<branch-name>]
- [--shared[=<permissions>]] [<directory>]
+`git init` [`-q` | `--quiet`] [`--bare`] [++--template=++__<template-directory>__]
+ [`--separate-git-dir` _<git-dir>_] [++--object-format=++__<format>__]
+ [++--ref-format=++__<format>__]
+ [`-b` _<branch-name>_ | ++--initial-branch=++__<branch-name>__]
+ [++--shared++[++=++__<permissions>__]] [_<directory>_]
DESCRIPTION
@@ -32,37 +33,43 @@ If the object storage directory is specified via the
are created underneath; otherwise, the default `$GIT_DIR/objects`
directory is used.
-Running 'git init' in an existing repository is safe. It will not
+Running `git init` in an existing repository is safe. It will not
overwrite things that are already there. The primary reason for
-rerunning 'git init' is to pick up newly added templates (or to move
-the repository to another place if --separate-git-dir is given).
+rerunning `git init` is to pick up newly added templates (or to move
+the repository to another place if `--separate-git-dir` is given).
OPTIONS
-------
--q::
---quiet::
+`-q`::
+`--quiet`::
Only print error and warning messages; all other output will be suppressed.
---bare::
+`--bare`::
Create a bare repository. If `GIT_DIR` environment is not set, it is set to the
current working directory.
---object-format=<format>::
+++--object-format=++__<format>__::
-Specify the given object format (hash algorithm) for the repository. The valid
-values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
+Specify the given object _<format>_ (hash algorithm) for the repository. The valid
+values are `sha1` and (if enabled) `sha256`. `sha1` is the default.
+
include::object-format-disclaimer.txt[]
---template=<template-directory>::
+++--ref-format=++__<format>__::
+
+Specify the given ref storage _<format>_ for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
+++--template=++__<template-directory>__::
Specify the directory from which templates will be used. (See the "TEMPLATE
DIRECTORY" section below.)
---separate-git-dir=<git-dir>::
+++--separate-git-dir=++__<git-dir>__::
Instead of initializing the repository as a directory to either `$GIT_DIR` or
`./.git/`, create a text file there containing the path to the actual
@@ -71,52 +78,56 @@ repository.
+
If this is a reinitialization, the repository will be moved to the specified path.
--b <branch-name>::
---initial-branch=<branch-name>::
+`-b` _<branch-name>_::
+++--initial-branch=++__<branch-name>__::
-Use the specified name for the initial branch in the newly created
+Use _<branch-name>_ for the initial branch in the newly created
repository. If not specified, fall back to the default name (currently
`master`, but this is subject to change in the future; the name can be
customized via the `init.defaultBranch` configuration variable).
---shared[=(false|true|umask|group|all|world|everybody|<perm>)]::
+++--shared++[++=++(`false`|`true`|`umask`|`group`|`all`|`world`|`everybody`|_<perm>_)]::
Specify that the Git repository is to be shared amongst several users. This
allows users belonging to the same group to push into that
-repository. When specified, the config variable "core.sharedRepository" is
+repository. When specified, the config variable `core.sharedRepository` is
set so that files and directories under `$GIT_DIR` are created with the
requested permissions. When not specified, Git will use permissions reported
-by umask(2).
+by `umask`(2).
+
-The option can have the following values, defaulting to 'group' if no value
+The option can have the following values, defaulting to `group` if no value
is given:
+
--
-'umask' (or 'false')::
+`umask`::
+`false`::
-Use permissions reported by umask(2). The default, when `--shared` is not
+Use permissions reported by `umask`(2). The default, when `--shared` is not
specified.
-'group' (or 'true')::
+`group`::
+`true`::
-Make the repository group-writable, (and g+sx, since the git group may not be
+Make the repository group-writable, (and `g+sx`, since the git group may not be
the primary group of all users). This is used to loosen the permissions of an
-otherwise safe umask(2) value. Note that the umask still applies to the other
-permission bits (e.g. if umask is '0022', using 'group' will not remove read
-privileges from other (non-group) users). See '0xxx' for how to exactly specify
+otherwise safe `umask`(2) value. Note that the umask still applies to the other
+permission bits (e.g. if umask is `0022`, using `group` will not remove read
+privileges from other (non-group) users). See `0xxx` for how to exactly specify
the repository permissions.
-'all' (or 'world' or 'everybody')::
+`all`::
+`world`::
+`everybody`::
-Same as 'group', but make the repository readable by all users.
+Same as `group`, but make the repository readable by all users.
-'<perm>'::
+_<perm>_::
-'<perm>' is a 3-digit octal number prefixed with `0` and each file
-will have mode '<perm>'. '<perm>' will override users' umask(2)
-value (and not only loosen permissions as 'group' and 'all'
-do). '0640' will create a repository which is group-readable, but
-not group-writable or accessible to others. '0660' will create a repo
+_<perm>_ is a 3-digit octal number prefixed with `0` and each file
+will have mode _<perm>_. _<perm>_ will override users' `umask`(2)
+value (and not only loosen permissions as `group` and `all`
+do). `0640` will create a repository which is group-readable, but
+not group-writable or accessible to others. `0660` will create a repo
that is readable and writable to the current user and group, but
inaccessible to others (directories and executable files get their
`x` bit from the `r` bit for corresponding classes of users).
@@ -126,7 +137,7 @@ By default, the configuration flag `receive.denyNonFastForwards` is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
-If you provide a 'directory', the command is run inside it. If this directory
+If you provide a _<directory>_, the command is run inside it. If this directory
does not exist, it will be created.
TEMPLATE DIRECTORY
@@ -165,7 +176,7 @@ $ git add . <2>
$ git commit <3>
----------------
+
-<1> Create a /path/to/my/codebase/.git directory.
+<1> Create a `/path/to/my/codebase/.git` directory.
<2> Add all existing files to the index.
<3> Record the pristine state as the first commit in the history.
@@ -174,6 +185,8 @@ CONFIGURATION
include::includes/cmd-config-section-all.txt[]
+:git-init:
+
include::config/init.txt[]
GIT
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 418265f044..d9dfb75fef 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git interpret-trailers' [--in-place] [--trim-empty]
- [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
+ [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]
[--parse] [<file>...]
DESCRIPTION
@@ -67,9 +67,9 @@ key: value
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`
+For convenience, a <key-alias> can be configured to make using `--trailer`
shorter to type on the command line. This can be configured using the
-'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix
of the full <key> string, although case sensitivity does not matter. For
example, if you have
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index f65a8cd91d..d08c7da8f4 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -119,8 +119,10 @@ OPTIONS
--exclude-per-directory=<file>::
Read additional exclude patterns that apply only to the
- directory and its subdirectories in <file>. Deprecated; use
- --exclude-standard instead.
+ directory and its subdirectories in <file>. If you are
+ trying to emulate the way Porcelain commands work, using
+ the `--exclude-standard` option instead is easier and more
+ thorough.
--exclude-standard::
Add the standard Git exclusions: .git/info/exclude, .gitignore
@@ -298,9 +300,8 @@ traversing the directory tree and finding files to show when the
flags --others or --ignored are specified. linkgit:gitignore[5]
specifies the format of exclude patterns.
-Generally, you should just use --exclude-standard, but for historical
-reasons the exclude patterns can be specified from the following
-places, in order:
+These exclude patterns can be specified from the following places,
+in order:
1. The command-line flag --exclude=<pattern> specifies a
single pattern. Patterns are ordered in the same order
@@ -322,6 +323,18 @@ top of the directory tree. A pattern read from a file specified
by --exclude-per-directory is relative to the directory that the
pattern file appears in.
+Generally, you should be able to use `--exclude-standard` when you
+want the exclude rules applied the same way as what Porcelain
+commands do. To emulate what `--exclude-standard` specifies, you
+can give `--exclude-per-directory=.gitignore`, and then specify:
+
+ 1. The file specified by the `core.excludesfile` configuration
+ variable, if exists, or the `$XDG_CONFIG_HOME/git/ignore` file.
+
+ 2. The `$GIT_DIR/info/exclude` file.
+
+via the `--exclude-from=` option.
+
SEE ALSO
--------
linkgit:git-read-tree[1], linkgit:gitignore[5]
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 6a081eacb7..71915a00fa 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -92,6 +92,12 @@ object store and the object ID of its blob is written to standard output.
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
--------
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index b50acace3b..dd388fa21d 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -64,10 +64,13 @@ OPTIONS
share no common history. This flag can be given to override that
check and make the merge proceed anyway.
---merge-base=<commit>::
+--merge-base=<tree-ish>::
Instead of finding the merge-bases for <branch1> and <branch2>,
specify a merge-base for the merge, and specifying multiple bases is
currently not supported. This option is incompatible with `--stdin`.
++
+As the merge-base is provided directly, <branch1> and <branch2> do not need
+to specify commits; trees are enough.
[[OUTPUT]]
OUTPUT
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index e8ab340319..1ab69f61f5 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -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,7 +33,7 @@ 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
@@ -46,21 +46,21 @@ a log message from the user describing the changes. Before the operation,
D---E---F---G---H master
------------
-The second syntax ("`git merge --abort`") can only be run after the
-merge has resulted in conflicts. 'git merge --abort' will abort the
-merge process and try to reconstruct the pre-merge state. However,
-if there were uncommitted changes when the merge started (and
-especially if those changes were further modified after the merge
-was started), 'git merge --abort' will in some cases be unable to
-reconstruct the original (pre-merge) changes. Therefore:
+A merge stops if there's a conflict that cannot be resolved
+automatically or if `--no-commit` was provided when initiating the
+merge. At that point you can run `git merge --abort` or `git merge
+--continue`.
-*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+`git merge --abort` will abort the merge process and try to reconstruct
+the pre-merge state. However, if there were uncommitted changes when the
+merge started (and especially if those changes were further modified
+after the merge was started), `git merge --abort` will in some cases be
+unable to reconstruct the original (pre-merge) changes. Therefore:
+
+*Warning*: Running `git merge` with non-trivial uncommitted changes is
discouraged: while possible, it may leave you in a state that is hard to
back out of in the case of a conflict.
-The third syntax ("`git merge --continue`") can only be run after the
-merge has resulted in conflicts.
-
OPTIONS
-------
:git-merge: 1
@@ -74,8 +74,8 @@ 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>::
@@ -104,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::
@@ -120,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>...::
@@ -144,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
@@ -196,7 +196,7 @@ happens:
can inspect the stages with `git ls-files -u`). The working
tree files contain the result of the merge operation; i.e. 3-way
merge results with familiar conflict markers `<<<` `===` `>>>`.
-5. A special ref `AUTO_MERGE` is written, pointing to a tree
+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).
@@ -269,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:
@@ -328,10 +328,10 @@ 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:
@@ -392,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[]
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index 7f991a3380..dc1bf61534 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -16,7 +16,7 @@ DESCRIPTION
Move or rename a file, directory, or symlink.
git mv [-v] [-f] [-n] [-k] <source> <destination>
- git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
+ git mv [-v] [-f] [-n] [-k] <source> ... <destination-directory>
In the first form, it renames <source>, which must exist and be either
a file, symlink or directory, to <destination>.
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index f8310e56a8..c9221a68cc 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -56,7 +56,7 @@ SUBCOMMANDS
list::
List the notes object for a given object. If no object is
given, show a list of all note objects and the objects they
- annotate (in the format "<note object> <annotated object>").
+ annotate (in the format "<note-object> <annotated-object>").
This is the default subcommand if no subcommand is given.
add::
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index 284956acb3..2dcabaf74c 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -8,7 +8,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
SYNOPSIS
--------
[verse]
-'git pack-refs' [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]
+'git pack-refs' [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]
DESCRIPTION
-----------
@@ -60,6 +60,19 @@ with many branches of historical interests.
The command usually removes loose refs under `$GIT_DIR/refs`
hierarchy after packing them. This option tells it not to.
+--auto::
+
+Pack refs as needed depending on the current state of the ref database. The
+behavior depends on the ref format used by the repository and may change in the
+future.
++
+ - "files": No special handling for `--auto` has been implemented.
++
+ - "reftable": Tables are compacted such that they form a geometric
+ sequence. For two tables N and N+1, where N+1 is newer, this
+ maintains the property that N is at least twice as big as N+1. Only
+ tables that violate this property are compacted.
+
--include <pattern>::
Pack refs based on a `glob(7)` pattern. Repetitions of this option
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 0e14f8b5b2..b2ae496e48 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -87,7 +87,7 @@ OPTIONS
--verbose::
Pass --verbose to git-fetch and git-merge.
---[no-]recurse-submodules[=yes|on-demand|no]::
+--[no-]recurse-submodules[=(yes|on-demand|no)]::
This option controls if new commits of populated submodules should
be fetched, and if the working trees of active submodules should be
updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
@@ -105,7 +105,7 @@ Options related to merging
include::merge-options.txt[]
-r::
---rebase[=false|true|merges|interactive]::
+--rebase[=(false|true|merges|interactive)]::
When true, rebase the current branch on top of the upstream
branch after fetching. If there is a remote-tracking branch
corresponding to the upstream branch and the upstream branch
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1dd6555f66..74df345f9e 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -12,7 +12,7 @@ SYNOPSIS
[--onto <newbase> | --keep-base] [<upstream> [<branch>]]
'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
-'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
+'git rebase' (--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
DESCRIPTION
-----------
@@ -289,17 +289,25 @@ See also INCOMPATIBLE OPTIONS below.
+
See also INCOMPATIBLE OPTIONS below.
---empty=(drop|keep|ask)::
+--empty=(drop|keep|stop)::
How to handle commits that are not empty to start and are not
clean cherry-picks of any upstream commit, but which become
empty after rebasing (because they contain a subset of already
- upstream changes). With drop (the default), commits that
- become empty are dropped. With keep, such commits are kept.
- With ask (implied by `--interactive`), the rebase will halt when
- an empty commit is applied allowing you to choose whether to
- drop it, edit files more, or just commit the empty changes.
- Other options, like `--exec`, will use the default of drop unless
- `-i`/`--interactive` is explicitly specified.
+ upstream changes):
++
+--
+`drop`;;
+ The commit will be dropped. This is the default behavior.
+`keep`;;
+ The commit will be kept. This option is implied when `--exec` is
+ specified unless `-i`/`--interactive` is also specified.
+`stop`;;
+`ask`;;
+ The rebase will halt when the commit is applied, allowing you to
+ choose whether to drop it, edit files more, or just commit the empty
+ changes. This option is implied when `-i`/`--interactive` is
+ specified. `ask` is a deprecated synonym of `stop`.
+--
+
Note that commits which start empty are kept (unless `--no-keep-empty`
is specified), and commits which are clean cherry-picks (as determined
@@ -523,7 +531,7 @@ 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.
@@ -607,7 +615,7 @@ The recommended way to create commits with squash markers is by using the
linkgit:git-commit[1], which take the target commit as an argument and
automatically fill in the subject line of the new commit from that.
+
-Settting configuration variable `rebase.autoSquash` to true enables
+Setting configuration variable `rebase.autoSquash` to true enables
auto-squashing by default for interactive rebase. The `--no-autosquash`
option can be used to override that setting.
+
@@ -626,13 +634,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::
@@ -701,7 +712,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|stop)` option for changing the behavior
of handling commits that become empty.
Directory rename detection
@@ -963,10 +974,9 @@ The interactive rebase will stop when a command fails (i.e. exits with
non-0 status) to give you an opportunity to fix the problem. You can
continue with `git rebase --continue`.
-The "exec" command launches the command in a shell (the one specified
-in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
-use shell features (like "cd", ">", ";" ...). The command is run from
-the root of the working tree.
+The "exec" command launches the command in a shell (the default one, usually
+/bin/sh), so you can use shell features (like "cd", ">", ";" ...). The command
+is run from the root of the working tree.
----------------------------------
$ git rebase -i --exec "make test"
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index ec64cbff4c..a929c52982 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git reflog' [show] [<log-options>] [<ref>]
+'git reflog list'
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
[--rewrite] [--updateref] [--stale-fix]
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
@@ -39,6 +40,8 @@ actions, and in addition the `HEAD` reflog records branch switching.
`git reflog show` is an alias for `git log -g --abbrev-commit
--pretty=oneline`; see linkgit:git-log[1] for more information.
+The "list" subcommand lists all refs which have a corresponding reflog.
+
The "expire" subcommand prunes older reflog entries. Entries older
than `expire` time, or entries older than `expire-unreachable` time
and not reachable from the current tip, are removed from the reflog.
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 1dec314834..932a5c3ea4 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -35,7 +35,7 @@ OPTIONS
-v::
--verbose::
Be a little more verbose and show remote url after name.
- For promisor remotes, also show which filter (`blob:none` etc.)
+ For promisor remotes, also show which filters (`blob:none` etc.)
are configured.
NOTE: This must be placed between `remote` and subcommand.
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index 4f257126e3..0a65460adb 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -114,11 +114,11 @@ FORMATS
The following formats are available:
* 'short':
- <replaced sha1>
+ <replaced-sha1>
* 'medium':
- <replaced sha1> -> <replacement sha1>
+ <replaced-sha1> -> <replacement-sha1>
* 'long':
- <replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>)
+ <replaced-sha1> (<replaced-type>) -> <replacement-sha1> (<replacement-type>)
CREATING REPLACEMENT OBJECTS
----------------------------
diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
new file mode 100644
index 0000000000..8f3300c683
--- /dev/null
+++ b/Documentation/git-replay.txt
@@ -0,0 +1,127 @@
+git-replay(1)
+=============
+
+NAME
+----
+git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos too
+
+
+SYNOPSIS
+--------
+[verse]
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
+
+DESCRIPTION
+-----------
+
+Takes ranges of commits and replays them onto a new location. Leaves
+the working tree and the index untouched, and updates no references.
+The output of this command is meant to be used as input to
+`git update-ref --stdin`, which would update the relevant branches
+(see the OUTPUT section below).
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+
+--onto <newbase>::
+ Starting point at which to create the new commits. May be any
+ valid commit, and not just an existing branch name.
++
+When `--onto` is specified, the update-ref command(s) in the output will
+update the branch(es) in the revision range to point at the new
+commits, similar to the way how `git rebase --update-refs` updates
+multiple branches in the affected range.
+
+--advance <branch>::
+ Starting point at which to create the new commits; must be a
+ branch name.
++
+When `--advance` is specified, the update-ref command(s) in the output
+will update the branch passed as an argument to `--advance` to point at
+the new commits (in other words, this mimics a cherry-pick operation).
+
+<revision-range>::
+ Range of commits to replay. More than one <revision-range> can
+ be passed, but in `--advance <branch>` mode, they should have
+ a single tip, so that it's clear where <branch> should point
+ to. See "Specifying Ranges" in linkgit:git-rev-parse[1] and the
+ "Commit Limiting" options below.
+
+include::rev-list-options.txt[]
+
+OUTPUT
+------
+
+When there are no conflicts, the output of this command is usable as
+input to `git update-ref --stdin`. It is of the form:
+
+ update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+ update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+ update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+
+where the number of refs updated depends on the arguments passed and
+the shape of the history being replayed. When using `--advance`, the
+number of refs updated is always one, but for `--onto`, it can be one
+or more (rebasing multiple branches simultaneously is supported).
+
+EXIT STATUS
+-----------
+
+For a successful, non-conflicted replay, the exit status is 0. When
+the replay has conflicts, the exit status is 1. If the replay is not
+able to complete (or start) due to some kind of error, the exit status
+is something other than 0 or 1.
+
+EXAMPLES
+--------
+
+To simply rebase `mybranch` onto `target`:
+
+------------
+$ git replay --onto target origin/main..mybranch
+update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
+------------
+
+To cherry-pick the commits from mybranch onto target:
+
+------------
+$ git replay --advance target origin/main..mybranch
+update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
+------------
+
+Note that the first two examples replay the exact same commits and on
+top of the exact same new base, they only differ in that the first
+provides instructions to make mybranch point at the new commits and
+the second provides instructions to make target point at them.
+
+What if you have a stack of branches, one depending upon another, and
+you'd really like to rebase the whole set?
+
+------------
+$ git replay --contained --onto origin/main origin/main..tipbranch
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
+------------
+
+When calling `git replay`, one does not need to specify a range of
+commits to replay using the syntax `A..B`; any range expression will
+do:
+
+------------
+$ git replay --onto origin/main ^base branch1 branch2 branch3
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+------------
+
+This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
+all commits they have since `base`, playing them on top of
+`origin/main`. These three branches may have commits on top of `base`
+that they have in common, but that does not need to be the case.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 912fab9f5e..f9d5a35fa0 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -9,7 +9,7 @@ git-rev-parse - Pick out and massage parameters
SYNOPSIS
--------
[verse]
-'git rev-parse' [<options>] <args>...
+'git rev-parse' [<options>] <arg>...
DESCRIPTION
-----------
@@ -130,7 +130,7 @@ for another option.
'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
the command input is still interpreted as usual.
---short[=length]::
+--short[=<length>]::
Same as `--verify` but shortens the object name to a unique
prefix with at least `length` characters. The minimum length
is 4, the default is the effective value of the `core.abbrev`
@@ -159,15 +159,27 @@ for another option.
unfortunately named tag "master"), and shows them as full
refnames (e.g. "refs/heads/master").
+--output-object-format=(sha1|sha256|storage)::
+
+ Allow oids to be input from any object format that the current
+ repository supports.
+
+ Specifying "sha1" translates if necessary and returns a sha1 oid.
+
+ Specifying "sha256" translates if necessary and returns a sha256 oid.
+
+ Specifying "storage" translates if necessary and returns an oid in
+ encoded in the storage hash algorithm.
+
Options for Objects
~~~~~~~~~~~~~~~~~~~
--all::
Show all refs found in `refs/`.
---branches[=pattern]::
---tags[=pattern]::
---remotes[=pattern]::
+--branches[=<pattern>]::
+--tags[=<pattern>]::
+--remotes[=<pattern>]::
Show all branches, tags, or remote-tracking branches,
respectively (i.e., refs found in `refs/heads`,
`refs/tags`, or `refs/remotes`, respectively).
@@ -176,7 +188,7 @@ If a `pattern` is given, only refs matching the given shell glob are
shown. If the pattern does not contain a globbing character (`?`,
`*`, or `[`), it is turned into a prefix match by appending `/*`.
---glob=pattern::
+--glob=<pattern>::
Show all refs matching the shell glob pattern `pattern`. If
the pattern does not start with `refs/`, this is automatically
prepended. If the pattern does not contain a globbing
@@ -197,7 +209,7 @@ respectively, and they must begin with `refs/` when applied to `--glob`
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
explicitly.
---exclude-hidden=[fetch|receive|uploadpack]::
+--exclude-hidden=(fetch|receive|uploadpack)::
Do not include refs that would be hidden by `git-fetch`,
`git-receive-pack` or `git-upload-pack` by consulting the appropriate
`fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs`
@@ -307,21 +319,24 @@ 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
~~~~~~~~~~~~~
---since=datestring::
---after=datestring::
+--since=<datestring>::
+--after=<datestring>::
Parse the date string, and output the corresponding
--max-age= parameter for 'git rev-list'.
---until=datestring::
---before=datestring::
+--until=<datestring>::
+--before=<datestring>::
Parse the date string, and output the corresponding
--min-age= parameter for 'git rev-list'.
-<args>...::
+<arg>...::
Flags and parameters to be parsed.
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index cbe0208834..568925db53 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -116,7 +116,7 @@ include::rerere-options.txt[]
--reference::
Instead of starting the body of the log message with "This
- reverts <full object name of the commit being reverted>.",
+ reverts <full-object-name-of-the-commit-being-reverted>.",
refer to the commit using "--pretty=reference" format
(cf. linkgit:git-log[1]). The `revert.reference`
configuration variable can be used to enable this option by
@@ -149,7 +149,7 @@ While git creates a basic commit message automatically, it is
_strongly_ recommended to explain why the original commit is being
reverted.
In addition, repeatedly reverting reverts will result in increasingly
-unwieldy subject lines, for example 'Reapply "Reapply "<original subject>""'.
+unwieldy subject lines, for example 'Reapply "Reapply "<original-subject>""'.
Please consider rewording these to be shorter and more unique.
CONFIGURATION
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 465011bad5..c5d664f451 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -9,8 +9,8 @@ git-send-email - Send a collection of patches as emails
SYNOPSIS
--------
[verse]
-'git send-email' [<options>] <file|directory>...
-'git send-email' [<options>] <format-patch options>
+'git send-email' [<options>] (<file>|<directory>)...
+'git send-email' [<options>] <format-patch-options>
'git send-email' --dump-aliases
@@ -138,7 +138,7 @@ Note that no attempts whatsoever are made to validate the encoding.
--compose-encoding=<encoding>::
Specify encoding of compose message. Default is the value of the
- 'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
+ 'sendemail.composeEncoding'; if that is unspecified, UTF-8 is assumed.
--transfer-encoding=(7bit|8bit|quoted-printable|base64|auto)::
Specify the transfer encoding to be used to send the message over SMTP.
@@ -174,7 +174,7 @@ Sending
Specify a command to run to send the email. The command should
be sendmail-like; specifically, it must support the `-i` option.
The command will be executed in the shell if necessary. Default
- is the value of `sendemail.sendmailcmd`. If unspecified, and if
+ is the value of `sendemail.sendmailCmd`. If unspecified, and if
--smtp-server is also unspecified, git-send-email will search
for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH.
@@ -269,7 +269,7 @@ must be used for each option.
certificates concatenated together: see verify(1) -CAfile and
-CApath for more information on these). Set it to an empty string
to disable certificate verification. Defaults to the value of the
- `sendemail.smtpsslcertpath` configuration variable, if set, or the
+ `sendemail.smtpSSLCertPath` configuration variable, if set, or the
backing SSL library's compiled-in default otherwise (which should
be the best choice on most platforms).
@@ -278,7 +278,7 @@ must be used for each option.
if a username is not specified (with `--smtp-user` or `sendemail.smtpUser`),
then authentication is not attempted.
---smtp-debug=0|1::
+--smtp-debug=(0|1)::
Enable (1) or disable (0) debug output. If enabled, SMTP
commands and replies will be printed. Useful to debug TLS
connection and authentication problems.
@@ -301,7 +301,9 @@ must be used for each option.
Automating
~~~~~~~~~~
---no-[to|cc|bcc]::
+--no-to::
+--no-cc::
+--no-bcc::
Clears any list of "To:", "Cc:", "Bcc:" addresses previously
set via config.
@@ -313,7 +315,7 @@ Automating
Specify a command to execute once per patch file which
should generate patch file specific "To:" entries.
Output of this command must be single email address per line.
- Default is the value of 'sendemail.tocmd' configuration value.
+ Default is the value of 'sendemail.toCmd' configuration value.
--cc-cmd=<command>::
Specify a command to execute once per patch file which
@@ -348,19 +350,19 @@ Automating
--[no-]signed-off-by-cc::
If this is set, add emails found in the `Signed-off-by` trailer or Cc: lines to the
- cc list. Default is the value of `sendemail.signedoffbycc` configuration
+ cc list. Default is the value of `sendemail.signedOffByCc` configuration
value; if that is unspecified, default to --signed-off-by-cc.
--[no-]cc-cover::
If this is set, emails found in Cc: headers in the first patch of
the series (typically the cover letter) are added to the cc list
- for each email set. Default is the value of 'sendemail.cccover'
+ for each email set. Default is the value of 'sendemail.ccCover'
configuration value; if that is unspecified, default to --no-cc-cover.
--[no-]to-cover::
If this is set, emails found in To: headers in the first patch of
the series (typically the cover letter) are added to the to list
- for each email set. Default is the value of 'sendemail.tocover'
+ for each email set. Default is the value of 'sendemail.toCover'
configuration value; if that is unspecified, default to --no-to-cover.
--suppress-cc=<category>::
@@ -384,7 +386,7 @@ Automating
- 'all' will suppress all auto cc values.
--
+
-Default is the value of `sendemail.suppresscc` configuration value; if
+Default is the value of `sendemail.suppressCc` configuration value; if
that is unspecified, default to 'self' if --suppress-from is
specified, as well as 'body' if --no-signed-off-cc is specified.
@@ -454,7 +456,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,
@@ -471,7 +473,7 @@ Information
Instead of the normal operation, dump the shorthand alias names from
the configured alias file(s), one per line in alphabetical order. Note
that this only includes the alias name and not its expanded email addresses.
- See 'sendemail.aliasesfile' for more information about aliases.
+ See 'sendemail.aliasesFile' for more information about aliases.
CONFIGURATION
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 10fecc51a7..9a376886a5 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -79,6 +79,8 @@ Consider enabling untracked cache and split index if supported (see
`git update-index --untracked-cache` and `git update-index
--split-index`), Otherwise you can use `no` to have `git status`
return more quickly without showing untracked files.
+All usual spellings for Boolean value `true` are taken as `normal`
+and `false` as `no`.
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
@@ -309,7 +311,7 @@ Line Notes
------------------------------------------------------------
# branch.oid <commit> | (initial) Current commit.
# branch.head <branch> | (detached) Current branch.
-# branch.upstream <upstream_branch> If upstream is set.
+# branch.upstream <upstream-branch> If upstream is set.
# branch.ab +<ahead> -<behind> If upstream is set and
the commit is present.
------------------------------------------------------------
@@ -472,7 +474,7 @@ 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):
+ `status.showUntrackedFiles=no` config (see above for both):
indicate that `git status` should not report untracked
files. This is the fastest option. `git status` will not list
the untracked files, so you need to be careful to remember if
@@ -502,7 +504,7 @@ results, so it could be faster on subsequent runs.
usually worth the additional size.
* `core.untrackedCache=true` and `core.fsmonitor=true` or
- `core.fsmonitor=<hook_command_pathname>` (see
+ `core.fsmonitor=<hook-command-pathname>` (see
linkgit:git-update-index[1]): enable both the untracked cache
and FSMonitor features and only search directories that have
been modified since the previous `git status` command. This
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 695730609a..ca0347a37b 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -136,7 +136,7 @@ If you really want to remove a submodule from the repository and commit
that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal
options.
-update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter spec>] [--] [<path>...]::
+update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter-spec>] [--] [<path>...]::
+
--
Update the registered submodules to match what the superproject
@@ -185,7 +185,7 @@ submodule with the `--init` option.
If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within.
-If `--filter <filter spec>` is specified, the given partial clone filter will be
+If `--filter <filter-spec>` is specified, the given partial clone filter will be
applied to the submodule. See linkgit:git-rev-list[1] for details on filter
specifications.
--
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 4e92308e85..43c68c2ec4 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -37,12 +37,12 @@ COMMANDS
argument. Normally this command initializes the current
directory.
--T<trunk_subdir>;;
---trunk=<trunk_subdir>;;
--t<tags_subdir>;;
---tags=<tags_subdir>;;
--b<branches_subdir>;;
---branches=<branches_subdir>;;
+-T<trunk-subdir>;;
+--trunk=<trunk-subdir>;;
+-t<tags-subdir>;;
+--tags=<tags-subdir>;;
+-b<branches-subdir>;;
+--branches=<branches-subdir>;;
-s;;
--stdlayout;;
These are optional command-line options for init. Each of
@@ -726,9 +726,9 @@ ADVANCED OPTIONS
when tracking a single URL. The 'log' and 'dcommit' commands
no longer require this switch as an argument.
--R<remote name>::
---svn-remote <remote name>::
- Specify the [svn-remote "<remote name>"] section to use,
+-R<remote-name>::
+--svn-remote <remote-name>::
+ Specify the [svn-remote "<remote-name>"] section to use,
this allows SVN multiple repositories to be tracked.
Default: "svn"
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
index c60fc9c138..f38e4c8afa 100644
--- a/Documentation/git-switch.txt
+++ b/Documentation/git-switch.txt
@@ -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::
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index d42efb3112..5fe519c31e 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -224,7 +224,7 @@ it in the repository configuration as follows:
-------------------------------------
[user]
- signingKey = <gpg-key_id>
+ signingKey = <gpg-key-id>
-------------------------------------
`pager.tag` is only respected when listing tags, i.e., when `-l` is
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 0561808cca..374a2ebd2b 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -8,21 +8,21 @@ git-update-ref - Update the object name stored in a ref safely
SYNOPSIS
--------
[verse]
-'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<oldvalue>] | [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<old-oid>] | [--create-reflog] <ref> <new-oid> [<old-oid>] | --stdin [-z])
DESCRIPTION
-----------
-Given two arguments, stores the <newvalue> in the <ref>, possibly
+Given two arguments, stores the <new-oid> in the <ref>, possibly
dereferencing the symbolic refs. E.g. `git update-ref HEAD
-<newvalue>` updates the current branch head to the new object.
+<new-oid>` updates the current branch head to the new object.
-Given three arguments, stores the <newvalue> in the <ref>,
+Given three arguments, stores the <new-oid> in the <ref>,
possibly dereferencing the symbolic refs, after verifying that
-the current value of the <ref> matches <oldvalue>.
-E.g. `git update-ref refs/heads/master <newvalue> <oldvalue>`
-updates the master branch head to <newvalue> only if its current
-value is <oldvalue>. You can specify 40 "0" or an empty string
-as <oldvalue> to make sure that the ref you are creating does
+the current value of the <ref> matches <old-oid>.
+E.g. `git update-ref refs/heads/master <new-oid> <old-oid>`
+updates the master branch head to <new-oid> only if its current
+value is <old-oid>. You can specify 40 "0" or an empty string
+as <old-oid> to make sure that the ref you are creating does
not exist.
It also allows a "ref" file to be a symbolic pointer to another
@@ -56,15 +56,15 @@ ref symlink to some other tree, if you have copied a whole
archive by creating a symlink tree).
With `-d` flag, it deletes the named <ref> after verifying it
-still contains <oldvalue>.
+still contains <old-oid>.
With `--stdin`, update-ref reads instructions from standard input and
performs all modifications together. Specify commands of the form:
- update SP <ref> SP <newvalue> [SP <oldvalue>] LF
- create SP <ref> SP <newvalue> LF
- delete SP <ref> [SP <oldvalue>] LF
- verify SP <ref> [SP <oldvalue>] LF
+ update SP <ref> SP <new-oid> [SP <old-oid>] LF
+ create SP <ref> SP <new-oid> LF
+ delete SP <ref> [SP <old-oid>] LF
+ verify SP <ref> [SP <old-oid>] LF
option SP <opt> LF
start LF
prepare LF
@@ -82,10 +82,10 @@ specify a missing value, omit the value and its preceding SP entirely.
Alternatively, use `-z` to specify in NUL-terminated format, without
quoting:
- update SP <ref> NUL <newvalue> NUL [<oldvalue>] NUL
- create SP <ref> NUL <newvalue> NUL
- delete SP <ref> NUL [<oldvalue>] NUL
- verify SP <ref> NUL [<oldvalue>] NUL
+ update SP <ref> NUL <new-oid> NUL [<old-oid>] NUL
+ create SP <ref> NUL <new-oid> NUL
+ delete SP <ref> NUL [<old-oid>] NUL
+ verify SP <ref> NUL [<old-oid>] NUL
option SP <opt> NUL
start NUL
prepare NUL
@@ -100,22 +100,22 @@ recognizes as an object name. Commands in any other format or a
repeated <ref> produce an error. Command meanings are:
update::
- Set <ref> to <newvalue> after verifying <oldvalue>, if given.
- Specify a zero <newvalue> to ensure the ref does not exist
- after the update and/or a zero <oldvalue> to make sure the
+ Set <ref> to <new-oid> after verifying <old-oid>, if given.
+ Specify a zero <new-oid> to ensure the ref does not exist
+ after the update and/or a zero <old-oid> to make sure the
ref does not exist before the update.
create::
- Create <ref> with <newvalue> after verifying it does not
- exist. The given <newvalue> may not be zero.
+ Create <ref> with <new-oid> after verifying it does not
+ exist. The given <new-oid> may not be zero.
delete::
- Delete <ref> after verifying it exists with <oldvalue>, if
- given. If given, <oldvalue> may not be zero.
+ Delete <ref> after verifying it exists with <old-oid>, if
+ given. If given, <old-oid> may not be zero.
verify::
- Verify <ref> against <oldvalue> but do not change it. If
- <oldvalue> is zero or missing, the ref must not exist.
+ Verify <ref> against <old-oid> but do not change it. If
+ <old-oid> is zero or missing, the ref must not exist.
option::
Modify the behavior of the next command naming a <ref>.
@@ -141,7 +141,7 @@ abort::
Abort the transaction, releasing all locks if the transaction is in
prepared state.
-If all <ref>s can be locked with matching <oldvalue>s
+If all <ref>s can be locked with matching <old-oid>s
simultaneously, all modifications are performed. Otherwise, no
modifications are performed. Note that while each individual
<ref> is updated or deleted atomically, a concurrent reader may
@@ -161,7 +161,7 @@ formatted as:
Where "oldsha1" is the 40 character hexadecimal value previously
stored in <ref>, "newsha1" is the 40 character hexadecimal value of
-<newvalue> and "committer" is the committer's name, email address
+<new-oid> and "committer" is the committer's name, email address
and date in the standard Git committer ident format.
Optionally with -m:
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 93d76f5d66..2a240f53ba 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -99,7 +99,7 @@ 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 orphan branch named `<branch>` (after
+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
@@ -234,7 +234,7 @@ This can also be set up as the default behaviour by using the
--orphan::
With `add`, make the new worktree and index empty, associating
- the worktree with a new orphan/unborn branch named `<new-branch>`.
+ the worktree with a new unborn branch named `<new-branch>`.
--porcelain::
With `list`, output in an easy-to-parse format for scripts.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 2535a30194..7a1b112a3e 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -174,8 +174,17 @@ If you just want to run git as if it was started in `<path>` then use
directory.
--no-replace-objects::
- Do not use replacement refs to replace Git objects. See
- linkgit:git-replace[1] for more information.
+ Do not use replacement refs to replace Git objects.
+ This is equivalent to exporting the `GIT_NO_REPLACE_OBJECTS`
+ environment variable with any value.
+ See linkgit:git-replace[1] for more information.
+
+--no-lazy-fetch::
+ Do not fetch missing objects from the promisor remote on
+ demand. Useful together with `git cat-file -e <object>` to
+ see if the object is locally available.
+ This is equivalent to setting the `GIT_NO_LAZY_FETCH`
+ environment variable to `1`.
--literal-pathspecs::
Treat pathspecs literally (i.e. no globbing, no pathspec magic).
@@ -202,7 +211,7 @@ If you just want to run git as if it was started in `<path>` then use
Do not perform optional operations that require locks. This is
equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
---list-cmds=group[,group...]::
+--list-cmds=<group>[,<group>...]::
List commands by group. This is an internal/experimental
option and may change or be removed in the future. Supported
groups are: builtins, parseopt (builtin commands that use
@@ -556,6 +565,11 @@ double-quotes and respecting backslash escapes. E.g., the value
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
~~~~~~~~~~~
`GIT_AUTHOR_NAME`::
@@ -724,13 +738,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.
@@ -838,7 +851,7 @@ of the SID and an optional counter (to avoid filename
collisions).
+
In addition, if the variable is set to
-`af_unix:[<socket_type>:]<absolute-pathname>`, Git will try
+`af_unix:[<socket-type>:]<absolute-pathname>`, Git will try
to open the path as a Unix Domain Socket. The socket type
can be either `stream` or `dgram`.
+
@@ -868,6 +881,10 @@ for full details.
header and packfile URIs. Set this Boolean environment variable to false to prevent this
redaction.
+`GIT_NO_REPLACE_OBJECTS`::
+ Setting and exporting this environment variable tells Git to
+ ignore replacement refs and do not replace Git objects.
+
`GIT_LITERAL_PATHSPECS`::
Setting this Boolean environment variable to true will cause Git to treat all
pathspecs literally, rather than as glob patterns. For example,
@@ -889,6 +906,11 @@ for full details.
Setting this Boolean environment variable to true will cause Git to treat all
pathspecs as case-insensitive.
+`GIT_NO_LAZY_FETCH`::
+ Setting this Boolean environment variable to true tells Git
+ not to lazily fetch missing objects from the promisor remote
+ on demand.
+
`GIT_REFLOG_ACTION`::
When a ref is updated, reflog entries are created to keep
track of the reason why the ref was updated (which is
@@ -917,9 +939,9 @@ for full details.
avoid issues with stale commit-graphs that contain references to
already-deleted commits, but comes with a performance penalty.
+
-The default is "true", which enables the aforementioned behavior.
-Setting this to "false" disables the existence check. This can lead to
-a performance improvement at the cost of consistency.
+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
@@ -938,7 +960,7 @@ a performance improvement at the cost of consistency.
`GIT_PROTOCOL`::
For internal use only. Used in handshaking the wire protocol.
Contains a colon ':' separated list of keys with optional values
- 'key[=value]'. Presence of unknown keys and values must be
+ '<key>[=<value>]'. Presence of unknown keys and values must be
ignored.
+
Note that servers may need to be configured to allow this variable to
@@ -1025,10 +1047,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
@@ -1071,7 +1094,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
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 8c1793c148..4338d023d9 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -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
-------
@@ -1122,11 +1137,11 @@ The `merge.*.name` variable gives the driver a human-readable
name.
The `merge.*.driver` variable's value is used to construct a
-command to run to merge ancestor's version (`%O`), current
+command to run to common ancestor's version (`%O`), current
version (`%A`) and the other branches' version (`%B`). These
three tokens are replaced with the names of temporary files that
hold the contents of these versions when the command line is
-built. Additionally, %L will be replaced with the conflict marker
+built. Additionally, `%L` will be replaced with the conflict marker
size (see below).
The merge driver is expected to leave the result of the merge in
@@ -1144,8 +1159,9 @@ When left unspecified, the driver itself is used for both
internal merge and the final merge.
The merge driver can learn the pathname in which the merged result
-will be stored via placeholder `%P`.
-
+will be stored via placeholder `%P`. The conflict labels to be used
+for the common ancestor, local head and other head can be passed by
+using '%S', '%X' and '%Y` respectively.
`conflict-marker-size`
^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index e5fac94322..7c709324ba 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -81,9 +81,6 @@ you will.
Here are the rules regarding the "flags" that you should follow when you are
scripting Git:
- * It's preferred to use the non-dashed form of Git commands, which means that
- you should prefer `git foo` to `git-foo`.
-
* Splitting short options to separate words (prefer `git foo -a -b`
to `git foo -ab`, the latter may not even work).
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index c0b95256cc..2122aeb976 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -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.
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index 3cda2e07c2..642c51227b 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -245,20 +245,20 @@ diffcore-pickaxe: For Detecting Addition/Deletion of Specified String
This transformation limits the set of filepairs to those that change
specified strings between the preimage and the postimage in a certain
-way. -S<block of text> and -G<regular expression> options are used to
+way. -S<block-of-text> and -G<regular-expression> options are used to
specify different ways these strings are sought.
-"-S<block of text>" detects filepairs whose preimage and postimage
+"-S<block-of-text>" detects filepairs whose preimage and postimage
have different number of occurrences of the specified block of text.
By definition, it will not detect in-file moves. Also, when a
changeset moves a file wholesale without affecting the interesting
string, diffcore-rename kicks in as usual, and `-S` omits the filepair
(since the number of occurrences of that string didn't change in that
rename-detected filepair). When used with `--pickaxe-regex`, treat
-the <block of text> as an extended POSIX regular expression to match,
+the <block-of-text> as an extended POSIX regular expression to match,
instead of a literal string.
-"-G<regular expression>" (mnemonic: grep) detects filepairs whose
+"-G<regular-expression>" (mnemonic: grep) detects filepairs whose
textual diff has an added or a deleted line that matches the given
regular expression. This means that it will detect in-file (or what
rename-detection considers the same file) moves, which is noise. The
diff --git a/Documentation/gitformat-index.txt b/Documentation/gitformat-index.txt
index 0773e5c380..145cace1fe 100644
--- a/Documentation/gitformat-index.txt
+++ b/Documentation/gitformat-index.txt
@@ -386,8 +386,8 @@ The remaining data of each directory block is grouped by type:
long, "REUC" extension that is M-bytes long, followed by "EOIE",
then the hash would be:
- Hash("TREE" + <binary representation of N> +
- "REUC" + <binary representation of M>)
+ Hash("TREE" + <binary-representation-of-N> +
+ "REUC" + <binary-representation-of-M>)
== Index Entry Offset Table
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 883982e7a0..ee9b92c90d 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -243,7 +243,7 @@ named remote is not being used both values will be the same.
Information about what is to be pushed is provided on the hook's standard
input with lines of the form:
- <local ref> SP <local object name> SP <remote ref> SP <remote object name> LF
+ <local-ref> SP <local-object-name> SP <remote-ref> SP <remote-object-name> LF
For instance, if the command +git push origin master:foreign+ were run the
hook would receive a line like the following:
@@ -251,9 +251,9 @@ hook would receive a line like the following:
refs/heads/master 67890 refs/heads/foreign 12345
although the full object name would be supplied. If the foreign ref does not
-yet exist the `<remote object name>` will be the all-zeroes object name. If a
-ref is to be deleted, the `<local ref>` will be supplied as `(delete)` and the
-`<local object name>` will be the all-zeroes object name. If the local commit
+yet exist the `<remote-object-name>` will be the all-zeroes object name. If a
+ref is to be deleted, the `<local-ref>` will be supplied as `(delete)` and the
+`<local-object-name>` will be the all-zeroes object name. If the local commit
was specified by something other than a name which could be expanded (such as
`HEAD~`, or an object name) it will be supplied as it was originally given.
@@ -275,12 +275,12 @@ This hook executes once for the receive operation. It takes no
arguments, but for each ref to be updated it receives on standard
input a line of the format:
- <old-value> SP <new-value> SP <ref-name> LF
+ <old-oid> SP <new-oid> SP <ref-name> LF
-where `<old-value>` is the old object name stored in the ref,
-`<new-value>` is the new object name to be stored in the ref and
+where `<old-oid>` is the old object name stored in the ref,
+`<new-oid>` is the new object name to be stored in the ref and
`<ref-name>` is the full name of the ref.
-When creating a new ref, `<old-value>` is the all-zeroes object name.
+When creating a new ref, `<old-oid>` is the all-zeroes object name.
If the hook exits with non-zero status, none of the refs will be
updated. If the hook exits with zero, updating of individual refs can
@@ -503,13 +503,13 @@ given reference transaction is in:
For each reference update that was added to the transaction, the hook
receives on standard input a line of the format:
- <old-value> SP <new-value> SP <ref-name> LF
+ <old-oid> SP <new-oid> SP <ref-name> LF
-where `<old-value>` is the old object name passed into the reference
-transaction, `<new-value>` is the new object name to be stored in the
+where `<old-oid>` is the old object name passed into the reference
+transaction, `<new-oid>` is the new object name to be stored in the
ref and `<ref-name>` is the full name of the ref. When force updating
the reference regardless of its current value or when the reference is
-to be created anew, `<old-value>` is the all-zeroes object name. To
+to be created anew, `<old-oid>` is the all-zeroes object name. To
distinguish these cases, you can inspect the current value of
`<ref-name>` via `git rev-parse`.
diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt
index c2213bb77b..35b3996029 100644
--- a/Documentation/gitk.txt
+++ b/Documentation/gitk.txt
@@ -8,7 +8,7 @@ gitk - The Git repository browser
SYNOPSIS
--------
[verse]
-'gitk' [<options>] [<revision range>] [--] [<path>...]
+'gitk' [<options>] [<revision-range>] [--] [<path>...]
DESCRIPTION
-----------
@@ -124,7 +124,7 @@ gitk-specific options
range to show. The command is expected to print on its
standard output a list of additional revisions to be shown,
one per line. Use this instead of explicitly specifying a
- '<revision range>' if the set of commits to show may vary
+ '<revision-range>' if the set of commits to show may vary
between refreshes.
--select-commit=<ref>::
diff --git a/Documentation/gitprotocol-capabilities.txt b/Documentation/gitprotocol-capabilities.txt
index d6c6effc21..2cf7735be4 100644
--- a/Documentation/gitprotocol-capabilities.txt
+++ b/Documentation/gitprotocol-capabilities.txt
@@ -378,7 +378,7 @@ fetch-pack may send "filter" commands to request a partial clone
or partial fetch and request that the server omit various objects
from the packfile.
-session-id=<session id>
+session-id=<session-id>
-----------------------
The server may advertise a session ID that can be used to identify this process
diff --git a/Documentation/gitprotocol-http.txt b/Documentation/gitprotocol-http.txt
index 21b73b7a1f..ec40a550cc 100644
--- a/Documentation/gitprotocol-http.txt
+++ b/Documentation/gitprotocol-http.txt
@@ -391,14 +391,14 @@ C: Start a queue, `c_pending`, ordered by commit time (popping newest
C: Send one `$GIT_URL/git-upload-pack` request:
- C: 0032want <want #1>...............................
- C: 0032want <want #2>...............................
+ C: 0032want <want-#1>...............................
+ C: 0032want <want-#2>...............................
....
- C: 0032have <common #1>.............................
- C: 0032have <common #2>.............................
+ C: 0032have <common-#1>.............................
+ C: 0032have <common-#2>.............................
....
- C: 0032have <have #1>...............................
- C: 0032have <have #2>...............................
+ C: 0032have <have-#1>...............................
+ C: 0032have <have-#2>...............................
....
C: 0000
@@ -512,7 +512,7 @@ Within the command portion of the request body clients SHOULD send
the id obtained through ref discovery as old_id.
update_request = command_list
- "PACK" <binary data>
+ "PACK" <binary-data>
command_list = PKT-LINE(command NUL cap_list LF)
*(command_pkt)
@@ -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
--------
diff --git a/Documentation/gitprotocol-v2.txt b/Documentation/gitprotocol-v2.txt
index 8c1e7c61ea..414bc625d5 100644
--- a/Documentation/gitprotocol-v2.txt
+++ b/Documentation/gitprotocol-v2.txt
@@ -199,7 +199,7 @@ which can be used to limit the refs sent from the server.
Additional features not supported in the base command will be advertised
as the value of the command in the capability advertisement in the form
-of a space separated list of features: "<command>=<feature 1> <feature 2>"
+of a space separated list of features: "<command>=<feature-1> <feature-2>"
ls-refs takes in the following arguments:
@@ -245,7 +245,7 @@ addition of future extensions.
Additional features not supported in the base command will be advertised
as the value of the command in the capability advertisement in the form
-of a space separated list of features: "<command>=<feature 1> <feature 2>"
+of a space separated list of features: "<command>=<feature-1> <feature-2>"
A `fetch` request can take the following arguments:
@@ -346,7 +346,8 @@ the 'wanted-refs' section in the server's response as explained below.
want-ref <ref>
Indicates to the server that the client wants to retrieve a
particular ref, where <ref> is the full name of a ref on the
- server.
+ server. It is a protocol error to send want-ref for the
+ same ref more than once.
If the 'sideband-all' feature is advertised, the following argument can be
included in the client's request:
@@ -361,9 +362,10 @@ included in the client's request:
If the 'packfile-uris' feature is advertised, the following argument
can be included in the client's request as well as the potential
addition of the 'packfile-uris' section in the server's response as
-explained below.
+explained below. Note that at most one `packfile-uris` line can be sent
+to the server.
- packfile-uris <comma-separated list of protocols>
+ packfile-uris <comma-separated-list-of-protocols>
Indicates to the server that the client is willing to receive
URIs of any of the given protocols in place of objects in the
sent packfile. Before performing the connectivity check, the
@@ -534,7 +536,7 @@ with objects using hash algorithm X. If not specified, the server is assumed to
only handle SHA-1. If the client would like to use a hash algorithm other than
SHA-1, it should specify its object-format string.
-session-id=<session id>
+session-id=<session-id>
~~~~~~~~~~~~~~~~~~~~~~~
The server may advertise a session ID that can be used to identify this process
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index ed8da428c9..d0be008e5e 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -479,14 +479,14 @@ set by Git if the remote helper has the 'option' capability.
'option depth' <depth>::
Deepens the history of a shallow repository.
-'option deepen-since <timestamp>::
+'option deepen-since' <timestamp>::
Deepens the history of a shallow repository based on time.
-'option deepen-not <ref>::
+'option deepen-not' <ref>::
Deepens the history of a shallow repository excluding ref.
Multiple options add up.
-'option deepen-relative {'true'|'false'}::
+'option deepen-relative' {'true'|'false'}::
Deepens the history of a shallow repository relative to
current boundary. Only valid when used with "option depth".
@@ -526,7 +526,7 @@ set by Git if the remote helper has the 'option' capability.
'option pushcert' {'true'|'false'}::
GPG sign pushes.
-'option push-option <string>::
+'option push-option' <string>::
Transmit <string> as a push option. As the push option
must not contain LF or NUL characters, the string is not encoded.
@@ -542,13 +542,10 @@ set by Git if the remote helper has the 'option' capability.
transaction. If successful, all refs will be updated, or none will. If the
remote side does not support this capability, the push will fail.
-'option object-format' {'true'|algorithm}::
- If 'true', indicate that the caller wants hash algorithm information
+'option object-format true'::
+ Indicate that the caller wants hash algorithm information
to be passed back from the remote. This mode is used when fetching
refs.
-+
-If set to an algorithm, indicate that the caller wants to interact with
-the remote side using that algorithm.
SEE ALSO
--------
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 1a2ef4c150..949cd8a31e 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -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
diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt
index 8400d591da..f7b5a25a0c 100644
--- a/Documentation/gitsubmodules.txt
+++ b/Documentation/gitsubmodules.txt
@@ -151,7 +151,7 @@ the superproject's `$GIT_DIR/config` file, so the superproject's history
is not affected. This can be undone using `git submodule init`.
* Deleted submodule: A submodule can be deleted by running
-`git rm <submodule path> && git commit`. This can be undone
+`git rm <submodule-path> && git commit`. This can be undone
using `git revert`.
+
The deletion removes the superproject's tracking data, which are
@@ -229,7 +229,7 @@ Workflow for a third party library
git submodule add <URL> <path>
# Occasionally update the submodule to a new version:
- git -C <path> checkout <new version>
+ git -C <path> checkout <new-version>
git add <path>
git commit -m "update submodule to new version"
diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt
index b078fef6f5..85983587fc 100644
--- a/Documentation/gitweb.conf.txt
+++ b/Documentation/gitweb.conf.txt
@@ -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
@@ -343,7 +343,7 @@ $home_link_str::
Label for the "home link" at the top of all pages, leading to `$home_link`
(usually the main gitweb page, which contains the projects list). It is
used as the first component of gitweb's "breadcrumb trail":
- `<home link> / <project> / <action>`. Can be set at build time using
+ `<home-link> / <project> / <action>`. Can be set at build time using
the `GITWEB_HOME_LINK_STR` variable. By default it is set to "projects",
as this link leads to the list of projects. Another popular choice is to
set it to the name of site. Note that it is treated as raw HTML so it
@@ -604,9 +604,9 @@ Many gitweb features can be enabled (or disabled) and configured using the
Each `%feature` hash element is a hash reference and has the following
structure:
----------------------------------------------------------------------
-"<feature_name>" => {
- "sub" => <feature-sub (subroutine)>,
- "override" => <allow-override (boolean)>,
+"<feature-name>" => {
+ "sub" => <feature-sub-(subroutine)>,
+ "override" => <allow-override-(boolean)>,
"default" => [ <options>... ]
},
----------------------------------------------------------------------
@@ -614,7 +614,7 @@ Some features cannot be overridden per project. For those
features the structure of appropriate `%feature` hash element has a simpler
form:
----------------------------------------------------------------------
-"<feature_name>" => {
+"<feature-name>" => {
"override" => 0,
"default" => [ <options>... ]
},
@@ -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*):
+
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
index 1030e9667e..56d24a30a3 100644
--- a/Documentation/gitweb.txt
+++ b/Documentation/gitweb.txt
@@ -28,7 +28,7 @@ Gitweb provides a web interface to Git repositories. Its features include:
revisions one at a time, viewing the history of the repository.
* 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.
@@ -305,7 +305,7 @@ pathnames. In most general form such path_info (component) based gitweb URL
looks like this:
-----------------------------------------------------------------------
-.../gitweb.cgi/<repo>/<action>/<revision_from>:/<path_from>..<revision_to>:/<path_to>?<arguments>
+.../gitweb.cgi/<repo>/<action>/<revision-from>:/<path-from>..<revision-to>:/<path-to>?<arguments>
-----------------------------------------------------------------------
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 59d8ab8572..d71b199955 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -202,6 +202,8 @@ 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 enable two otherwise different lines of development to be joined
@@ -312,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
@@ -630,6 +638,20 @@ The most notable example is `HEAD`.
An <<def_object,object>> used to temporarily store the contents of a
<<def_dirty,dirty>> working directory and the index for future reuse.
+[[def_special_ref]]special ref::
+ A ref that has different semantics than normal refs. These refs can be
+ accessed via normal Git commands but may not behave the same as a
+ normal ref in some cases.
++
+The following special refs are known to Git:
+
+ - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+ may refer to multiple object IDs. Each object ID is annotated with metadata
+ indicating where it was fetched from and its fetch status.
+
+ - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
+ conflicts. It contains all commit IDs which are being merged.
+
[[def_submodule]]submodule::
A <<def_repository,repository>> that holds the history of a
separate project inside another repository (the latter of
@@ -695,6 +717,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>>.
diff --git a/Documentation/howto/keep-canonical-history-correct.txt b/Documentation/howto/keep-canonical-history-correct.txt
index 35d48ef714..5f800fd85a 100644
--- a/Documentation/howto/keep-canonical-history-correct.txt
+++ b/Documentation/howto/keep-canonical-history-correct.txt
@@ -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
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
index 151ee84ceb..4e727deedd 100644
--- a/Documentation/howto/update-hook-example.txt
+++ b/Documentation/howto/update-hook-example.txt
@@ -100,7 +100,7 @@ info "The user is: '$username'"
if test -f "$allowed_users_file"
then
- rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
+ rc=$(grep -Ev '^(#|$)' $allowed_users_file |
while read heads user_patterns
do
# does this rule apply to us?
@@ -138,7 +138,7 @@ info "'$groups'"
if test -f "$allowed_groups_file"
then
- rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
+ rc=$(grep -Ev '^(#|$)' $allowed_groups_file |
while read heads group_patterns
do
# does this rule apply to us?
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index d8f7cd7ca0..3eaefc4e94 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -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
diff --git a/Documentation/mergetools/vimdiff.txt b/Documentation/mergetools/vimdiff.txt
index d1a4c468e6..befa86d692 100644
--- a/Documentation/mergetools/vimdiff.txt
+++ b/Documentation/mergetools/vimdiff.txt
@@ -177,7 +177,8 @@ Instead of `--tool=vimdiff`, you can also use one of these other variants:
When using these variants, in order to specify a custom layout you will have to
set configuration variables `mergetool.gvimdiff.layout` and
-`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout`
+`mergetool.nvimdiff.layout` instead of `mergetool.vimdiff.layout` (though the
+latter will be used as fallback if the variant-specific one is not set).
In addition, for backwards compatibility with previous Git versions, you can
also append `1`, `2` or `3` to either `vimdiff` or any of the variants (ex:
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index d38b4ab566..8ee940b6a4 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -316,9 +316,8 @@ multiple times, the last occurrence wins.
`Reviewed-by`.
** 'only[=<bool>]': select whether non-trailer lines from the trailer
block should be included.
-** 'separator=<sep>': specify a separator inserted between trailer
- lines. When this option is not given each trailer line is
- terminated with a line feed character. The string <sep> may contain
+** 'separator=<sep>': specify the separator inserted between trailer
+ lines. Defaults to a line feed character. The string <sep> may contain
the literal formatting codes described above. To use comma as
separator one must use `%x2C` as it would otherwise be parsed as
next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
@@ -329,10 +328,9 @@ multiple times, the last occurrence wins.
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
** 'keyonly[=<bool>]': only show the key part of the trailer.
** 'valueonly[=<bool>]': only show the value part of the trailer.
-** 'key_value_separator=<sep>': specify a separator inserted between
- trailer lines. When this option is not given each trailer key-value
- pair is separated by ": ". Otherwise it shares the same semantics
- as 'separator=<sep>' above.
+** 'key_value_separator=<sep>': specify the separator inserted between
+ the key and value of each trailer. Defaults to ": ". Otherwise it
+ shares the same semantics as 'separator=<sep>' above.
NOTE: Some placeholders may depend on other options given to the
revision traversal engine. For example, the `%g*` reflog options will
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644
index 0000000000..14fff8a9c6
--- /dev/null
+++ b/Documentation/ref-storage-format.txt
@@ -0,0 +1,3 @@
+* `files` for loose files with packed-refs. This is the default.
+* `reftable` for the reftable format. This format is experimental and its
+ internals are subject to change.
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 2bf239ff03..00ccf68744 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -316,12 +316,12 @@ list.
With `--pretty` format other than `oneline` and `reference` (for obvious reasons),
this causes the output to have two extra lines of information
taken from the reflog. The reflog designator in the output may be shown
-as `ref@{Nth}` (where `Nth` is the reverse-chronological index in the
-reflog) or as `ref@{timestamp}` (with the timestamp for that entry),
+as `ref@{<Nth>}` (where _<Nth>_ is the reverse-chronological index in the
+reflog) or as `ref@{<timestamp>}` (with the _<timestamp>_ for that entry),
depending on a few rules:
+
--
-1. If the starting point is specified as `ref@{Nth}`, show the index
+1. If the starting point is specified as `ref@{<Nth>}`, show the index
format.
+
2. If the starting point was specified as `ref@{now}`, show the
@@ -341,8 +341,11 @@ See also linkgit:git-reflog[1].
Under `--pretty=reference`, this information will not be shown at all.
--merge::
- After a failed merge, show refs that touch files having a
- conflict and don't exist on all heads to merge.
+ Show commits touching conflicted paths in the range `HEAD...<other>`,
+ where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
+ `CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+ when the index has unmerged entries. This option can be used to show
+ relevant commits when resolving conflicts from a 3-way merge.
--boundary::
Output excluded boundary commits. Boundary commits are
@@ -947,10 +950,10 @@ ifdef::git-rev-list[]
+
The form '--filter=blob:none' omits all blobs.
+
-The form '--filter=blob:limit=<n>[kmg]' omits blobs larger than n bytes
-or units. n may be zero. The suffixes k, m, and g can be used to name
-units in KiB, MiB, or GiB. For example, 'blob:limit=1k' is the same
-as 'blob:limit=1024'.
+The form '--filter=blob:limit=<n>[kmg]' omits blobs of size at least n
+bytes or units. n may be zero. The suffixes k, m, and g can be used
+to name units in KiB, MiB, or GiB. For example, 'blob:limit=1k'
+is the same as 'blob:limit=1024'.
+
The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
which are not of the requested type.
@@ -1019,6 +1022,10 @@ Unexpected missing objects will raise an error.
+
The form '--missing=print' is like 'allow-any', but will also print a
list of the missing objects. Object IDs are prefixed with a ``?'' character.
++
+If some tips passed to the traversal are missing, they will be
+considered as missing too, and the traversal will ignore them. In case
+we cannot get their Object ID though, an error will be raised.
--exclude-promisor-objects::
(For internal use only.) Prefilter object traversal at
diff --git a/Documentation/signoff-option.txt b/Documentation/signoff-option.txt
index 12aa2333e4..d98758f3cb 100644
--- a/Documentation/signoff-option.txt
+++ b/Documentation/signoff-option.txt
@@ -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.
diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt
index 045a76756f..47281420fc 100644
--- a/Documentation/technical/repository-version.txt
+++ b/Documentation/technical/repository-version.txt
@@ -100,3 +100,9 @@ If set, by default "git config" reads from both "config" and
multiple working directory mode, "config" file is shared while
"config.worktree" is per-working directory (i.e., it's in
GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The valid values are
+`files` (loose references with a packed-refs file) and `reftable` (see
+Documentation/technical/reftable.txt).
diff --git a/Documentation/trace2-target-values.txt b/Documentation/trace2-target-values.txt
index 3985b6d3c2..06f1953313 100644
--- a/Documentation/trace2-target-values.txt
+++ b/Documentation/trace2-target-values.txt
@@ -5,7 +5,7 @@
* `<absolute-pathname>` - Writes to the file in append mode. If the target
already exists and is a directory, the traces will be written to files (one
per process) underneath the given directory.
-* `af_unix:[<socket_type>:]<absolute-pathname>` - Write to a
+* `af_unix:[<socket-type>:]<absolute-pathname>` - Write to a
Unix DomainSocket (on platforms that support them). Socket
type can be either `stream` or `dgram`; if omitted Git will
try both.
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 4e79c1589e..7cec85aef1 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -15,14 +15,14 @@ should be used with caution on unsecured networks.
The following syntaxes may be used with them:
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}:__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++http++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
+- ++ftp++{startsb}++s++{endsb}++://++__<host>__{startsb}++:++__<port>__{endsb}++/++__<path-to-git-repo>__
An alternative scp-like syntax may also be used with the ssh protocol:
-- {startsb}user@{endsb}host.xz:path/to/repo.git/
+- {startsb}__<user>__++@++{endsb}__<host>__++:/++__<path-to-git-repo>__
This syntax is only recognized if there are no slashes before the
first colon. This helps differentiate a local path that contains a
@@ -30,40 +30,40 @@ colon. For example the local path `foo:bar` could be specified as an
absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
url.
-The ssh and git protocols additionally support ~username expansion:
+The ssh and git protocols additionally support ++~++__<username>__ expansion:
-- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+- ++ssh://++{startsb}__<user>__++@++{endsb}__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- ++git://++__<host>__{startsb}++:++__<port>__{endsb}++/~++__<user>__++/++__<path-to-git-repo>__
+- {startsb}__<user>__++@++{endsb}__<host>__++:~++__<user>__++/++__<path-to-git-repo>__
For local repositories, also supported by Git natively, the following
syntaxes may be used:
-- /path/to/repo.git/
-- \file:///path/to/repo.git/
+- `/path/to/repo.git/`
+- ++file:///path/to/repo.git/++
ifndef::git-clone[]
These two syntaxes are mostly equivalent, except when cloning, when
-the former implies --local option. See linkgit:git-clone[1] for
+the former implies `--local` option. See linkgit:git-clone[1] for
details.
endif::git-clone[]
ifdef::git-clone[]
These two syntaxes are mostly equivalent, except the former implies
---local option.
+`--local` option.
endif::git-clone[]
-'git clone', 'git fetch' and 'git pull', but not 'git push', will also
+`git clone`, `git fetch` and `git pull`, but not `git push`, will also
accept a suitable bundle file. See linkgit:git-bundle[1].
When Git doesn't know how to handle a certain transport protocol, it
-attempts to use the 'remote-<transport>' remote helper, if one
+attempts to use the `remote-`{empty}__<transport>__ remote helper, if one
exists. To explicitly request a remote helper, the following syntax
may be used:
-- <transport>::<address>
+- _<transport>_::__<address>__
-where <address> may be a path, a server and path, or an arbitrary
+where _<address>_ may be a path, a server and path, or an arbitrary
URL-like string recognized by the specific remote helper being
invoked. See linkgit:gitremote-helpers[7] for details.
@@ -72,10 +72,11 @@ you want to use a different format for them (such that the URLs you
use will be rewritten into URLs that work), you can create a
configuration section of the form:
-------------
- [url "<actual url base>"]
- insteadOf = <other url base>
-------------
+[verse]
+--
+ [url "__<actual-url-base>__"]
+ insteadOf = _<other-url-base>_
+--
For example, with this:
@@ -91,10 +92,11 @@ rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
If you want to rewrite URLs for push only, you can create a
configuration section of the form:
-------------
- [url "<actual url base>"]
- pushInsteadOf = <other url base>
-------------
+[verse]
+--
+ [url "__<actual-url-base>__"]
+ pushInsteadOf = _<other-url-base>_
+--
For example, with this:
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index d8dbe6b56d..90a4189358 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1344,7 +1344,7 @@ $ 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 special ref named AUTO_MERGE
+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
@@ -4093,15 +4093,46 @@ that not only specifies their type, but also provides size information
about the data in the object. It's worth noting that the SHA-1 hash
that is used to name the object is the hash of the original data
plus this header, so `sha1sum` 'file' does not match the object name
-for 'file'.
+for 'file' (the earliest versions of Git hashed slightly differently
+but the conclusion is still the same).
+
+The following is a short example that demonstrates how these hashes
+can be generated manually:
+
+Let's assume a small text file with some simple content:
+
+-------------------------------------------------
+$ echo "Hello world" >hello.txt
+-------------------------------------------------
+
+We can now manually generate the hash Git would use for this file:
+
+- The object we want the hash for is of type "blob" and its size is
+ 12 bytes.
+
+- Prepend the object header to the file content and feed this to
+ `sha1sum`:
+
+-------------------------------------------------
+$ { printf "blob 12\0"; cat hello.txt; } | sha1sum
+802992c4220de19a90767f3000a79a31b98d0df7 -
+-------------------------------------------------
+
+This manually constructed hash can be verified using `git hash-object`
+which of course hides the addition of the header:
+
+-------------------------------------------------
+$ git hash-object hello.txt
+802992c4220de19a90767f3000a79a31b98d0df7
+-------------------------------------------------
As a result, the general consistency of an object can always be tested
independently of the contents or the type of the object: all objects can
be validated by verifying that (a) their hashes match the content of the
file and (b) the object successfully inflates to a stream of bytes that
forms a sequence of
-`<ascii type without space> + <space> + <ascii decimal size> +
-<byte\0> + <binary object data>`.
+`<ascii-type-without-space> + <space> + <ascii-decimal-size> +
+<byte\0> + <binary-object-data>`.
The structured objects can further have their structure and
connectivity to other objects verified. This is generally done with
@@ -4123,7 +4154,8 @@ $ git switch --detach e83c5163
----------------------------------------------------
The initial revision lays the foundation for almost everything Git has
-today, but is small enough to read in one sitting.
+today (even though details may differ in a few places), but is small
+enough to read in one sitting.
Note that terminology has changed since that revision. For example, the
README in that revision uses the word "changeset" to describe what we
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 0901e1bb23..dabd2b5b89 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.43.GIT
+DEF_VER=v2.45.0-rc0
LF='
'
diff --git a/INSTALL b/INSTALL
index 06f29a8ae7..2a46d04592 100644
--- a/INSTALL
+++ b/INSTALL
@@ -124,7 +124,7 @@ Issues of note:
interacting with svn repositories with "git svn"). If you can
live without these, use NO_PERL. Note that recent releases of
Redhat/Fedora are reported to ship Perl binary package with some
- core modules stripped away (see http://lwn.net/Articles/477234/),
+ core modules stripped away (see https://lwn.net/Articles/477234/),
so you might need to install additional packages other than Perl
itself, e.g. Digest::MD5, File::Spec, File::Temp, Net::Domain,
Net::SMTP, and Time::HiRes.
@@ -139,7 +139,7 @@ Issues of note:
not need that functionality, use NO_CURL to build without
it.
- Git requires version "7.19.5" or later of "libcurl" to build
+ Git requires version "7.21.3" or later of "libcurl" to build
without NO_CURL. This version requirement may be bumped in
the future.
diff --git a/Makefile b/Makefile
index 312d95084c..1e31acc72e 100644
--- a/Makefile
+++ b/Makefile
@@ -186,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
@@ -752,7 +752,13 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
ETAGS_TARGET = TAGS
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
.PHONY: fuzz-objs
@@ -761,7 +767,7 @@ fuzz-objs: $(FUZZ_OBJS)
# Always build fuzz objects even if not testing, to prevent bit-rot.
all:: $(FUZZ_OBJS)
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
# Empty...
EXTRA_PROGRAMS =
@@ -791,8 +797,8 @@ TEST_BUILTINS_OBJS += test-chmtime.o
TEST_BUILTINS_OBJS += test-config.o
TEST_BUILTINS_OBJS += test-crontab.o
TEST_BUILTINS_OBJS += test-csprng.o
-TEST_BUILTINS_OBJS += test-ctype.o
TEST_BUILTINS_OBJS += test-date.o
+TEST_BUILTINS_OBJS += test-delete-gpgsig.o
TEST_BUILTINS_OBJS += test-delta.o
TEST_BUILTINS_OBJS += test-dir-iterator.o
TEST_BUILTINS_OBJS += test-drop-caches.o
@@ -802,7 +808,6 @@ 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
@@ -828,7 +833,6 @@ TEST_BUILTINS_OBJS += test-partial-clone.o
TEST_BUILTINS_OBJS += test-path-utils.o
TEST_BUILTINS_OBJS += test-pcre2-config.o
TEST_BUILTINS_OBJS += test-pkt-line.o
-TEST_BUILTINS_OBJS += test-prio-queue.o
TEST_BUILTINS_OBJS += test-proc-receive.o
TEST_BUILTINS_OBJS += test-progress.o
TEST_BUILTINS_OBJS += test-reach.o
@@ -1058,6 +1062,7 @@ LIB_OBJS += list-objects-filter.o
LIB_OBJS += list-objects.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
+LIB_OBJS += loose.o
LIB_OBJS += ls-refs.o
LIB_OBJS += mailinfo.o
LIB_OBJS += mailmap.o
@@ -1070,6 +1075,7 @@ LIB_OBJS += merge-ort-wrappers.o
LIB_OBJS += merge-recursive.o
LIB_OBJS += merge.o
LIB_OBJS += midx.o
+LIB_OBJS += midx-write.o
LIB_OBJS += name-hash.o
LIB_OBJS += negotiator/default.o
LIB_OBJS += negotiator/noop.o
@@ -1078,6 +1084,7 @@ LIB_OBJS += notes-cache.o
LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
LIB_OBJS += notes.o
+LIB_OBJS += object-file-convert.o
LIB_OBJS += object-file.o
LIB_OBJS += object-name.o
LIB_OBJS += object.o
@@ -1124,6 +1131,7 @@ LIB_OBJS += reflog.o
LIB_OBJS += refs.o
LIB_OBJS += refs/debug.o
LIB_OBJS += refs/files-backend.o
+LIB_OBJS += refs/reftable-backend.o
LIB_OBJS += refs/iterator.o
LIB_OBJS += refs/packed-backend.o
LIB_OBJS += refs/ref-cache.o
@@ -1293,6 +1301,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
@@ -1339,7 +1348,10 @@ THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
UNIT_TEST_PROGRAMS += t-basic
+UNIT_TEST_PROGRAMS += t-mem-pool
UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-prio-queue
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
@@ -1546,23 +1558,23 @@ ifneq (,$(SOCKLEN_T))
endif
ifeq ($(uname_S),Darwin)
- ifndef NO_FINK
- ifeq ($(shell test -d /sw/lib && echo y),y)
+ ifndef NO_FINK
+ ifeq ($(shell test -d /sw/lib && echo y),y)
BASIC_CFLAGS += -I/sw/include
BASIC_LDFLAGS += -L/sw/lib
- endif
- endif
- ifndef NO_DARWIN_PORTS
- ifeq ($(shell test -d /opt/local/lib && echo y),y)
+ endif
+ endif
+ ifndef NO_DARWIN_PORTS
+ ifeq ($(shell test -d /opt/local/lib && echo y),y)
BASIC_CFLAGS += -I/opt/local/include
BASIC_LDFLAGS += -L/opt/local/lib
- endif
- endif
- ifndef NO_APPLE_COMMON_CRYPTO
+ endif
+ endif
+ ifndef NO_APPLE_COMMON_CRYPTO
NO_OPENSSL = YesPlease
APPLE_COMMON_CRYPTO = YesPlease
COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
- endif
+ endif
PTHREAD_LIBS =
endif
@@ -1584,7 +1596,7 @@ endif
ifdef LIBPCREDIR
BASIC_CFLAGS += -I$(LIBPCREDIR)/include
- EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
+ EXTLIBS += $(call libpath_template,$(LIBPCREDIR)/$(lib))
endif
ifdef HAVE_ALLOCA_H
@@ -1601,23 +1613,23 @@ ifdef NO_CURL
REMOTE_CURL_NAMES =
EXCLUDED_PROGRAMS += git-http-fetch git-http-push
else
- ifdef CURLDIR
+ ifdef CURLDIR
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
CURL_CFLAGS = -I$(CURLDIR)/include
- CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib)
- else
+ CURL_LIBCURL = $(call libpath_template,$(CURLDIR)/$(lib))
+ else
CURL_CFLAGS =
CURL_LIBCURL =
- endif
+ endif
- ifndef CURL_LDFLAGS
+ ifndef CURL_LDFLAGS
CURL_LDFLAGS = $(eval CURL_LDFLAGS := $$(shell $$(CURL_CONFIG) --libs))$(CURL_LDFLAGS)
- endif
+ endif
CURL_LIBCURL += $(CURL_LDFLAGS)
- ifndef CURL_CFLAGS
+ ifndef CURL_CFLAGS
CURL_CFLAGS = $(eval CURL_CFLAGS := $$(shell $$(CURL_CONFIG) --cflags))$(CURL_CFLAGS)
- endif
+ endif
BASIC_CFLAGS += $(CURL_CFLAGS)
REMOTE_CURL_PRIMARY = git-remote-http$X
@@ -1625,49 +1637,49 @@ else
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
PROGRAM_OBJS += http-fetch.o
PROGRAMS += $(REMOTE_CURL_NAMES)
- ifndef NO_EXPAT
+ ifndef NO_EXPAT
PROGRAM_OBJS += http-push.o
- endif
+ endif
curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
- ifeq "$(curl_check)" "072200"
+ ifeq "$(curl_check)" "072200"
USE_CURL_FOR_IMAP_SEND = YesPlease
- endif
- ifdef USE_CURL_FOR_IMAP_SEND
+ endif
+ ifdef USE_CURL_FOR_IMAP_SEND
BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND
IMAP_SEND_BUILDDEPS = http.o
IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
- endif
- ifndef NO_EXPAT
- ifdef EXPATDIR
+ endif
+ ifndef NO_EXPAT
+ ifdef EXPATDIR
BASIC_CFLAGS += -I$(EXPATDIR)/include
- EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
- else
+ EXPAT_LIBEXPAT = $(call libpath_template,$(EXPATDIR)/$(lib)) -lexpat
+ else
EXPAT_LIBEXPAT = -lexpat
- endif
- ifdef EXPAT_NEEDS_XMLPARSE_H
+ endif
+ ifdef EXPAT_NEEDS_XMLPARSE_H
BASIC_CFLAGS += -DEXPAT_NEEDS_XMLPARSE_H
- endif
- endif
+ endif
+ endif
endif
IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
ifdef ZLIB_PATH
BASIC_CFLAGS += -I$(ZLIB_PATH)/include
- EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
+ EXTLIBS += $(call libpath_template,$(ZLIB_PATH)/$(lib))
endif
EXTLIBS += -lz
ifndef NO_OPENSSL
OPENSSL_LIBSSL = -lssl
- ifdef OPENSSLDIR
+ ifdef OPENSSLDIR
BASIC_CFLAGS += -I$(OPENSSLDIR)/include
- OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib)
- else
+ OPENSSL_LINK = $(call libpath_template,$(OPENSSLDIR)/$(lib))
+ else
OPENSSL_LINK =
- endif
- ifdef NEEDS_CRYPTO_WITH_SSL
+ endif
+ ifdef NEEDS_CRYPTO_WITH_SSL
OPENSSL_LIBSSL += -lcrypto
- endif
+ endif
else
BASIC_CFLAGS += -DNO_OPENSSL
OPENSSL_LIBSSL =
@@ -1685,18 +1697,18 @@ ifdef APPLE_COMMON_CRYPTO
endif
endif
ifndef NO_ICONV
- ifdef NEEDS_LIBICONV
- ifdef ICONVDIR
+ ifdef NEEDS_LIBICONV
+ ifdef ICONVDIR
BASIC_CFLAGS += -I$(ICONVDIR)/include
- ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib)
- else
+ ICONV_LINK = $(call libpath_template,$(ICONVDIR)/$(lib))
+ else
ICONV_LINK =
- endif
- ifdef NEEDS_LIBINTL_BEFORE_LIBICONV
+ endif
+ ifdef NEEDS_LIBINTL_BEFORE_LIBICONV
ICONV_LINK += -lintl
- endif
+ endif
EXTLIBS += $(ICONV_LINK) -liconv
- endif
+ endif
endif
ifdef ICONV_OMITS_BOM
BASIC_CFLAGS += -DICONV_OMITS_BOM
@@ -1817,10 +1829,10 @@ ifdef NO_MMAP
COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o
else
- ifdef USE_WIN32_MMAP
+ ifdef USE_WIN32_MMAP
COMPAT_CFLAGS += -DUSE_WIN32_MMAP
COMPAT_OBJS += compat/win32mmap.o
- endif
+ endif
endif
ifdef MMAP_PREVENTS_DELETE
BASIC_CFLAGS += -DMMAP_PREVENTS_DELETE
@@ -1945,11 +1957,11 @@ else
BASIC_CFLAGS += -DSHA1_DC
LIB_OBJS += sha1dc_git.o
ifdef DC_SHA1_EXTERNAL
- ifdef DC_SHA1_SUBMODULE
- ifneq ($(DC_SHA1_SUBMODULE),auto)
+ ifdef DC_SHA1_SUBMODULE
+ ifneq ($(DC_SHA1_SUBMODULE),auto)
$(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
- endif
- endif
+ endif
+ endif
BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
EXTLIBS += -lsha1detectcoll
else
@@ -2733,7 +2745,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)
@@ -3674,14 +3686,14 @@ cocciclean:
$(RM) contrib/coccinelle/*.cocci.patch
clean: profile-clean coverage-clean cocciclean
- $(RM) -r .build
+ $(RM) -r .build $(UNIT_TEST_BIN)
$(RM) po/git.pot po/git-core.pot
$(RM) git.res
$(RM) $(OBJECTS)
$(RM) headless-git.o
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
- $(RM) $(TEST_PROGRAMS) $(UNIT_TEST_PROGS)
+ $(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
$(RM) $(SP_OBJ)
$(RM) $(HCC)
@@ -3848,23 +3860,22 @@ cover_db_html: cover_db
#
# make CC=clang CXX=clang++ \
# CFLAGS="-fsanitize=fuzzer-no-link,address" \
-# LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \
+# LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
# fuzz-all
#
-FUZZ_CXXFLAGS ?= $(CFLAGS)
+FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
.PHONY: fuzz-all
-$(FUZZ_PROGRAMS): all
- $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
- $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
+ $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+ -Wl,--allow-multiple-definition \
+ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
fuzz-all: $(FUZZ_PROGRAMS)
-$(UNIT_TEST_BIN):
- @mkdir -p $(UNIT_TEST_BIN)
-
-$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS $(UNIT_TEST_BIN)
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
+ $(call mkdir_p_parent_template)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
diff --git a/README.md b/README.md
index 7ce4f05bae..665ce5f5a8 100644
--- a/README.md
+++ b/README.md
@@ -39,10 +39,10 @@ Those wishing to help with error message, usage and informational message
string translations (localization l10) should see [po/README.md][]
(a `po` file is a Portable Object file that holds the translations).
-To subscribe to the list, send an email with just "subscribe git" in
-the body to majordomo@vger.kernel.org (not the Git list). The mailing
+To subscribe to the list, send an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details). The mailing
list archives are available at <https://lore.kernel.org/git/>,
-<http://marc.info/?l=git> and other archival sites.
+<https://marc.info/?l=git> and other archival sites.
Issues which are security relevant should be disclosed privately to
the Git Security mailing list <git-security@googlegroups.com>.
diff --git a/RelNotes b/RelNotes
index a55478f9ad..ae70277109 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.44.0.txt \ No newline at end of file
+Documentation/RelNotes/2.45.0.txt \ No newline at end of file
diff --git a/add-patch.c b/add-patch.c
index bfe19876cd..a06dd18985 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -12,7 +12,6 @@
#include "strvec.h"
#include "pathspec.h"
#include "color.h"
-#include "diff.h"
#include "compat/terminal.h"
#include "prompt.h"
@@ -1106,26 +1105,26 @@ 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, comment_line_char,
+ strbuf_commented_addf(&s->buf, comment_line_str,
_("Manual hunk edit mode -- see bottom for "
"a quick guide.\n"));
render_hunk(s, hunk, 0, 0, &s->buf);
- strbuf_commented_addf(&s->buf, comment_line_char,
+ strbuf_commented_addf(&s->buf, comment_line_str,
_("---\n"
"To remove '%c' lines, make them ' ' lines "
"(context).\n"
"To remove '%c' lines, delete them.\n"
- "Lines starting with %c will be removed.\n"),
+ "Lines starting with %s will be removed.\n"),
s->mode->is_reverse ? '+' : '-',
s->mode->is_reverse ? '-' : '+',
- comment_line_char);
- strbuf_commented_addf(&s->buf, comment_line_char, "%s",
+ comment_line_str);
+ strbuf_commented_addf(&s->buf, comment_line_str, "%s",
_(s->mode->edit_hunk_hint));
/*
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
* messages.
*/
- strbuf_commented_addf(&s->buf, comment_line_char,
+ strbuf_commented_addf(&s->buf, comment_line_str,
_("If it does not apply cleanly, you will be "
"given an opportunity to\n"
"edit again. If all lines of the hunk are "
@@ -1140,7 +1139,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
for (i = 0; i < s->buf.len; ) {
size_t next = find_next_line(&s->buf, i);
- if (s->buf.buf[i] != comment_line_char)
+ if (!starts_with(s->buf.buf + i, comment_line_str))
strbuf_add(&s->plain, s->buf.buf + i, next - i);
i = next;
}
@@ -1389,13 +1388,14 @@ N_("j - leave this hunk undecided, see next undecided hunk\n"
"/ - search for a hunk matching the given regex\n"
"s - split the current hunk into smaller hunks\n"
"e - manually edit the current hunk\n"
+ "p - print the current hunk\n"
"? - print help\n");
static int patch_update_file(struct add_p_state *s,
struct file_diff *file_diff)
{
size_t hunk_index = 0;
- ssize_t i, undecided_previous, undecided_next;
+ ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1;
struct hunk *hunk;
char ch;
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1448,8 +1448,11 @@ static int patch_update_file(struct add_p_state *s,
strbuf_reset(&s->buf);
if (file_diff->hunk_nr) {
- render_hunk(s, hunk, 0, colored, &s->buf);
- fputs(s->buf.buf, stdout);
+ if (rendered_hunk_index != hunk_index) {
+ render_hunk(s, hunk, 0, colored, &s->buf);
+ fputs(s->buf.buf, stdout);
+ rendered_hunk_index = hunk_index;
+ }
strbuf_reset(&s->buf);
if (undecided_previous >= 0) {
@@ -1481,6 +1484,7 @@ static int patch_update_file(struct add_p_state *s,
permitted |= ALLOW_EDIT;
strbuf_addstr(&s->buf, ",e");
}
+ strbuf_addstr(&s->buf, ",p");
}
if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION;
@@ -1645,13 +1649,15 @@ soft_increment:
hunk_index = i;
} else if (s->answer.buf[0] == 's') {
size_t splittable_into = hunk->splittable_into;
- if (!(permitted & ALLOW_SPLIT))
+ if (!(permitted & ALLOW_SPLIT)) {
err(s, _("Sorry, cannot split this hunk"));
- else if (!split_hunk(s, file_diff,
- hunk - file_diff->hunk))
+ } else if (!split_hunk(s, file_diff,
+ hunk - file_diff->hunk)) {
color_fprintf_ln(stdout, s->s.header_color,
_("Split into %d hunks."),
(int)splittable_into);
+ rendered_hunk_index = -1;
+ }
} else if (s->answer.buf[0] == 'e') {
if (!(permitted & ALLOW_EDIT))
err(s, _("Sorry, cannot edit this hunk"));
@@ -1659,6 +1665,8 @@ soft_increment:
hunk->use = USE_HUNK;
goto soft_increment;
}
+ } else if (s->answer.buf[0] == 'p') {
+ rendered_hunk_index = -1;
} else {
const char *p = _(help_patch_remainder), *eol = p;
@@ -1730,14 +1738,6 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
if (mode == ADD_P_STASH)
s.mode = &patch_mode_stash;
else if (mode == ADD_P_RESET) {
- /*
- * NEEDSWORK: Instead of comparing to the literal "HEAD",
- * compare the commit objects instead so that other ways of
- * saying the same thing (such as "@") are also handled
- * appropriately.
- *
- * This applies to the cases below too.
- */
if (!revision || !strcmp(revision, "HEAD"))
s.mode = &patch_mode_reset_head;
else
diff --git a/advice.c b/advice.c
index 50c79443ba..75111191ad 100644
--- a/advice.c
+++ b/advice.c
@@ -33,52 +33,59 @@ static const char *advise_get_color(enum color_advice ix)
return "";
}
+enum advice_level {
+ ADVICE_LEVEL_NONE = 0,
+ ADVICE_LEVEL_DISABLED,
+ ADVICE_LEVEL_ENABLED,
+};
+
static struct {
const char *key;
- int enabled;
+ enum advice_level level;
} advice_setting[] = {
- [ADVICE_ADD_EMBEDDED_REPO] = { "addEmbeddedRepo", 1 },
- [ADVICE_ADD_EMPTY_PATHSPEC] = { "addEmptyPathspec", 1 },
- [ADVICE_ADD_IGNORED_FILE] = { "addIgnoredFile", 1 },
- [ADVICE_AM_WORK_DIR] = { "amWorkDir", 1 },
- [ADVICE_AMBIGUOUS_FETCH_REFSPEC] = { "ambiguousFetchRefspec", 1 },
- [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName", 1 },
- [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge", 1 },
- [ADVICE_DETACHED_HEAD] = { "detachedHead", 1 },
- [ADVICE_SUGGEST_DETACHING_HEAD] = { "suggestDetachingHead", 1 },
- [ADVICE_DIVERGING] = { "diverging", 1 },
- [ADVICE_FETCH_SHOW_FORCED_UPDATES] = { "fetchShowForcedUpdates", 1 },
- [ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated", 1 },
- [ADVICE_IGNORED_HOOK] = { "ignoredHook", 1 },
- [ADVICE_IMPLICIT_IDENTITY] = { "implicitIdentity", 1 },
- [ADVICE_NESTED_TAG] = { "nestedTag", 1 },
- [ADVICE_OBJECT_NAME_WARNING] = { "objectNameWarning", 1 },
- [ADVICE_PUSH_ALREADY_EXISTS] = { "pushAlreadyExists", 1 },
- [ADVICE_PUSH_FETCH_FIRST] = { "pushFetchFirst", 1 },
- [ADVICE_PUSH_NEEDS_FORCE] = { "pushNeedsForce", 1 },
- [ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate", 1 },
-
- /* make this an alias for backward compatibility */
- [ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward", 1 },
-
- [ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent", 1 },
- [ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching", 1 },
- [ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName", 1 },
- [ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected", 1 },
- [ADVICE_RESET_NO_REFRESH_WARNING] = { "resetNoRefresh", 1 },
- [ADVICE_RESOLVE_CONFLICT] = { "resolveConflict", 1 },
- [ADVICE_RM_HINTS] = { "rmHints", 1 },
- [ADVICE_SEQUENCER_IN_USE] = { "sequencerInUse", 1 },
- [ADVICE_SET_UPSTREAM_FAILURE] = { "setUpstreamFailure", 1 },
- [ADVICE_SKIPPED_CHERRY_PICKS] = { "skippedCherryPicks", 1 },
- [ADVICE_STATUS_AHEAD_BEHIND_WARNING] = { "statusAheadBehindWarning", 1 },
- [ADVICE_STATUS_HINTS] = { "statusHints", 1 },
- [ADVICE_STATUS_U_OPTION] = { "statusUoption", 1 },
- [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
- [ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 },
- [ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 },
- [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
- [ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan", 1 },
+ [ADVICE_ADD_EMBEDDED_REPO] = { "addEmbeddedRepo" },
+ [ADVICE_ADD_EMPTY_PATHSPEC] = { "addEmptyPathspec" },
+ [ADVICE_ADD_IGNORED_FILE] = { "addIgnoredFile" },
+ [ADVICE_AMBIGUOUS_FETCH_REFSPEC] = { "ambiguousFetchRefspec" },
+ [ADVICE_AM_WORK_DIR] = { "amWorkDir" },
+ [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName" },
+ [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" },
+ [ADVICE_DETACHED_HEAD] = { "detachedHead" },
+ [ADVICE_DIVERGING] = { "diverging" },
+ [ADVICE_FETCH_SHOW_FORCED_UPDATES] = { "fetchShowForcedUpdates" },
+ [ADVICE_FORCE_DELETE_BRANCH] = { "forceDeleteBranch" },
+ [ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated" },
+ [ADVICE_IGNORED_HOOK] = { "ignoredHook" },
+ [ADVICE_IMPLICIT_IDENTITY] = { "implicitIdentity" },
+ [ADVICE_MERGE_CONFLICT] = { "mergeConflict" },
+ [ADVICE_NESTED_TAG] = { "nestedTag" },
+ [ADVICE_OBJECT_NAME_WARNING] = { "objectNameWarning" },
+ [ADVICE_PUSH_ALREADY_EXISTS] = { "pushAlreadyExists" },
+ [ADVICE_PUSH_FETCH_FIRST] = { "pushFetchFirst" },
+ [ADVICE_PUSH_NEEDS_FORCE] = { "pushNeedsForce" },
+ [ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent" },
+ [ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching" },
+ [ADVICE_PUSH_REF_NEEDS_UPDATE] = { "pushRefNeedsUpdate" },
+ [ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName" },
+ [ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected" },
+ [ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward" }, /* backwards compatibility */
+ [ADVICE_REF_SYNTAX] = { "refSyntax" },
+ [ADVICE_RESET_NO_REFRESH_WARNING] = { "resetNoRefresh" },
+ [ADVICE_RESOLVE_CONFLICT] = { "resolveConflict" },
+ [ADVICE_RM_HINTS] = { "rmHints" },
+ [ADVICE_SEQUENCER_IN_USE] = { "sequencerInUse" },
+ [ADVICE_SET_UPSTREAM_FAILURE] = { "setUpstreamFailure" },
+ [ADVICE_SKIPPED_CHERRY_PICKS] = { "skippedCherryPicks" },
+ [ADVICE_STATUS_AHEAD_BEHIND_WARNING] = { "statusAheadBehindWarning" },
+ [ADVICE_STATUS_HINTS] = { "statusHints" },
+ [ADVICE_STATUS_U_OPTION] = { "statusUoption" },
+ [ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated" },
+ [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie" },
+ [ADVICE_SUBMODULE_MERGE_CONFLICT] = { "submoduleMergeConflict" },
+ [ADVICE_SUGGEST_DETACHING_HEAD] = { "suggestDetachingHead" },
+ [ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath" },
+ [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor" },
+ [ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan" },
};
static const char turn_off_instructions[] =
@@ -98,8 +105,9 @@ static void vadvise(const char *advice, int display_instructions,
for (cp = buf.buf; *cp; cp = np) {
np = strchrnul(cp, '\n');
- fprintf(stderr, _("%shint: %.*s%s\n"),
+ fprintf(stderr, _("%shint:%s%.*s%s\n"),
advise_get_color(ADVICE_COLOR_HINT),
+ (np == cp) ? "" : " ",
(int)(np - cp), cp,
advise_get_color(ADVICE_COLOR_RESET));
if (*np)
@@ -118,13 +126,13 @@ void advise(const char *advice, ...)
int advice_enabled(enum advice_type type)
{
- switch(type) {
- case ADVICE_PUSH_UPDATE_REJECTED:
- return advice_setting[ADVICE_PUSH_UPDATE_REJECTED].enabled &&
- advice_setting[ADVICE_PUSH_UPDATE_REJECTED_ALIAS].enabled;
- default:
- return advice_setting[type].enabled;
- }
+ int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+
+ if (type == ADVICE_PUSH_UPDATE_REJECTED)
+ return enabled &&
+ advice_enabled(ADVICE_PUSH_UPDATE_REJECTED_ALIAS);
+
+ return enabled;
}
void advise_if_enabled(enum advice_type type, const char *advice, ...)
@@ -135,7 +143,8 @@ void advise_if_enabled(enum advice_type type, const char *advice, ...)
return;
va_start(params, advice);
- vadvise(advice, 1, advice_setting[type].key, params);
+ vadvise(advice, !advice_setting[type].level, advice_setting[type].key,
+ params);
va_end(params);
}
@@ -164,7 +173,9 @@ int git_default_advice_config(const char *var, const char *value)
for (i = 0; i < ARRAY_SIZE(advice_setting); i++) {
if (strcasecmp(k, advice_setting[i].key))
continue;
- advice_setting[i].enabled = git_config_bool(var, value);
+ advice_setting[i].level = git_config_bool(var, value)
+ ? ADVICE_LEVEL_ENABLED
+ : ADVICE_LEVEL_DISABLED;
return 0;
}
diff --git a/advice.h b/advice.h
index 2affbe1426..c8d29f97f3 100644
--- a/advice.h
+++ b/advice.h
@@ -10,21 +10,22 @@ struct string_list;
* Add the new config variable to Documentation/config/advice.txt.
* Call advise_if_enabled to print your advice.
*/
- enum advice_type {
+enum advice_type {
ADVICE_ADD_EMBEDDED_REPO,
ADVICE_ADD_EMPTY_PATHSPEC,
ADVICE_ADD_IGNORED_FILE,
- ADVICE_AM_WORK_DIR,
ADVICE_AMBIGUOUS_FETCH_REFSPEC,
+ ADVICE_AM_WORK_DIR,
ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
ADVICE_COMMIT_BEFORE_MERGE,
ADVICE_DETACHED_HEAD,
ADVICE_DIVERGING,
- ADVICE_SUGGEST_DETACHING_HEAD,
ADVICE_FETCH_SHOW_FORCED_UPDATES,
+ ADVICE_FORCE_DELETE_BRANCH,
ADVICE_GRAFT_FILE_DEPRECATED,
ADVICE_IGNORED_HOOK,
ADVICE_IMPLICIT_IDENTITY,
+ ADVICE_MERGE_CONFLICT,
ADVICE_NESTED_TAG,
ADVICE_OBJECT_NAME_WARNING,
ADVICE_PUSH_ALREADY_EXISTS,
@@ -32,23 +33,26 @@ struct string_list;
ADVICE_PUSH_NEEDS_FORCE,
ADVICE_PUSH_NON_FF_CURRENT,
ADVICE_PUSH_NON_FF_MATCHING,
+ ADVICE_PUSH_REF_NEEDS_UPDATE,
ADVICE_PUSH_UNQUALIFIED_REF_NAME,
- ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
ADVICE_PUSH_UPDATE_REJECTED,
- ADVICE_PUSH_REF_NEEDS_UPDATE,
+ ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
+ ADVICE_REF_SYNTAX,
ADVICE_RESET_NO_REFRESH_WARNING,
ADVICE_RESOLVE_CONFLICT,
ADVICE_RM_HINTS,
ADVICE_SEQUENCER_IN_USE,
ADVICE_SET_UPSTREAM_FAILURE,
+ ADVICE_SKIPPED_CHERRY_PICKS,
ADVICE_STATUS_AHEAD_BEHIND_WARNING,
ADVICE_STATUS_HINTS,
ADVICE_STATUS_U_OPTION,
- ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
ADVICE_SUBMODULES_NOT_UPDATED,
+ ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+ ADVICE_SUBMODULE_MERGE_CONFLICT,
+ ADVICE_SUGGEST_DETACHING_HEAD,
ADVICE_UPDATE_SPARSE_PATH,
ADVICE_WAITING_FOR_EDITOR,
- ADVICE_SKIPPED_CHERRY_PICKS,
ADVICE_WORKTREE_ADD_ORPHAN,
};
diff --git a/apply.c b/apply.c
index 3d69fec836..34f20326a7 100644
--- a/apply.c
+++ b/apply.c
@@ -12,7 +12,6 @@
#include "base85.h"
#include "config.h"
#include "object-store-ll.h"
-#include "blob.h"
#include "delta.h"
#include "diff.h"
#include "dir.h"
@@ -78,7 +77,8 @@ static int parse_whitespace_option(struct apply_state *state, const char *option
return 0;
}
/*
- * Please update $__git_whitespacelist in git-completion.bash
+ * Please update $__git_whitespacelist in git-completion.bash,
+ * Documentation/git-apply.txt, and Documentation/git-am.txt
* when you add new options.
*/
return error(_("unrecognized whitespace option '%s'"), option);
@@ -1292,8 +1292,15 @@ static char *git_header_name(int p_value,
return NULL; /* no postimage name */
second = skip_tree_prefix(p_value, name + len + 1,
line_len - (len + 1));
+ /*
+ * If we are at the SP at the end of a directory,
+ * skip_tree_prefix() may return NULL as that makes
+ * it appears as if we have an absolute path.
+ * Keep going to find another SP.
+ */
if (!second)
- return NULL;
+ continue;
+
/*
* Does len bytes starting at "name" and "second"
* (that are separated by one HT or SP we just
@@ -2220,7 +2227,8 @@ static void reverse_patches(struct patch *p)
struct fragment *frag = p->fragments;
SWAP(p->new_name, p->old_name);
- SWAP(p->new_mode, p->old_mode);
+ if (p->new_mode)
+ SWAP(p->new_mode, p->old_mode);
SWAP(p->is_new, p->is_delete);
SWAP(p->lines_added, p->lines_deleted);
SWAP(p->old_oid_prefix, p->new_oid_prefix);
@@ -3778,8 +3786,17 @@ static int check_preimage(struct apply_state *state,
return error_errno("%s", old_name);
}
- if (!state->cached && !previous)
- st_mode = ce_mode_from_stat(*ce, st->st_mode);
+ if (!state->cached && !previous) {
+ if (*ce && !(*ce)->ce_mode)
+ BUG("ce_mode == 0 for path '%s'", old_name);
+
+ if (trust_executable_bit)
+ st_mode = ce_mode_from_stat(*ce, st->st_mode);
+ else if (*ce)
+ st_mode = (*ce)->ce_mode;
+ else
+ st_mode = patch->old_mode;
+ }
if (patch->is_new < 0)
patch->is_new = 0;
@@ -4431,6 +4448,7 @@ static int create_one_file(struct apply_state *state,
const char *buf,
unsigned long size)
{
+ char *newpath = NULL;
int res;
if (state->cached)
@@ -4492,24 +4510,26 @@ static int create_one_file(struct apply_state *state,
unsigned int nr = getpid();
for (;;) {
- char newpath[PATH_MAX];
- mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
+ newpath = mkpathdup("%s~%u", path, nr);
res = try_create_file(state, newpath, mode, buf, size);
if (res < 0)
- return -1;
+ goto out;
if (!res) {
if (!rename(newpath, path))
- return 0;
+ goto out;
unlink_or_warn(newpath);
break;
}
if (errno != EEXIST)
break;
++nr;
+ FREE_AND_NULL(newpath);
}
}
- return error_errno(_("unable to write file '%s' mode %o"),
- path, mode);
+ res = error_errno(_("unable to write file '%s' mode %o"), path, mode);
+out:
+ free(newpath);
+ return res;
}
static int add_conflicted_stages_file(struct apply_state *state,
@@ -4645,8 +4665,11 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
return error_errno(_("cannot open %s"), namebuf);
}
rej = fdopen(fd, "w");
- if (!rej)
- return error_errno(_("cannot open %s"), namebuf);
+ if (!rej) {
+ error_errno(_("cannot open %s"), namebuf);
+ close(fd);
+ return -1;
+ }
/* Normal git tools never deal with .rej, so do not pretend
* this is a git patch by saying --git or giving extended
diff --git a/archive-tar.c b/archive-tar.c
index 0726996839..8ae30125f8 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -9,6 +9,7 @@
#include "tar.h"
#include "archive.h"
#include "object-store-ll.h"
+#include "strbuf.h"
#include "streaming.h"
#include "run-command.h"
#include "write-or-die.h"
@@ -364,7 +365,7 @@ static struct archiver *find_tar_filter(const char *name, size_t len)
int i;
for (i = 0; i < nr_tar_filters; i++) {
struct archiver *ar = tar_filters[i];
- if (!strncmp(ar->name, name, len) && !ar->name[len])
+ if (!xstrncmpz(ar->name, name, len))
return ar;
}
return NULL;
diff --git a/archive-zip.c b/archive-zip.c
index 7229e3e454..fd1d3f816d 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -10,6 +10,7 @@
#include "streaming.h"
#include "utf8.h"
#include "object-store-ll.h"
+#include "strbuf.h"
#include "userdiff.h"
#include "write-or-die.h"
#include "xdiff-interface.h"
diff --git a/archive.c b/archive.c
index ca11db185b..5287fcdd8e 100644
--- a/archive.c
+++ b/archive.c
@@ -5,6 +5,7 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
+#include "object-name.h"
#include "path.h"
#include "pretty.h"
#include "setup.h"
@@ -17,7 +18,6 @@
#include "archive.h"
#include "parse-options.h"
#include "unpack-trees.h"
-#include "dir.h"
#include "quote.h"
static char const * const archive_usage[] = {
@@ -339,7 +339,8 @@ int write_archive_entries(struct archiver_args *args,
opts.src_index = args->repo->index;
opts.dst_index = args->repo->index;
opts.fn = oneway_merge;
- init_tree_desc(&t, args->tree->buffer, args->tree->size);
+ init_tree_desc(&t, &args->tree->object.oid,
+ args->tree->buffer, args->tree->size);
if (unpack_trees(1, &t, &opts))
return -1;
git_attr_set_direction(GIT_ATTR_INDEX);
@@ -685,6 +686,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);
diff --git a/archive.h b/archive.h
index 3a4bdfbd07..bbe65ba0f9 100644
--- a/archive.h
+++ b/archive.h
@@ -1,7 +1,6 @@
#ifndef ARCHIVE_H
#define ARCHIVE_H
-#include "object-name.h"
#include "pathspec.h"
#include "string-list.h"
diff --git a/attr.c b/attr.c
index e62876dfd3..679e42258c 100644
--- a/attr.c
+++ b/attr.c
@@ -17,6 +17,7 @@
#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"
@@ -183,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)
{
/*
@@ -315,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;
}
@@ -379,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;
}
@@ -1240,6 +1250,85 @@ static struct object_id *default_attr_source(void)
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)
@@ -1253,7 +1342,7 @@ void git_check_attr(struct index_state *istate,
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;
}
}
diff --git a/bisect.c b/bisect.c
index 1be8e0a271..60aae2fe50 100644
--- a/bisect.c
+++ b/bisect.c
@@ -9,7 +9,6 @@
#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"
@@ -159,6 +158,9 @@ static void show_list(const char *debug, int counted, int nr,
const char *subject_start;
int subject_len;
+ if (!buf)
+ die(_("unable to read %s"), oid_to_hex(&commit->object.oid));
+
fprintf(stderr, "%c%c%c ",
(commit_flags & TREESAME) ? ' ' : 'T',
(commit_flags & UNINTERESTING) ? 'U' : ' ',
@@ -471,7 +473,6 @@ 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")
@@ -707,26 +708,10 @@ 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))
- return 0;
-
- fp = fopen_or_warn(filename, "r");
- if (!fp)
+ struct object_id expected_oid;
+ if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
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,
@@ -851,10 +836,11 @@ static void handle_skipped_merge_base(const struct object_id *mb)
static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
{
enum bisect_error res = BISECT_OK;
- struct commit_list *result;
+ struct commit_list *result = NULL;
- result = repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
- rev + 1);
+ if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
+ rev + 1, &result) < 0)
+ exit(128);
for (; result; result = result->next) {
const struct object_id *mb = &result->item->object.oid;
@@ -1185,10 +1171,10 @@ 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());
diff --git a/blame.c b/blame.c
index 141756975b..1a16d4eb6a 100644
--- a/blame.c
+++ b/blame.c
@@ -3,6 +3,7 @@
#include "object-store-ll.h"
#include "cache-tree.h"
#include "mergesort.h"
+#include "commit.h"
#include "convert.h"
#include "diff.h"
#include "diffcore.h"
@@ -10,6 +11,7 @@
#include "hex.h"
#include "path.h"
#include "read-cache.h"
+#include "revision.h"
#include "setup.h"
#include "tag.h"
#include "trace2.h"
diff --git a/blame.h b/blame.h
index 31ddc85f19..5b4e47d44c 100644
--- a/blame.h
+++ b/blame.h
@@ -1,12 +1,9 @@
#ifndef BLAME_H
#define BLAME_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 888e28a559..3fb2922b1a 100644
--- a/blob.c
+++ b/blob.c
@@ -1,6 +1,5 @@
#include "git-compat-util.h"
#include "blob.h"
-#include "repository.h"
#include "alloc.h"
const char *blob_type = "blob";
diff --git a/bloom.c b/bloom.c
index 1474aa19fa..e529f7605c 100644
--- a/bloom.c
+++ b/bloom.c
@@ -2,7 +2,6 @@
#include "bloom.h"
#include "diff.h"
#include "diffcore.h"
-#include "revision.h"
#include "hashmap.h"
#include "commit-graph.h"
#include "commit.h"
diff --git a/branch.c b/branch.c
index 06f7af9dd4..e4a738fc7b 100644
--- a/branch.c
+++ b/branch.c
@@ -370,8 +370,12 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
*/
int validate_branchname(const char *name, struct strbuf *ref)
{
- if (strbuf_check_branch_ref(ref, name))
- die(_("'%s' is not a valid branch name"), name);
+ if (strbuf_check_branch_ref(ref, name)) {
+ int code = die_message(_("'%s' is not a valid branch name"), name);
+ advise_if_enabled(ADVICE_REF_SYNTAX,
+ _("See `man git check-ref-format`"));
+ exit(code);
+ }
return ref_exists(ref->buf);
}
@@ -420,9 +424,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));
@@ -734,7 +738,7 @@ static int submodule_create_branch(struct repository *r,
}
void create_branches_recursively(struct repository *r, const char *name,
- const char *start_commitish,
+ const char *start_committish,
const char *tracking_name, int force,
int reflog, int quiet, enum branch_track track,
int dry_run)
@@ -744,8 +748,8 @@ void create_branches_recursively(struct repository *r, const char *name,
struct object_id super_oid;
struct submodule_entry_list submodule_entry_list;
- /* Perform dwim on start_commitish to get super_oid and branch_point. */
- dwim_branch_start(r, start_commitish, BRANCH_TRACK_NEVER,
+ /* Perform dwim on start_committish to get super_oid and branch_point. */
+ dwim_branch_start(r, start_committish, BRANCH_TRACK_NEVER,
&branch_point, &super_oid);
/*
@@ -768,7 +772,7 @@ void create_branches_recursively(struct repository *r, const char *name,
submodule_entry_list.entries[i].submodule->name);
if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED))
advise(_("You may try updating the submodules using 'git checkout --no-recurse-submodules %s && git submodule update --init'"),
- start_commitish);
+ start_committish);
exit(code);
}
@@ -783,7 +787,7 @@ void create_branches_recursively(struct repository *r, const char *name,
name);
}
- create_branch(r, name, start_commitish, force, 0, reflog, quiet,
+ create_branch(r, name, start_committish, force, 0, reflog, quiet,
BRANCH_TRACK_NEVER, dry_run);
if (dry_run)
return;
@@ -817,8 +821,9 @@ void remove_merge_branch_state(struct repository *r)
unlink(git_path_merge_rr(r));
unlink(git_path_merge_msg(r));
unlink(git_path_merge_mode(r));
- unlink(git_path_auto_merge(r));
- save_autostash(git_path_merge_autostash(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
+ save_autostash_ref(r, "MERGE_AUTOSTASH");
}
void remove_branch_state(struct repository *r, int verbose)
diff --git a/branch.h b/branch.h
index 30c01aed73..ec2f35fda4 100644
--- a/branch.h
+++ b/branch.h
@@ -78,26 +78,26 @@ void create_branch(struct repository *r,
* those of create_branch() except for start_name, which is represented
* by two different parameters:
*
- * - start_commitish is the commit-ish, in repository r, that determines
+ * - start_committish is the commit-ish, in repository r, that determines
* which commits the branches will point to. The superproject branch
- * will point to the commit of start_commitish and the submodule
- * branches will point to the gitlink commit oids in start_commitish's
+ * will point to the commit of start_committish and the submodule
+ * branches will point to the gitlink commit oids in start_committish's
* tree.
*
* - tracking_name is the name of the ref, in repository r, that will be
* used to set up tracking information. This value is propagated to
* all submodules, which will evaluate the ref using their own ref
- * stores. If NULL, this defaults to start_commitish.
+ * stores. If NULL, this defaults to start_committish.
*
- * When this function is called on the superproject, start_commitish
+ * When this function is called on the superproject, start_committish
* can be any user-provided ref and tracking_name can be NULL (similar
* to create_branches()). But when recursing through submodules,
- * start_commitish is the plain gitlink commit oid. Since the oid cannot
+ * start_committish is the plain gitlink commit oid. Since the oid cannot
* be used for tracking information, tracking_name is propagated and
* used for tracking instead.
*/
void create_branches_recursively(struct repository *r, const char *name,
- const char *start_commitish,
+ const char *start_committish,
const char *tracking_name, int force,
int reflog, int quiet, enum branch_track track,
int dry_run);
diff --git a/builtin.h b/builtin.h
index d560baa661..28280636da 100644
--- a/builtin.h
+++ b/builtin.h
@@ -211,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);
diff --git a/builtin/add.c b/builtin/add.c
index d46e4d10e9..ae723bc85e 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -12,14 +12,11 @@
#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"
@@ -118,7 +115,7 @@ static int refresh(int verbose, const struct pathspec *pathspec)
int i, ret = 0;
char *skip_worktree_seen = NULL;
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
- int flags = REFRESH_IGNORE_SKIP_WORKTREE |
+ unsigned int flags = REFRESH_IGNORE_SKIP_WORKTREE |
(verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
seen = xcalloc(pathspec->nr, 1);
@@ -313,9 +310,9 @@ static void check_embedded_repo(const char *path)
strbuf_strip_suffix(&name, "/");
warning(_("adding embedded git repository: %s"), name.buf);
- if (!adviced_on_embedded_repo &&
- advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) {
- advise(embedded_advice, name.buf, name.buf);
+ if (!adviced_on_embedded_repo) {
+ advise_if_enabled(ADVICE_ADD_EMBEDDED_REPO,
+ embedded_advice, name.buf, name.buf);
adviced_on_embedded_repo = 1;
}
@@ -331,10 +328,8 @@ static int add_files(struct dir_struct *dir, int flags)
fprintf(stderr, _(ignore_error));
for (i = 0; i < dir->ignored_nr; i++)
fprintf(stderr, "%s\n", dir->ignored[i]->name);
- if (advice_enabled(ADVICE_ADD_IGNORED_FILE))
- advise(_("Use -f if you really want to add them.\n"
- "Turn this message off by running\n"
- "\"git config advice.addIgnoredFile false\""));
+ advise_if_enabled(ADVICE_ADD_IGNORED_FILE,
+ _("Use -f if you really want to add them."));
exit_status = 1;
}
@@ -373,6 +368,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int add_new_files;
int require_pathspec;
char *seen = NULL;
+ char *ps_matched = NULL;
struct lock_file lock_file = LOCK_INIT;
git_config(add_config, NULL);
@@ -443,10 +439,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (require_pathspec && pathspec.nr == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
- if (advice_enabled(ADVICE_ADD_EMPTY_PATHSPEC))
- advise( _("Maybe you wanted to say 'git add .'?\n"
- "Turn this message off by running\n"
- "\"git config advice.addEmptyPathspec false\""));
+ advise_if_enabled(ADVICE_ADD_EMPTY_PATHSPEC,
+ _("Maybe you wanted to say 'git add .'?"));
return 0;
}
@@ -552,12 +546,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
begin_odb_transaction();
+ ps_matched = xcalloc(pathspec.nr, 1);
if (add_renormalize)
exit_status |= renormalize_tracked_files(&pathspec, flags);
else
exit_status |= add_files_to_cache(the_repository, prefix,
- &pathspec, include_sparse,
- flags);
+ &pathspec, ps_matched,
+ include_sparse, flags);
+
+ if (take_worktree_changes && !add_renormalize && !ignore_add_errors &&
+ report_path_error(ps_matched, &pathspec))
+ exit(128);
if (add_new_files)
exit_status |= add_files(&dir, flags);
@@ -571,6 +570,7 @@ finish:
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
+ free(ps_matched);
dir_clear(&dir);
clear_pathspec(&pathspec);
return exit_status;
diff --git a/builtin/am.c b/builtin/am.c
index 9f084d58bc..022cae2e8d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -10,7 +10,6 @@
#include "config.h"
#include "editor.h"
#include "environment.h"
-#include "exec-cmd.h"
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
@@ -24,7 +23,6 @@
#include "refs.h"
#include "commit.h"
#include "diff.h"
-#include "diffcore.h"
#include "unpack-trees.h"
#include "branch.h"
#include "object-name.h"
@@ -35,11 +33,9 @@
#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"
@@ -1154,19 +1150,23 @@ static const char *msgnum(const struct am_state *state)
static void NORETURN die_user_resolve(const struct am_state *state)
{
if (state->resolvemsg) {
- printf_ln("%s", state->resolvemsg);
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", state->resolvemsg);
} else {
const char *cmdline = state->interactive ? "git am -i" : "git am";
+ struct strbuf sb = STRBUF_INIT;
- printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
- printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+ strbuf_addf(&sb, _("When you have resolved this problem, run \"%s --continue\".\n"), cmdline);
+ strbuf_addf(&sb, _("If you prefer to skip this patch, run \"%s --skip\" instead.\n"), cmdline);
if (advice_enabled(ADVICE_AM_WORK_DIR) &&
is_empty_or_missing_file(am_path(state, "patch")) &&
!repo_index_has_changes(the_repository, NULL, NULL))
- printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+ strbuf_addf(&sb, _("To record the empty patch as an empty commit, run \"%s --allow-empty\".\n"), cmdline);
- printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+ strbuf_addf(&sb, _("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
+
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", sb.buf);
+ strbuf_release(&sb);
}
exit(128);
@@ -1290,7 +1290,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, NULL);
assert(!state->author_name);
state->author_name = strbuf_detach(&author_name, NULL);
@@ -1998,8 +1998,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = twoway_merge;
- init_tree_desc(&t[0], head->buffer, head->size);
- init_tree_desc(&t[1], remote->buffer, remote->size);
+ init_tree_desc(&t[0], &head->object.oid, head->buffer, head->size);
+ init_tree_desc(&t[1], &remote->object.oid, remote->buffer, remote->size);
if (unpack_trees(2, t, &opts)) {
rollback_lock_file(&lock_file);
@@ -2033,7 +2033,7 @@ static int merge_tree(struct tree *tree)
opts.dst_index = &the_index;
opts.merge = 1;
opts.fn = oneway_merge;
- init_tree_desc(&t[0], tree->buffer, tree->size);
+ init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, t, &opts)) {
rollback_lock_file(&lock_file);
diff --git a/builtin/apply.c b/builtin/apply.c
index c18b7ea5d3..861a01910c 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,6 +1,5 @@
#include "builtin.h"
#include "gettext.h"
-#include "parse-options.h"
#include "repository.h"
#include "apply.h"
diff --git a/builtin/archive.c b/builtin/archive.c
index 90761fdfee..15ee1ec7bb 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -9,7 +9,6 @@
#include "parse-options.h"
#include "pkt-line.h"
#include "repository.h"
-#include "sideband.h"
static void create_output_file(const char *output_file)
{
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 35938b05fd..f69c3f7e43 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -7,7 +7,6 @@
#include "parse-options.h"
#include "bisect.h"
#include "refs.h"
-#include "dir.h"
#include "strvec.h"
#include "run-command.h"
#include "oid-array.h"
@@ -17,7 +16,6 @@
#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")
@@ -233,11 +231,10 @@ 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;
@@ -246,7 +243,7 @@ static int bisect_reset(const char *commit)
strbuf_addstr(&branch, commit);
}
- if (!ref_exists("BISECT_HEAD")) {
+ if (branch.len && !ref_exists("BISECT_HEAD")) {
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
@@ -921,7 +918,6 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
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)
@@ -976,10 +972,8 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
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)) {
@@ -988,7 +982,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
}
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;
}
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 9c987d6567..9aa74680a3 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -25,7 +25,6 @@
#include "userdiff.h"
#include "line-range.h"
#include "line-log.h"
-#include "dir.h"
#include "progress.h"
#include "object-name.h"
#include "object-store-ll.h"
@@ -317,7 +316,7 @@ static const char *format_time(timestamp_t time, const char *tz_str,
size_t time_width;
int tz;
tz = atoi(tz_str);
- time_str = show_date(time, tz, &blame_date_mode);
+ time_str = show_date(time, tz, blame_date_mode);
strbuf_addstr(&time_buf, time_str);
/*
* Add space paddings to time_buf to display a fixed width
@@ -748,6 +747,8 @@ static int git_blame_config(const char *var, const char *value,
}
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")) {
@@ -1028,7 +1029,7 @@ parse_done:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
break;
case DATE_STRFTIME:
- blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
+ blame_date_width = strlen(show_date(0, 0, blame_date_mode)) + 1; /* add the null */
break;
}
blame_date_width -= 1; /* strip the null */
diff --git a/builtin/branch.c b/builtin/branch.c
index 2ed59f16f1..dd3e3a7dc0 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -17,16 +17,14 @@
#include "remote.h"
#include "parse-options.h"
#include "branch.h"
-#include "diff.h"
#include "path.h"
-#include "revision.h"
#include "string-list.h"
#include "column.h"
#include "utf8.h"
-#include "wt-status.h"
#include "ref-filter.h"
#include "worktree.h"
#include "help.h"
+#include "advice.h"
#include "commit-reach.h"
static const char * const builtin_branch_usage[] = {
@@ -160,6 +158,8 @@ static int branch_merged(int kind, const char *name,
merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
reference_rev) : 0;
+ if (merged < 0)
+ exit(128);
/*
* After the safety valve is fully redefined to "check with
@@ -168,9 +168,13 @@ static int branch_merged(int kind, const char *name,
* any of the following code, but during the transition period,
* a gentle reminder is in order.
*/
- if ((head_rev != reference_rev) &&
- (head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
- if (merged)
+ if (head_rev != reference_rev) {
+ int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
+ if (expect < 0)
+ exit(128);
+ if (expect == merged)
+ ; /* okay */
+ else if (merged)
warning(_("deleting branch '%s' that has been merged to\n"
" '%s', but not yet merged to HEAD"),
name, reference_name);
@@ -193,9 +197,10 @@ static int check_branch_commit(const char *branchname, const char *refname,
return -1;
}
if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
- error(_("the branch '%s' is not fully merged.\n"
- "If you are sure you want to delete it, "
- "run 'git branch -D %s'"), branchname, branchname);
+ error(_("the branch '%s' is not fully merged"), branchname);
+ advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+ _("If you are sure you want to delete it, "
+ "run 'git branch -D %s'"), branchname);
return -1;
}
return 0;
@@ -577,8 +582,12 @@ 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);
+ else {
+ int code = die_message(_("invalid branch name: '%s'"), oldname);
+ advise_if_enabled(ADVICE_REF_SYNTAX,
+ _("See `man git check-ref-format`"));
+ exit(code);
+ }
}
for (int i = 0; worktrees[i]; i++) {
@@ -668,18 +677,18 @@ 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, comment_line_char,
+ strbuf_commented_addf(&buf, comment_line_str,
_("Please edit the description for the branch\n"
" %s\n"
- "Lines starting with '%c' will be stripped.\n"),
- branch_name, comment_line_char);
+ "Lines starting with '%s' will be stripped.\n"),
+ branch_name, comment_line_str);
write_file_buf(edit_description(), buf.buf, buf.len);
strbuf_reset(&buf);
if (launch_editor(edit_description(), &buf, NULL)) {
strbuf_release(&buf);
return -1;
}
- strbuf_stripspace(&buf, comment_line_char);
+ strbuf_stripspace(&buf, comment_line_str);
strbuf_addf(&name, "branch.%s.description", branch_name);
if (buf.len || exists)
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 3106e56a13..25f860a0d9 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -64,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) <path>] [(-s | --suffix) <format>]\n"
+ N_("git bugreport [(-o | --output-directory) <path>]\n"
+ " [(-s | --suffix) <format> | --no-suffix]\n"
" [--diagnose[=<mode>]]"),
NULL
};
@@ -138,8 +139,11 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
strbuf_complete(&report_path, '/');
output_path_len = report_path.len;
- strbuf_addstr(&report_path, "git-bugreport-");
- strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ strbuf_addstr(&report_path, "git-bugreport");
+ if (option_suffix) {
+ strbuf_addch(&report_path, '-');
+ strbuf_addftime(&report_path, option_suffix, localtime_r(&now, &tm), 0, 0);
+ }
strbuf_addstr(&report_path, ".txt");
switch (safe_create_leading_directories(report_path.buf)) {
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index ea8ad601ec..0c948f40fb 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -15,7 +15,6 @@
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
-#include "tree-walk.h"
#include "oid-array.h"
#include "packfile.h"
#include "object-file.h"
@@ -107,7 +106,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
- unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
+ unsigned get_oid_flags =
+ GET_OID_RECORD_PATH |
+ GET_OID_ONLY_TO_DIE |
+ GET_OID_HASH_ANY;
const char *path = force_path;
const int opt_cw = (opt == 'c' || opt == 'w');
if (!path && opt_cw)
@@ -222,8 +224,13 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
&type,
&size);
const char *target;
+
+ if (!buffer)
+ die(_("unable to read %s"), oid_to_hex(&oid));
+
if (!skip_prefix(buffer, "object ", &target) ||
- get_oid_hex(target, &blob_oid))
+ get_oid_hex_algop(target, &blob_oid,
+ &hash_algos[oid.algo]))
die("%s not a valid tag", oid_to_hex(&oid));
free(buffer);
} else
@@ -307,8 +314,8 @@ static int is_atom(const char *atom, const char *s, int slen)
return alen == slen && !memcmp(atom, s, alen);
}
-static void expand_atom(struct strbuf *sb, const char *atom, int len,
- struct expand_data *data)
+static int expand_atom(struct strbuf *sb, const char *atom, int len,
+ struct expand_data *data)
{
if (is_atom("objectname", atom, len)) {
if (!data->mark_query)
@@ -340,7 +347,8 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
strbuf_addstr(sb,
oid_to_hex(&data->delta_base_oid));
} else
- die("unknown format element: %.*s", len, atom);
+ return 0;
+ return 1;
}
static void expand_format(struct strbuf *sb, const char *start,
@@ -351,12 +359,11 @@ static void expand_format(struct strbuf *sb, const char *start,
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);
+ else if ((end = strchr(start + 1, ')')) &&
+ expand_atom(sb, start + 1, end - start - 1, data))
start = end + 1;
- }
+ else
+ strbuf_expand_bad_format(start, "cat-file");
}
}
@@ -417,6 +424,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
contents = repo_read_object_file(the_repository, oid, &type,
&size);
+ if (!contents)
+ die("object %s disappeared", oid_to_hex(oid));
if (use_mailmap) {
size_t s = size;
@@ -424,8 +433,6 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
size = cast_size_t_to_ulong(s);
}
- if (!contents)
- die("object %s disappeared", oid_to_hex(oid));
if (type != data->type)
die("object %s changed type!?", oid_to_hex(oid));
if (data->info.sizep && size != data->size && !use_mailmap)
@@ -482,6 +489,8 @@ static void batch_object_write(const char *obj_name,
buf = repo_read_object_file(the_repository, &data->oid, &data->type,
&data->size);
+ if (!buf)
+ die(_("unable to read %s"), oid_to_hex(&data->oid));
buf = replace_idents_using_mailmap(buf, &s);
data->size = cast_size_t_to_ulong(s);
@@ -512,7 +521,9 @@ static void batch_one_object(const char *obj_name,
struct expand_data *data)
{
struct object_context ctx;
- int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0;
+ int flags =
+ GET_OID_HASH_ANY |
+ (opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0);
enum get_oid_result result;
result = get_oid_with_context(the_repository, obj_name,
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 3b68b47615..2e086a204d 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -7,7 +7,6 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "config.h"
-#include "dir.h"
#include "gettext.h"
#include "lockfile.h"
#include "quote.h"
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f02434bc15..71e6036aab 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,7 +1,6 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "advice.h"
-#include "blob.h"
#include "branch.h"
#include "cache-tree.h"
#include "checkout.h"
@@ -27,10 +26,8 @@
#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"
@@ -94,7 +91,7 @@ struct checkout_opts {
int new_branch_log;
enum branch_track track;
struct diff_options diff_options;
- char *conflict_style;
+ int conflict_style;
int branch_exists;
const char *prefix;
@@ -103,6 +100,8 @@ struct checkout_opts {
struct tree *source_tree;
};
+#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 }
+
struct branch_info {
char *name; /* The short name used */
char *path; /* The full name of a real branch */
@@ -254,7 +253,8 @@ 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)
+ int *nr_checkouts, struct mem_pool *ce_mem_pool,
+ int conflict_style)
{
struct cache_entry *ce = the_index.cache[pos];
const char *path = ce->name;
@@ -265,7 +265,7 @@ static int checkout_merged(int pos, const struct checkout *state,
mmbuffer_t result_buf;
struct object_id threeway[3];
unsigned mode = 0;
- struct ll_merge_options ll_opts;
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
int renormalize = 0;
memset(threeway, 0, sizeof(threeway));
@@ -287,9 +287,9 @@ static int checkout_merged(int pos, const struct checkout *state,
read_mmblob(&ours, &threeway[1]);
read_mmblob(&theirs, &threeway[2]);
- memset(&ll_opts, 0, sizeof(ll_opts));
git_config_get_bool("merge.renormalize", &renormalize);
ll_opts.renormalize = renormalize;
+ ll_opts.conflict_style = conflict_style;
merge_status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs",
state->istate, &ll_opts);
@@ -420,7 +420,8 @@ static int checkout_worktree(const struct checkout_opts *opts,
else if (opts->merge)
errs |= checkout_merged(pos, &state,
&nr_unmerged,
- &ce_mem_pool);
+ &ce_mem_pool,
+ opts->conflict_style);
pos = skip_same_name(ce, pos) - 1;
}
}
@@ -707,8 +708,9 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
init_checkout_metadata(&opts.meta, info->refname,
info->commit ? &info->commit->object.oid : null_oid(),
NULL);
- parse_tree(tree);
- init_tree_desc(&tree_desc, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ return 128;
+ init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size);
switch (unpack_trees(1, &tree_desc, &opts)) {
case -2:
*writeout_error = 1;
@@ -786,9 +788,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (new_branch_info->commit)
BUG("'switch --orphan' should never accept a commit as starting point");
new_tree = parse_tree_indirect(the_hash_algo->empty_tree);
- } else
+ if (!new_tree)
+ BUG("unable to read empty tree");
+ } else {
new_tree = repo_get_commit_tree(the_repository,
new_branch_info->commit);
+ if (!new_tree)
+ return error(_("unable to read tree (%s)"),
+ oid_to_hex(&new_branch_info->commit->object.oid));
+ }
if (opts->discard_changes) {
ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
if (ret)
@@ -822,10 +830,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
die(_("unable to parse commit %s"),
oid_to_hex(old_commit_oid));
- init_tree_desc(&trees[0], tree->buffer, tree->size);
- parse_tree(new_tree);
+ init_tree_desc(&trees[0], &tree->object.oid,
+ tree->buffer, tree->size);
+ if (parse_tree(new_tree) < 0)
+ exit(128);
tree = new_tree;
- init_tree_desc(&trees[1], tree->buffer, tree->size);
+ init_tree_desc(&trees[1], &tree->object.oid,
+ tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
clear_unpack_trees_porcelain(&topts);
@@ -871,7 +882,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
* entries in the index.
*/
- add_files_to_cache(the_repository, NULL, NULL, 0, 0);
+ add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
+ 0);
init_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_in_core_index_as_tree(the_repository);
@@ -890,6 +902,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
}
o.branch1 = new_branch_info->name;
o.branch2 = "local";
+ o.conflict_style = opts->conflict_style;
ret = merge_trees(&o,
new_tree,
work,
@@ -1023,7 +1036,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
remove_branch_state(the_repository, !opts->quiet);
strbuf_release(&msg);
if (!opts->quiet &&
- (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
+ !opts->force_detach &&
+ (new_branch_info->path || !strcmp(new_branch_info->name, "HEAD")))
report_tracking(new_branch_info);
}
@@ -1202,6 +1216,8 @@ static int git_checkout_config(const char *var, const char *value,
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;
}
@@ -1225,7 +1241,9 @@ static void setup_new_branch_info_and_source_tree(
struct tree **source_tree = &opts->source_tree;
struct object_id branch_rev;
- new_branch_info->name = xstrdup(arg);
+ /* treat '@' as a shortcut for 'HEAD' */
+ new_branch_info->name = !strcmp(arg, "@") ? xstrdup("HEAD") :
+ xstrdup(arg);
setup_branch_path(new_branch_info);
if (!check_refname_format(new_branch_info->path, 0) &&
@@ -1239,10 +1257,15 @@ static void setup_new_branch_info_and_source_tree(
if (!new_branch_info->commit) {
/* not a commit */
*source_tree = parse_tree_indirect(rev);
+ if (!*source_tree)
+ die(_("unable to read tree (%s)"), oid_to_hex(rev));
} else {
parse_commit_or_die(new_branch_info->commit);
*source_tree = repo_get_commit_tree(the_repository,
new_branch_info->commit);
+ if (!*source_tree)
+ die(_("unable to read tree (%s)"),
+ oid_to_hex(&new_branch_info->commit->object.oid));
}
}
@@ -1516,6 +1539,26 @@ static void die_if_some_operation_in_progress(void)
wt_status_state_free_buffers(&state);
}
+/*
+ * die if attempting to checkout an existing branch that is in use
+ * in another worktree, unless ignore-other-wortrees option is given.
+ * The check is bypassed when the branch is already the current one,
+ * as it will not make things any worse.
+ */
+static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
+ const char *full_ref)
+{
+ int flags;
+ char *head_ref;
+
+ if (opts->ignore_other_worktrees)
+ return;
+ head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+ if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
+ die_if_checked_out(full_ref, 1);
+ free(head_ref);
+}
+
static int checkout_branch(struct checkout_opts *opts,
struct branch_info *new_branch_info)
{
@@ -1576,14 +1619,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) {
@@ -1597,6 +1641,24 @@ static int checkout_branch(struct checkout_opts *opts,
return switch_branches(opts, new_branch_info);
}
+static int parse_opt_conflict(const struct option *o, const char *arg, int unset)
+{
+ struct checkout_opts *opts = o->value;
+
+ if (unset) {
+ opts->conflict_style = -1;
+ return 0;
+ }
+ opts->conflict_style = parse_conflict_style_name(arg);
+ if (opts->conflict_style < 0)
+ return error(_("unknown conflict style '%s'"), arg);
+ /* --conflict overrides a previous --no-merge */
+ if (!opts->merge)
+ opts->merge = -1;
+
+ return 0;
+}
+
static struct option *add_common_options(struct checkout_opts *opts,
struct option *prevopts)
{
@@ -1607,8 +1669,9 @@ static struct option *add_common_options(struct checkout_opts *opts,
PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
- OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
- N_("conflict style (merge, diff3, or zdiff3)")),
+ OPT_CALLBACK(0, "conflict", opts, N_("style"),
+ N_("conflict style (merge, diff3, or zdiff3)"),
+ parse_opt_conflict),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1627,7 +1690,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),
@@ -1667,10 +1730,11 @@ static char cb_option = 'b';
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[],
- struct branch_info *new_branch_info)
+ const char * const usagestr[])
{
int parseopt_flags = 0;
+ struct branch_info new_branch_info = { 0 };
+ int ret;
opts->overwrite_ignore = 1;
opts->prefix = prefix;
@@ -1699,15 +1763,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->show_progress = isatty(2);
}
- 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,
- &ctx, NULL);
- }
+ /* --conflicts implies --merge */
+ if (opts->merge == -1)
+ opts->merge = opts->conflict_style >= 0;
+
if (opts->force) {
opts->discard_changes = 1;
opts->ignore_unmerged_opt = "--force";
@@ -1786,7 +1845,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
- new_branch_info, opts, &rev);
+ &new_branch_info, opts, &rev);
argv += n;
argc -= n;
} else if (!opts->accept_ref && opts->from_treeish) {
@@ -1795,7 +1854,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
if (repo_get_oid_mb(the_repository, opts->from_treeish, &rev))
die(_("could not resolve %s"), opts->from_treeish);
- setup_new_branch_info_and_source_tree(new_branch_info,
+ setup_new_branch_info_and_source_tree(&new_branch_info,
opts, &rev,
opts->from_treeish);
@@ -1815,7 +1874,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts->new_branch && argc == 1 && !new_branch_info->commit)
+ if (opts->new_branch && argc == 1 && !new_branch_info.commit)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
argv[0], opts->new_branch);
@@ -1865,14 +1924,21 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, new_branch_info);
+ ret = checkout_paths(opts, &new_branch_info);
else
- return checkout_branch(opts, new_branch_info);
+ ret = checkout_branch(opts, &new_branch_info);
+
+ branch_info_release(&new_branch_info);
+ clear_pathspec(&opts->pathspec);
+ free(opts->pathspec_from_file);
+ free(options);
+
+ return ret;
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options;
struct option checkout_options[] = {
OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1885,10 +1951,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
opts.switch_branch_doing_nothing_is_ok = 1;
opts.only_merge_on_switching_branches = 0;
@@ -1916,18 +1979,13 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
options = add_common_switch_branch_options(&opts, options);
options = add_checkout_path_options(&opts, options);
- ret = checkout_main(argc, argv, prefix, &opts,
- options, checkout_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- clear_pathspec(&opts.pathspec);
- free(opts.pathspec_from_file);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ checkout_usage);
}
int cmd_switch(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options = NULL;
struct option switch_options[] = {
OPT_STRING('c', "create", &opts.new_branch, N_("branch"),
@@ -1940,10 +1998,7 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
N_("throw away local modifications")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
opts.accept_ref = 1;
opts.accept_pathspec = 0;
@@ -1960,16 +2015,13 @@ int cmd_switch(int argc, const char **argv, const char *prefix)
cb_option = 'c';
- ret = checkout_main(argc, argv, prefix, &opts,
- options, switch_branch_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ switch_branch_usage);
}
int cmd_restore(int argc, const char **argv, const char *prefix)
{
- struct checkout_opts opts;
+ struct checkout_opts opts = CHECKOUT_OPTS_INIT;
struct option *options;
struct option restore_options[] = {
OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>",
@@ -1983,10 +2035,7 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")),
OPT_END()
};
- int ret;
- struct branch_info new_branch_info = { 0 };
- memset(&opts, 0, sizeof(opts));
opts.accept_ref = 0;
opts.accept_pathspec = 1;
opts.empty_pathspec_ok = 0;
@@ -1999,9 +2048,6 @@ int cmd_restore(int argc, const char **argv, const char *prefix)
options = add_common_options(&opts, options);
options = add_checkout_path_options(&opts, options);
- ret = checkout_main(argc, argv, prefix, &opts,
- options, restore_usage, &new_branch_info);
- branch_info_release(&new_branch_info);
- FREE_AND_NULL(options);
- return ret;
+ return checkout_main(argc, argv, prefix, &opts, options,
+ restore_usage);
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 49c224e626..29efe84153 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -25,7 +25,7 @@
#include "help.h"
#include "prompt.h"
-static int force = -1; /* unset */
+static int require_force = -1; /* unset */
static int interactive;
static struct string_list del_list = STRING_LIST_INIT_DUP;
static unsigned int colopts;
@@ -128,7 +128,7 @@ static int git_clean_config(const char *var, const char *value,
}
if (!strcmp(var, "clean.requireforce")) {
- force = !git_config_bool(var, value);
+ require_force = git_config_bool(var, value);
return 0;
}
@@ -920,7 +920,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i, res;
int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
- int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
+ int ignored_only = 0, force = 0, errors = 0, gone = 1;
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
struct strbuf abs_path = STRBUF_INIT;
struct dir_struct dir = DIR_INIT;
@@ -946,22 +946,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
};
git_config(git_clean_config, NULL);
- if (force < 0)
- force = 0;
- else
- config_set = 1;
argc = parse_options(argc, argv, prefix, options, builtin_clean_usage,
0);
- if (!interactive && !dry_run && !force) {
- if (config_set)
- die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
- "refusing to clean"));
- else
- die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;"
- " refusing to clean"));
- }
+ if (require_force != 0 && !force && !interactive && !dry_run)
+ die(_("clean.requireForce is true and -f not given: refusing to clean"));
if (force > 1)
rm_flags = 0;
@@ -971,7 +961,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)
diff --git a/builtin/clone.c b/builtin/clone.c
index c6357af949..74ec14542e 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -19,7 +19,6 @@
#include "hex.h"
#include "lockfile.h"
#include "parse-options.h"
-#include "fetch-pack.h"
#include "refs.h"
#include "refspec.h"
#include "object-file.h"
@@ -72,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;
@@ -116,7 +116,7 @@ static struct option builtin_clone_options[] = {
OPT_HIDDEN_BOOL(0, "naked", &option_bare,
N_("create a bare repository")),
OPT_BOOL(0, "mirror", &option_mirror,
- N_("create a mirror repository (implies bare)")),
+ N_("create a mirror repository (implies --bare)")),
OPT_BOOL('l', "local", &option_local,
N_("to clone from a local repository")),
OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks,
@@ -157,6 +157,8 @@ 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,
@@ -736,8 +738,9 @@ static int checkout(int submodule_progress, int filter_submodules)
tree = parse_tree_indirect(&oid);
if (!tree)
die(_("unable to parse commit %s"), oid_to_hex(&oid));
- parse_tree(tree);
- init_tree_desc(&t, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ exit(128);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts) < 0)
die(_("unable to checkout working tree"));
@@ -791,6 +794,8 @@ 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);
}
@@ -922,6 +927,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct ref *mapped_refs = NULL;
const struct ref *ref;
struct strbuf key = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL;
const char *src_ref_prefix = "refs/heads/";
@@ -930,6 +936,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
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 =
@@ -955,6 +962,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;
@@ -965,7 +978,9 @@ 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];
@@ -1097,8 +1112,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,
- do_not_override_repo_unix_permissions, 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);
@@ -1106,6 +1128,50 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
/*
+ * We have a chicken-and-egg situation between initializing the refdb
+ * and spawning transport helpers:
+ *
+ * - Initializing the refdb requires us to know about the object
+ * format. We thus have to spawn the transport helper to learn
+ * about it.
+ *
+ * - The transport helper may want to access the Git repository. But
+ * because the refdb has not been initialized, we don't have "HEAD"
+ * or "refs/". Thus, the helper cannot find the Git repository.
+ *
+ * Ideally, we would have structured the helper protocol such that it's
+ * mandatory for the helper to first announce its capabilities without
+ * yet assuming a fully initialized repository. Like that, we could
+ * have added a "lazy-refdb-init" capability that announces whether the
+ * helper is ready to handle not-yet-initialized refdbs. If any helper
+ * didn't support them, we would have fully initialized the refdb with
+ * the SHA1 object format, but later on bailed out if we found out that
+ * the remote repository used a different object format.
+ *
+ * But we didn't, and thus we use the following workaround to partially
+ * initialize the repository's refdb such that it can be discovered by
+ * Git commands. To do so, we:
+ *
+ * - Create an invalid HEAD ref pointing at "refs/heads/.invalid".
+ *
+ * - Create the "refs/" directory.
+ *
+ * - Set up the ref storage format and repository version as
+ * required.
+ *
+ * This is sufficient for Git commands to discover the Git directory.
+ */
+ initialize_repository_version(GIT_HASH_UNKNOWN,
+ the_repository->ref_storage_format, 1);
+
+ strbuf_addf(&buf, "%s/HEAD", git_dir);
+ write_file(buf.buf, "ref: refs/heads/.invalid");
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "%s/refs", git_dir);
+ safe_create_dir(buf.buf, 1);
+
+ /*
* additional config can be injected with -c, make sure it's included
* after init_db, which clears the entire config environment.
*/
@@ -1185,10 +1251,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);
@@ -1266,6 +1329,27 @@ 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;
+ strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+ refspec_ref_prefixes(&remote->fetch,
+ &transport_ls_refs_options.ref_prefixes);
+ if (option_branch)
+ expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
+ option_branch);
+ if (!option_no_tags)
+ strvec_push(&transport_ls_refs_options.ref_prefixes,
+ "refs/tags/");
+
+ refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
+
+ /*
+ * Now that we know what algorithm the remote side is using, let's set
+ * ours to the same thing.
+ */
+ hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+ initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
+ repo_set_hash_algo(the_repository, hash_algo);
+ create_reference_database(the_repository->ref_storage_format, NULL, 1);
+
/*
* Before fetching from the remote, download and install bundle
* data from the --bundle-uri option.
@@ -1281,24 +1365,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
bundle_uri);
else if (has_heuristic)
git_config_set_gently("fetch.bundleuri", bundle_uri);
- }
-
- strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
- refspec_ref_prefixes(&remote->fetch,
- &transport_ls_refs_options.ref_prefixes);
- if (option_branch)
- expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
- option_branch);
- if (!option_no_tags)
- strvec_push(&transport_ls_refs_options.ref_prefixes,
- "refs/tags/");
-
- refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
-
- if (refs)
- mapped_refs = wanted_peer_refs(refs, &remote->fetch);
-
- if (!bundle_uri) {
+ } else {
/*
* Populate transport->got_remote_bundle_uri and
* transport->bundle_uri. We might get nothing.
@@ -1319,13 +1386,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
}
- /*
- * 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, 1);
- repo_set_hash_algo(the_repository, hash_algo);
+ if (refs)
+ mapped_refs = wanted_peer_refs(refs, &remote->fetch);
if (mapped_refs) {
/*
@@ -1428,12 +1490,16 @@ 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);
free(remote_name);
strbuf_release(&reflog_msg);
strbuf_release(&branch_top);
+ strbuf_release(&buf);
strbuf_release(&key);
free_refs(mapped_refs);
free_refs(remote_head_points_at);
diff --git a/builtin/column.c b/builtin/column.c
index a83be8bc99..10ff7e0166 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -45,6 +45,8 @@ int cmd_column(int argc, const char **argv, const char *prefix)
memset(&copts, 0, sizeof(copts));
copts.padding = 1;
argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0);
+ if (copts.padding < 0)
+ die(_("%s must be non-negative"), "--padding");
if (argc)
usage_with_options(builtin_column_usage, options);
if (real_command || command) {
@@ -56,5 +58,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;
}
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 45d035af60..7102ee90a0 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -1,17 +1,16 @@
#include "builtin.h"
#include "commit.h"
#include "config.h"
-#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "lockfile.h"
#include "parse-options.h"
#include "repository.h"
#include "commit-graph.h"
#include "object-store-ll.h"
#include "progress.h"
#include "replace-object.h"
+#include "strbuf.h"
#include "tag.h"
#include "trace2.h"
@@ -22,7 +21,7 @@
N_("git commit-graph write [--object-dir <dir>] [--append]\n" \
" [--split[=<strategy>]] [--reachable | --stdin-packs | --stdin-commits]\n" \
" [--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress]\n" \
- " <split options>")
+ " <split-options>")
static const char * builtin_commit_graph_verify_usage[] = {
BUILTIN_COMMIT_GRAPH_VERIFY_USAGE,
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 02625e7176..1bb7819839 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -11,9 +11,6 @@
#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
-#include "tree.h"
-#include "utf8.h"
-#include "gpg-interface.h"
#include "parse-options.h"
static const char * const commit_tree_usage[] = {
diff --git a/builtin/commit.c b/builtin/commit.c
index 781af2e206..6e1484446b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -16,17 +16,12 @@
#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"
@@ -35,9 +30,6 @@
#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"
@@ -339,8 +331,9 @@ static void create_base_index(const struct commit *current_head)
tree = parse_tree_indirect(&current_head->object.oid);
if (!tree)
die(_("failed to unpack HEAD tree object"));
- parse_tree(tree);
- init_tree_desc(&t, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ exit(128);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts))
exit(128); /* We've already reported the error, finish dying */
}
@@ -448,16 +441,21 @@ static const char *prepare_index(const char **argv, const char *prefix,
* (B) on failure, rollback the real index.
*/
if (all || (also && pathspec.nr)) {
+ char *ps_matched = xcalloc(pathspec.nr, 1);
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
add_files_to_cache(the_repository, also ? prefix : NULL,
- &pathspec, 0, 0);
+ &pathspec, ps_matched, 0, 0);
+ if (!all && report_path_error(ps_matched, &pathspec))
+ exit(128);
+
refresh_cache_or_die(refresh_flags);
cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new index file"));
commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock);
+ free(ps_matched);
goto out;
}
@@ -692,9 +690,10 @@ static void adjust_comment_line_char(const struct strbuf *sb)
char *candidate;
const char *p;
- comment_line_char = candidates[0];
- if (!memchr(sb->buf, comment_line_char, sb->len))
+ if (!memchr(sb->buf, candidates[0], sb->len)) {
+ comment_line_str = xstrfmt("%c", candidates[0]);
return;
+ }
p = sb->buf;
candidate = strchr(candidates, *p);
@@ -713,7 +712,7 @@ static void adjust_comment_line_char(const struct strbuf *sb)
if (!*p)
die(_("unable to select a comment character that is not used\n"
"in the current commit message"));
- comment_line_char = *p;
+ comment_line_str = xstrfmt("%c", *p);
}
static void prepare_amend_commit(struct commit *commit, struct strbuf *sb,
@@ -745,7 +744,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
const char *hook_arg2 = NULL;
int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
int old_display_comment_prefix;
- int merge_contains_scissors = 0;
int invoked_hook;
/* This checks and barfs if author is badly specified */
@@ -849,7 +847,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
wt_status_locate_end(sb.buf + merge_msg_start,
sb.len - merge_msg_start) <
sb.len - merge_msg_start)
- merge_contains_scissors = 1;
+ s->added_cut_line = 1;
} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
die_errno(_("could not read SQUASH_MSG"));
@@ -897,10 +895,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, NULL);
if (signoff)
- append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
+ append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0);
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
die_errno(_("could not write commit template"));
@@ -917,24 +915,23 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
struct ident_split ci, ai;
const char *hint_cleanup_all = allow_empty_message ?
_("Please enter the commit message for your changes."
- " Lines starting\nwith '%c' will be ignored.\n") :
+ " Lines starting\nwith '%s' will be ignored.\n") :
_("Please enter the commit message for your changes."
- " Lines starting\nwith '%c' will be ignored, and an empty"
+ " Lines starting\nwith '%s' will be ignored, and an empty"
" message aborts the commit.\n");
const char *hint_cleanup_space = allow_empty_message ?
_("Please enter the commit message for your changes."
" Lines starting\n"
- "with '%c' will be kept; you may remove them"
+ "with '%s' will be kept; you may remove them"
" yourself if you want to.\n") :
_("Please enter the commit message for your changes."
" Lines starting\n"
- "with '%c' will be kept; you may remove them"
+ "with '%s' will be kept; you may remove them"
" yourself if you want to.\n"
"An empty message aborts the commit.\n");
if (whence != FROM_COMMIT) {
- if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
- !merge_contains_scissors)
- wt_status_add_cut_line(s->fp);
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+ wt_status_add_cut_line(s);
status_printf_ln(
s, GIT_COLOR_NORMAL,
whence == FROM_MERGE ?
@@ -952,12 +949,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fprintf(s->fp, "\n");
if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
- status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
+ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_str);
else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
- if (whence == FROM_COMMIT && !merge_contains_scissors)
- wt_status_add_cut_line(s->fp);
+ if (whence == FROM_COMMIT)
+ wt_status_add_cut_line(s);
} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
- status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_char);
+ status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_space, comment_line_str);
/*
* These should never fail because they come from our own
@@ -1166,22 +1163,45 @@ static void handle_ignored_arg(struct wt_status *s)
die(_("Invalid ignored mode '%s'"), ignored_arg);
}
-static void handle_untracked_files_arg(struct wt_status *s)
+static enum untracked_status_type parse_untracked_setting_name(const char *u)
{
- if (!untracked_files_arg)
- ; /* default already initialized */
- else if (!strcmp(untracked_files_arg, "no"))
- s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(untracked_files_arg, "normal"))
- s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(untracked_files_arg, "all"))
- s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
/*
* Please update $__git_untracked_file_modes in
* git-completion.bash when you add new options
*/
+ switch (git_parse_maybe_bool(u)) {
+ case 0:
+ u = "no";
+ break;
+ case 1:
+ u = "normal";
+ break;
+ default:
+ break;
+ }
+
+ if (!strcmp(u, "no"))
+ return SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(u, "normal"))
+ return SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(u, "all"))
+ return SHOW_ALL_UNTRACKED_FILES;
else
- die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
+ return SHOW_UNTRACKED_FILES_ERROR;
+}
+
+static void handle_untracked_files_arg(struct wt_status *s)
+{
+ enum untracked_status_type u;
+
+ if (!untracked_files_arg)
+ return; /* default already initialized */
+
+ u = parse_untracked_setting_name(untracked_files_arg);
+ if (u == SHOW_UNTRACKED_FILES_ERROR)
+ die(_("Invalid untracked files mode '%s'"),
+ untracked_files_arg);
+ s->show_untracked_files = u;
}
static const char *read_commit_message(const char *name)
@@ -1464,16 +1484,12 @@ static int git_status_config(const char *k, const char *v,
return 0;
}
if (!strcmp(k, "status.showuntrackedfiles")) {
- if (!v)
- return config_error_nonbool(k);
- else if (!strcmp(v, "no"))
- s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(v, "normal"))
- s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(v, "all"))
- s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
- else
+ enum untracked_status_type u;
+
+ u = parse_untracked_setting_name(v);
+ if (u == SHOW_UNTRACKED_FILES_ERROR)
return error(_("Invalid untracked files mode '%s'"), v);
+ s->show_untracked_files = u;
return 0;
}
if (!strcmp(k, "diff.renamelimit")) {
@@ -1885,7 +1901,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
&oid, flags);
}
- apply_autostash(git_path_merge_autostash(the_repository));
+ apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
cleanup:
strbuf_release(&author_ident);
diff --git a/builtin/config.c b/builtin/config.c
index 11a4d4ef14..0015620dde 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -44,6 +44,7 @@ static struct config_options config_options;
static int show_origin;
static int show_scope;
static int fixed_value;
+static const char *comment;
#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
@@ -173,6 +174,7 @@ static struct option builtin_config_options[] = {
OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
+ OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
OPT_END(),
};
@@ -708,10 +710,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
if (use_global_config) {
- char *user_config, *xdg_config;
-
- git_global_config(&user_config, &xdg_config);
- if (!user_config)
+ given_config_source.file = git_global_config();
+ if (!given_config_source.file)
/*
* It is unknown if HOME/.gitconfig exists, so
* we do not know if we should write to XDG
@@ -719,19 +719,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
* is set and points at a sane location.
*/
die(_("$HOME not set"));
-
given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-
- if (access_or_warn(user_config, R_OK, 0) &&
- xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
- given_config_source.file = xdg_config;
- free(user_config);
- } else {
- given_config_source.file = user_config;
- free(xdg_config);
- }
- }
- else if (use_system_config) {
+ } else if (use_system_config) {
given_config_source.file = git_system_config();
given_config_source.scope = CONFIG_SCOPE_SYSTEM;
} else if (use_local_config) {
@@ -760,7 +749,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
given_config_source.scope = CONFIG_SCOPE_COMMAND;
}
-
if (respect_includes_opt == -1)
config_options.respect_includes = !given_config_source.file;
else
@@ -811,6 +799,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_builtin_config();
}
+ if (comment &&
+ !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
+ error(_("--comment is only applicable to add/set/replace operations"));
+ usage_builtin_config();
+ }
+
/* check usage of --fixed-value */
if (fixed_value) {
int allowed_usage = 0;
@@ -847,6 +841,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
flags |= CONFIG_FLAGS_FIXED_VALUE;
}
+ comment = git_config_prepare_comment_string(comment);
+
if (actions & PAGING_ACTIONS)
setup_auto_pager("config", 1);
@@ -894,7 +890,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1], &default_kvi);
- ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value);
+ ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
if (ret == CONFIG_NOTHING_SET)
error(_("cannot overwrite multiple values with a single value\n"
" Use a regexp, --add or --replace-all to change %s."), argv[0]);
@@ -905,7 +901,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2],
- flags);
+ comment, flags);
}
else if (actions == ACTION_ADD) {
check_write();
@@ -914,7 +910,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value,
CONFIG_REGEX_NONE,
- flags);
+ comment, flags);
}
else if (actions == ACTION_REPLACE_ALL) {
check_write();
@@ -922,7 +918,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
value = normalize_value(argv[0], argv[1], &default_kvi);
ret = git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], value, argv[2],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
+ comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_GET) {
check_argc(argc, 1, 2);
@@ -950,17 +946,17 @@ int cmd_config(int argc, const char **argv, const char *prefix)
if (argc == 2)
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], NULL, argv[1],
- flags);
+ NULL, flags);
else
return git_config_set_in_file_gently(given_config_source.file,
- argv[0], NULL);
+ argv[0], NULL, NULL);
}
else if (actions == ACTION_UNSET_ALL) {
check_write();
check_argc(argc, 1, 2);
return git_config_set_multivar_in_file_gently(given_config_source.file,
argv[0], NULL, argv[1],
- flags | CONFIG_FLAGS_MULTI_REPLACE);
+ NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
}
else if (actions == ACTION_RENAME_SECTION) {
check_write();
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 3a6a750a8e..17f929dede 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -294,6 +294,8 @@ int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, usage, 0);
socket_path = argv[0];
+ if (!have_unix_sockets())
+ die(_("credential-cache--daemon unavailable; no unix socket support"));
if (!socket_path)
usage_with_options(usage, options);
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index 43b9d0e5b1..bef120b537 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -7,8 +7,6 @@
#ifndef NO_UNIX_SOCKETS
-#include "credential.h"
-#include "string-list.h"
#include "unix-socket.h"
#include "run-command.h"
@@ -151,6 +149,9 @@ int cmd_credential_cache(int argc, const char **argv, const char *prefix)
usage_with_options(usage, options);
op = argv[0];
+ if (!have_unix_sockets())
+ die(_("credential-cache unavailable; no unix socket support"));
+
if (!socket_path)
socket_path = get_socket_path();
if (!socket_path)
diff --git a/builtin/describe.c b/builtin/describe.c
index fb6b0508f3..d6c77a714f 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -7,9 +7,7 @@
#include "lockfile.h"
#include "commit.h"
#include "tag.h"
-#include "blob.h"
#include "refs.h"
-#include "exec-cmd.h"
#include "object-name.h"
#include "parse-options.h"
#include "read-cache-ll.h"
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index f38912cd40..018011f29e 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -11,7 +11,6 @@
#include "preload-index.h"
#include "repository.h"
#include "revision.h"
-#include "submodule.h"
static const char diff_files_usage[] =
"git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 220f341ffa..3e05260ac0 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -7,8 +7,6 @@
#include "repository.h"
#include "revision.h"
#include "setup.h"
-#include "sparse-index.h"
-#include "submodule.h"
static const char diff_cache_usage[] =
"git diff-index [-m] [--cached] [--merge-base] "
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 86be634286..a8e68ce8ef 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -6,7 +6,6 @@
#include "gettext.h"
#include "hex.h"
#include "log-tree.h"
-#include "submodule.h"
#include "read-cache-ll.h"
#include "repository.h"
#include "revision.h"
diff --git a/builtin/diff.c b/builtin/diff.c
index 55e7d21755..6e196e0c7d 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -10,7 +10,6 @@
#include "lockfile.h"
#include "color.h"
#include "commit.h"
-#include "blob.h"
#include "gettext.h"
#include "tag.h"
#include "diff.h"
@@ -21,7 +20,6 @@
#include "revision.h"
#include "log-tree.h"
#include "setup.h"
-#include "submodule.h"
#include "oid-array.h"
#include "tree.h"
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 0f5eae9cd4..a3c72b8258 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -18,7 +18,6 @@
#include "copy.h"
#include "run-command.h"
#include "environment.h"
-#include "exec-cmd.h"
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 70aff515ac..4693d18cc9 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -25,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>]"),
@@ -137,8 +136,7 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED,
a = container_of(eptr, const struct anonymized_entry, hash);
if (keydata) {
const struct anonymized_entry_key *key = keydata;
- int equal = !strncmp(a->orig, key->orig, key->orig_len) &&
- !a->orig[key->orig_len];
+ int equal = !xstrncmpz(a->orig, key->orig, key->orig_len);
return !equal;
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 444f41cf8c..dc5a9d32dd 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1236,20 +1236,6 @@ static void *gfi_unpack_entry(
return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep);
}
-static const char *get_mode(const char *str, uint16_t *modep)
-{
- unsigned char c;
- uint16_t mode = 0;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
static void load_tree(struct tree_entry *root)
{
struct object_id *oid = &root->versions[1].oid;
@@ -1287,7 +1273,7 @@ static void load_tree(struct tree_entry *root)
t->entries[t->entry_count++] = e;
e->tree = NULL;
- c = get_mode(c, &e->versions[1].mode);
+ c = parse_mode(c, &e->versions[1].mode);
if (!c)
die("Corrupt mode in %s", oid_to_hex(oid));
e->versions[0].mode = e->versions[1].mode;
@@ -1625,6 +1611,7 @@ static int update_branch(struct branch *b)
oidclr(&old_oid);
if (!force_update && !is_null_oid(&old_oid)) {
struct commit *old_cmit, *new_cmit;
+ int ret;
old_cmit = lookup_commit_reference_gently(the_repository,
&old_oid, 0);
@@ -1633,7 +1620,10 @@ static int update_branch(struct branch *b)
if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
- if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) {
+ ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit);
+ if (ret < 0)
+ exit(128);
+ if (!ret) {
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, oid_to_hex(&b->oid),
@@ -2220,7 +2210,7 @@ static int parse_mapped_oid_hex(const char *hex, struct object_id *oid, const ch
*
* idnum ::= ':' bigint;
*
- * Return the first character after the value in *endptr.
+ * Update *endptr to point to the first character after the value.
*
* Complain if the following character is not what is expected,
* either a space or end of the string.
@@ -2253,8 +2243,8 @@ static uintmax_t parse_mark_ref_eol(const char *p)
}
/*
- * Parse the mark reference, demanding a trailing space. Return a
- * pointer to the space.
+ * Parse the mark reference, demanding a trailing space. Update *p to
+ * point to the first character after the space.
*/
static uintmax_t parse_mark_ref_space(const char **p)
{
@@ -2268,15 +2258,67 @@ static uintmax_t parse_mark_ref_space(const char **p)
return mark;
}
+/*
+ * Parse the path string into the strbuf. The path can either be quoted with
+ * escape sequences or unquoted without escape sequences. Unquoted strings may
+ * contain spaces only if `is_last_field` is nonzero; otherwise, it stops
+ * parsing at the first space.
+ */
+static void parse_path(struct strbuf *sb, const char *p, const char **endp,
+ int is_last_field, const char *field)
+{
+ if (*p == '"') {
+ if (unquote_c_style(sb, p, endp))
+ die("Invalid %s: %s", field, command_buf.buf);
+ if (strlen(sb->buf) != sb->len)
+ die("NUL in %s: %s", field, command_buf.buf);
+ } else {
+ /*
+ * Unless we are parsing the last field of a line,
+ * SP is the end of this field.
+ */
+ *endp = is_last_field
+ ? p + strlen(p)
+ : strchrnul(p, ' ');
+ strbuf_add(sb, p, *endp - p);
+ }
+}
+
+/*
+ * Parse the path string into the strbuf, and complain if this is not the end of
+ * the string. Unquoted strings may contain spaces.
+ */
+static void parse_path_eol(struct strbuf *sb, const char *p, const char *field)
+{
+ const char *end;
+
+ parse_path(sb, p, &end, 1, field);
+ if (*end)
+ die("Garbage after %s: %s", field, command_buf.buf);
+}
+
+/*
+ * Parse the path string into the strbuf, and ensure it is followed by a space.
+ * Unquoted strings may not contain spaces. Update *endp to point to the first
+ * character after the space.
+ */
+static void parse_path_space(struct strbuf *sb, const char *p,
+ const char **endp, const char *field)
+{
+ parse_path(sb, p, endp, 0, field);
+ if (**endp != ' ')
+ die("Missing space after %s: %s", field, command_buf.buf);
+ (*endp)++;
+}
+
static void file_change_m(const char *p, struct branch *b)
{
- static struct strbuf uq = STRBUF_INIT;
- const char *endp;
+ static struct strbuf path = STRBUF_INIT;
struct object_entry *oe;
struct object_id oid;
uint16_t mode, inline_data = 0;
- p = get_mode(p, &mode);
+ p = parse_mode(p, &mode);
if (!p)
die("Corrupt mode: %s", command_buf.buf);
switch (mode) {
@@ -2308,16 +2350,12 @@ static void file_change_m(const char *p, struct branch *b)
die("Missing space after SHA1: %s", command_buf.buf);
}
- strbuf_reset(&uq);
- if (!unquote_c_style(&uq, p, &endp)) {
- if (*endp)
- die("Garbage after path in: %s", command_buf.buf);
- p = uq.buf;
- }
+ strbuf_reset(&path);
+ parse_path_eol(&path, p, "path");
/* Git does not track empty, non-toplevel directories. */
- if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) {
- tree_content_remove(&b->branch_tree, p, NULL, 0);
+ if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *path.buf) {
+ tree_content_remove(&b->branch_tree, path.buf, NULL, 0);
return;
}
@@ -2338,10 +2376,6 @@ static void file_change_m(const char *p, struct branch *b)
if (S_ISDIR(mode))
die("Directories cannot be specified 'inline': %s",
command_buf.buf);
- if (p != uq.buf) {
- strbuf_addstr(&uq, p);
- p = uq.buf;
- }
while (read_next_command() != EOF) {
const char *v;
if (skip_prefix(command_buf.buf, "cat-blob ", &v))
@@ -2367,74 +2401,48 @@ static void file_change_m(const char *p, struct branch *b)
command_buf.buf);
}
- if (!*p) {
+ if (!*path.buf) {
tree_content_replace(&b->branch_tree, &oid, mode, NULL);
return;
}
- tree_content_set(&b->branch_tree, p, &oid, mode, NULL);
+ tree_content_set(&b->branch_tree, path.buf, &oid, mode, NULL);
}
static void file_change_d(const char *p, struct branch *b)
{
- static struct strbuf uq = STRBUF_INIT;
- const char *endp;
+ static struct strbuf path = STRBUF_INIT;
- strbuf_reset(&uq);
- if (!unquote_c_style(&uq, p, &endp)) {
- if (*endp)
- die("Garbage after path in: %s", command_buf.buf);
- p = uq.buf;
- }
- tree_content_remove(&b->branch_tree, p, NULL, 1);
+ strbuf_reset(&path);
+ parse_path_eol(&path, p, "path");
+ tree_content_remove(&b->branch_tree, path.buf, NULL, 1);
}
-static void file_change_cr(const char *s, struct branch *b, int rename)
+static void file_change_cr(const char *p, struct branch *b, int rename)
{
- const char *d;
- static struct strbuf s_uq = STRBUF_INIT;
- static struct strbuf d_uq = STRBUF_INIT;
- const char *endp;
+ static struct strbuf source = STRBUF_INIT;
+ static struct strbuf dest = STRBUF_INIT;
struct tree_entry leaf;
- strbuf_reset(&s_uq);
- if (!unquote_c_style(&s_uq, s, &endp)) {
- if (*endp != ' ')
- die("Missing space after source: %s", command_buf.buf);
- } else {
- endp = strchr(s, ' ');
- if (!endp)
- die("Missing space after source: %s", command_buf.buf);
- strbuf_add(&s_uq, s, endp - s);
- }
- s = s_uq.buf;
-
- endp++;
- if (!*endp)
- die("Missing dest: %s", command_buf.buf);
-
- d = endp;
- strbuf_reset(&d_uq);
- if (!unquote_c_style(&d_uq, d, &endp)) {
- if (*endp)
- die("Garbage after dest in: %s", command_buf.buf);
- d = d_uq.buf;
- }
+ strbuf_reset(&source);
+ parse_path_space(&source, p, &p, "source");
+ strbuf_reset(&dest);
+ parse_path_eol(&dest, p, "dest");
memset(&leaf, 0, sizeof(leaf));
if (rename)
- tree_content_remove(&b->branch_tree, s, &leaf, 1);
+ tree_content_remove(&b->branch_tree, source.buf, &leaf, 1);
else
- tree_content_get(&b->branch_tree, s, &leaf, 1);
+ tree_content_get(&b->branch_tree, source.buf, &leaf, 1);
if (!leaf.versions[1].mode)
- die("Path %s not in branch", s);
- if (!*d) { /* C "path/to/subdir" "" */
+ die("Path %s not in branch", source.buf);
+ if (!*dest.buf) { /* C "path/to/subdir" "" */
tree_content_replace(&b->branch_tree,
&leaf.versions[1].oid,
leaf.versions[1].mode,
leaf.tree);
return;
}
- tree_content_set(&b->branch_tree, d,
+ tree_content_set(&b->branch_tree, dest.buf,
&leaf.versions[1].oid,
leaf.versions[1].mode,
leaf.tree);
@@ -2442,7 +2450,6 @@ static void file_change_cr(const char *s, struct branch *b, int rename)
static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout)
{
- static struct strbuf uq = STRBUF_INIT;
struct object_entry *oe;
struct branch *s;
struct object_id oid, commit_oid;
@@ -2507,10 +2514,6 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
die("Invalid ref name or SHA1 expression: %s", p);
if (inline_data) {
- if (p != uq.buf) {
- strbuf_addstr(&uq, p);
- p = uq.buf;
- }
read_next_command();
parse_and_store_blob(&last_blob, &oid, 0);
} else if (oe) {
@@ -2809,8 +2812,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;
@@ -3163,6 +3165,7 @@ static void print_ls(int mode, const unsigned char *hash, const char *path)
static void parse_ls(const char *p, struct branch *b)
{
+ static struct strbuf path = STRBUF_INIT;
struct tree_entry *root = NULL;
struct tree_entry leaf = {NULL};
@@ -3179,17 +3182,9 @@ static void parse_ls(const char *p, struct branch *b)
root->versions[1].mode = S_IFDIR;
load_tree(root);
}
- if (*p == '"') {
- static struct strbuf uq = STRBUF_INIT;
- const char *endp;
- strbuf_reset(&uq);
- if (unquote_c_style(&uq, p, &endp))
- die("Invalid path: %s", command_buf.buf);
- if (*endp)
- die("Garbage after path in: %s", command_buf.buf);
- p = uq.buf;
- }
- tree_content_get(root, p, &leaf, 1);
+ strbuf_reset(&path);
+ parse_path_eol(&path, p, "path");
+ tree_content_get(root, path.buf, &leaf, 1);
/*
* A directory in preparation would have a sha1 of zero
* until it is saved. Save, for simplicity.
@@ -3197,7 +3192,7 @@ static void parse_ls(const char *p, struct branch *b)
if (S_ISDIR(leaf.versions[1].mode))
store_tree(&leaf);
- print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p);
+ print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, path.buf);
if (leaf.tree)
release_tree_content_recursive(leaf.tree);
if (!b || root != &b->branch_tree)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index fd134ba74d..5857d860db 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -26,7 +26,6 @@
#include "connected.h"
#include "strvec.h"
#include "utf8.h"
-#include "packfile.h"
#include "pager.h"
#include "path.h"
#include "pkt-line.h"
@@ -38,7 +37,6 @@
#include "shallow.h"
#include "trace.h"
#include "trace2.h"
-#include "worktree.h"
#include "bundle-uri.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -102,6 +100,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
struct fetch_config {
enum display_format display_format;
+ int all;
int prune;
int prune_tags;
int show_forced_updates;
@@ -115,6 +114,11 @@ static int git_fetch_config(const char *k, const char *v,
{
struct fetch_config *fetch_config = cb;
+ if (!strcmp(k, "fetch.all")) {
+ fetch_config->all = git_config_bool(k, v);
+ return 0;
+ }
+
if (!strcmp(k, "fetch.prune")) {
fetch_config->prune = git_config_bool(k, v);
return 0;
@@ -134,6 +138,7 @@ static int git_fetch_config(const char *k, const char *v,
int r = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
fetch_config->recurse_submodules = r;
+ return 0;
}
if (!strcmp(k, "submodule.fetchjobs")) {
@@ -444,9 +449,8 @@ static void filter_prefetch_refspec(struct refspec *rs)
continue;
if (!rs->items[i].dst ||
(rs->items[i].src &&
- !strncmp(rs->items[i].src,
- ref_namespace[NAMESPACE_TAGS].ref,
- strlen(ref_namespace[NAMESPACE_TAGS].ref)))) {
+ starts_with(rs->items[i].src,
+ ref_namespace[NAMESPACE_TAGS].ref))) {
int j;
free(rs->items[i].src);
@@ -978,6 +982,8 @@ static int update_local_ref(struct ref *ref,
uint64_t t_before = getnanotime();
fast_forward = repo_in_merge_bases(the_repository, current,
updated);
+ if (fast_forward < 0)
+ exit(128);
forced_updates_ms += (getnanotime() - t_before) / 1000000;
} else {
fast_forward = 1;
@@ -1651,7 +1657,7 @@ static int do_fetch(struct transport *transport,
if (atomic_fetch) {
transaction = ref_transaction_begin(&err);
if (!transaction) {
- retcode = error("%s", err.buf);
+ retcode = -1;
goto cleanup;
}
}
@@ -1711,7 +1717,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;
@@ -1775,9 +1780,14 @@ 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);
@@ -2128,7 +2138,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
const char *bundle_uri;
struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote = NULL;
- int all = 0, multiple = 0;
+ int all = -1, multiple = 0;
int result = 0;
int prune_tags_ok = 1;
int enable_auto_gc = 1;
@@ -2333,11 +2343,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
fetch_bundle_uri(the_repository, bundle_uri, NULL))
warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+ if (all < 0) {
+ /*
+ * no --[no-]all given;
+ * only use config option if no remote was explicitly specified
+ */
+ all = (!argc) ? config.all : 0;
+ }
+
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
else if (argc > 1)
die(_("fetch --all does not make sense with refspecs"));
+
(void) for_each_remote(get_one_remote_for_fetch, &list);
/* do not do fetch_multiple() of one */
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 1c19cd5bd3..919282e12a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,13 +1,12 @@
#include "builtin.h"
+#include "commit.h"
#include "config.h"
#include "gettext.h"
-#include "refs.h"
#include "object.h"
#include "parse-options.h"
#include "ref-filter.h"
#include "strbuf.h"
#include "strvec.h"
-#include "commit-reach.h"
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
@@ -21,10 +20,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
- int icase = 0;
+ int icase = 0, include_root_refs = 0, from_stdin = 0;
struct ref_filter filter = REF_FILTER_INIT;
struct ref_format format = REF_FORMAT_INIT;
- int from_stdin = 0;
+ unsigned int flags = FILTER_REFS_REGULAR;
struct strvec vec = STRVEC_INIT;
struct option opts[] = {
@@ -54,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
+ OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
OPT_END(),
};
@@ -97,8 +97,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
filter.name_patterns = argv;
}
+ if (include_root_refs)
+ flags |= FILTER_REFS_ROOT_REFS;
+
filter.match_as_path = 1;
- filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
+ filter_and_format_refs(&filter, flags, sorting, &format);
ref_filter_clear(&filter);
ref_sorting_release(sorting);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 611925905e..f892487c9b 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -10,13 +10,10 @@
#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-file.h"
#include "object-name.h"
@@ -512,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
return 0;
}
-static int fsck_handle_reflog(const char *logname,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int fsck_handle_reflog(const char *logname, void *cb_data)
{
struct strbuf refname = STRBUF_INIT;
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index 5d01db5c02..1593713f4c 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -1,19 +1,20 @@
#include "builtin.h"
#include "abspath.h"
#include "config.h"
+#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "parse-options.h"
#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
-#include "fsmonitor-path-utils.h"
#include "fsmonitor-settings.h"
#include "compat/fsmonitor/fsm-health.h"
#include "compat/fsmonitor/fsm-listen.h"
#include "fsmonitor--daemon.h"
+#include "repository.h"
#include "simple-ipc.h"
#include "khash.h"
-#include "pkt-line.h"
+#include "run-command.h"
#include "trace.h"
#include "trace2.h"
diff --git a/builtin/gc.c b/builtin/gc.c
index 7c11d5ebef..d187cec1ea 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -180,13 +180,51 @@ static void gc_config(void)
git_config(git_default_config, NULL);
}
-struct maintenance_run_opts;
+enum schedule_priority {
+ SCHEDULE_NONE = 0,
+ SCHEDULE_WEEKLY = 1,
+ SCHEDULE_DAILY = 2,
+ SCHEDULE_HOURLY = 3,
+};
+
+static enum schedule_priority parse_schedule(const char *value)
+{
+ if (!value)
+ return SCHEDULE_NONE;
+ if (!strcasecmp(value, "hourly"))
+ return SCHEDULE_HOURLY;
+ if (!strcasecmp(value, "daily"))
+ return SCHEDULE_DAILY;
+ if (!strcasecmp(value, "weekly"))
+ return SCHEDULE_WEEKLY;
+ return SCHEDULE_NONE;
+}
+
+struct maintenance_run_opts {
+ int auto_flag;
+ int quiet;
+ enum schedule_priority schedule;
+};
+
+static int pack_refs_condition(void)
+{
+ /*
+ * The auto-repacking logic for refs is handled by the ref backends and
+ * exposed via `git pack-refs --auto`. We thus always return truish
+ * here and let the backend decide for us.
+ */
+ return 1;
+}
+
static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
{
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.git_cmd = 1;
strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL);
+ if (opts->auto_flag)
+ strvec_push(&cmd.args, "--auto");
+
return run_command(&cmd);
}
@@ -547,7 +585,7 @@ done:
return ret;
}
-static void gc_before_repack(void)
+static void gc_before_repack(struct maintenance_run_opts *opts)
{
/*
* We may be called twice, as both the pre- and
@@ -558,7 +596,7 @@ static void gc_before_repack(void)
if (done++)
return;
- if (pack_refs && maintenance_task_pack_refs(NULL))
+ if (pack_refs && maintenance_task_pack_refs(opts))
die(FAILED_RUN, "pack-refs");
if (prune_reflogs) {
@@ -574,7 +612,6 @@ static void gc_before_repack(void)
int cmd_gc(int argc, const char **argv, const char *prefix)
{
int aggressive = 0;
- int auto_gc = 0;
int quiet = 0;
int force = 0;
const char *name;
@@ -583,6 +620,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
int keep_largest_pack = -1;
timestamp_t dummy;
struct child_process rerere_cmd = CHILD_PROCESS_INIT;
+ struct maintenance_run_opts opts = {0};
struct option builtin_gc_options[] = {
OPT__QUIET(&quiet, N_("suppress progress reporting")),
@@ -593,7 +631,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
- OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+ OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL_F(0, "force", &force,
N_("force running gc even if there may be another gc running"),
@@ -638,7 +676,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (quiet)
strvec_push(&repack, "-q");
- if (auto_gc) {
+ if (opts.auto_flag) {
/*
* Auto-gc should be least intrusive as possible.
*/
@@ -663,7 +701,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
if (lock_repo_for_gc(force, &pid))
return 0;
- gc_before_repack(); /* dies on failure */
+ gc_before_repack(&opts); /* dies on failure */
delete_tempfile(&pidfile);
/*
@@ -688,7 +726,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
name = lock_repo_for_gc(force, &pid);
if (name) {
- if (auto_gc)
+ if (opts.auto_flag)
return 0; /* be quiet on --auto */
die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"),
name, (uintmax_t)pid);
@@ -703,7 +741,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
atexit(process_log_file_at_exit);
}
- gc_before_repack();
+ gc_before_repack(&opts);
if (!repository_format_precious_objects) {
struct child_process repack_cmd = CHILD_PROCESS_INIT;
@@ -758,7 +796,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
!quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
NULL);
- if (auto_gc && too_many_loose_objects())
+ if (opts.auto_flag && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
@@ -773,26 +811,6 @@ static const char *const builtin_maintenance_run_usage[] = {
NULL
};
-enum schedule_priority {
- SCHEDULE_NONE = 0,
- SCHEDULE_WEEKLY = 1,
- SCHEDULE_DAILY = 2,
- SCHEDULE_HOURLY = 3,
-};
-
-static enum schedule_priority parse_schedule(const char *value)
-{
- if (!value)
- return SCHEDULE_NONE;
- if (!strcasecmp(value, "hourly"))
- return SCHEDULE_HOURLY;
- if (!strcasecmp(value, "daily"))
- return SCHEDULE_DAILY;
- if (!strcasecmp(value, "weekly"))
- return SCHEDULE_WEEKLY;
- return SCHEDULE_NONE;
-}
-
static int maintenance_opt_schedule(const struct option *opt, const char *arg,
int unset)
{
@@ -809,12 +827,6 @@ static int maintenance_opt_schedule(const struct option *opt, const char *arg,
return 0;
}
-struct maintenance_run_opts {
- int auto_flag;
- int quiet;
- enum schedule_priority schedule;
-};
-
/* Remember to update object flag allocation in object.h */
#define SEEN (1u<<0)
@@ -1296,7 +1308,7 @@ static struct maintenance_task tasks[] = {
[TASK_PACK_REFS] = {
"pack-refs",
maintenance_task_pack_refs,
- NULL,
+ pack_refs_condition,
},
};
@@ -1543,19 +1555,18 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
if (!found) {
int rc;
- char *user_config = NULL, *xdg_config = NULL;
+ char *global_config_file = NULL;
if (!config_file) {
- git_global_config(&user_config, &xdg_config);
- config_file = user_config;
- if (!user_config)
- die(_("$HOME not set"));
+ global_config_file = git_global_config();
+ config_file = global_config_file;
}
+ if (!config_file)
+ die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently(
config_file, "maintenance.repo", maintpath,
- CONFIG_REGEX_NONE, 0);
- free(user_config);
- free(xdg_config);
+ CONFIG_REGEX_NONE, NULL, 0);
+ free(global_config_file);
if (rc)
die(_("unable to add '%s' value of '%s'"),
@@ -1612,18 +1623,18 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
if (found) {
int rc;
- char *user_config = NULL, *xdg_config = NULL;
+ char *global_config_file = NULL;
+
if (!config_file) {
- git_global_config(&user_config, &xdg_config);
- config_file = user_config;
- if (!user_config)
- die(_("$HOME not set"));
+ global_config_file = git_global_config();
+ config_file = global_config_file;
}
+ if (!config_file)
+ die(_("$HOME not set"));
rc = git_config_set_multivar_in_file_gently(
- config_file, key, NULL, maintpath,
+ config_file, key, NULL, maintpath, NULL,
CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
- free(user_config);
- free(xdg_config);
+ free(global_config_file);
if (rc &&
(!force || rc == CONFIG_NOTHING_SET))
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 20d0dfe9cf..66a7389f9f 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -4,7 +4,6 @@
#include "builtin.h"
#include "commit.h"
#include "tar.h"
-#include "quote.h"
static const char builtin_get_tar_commit_id_usage[] =
"git get-tar-commit-id";
diff --git a/builtin/grep.c b/builtin/grep.c
index fe78d4c98b..5777ba82a9 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -9,15 +9,11 @@
#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 "parse-options.h"
#include "string-list.h"
#include "run-command.h"
-#include "userdiff.h"
#include "grep.h"
#include "quote.h"
#include "dir.h"
@@ -531,7 +527,7 @@ static int grep_submodule(struct grep_opt *opt,
strbuf_addstr(&base, filename);
strbuf_addch(&base, '/');
- init_tree_desc(&tree, data, size);
+ init_tree_desc(&tree, oid, data, size);
hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
object_type == OBJ_COMMIT);
strbuf_release(&base);
@@ -575,7 +571,9 @@ static int grep_cache(struct grep_opt *opt,
data = repo_read_object_file(the_repository, &ce->oid,
&type, &size);
- init_tree_desc(&tree, data, size);
+ if (!data)
+ die(_("unable to read tree %s"), oid_to_hex(&ce->oid));
+ init_tree_desc(&tree, &ce->oid, data, size);
hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
strbuf_setlen(&name, name_base_len);
@@ -671,7 +669,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
oid_to_hex(&entry.oid));
strbuf_addch(base, '/');
- init_tree_desc(&sub, data, size);
+ init_tree_desc(&sub, &entry.oid, data, size);
hit |= grep_tree(opt, pathspec, &sub, base, tn_len,
check_attr);
free(data);
@@ -715,7 +713,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
strbuf_add(&base, name, len);
strbuf_addch(&base, ':');
}
- init_tree_desc(&tree, data, size);
+ init_tree_desc(&tree, &obj->oid, data, size);
hit = grep_tree(opt, pathspec, &tree, &base, base.len,
obj->type == OBJ_COMMIT);
strbuf_release(&base);
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 5ffec99dce..82ca6d2bfd 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -14,7 +14,6 @@
#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"
diff --git a/builtin/hook.c b/builtin/hook.c
index 09b51a6487..5234693a94 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -3,7 +3,6 @@
#include "gettext.h"
#include "hook.h"
#include "parse-options.h"
-#include "strbuf.h"
#include "strvec.h"
#define BUILTIN_HOOK_RUN_USAGE \
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index dda94a9f46..856428fef9 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -8,11 +8,9 @@
#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"
@@ -26,7 +24,7 @@
#include "setup.h"
static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
struct object_entry {
struct pack_idx_entry idx;
@@ -1257,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);
@@ -1264,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();
@@ -1524,14 +1524,12 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
struct strbuf pack_name = STRBUF_INIT;
struct strbuf index_name = STRBUF_INIT;
struct strbuf rev_index_name = STRBUF_INIT;
- int err;
if (!from_stdin) {
close(input_fd);
} else {
fsync_component_or_die(FSYNC_COMPONENT_PACK, output_fd, curr_pack_name);
- err = close(output_fd);
- if (err)
+ if (close(output_fd))
die_errno(_("error while closing pack file"));
}
@@ -1566,17 +1564,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
write_or_die(1, buf.buf, buf.len);
strbuf_release(&buf);
- /*
- * Let's just mimic git-unpack-objects here and write
- * the last part of the input buffer to stdout.
- */
- while (input_len) {
- err = xwrite(1, input_buffer + input_offset, input_len);
- if (err <= 0)
- break;
- input_len -= err;
- input_offset += err;
- }
+ /* Write the last part of the buffer to stdout */
+ write_in_full(1, input_buffer + input_offset, input_len);
}
strbuf_release(&rev_index_name);
@@ -1785,8 +1774,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
} else if (!strcmp(arg, "--check-self-contained-and-connected")) {
strict = 1;
check_self_contained_and_connected = 1;
- } else if (!strcmp(arg, "--fsck-objects")) {
+ } else if (skip_to_optional_arg(arg, "--fsck-objects", &arg)) {
do_fsck_object = 1;
+ fsck_set_msg_types(&fsck_options, arg);
} else if (!strcmp(arg, "--verify")) {
verify = 1;
} else if (!strcmp(arg, "--verify-stat")) {
diff --git a/builtin/init-db.c b/builtin/init-db.c
index cb727c826f..0170469b84 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -5,12 +5,13 @@
*/
#include "builtin.h"
#include "abspath.h"
-#include "config.h"
#include "environment.h"
#include "gettext.h"
#include "object-file.h"
#include "parse-options.h"
#include "path.h"
+#include "refs.h"
+#include "repository.h"
#include "setup.h"
#include "strbuf.h"
@@ -57,6 +58,7 @@ 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>]\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
@@ -76,8 +78,10 @@ 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"),
@@ -95,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()
};
@@ -158,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);
@@ -236,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, init_shared_repository, flags);
+ ref_storage_format, initial_branch,
+ init_shared_repository, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 033bd1556c..8768bfea3c 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -9,12 +9,13 @@
#include "gettext.h"
#include "parse-options.h"
#include "string-list.h"
+#include "tempfile.h"
#include "trailer.h"
#include "config.h"
static const char * const git_interpret_trailers_usage[] = {
N_("git interpret-trailers [--in-place] [--trim-empty]\n"
- " [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
+ " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
" [--parse] [<file>...]"),
NULL
};
@@ -91,6 +92,102 @@ static int parse_opt_parse(const struct option *opt, const char *arg,
return 0;
}
+static struct tempfile *trailers_tempfile;
+
+static FILE *create_in_place_tempfile(const char *file)
+{
+ struct stat st;
+ struct strbuf filename_template = STRBUF_INIT;
+ const char *tail;
+ FILE *outfile;
+
+ if (stat(file, &st))
+ die_errno(_("could not stat %s"), file);
+ if (!S_ISREG(st.st_mode))
+ die(_("file %s is not a regular file"), file);
+ if (!(st.st_mode & S_IWUSR))
+ die(_("file %s is not writable by user"), file);
+
+ /* Create temporary file in the same directory as the original */
+ tail = strrchr(file, '/');
+ if (tail)
+ strbuf_add(&filename_template, file, tail - file + 1);
+ strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
+
+ trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
+ strbuf_release(&filename_template);
+ outfile = fdopen_tempfile(trailers_tempfile, "w");
+ if (!outfile)
+ die_errno(_("could not open temporary file"));
+
+ return outfile;
+}
+
+static void read_input_file(struct strbuf *sb, const char *file)
+{
+ if (file) {
+ if (strbuf_read_file(sb, file, 0) < 0)
+ die_errno(_("could not read input file '%s'"), file);
+ } else {
+ if (strbuf_read(sb, fileno(stdin), 0) < 0)
+ die_errno(_("could not read from stdin"));
+ }
+}
+
+static void interpret_trailers(const struct process_trailer_options *opts,
+ struct list_head *new_trailer_head,
+ const char *file)
+{
+ LIST_HEAD(head);
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf trailer_block = STRBUF_INIT;
+ struct trailer_info info;
+ FILE *outfile = stdout;
+
+ trailer_config_init();
+
+ read_input_file(&sb, file);
+
+ if (opts->in_place)
+ outfile = create_in_place_tempfile(file);
+
+ parse_trailers(opts, &info, sb.buf, &head);
+
+ /* Print the lines before the trailers */
+ if (!opts->only_trailers)
+ fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+
+ if (!opts->only_trailers && !info.blank_line_before_trailer)
+ fprintf(outfile, "\n");
+
+
+ if (!opts->only_input) {
+ LIST_HEAD(config_head);
+ LIST_HEAD(arg_head);
+ parse_trailers_from_config(&config_head);
+ parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
+ list_splice(&config_head, &arg_head);
+ process_trailers_lists(&head, &arg_head);
+ }
+
+ /* Print trailer block. */
+ format_trailers(opts, &head, &trailer_block);
+ free_trailers(&head);
+ fwrite(trailer_block.buf, 1, trailer_block.len, outfile);
+ strbuf_release(&trailer_block);
+
+ /* Print the lines after the trailers as is */
+ if (!opts->only_trailers)
+ fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
+ trailer_info_release(&info);
+
+ if (opts->in_place)
+ if (rename_tempfile(&trailers_tempfile, file))
+ die_errno(_("could not rename temporary file to %s"), file);
+
+ strbuf_release(&sb);
+}
+
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
{
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
@@ -132,11 +229,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
if (argc) {
int i;
for (i = 0; i < argc; i++)
- process_trailers(argv[i], &opts, &trailers);
+ interpret_trailers(&opts, &trailers, argv[i]);
} else {
if (opts.in_place)
die(_("no input file given for in-place editing"));
- process_trailers(NULL, &opts, &trailers);
+ interpret_trailers(&opts, &trailers, NULL);
}
new_trailers_clear(&trailers);
diff --git a/builtin/log.c b/builtin/log.c
index 87fd1c8560..c0a8bb95e9 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -26,7 +26,6 @@
#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"
@@ -36,7 +35,6 @@
#include "streaming.h"
#include "version.h"
#include "mailmap.h"
-#include "gpg-interface.h"
#include "progress.h"
#include "commit-slab.h"
#include "repository.h"
@@ -594,8 +592,11 @@ static int git_log_config(const char *var, const char *value,
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;
@@ -1296,7 +1297,7 @@ static void prepare_cover_text(struct pretty_print_context *pp,
subject = subject_sb.buf;
do_pp:
- pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+ pp_email_subject(pp, &subject, sb, encoding, need_8bit_cte);
pp_remainder(pp, &body, sb, 0);
strbuf_release(&description_sb);
@@ -1363,13 +1364,13 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode.type = DATE_RFC2822;
pp.rev = rev;
- pp.print_email_subject = 1;
pp.encode_email_headers = rev->encode_email_headers;
pp_user_info(&pp, NULL, &sb, committer, encoding);
prepare_cover_text(&pp, description_file, branch_name, &sb,
encoding, need_8bit_cte);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
+ free(pp.after_subject);
strbuf_release(&sb);
shortlog_init(&log);
@@ -1624,7 +1625,7 @@ static struct commit *get_base_commit(const char *base_commit,
{
struct commit *base = NULL;
struct commit **rev;
- int i = 0, rev_nr = 0, auto_select, die_on_failure;
+ int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
switch (auto_base) {
case AUTO_BASE_NEVER:
@@ -1657,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit,
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) {
- struct commit_list *base_list;
+ struct commit_list *base_list = NULL;
struct commit *commit;
struct object_id oid;
@@ -1668,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit,
return NULL;
}
commit = lookup_commit_or_die(&oid, "upstream base");
- 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 (repo_get_merge_bases_many(the_repository,
+ commit, total,
+ list,
+ &base_list) < 0 ||
+ /* There should be one and only one merge base. */
+ !base_list || base_list->next) {
if (die_on_failure) {
die(_("could not find exact merge base"));
} else {
@@ -1703,11 +1705,11 @@ 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 = repo_get_merge_bases(the_repository,
- rev[2 * i],
- rev[2 * i + 1]);
- if (!merge_base || merge_base->next) {
+ struct commit_list *merge_base = NULL;
+ if (repo_get_merge_bases(the_repository,
+ rev[2 * i],
+ rev[2 * i + 1], &merge_base) < 0 ||
+ !merge_base || merge_base->next) {
if (die_on_failure) {
die(_("failed to find exact merge base"));
} else {
@@ -1724,7 +1726,10 @@ static struct commit *get_base_commit(const char *base_commit,
rev_nr = DIV_ROUND_UP(rev_nr, 2);
}
- if (!repo_in_merge_bases(the_repository, base, rev[0])) {
+ ret = repo_in_merge_bases(the_repository, base, rev[0]);
+ if (ret < 0)
+ exit(128);
+ if (!ret) {
if (die_on_failure) {
die(_("base commit should be the ancestor of revision list"));
} else {
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a0229c3277..6eeb5cba78 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -14,19 +14,15 @@
#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 "read-cache.h"
-#include "run-command.h"
#include "setup.h"
#include "sparse-index.h"
#include "submodule.h"
-#include "submodule-config.h"
#include "object-store.h"
#include "hex.h"
@@ -270,7 +266,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
struct strbuf sb = STRBUF_INIT;
while (strbuf_expand_step(&sb, &format)) {
- const char *end;
size_t len;
struct stat st;
@@ -278,12 +273,6 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
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))
@@ -312,8 +301,7 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
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_expand_bad_format(format, "ls-files");
}
strbuf_addch(&sb, line_terminator);
fwrite(sb.buf, sb.len, 1, stdout);
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index b416602b4d..e8d65ebbdc 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -5,7 +5,6 @@
#include "pkt-line.h"
#include "ref-filter.h"
#include "remote.h"
-#include "refs.h"
#include "parse-options.h"
#include "wildmatch.h"
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 209d2dc0d5..7bf84b235c 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -9,9 +9,7 @@
#include "hex.h"
#include "object-name.h"
#include "object-store-ll.h"
-#include "blob.h"
#include "tree.h"
-#include "commit.h"
#include "path.h"
#include "quote.h"
#include "parse-options.h"
@@ -102,19 +100,12 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
return 0;
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))
@@ -137,8 +128,7 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
strbuf_setlen(base, baselen);
strbuf_release(&sbuf);
} else
- die(_("bad ls-tree format: %%%.*s"),
- (int)(end - format + 1), format);
+ strbuf_expand_bad_format(format, "ls-tree");
}
strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
fwrite(sb.buf, sb.len, 1, stdout);
@@ -377,6 +367,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
OPT_END()
};
struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
+ struct object_context obj_context;
int ret;
git_config(git_default_config, NULL);
@@ -408,7 +399,9 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
ls_tree_usage, ls_tree_options);
if (argc < 1)
usage_with_options(ls_tree_usage, ls_tree_options);
- if (repo_get_oid(the_repository, argv[0], &oid))
+ if (get_oid_with_context(the_repository, argv[0],
+ GET_OID_HASH_ANY, &oid,
+ &obj_context))
die("Not a valid object name %s", argv[0]);
/*
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index 53b55dd71c..53a22645da 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -6,7 +6,6 @@
#include "abspath.h"
#include "environment.h"
#include "gettext.h"
-#include "utf8.h"
#include "strbuf.h"
#include "mailinfo.h"
#include "parse-options.h"
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index e68b7fe45d..5a8e729502 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -3,9 +3,6 @@
#include "commit.h"
#include "gettext.h"
#include "hex.h"
-#include "refs.h"
-#include "diff.h"
-#include "revision.h"
#include "object-name.h"
#include "parse-options.h"
#include "repository.h"
@@ -13,10 +10,13 @@
static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
{
- struct commit_list *result, *r;
+ struct commit_list *result = NULL, *r;
- result = repo_get_merge_bases_many_dirty(the_repository, rev[0],
- rev_nr - 1, rev + 1);
+ if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
+ rev_nr - 1, rev + 1, &result) < 0) {
+ free_commit_list(result);
+ return -1;
+ }
if (!result)
return 1;
@@ -77,13 +77,17 @@ static int handle_independent(int count, const char **args)
static int handle_octopus(int count, const char **args, int show_all)
{
struct commit_list *revs = NULL;
- struct commit_list *result, *rev;
+ struct commit_list *result = NULL, *rev;
int i;
for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs);
- result = get_octopus_merge_bases(revs);
+ if (get_octopus_merge_bases(revs, &result) < 0) {
+ free_commit_list(revs);
+ free_commit_list(result);
+ return 128;
+ }
free_commit_list(revs);
reduce_heads_replace(&result);
@@ -103,12 +107,16 @@ static int handle_octopus(int count, const char **args, int show_all)
static int handle_is_ancestor(int argc, const char **argv)
{
struct commit *one, *two;
+ int ret;
if (argc != 2)
die("--is-ancestor takes exactly two commits");
one = get_commit_reference(argv[0]);
two = get_commit_reference(argv[1]);
- if (repo_in_merge_bases(the_repository, one, two))
+ ret = repo_in_merge_bases(the_repository, one, two);
+ if (ret < 0)
+ exit(128);
+ if (ret)
return 0;
else
return 1;
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 832c93d8d5..1f987334a3 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "abspath.h"
+#include "diff.h"
#include "hex.h"
#include "object-name.h"
#include "object-store.h"
@@ -28,6 +29,30 @@ 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 };
@@ -48,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")),
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 3366699657..c2ce044a20 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -1,13 +1,10 @@
#include "builtin.h"
#include "advice.h"
-#include "commit.h"
#include "gettext.h"
#include "hash.h"
-#include "tag.h"
#include "merge-recursive.h"
#include "object-name.h"
#include "repository.h"
-#include "xdiff-interface.h"
static const char builtin_merge_recursive_usage[] =
"git %s <base>... -- <head> <remote> ...";
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index a35e0452d6..8bdb439131 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -13,7 +13,6 @@
#include "parse-options.h"
#include "repository.h"
#include "blob.h"
-#include "exec-cmd.h"
#include "merge-blobs.h"
#include "quote.h"
#include "tree.h"
@@ -430,41 +429,56 @@ static int real_merge(struct merge_tree_options *o,
struct merge_options opt;
copy_merge_options(&opt, &o->merge_options);
- parent1 = get_merge_parent(branch1);
- if (!parent1)
- help_unknown_ref(branch1, "merge-tree",
- _("not something we can merge"));
-
- parent2 = get_merge_parent(branch2);
- if (!parent2)
- help_unknown_ref(branch2, "merge-tree",
- _("not something we can merge"));
-
opt.show_rename_progress = 0;
opt.branch1 = branch1;
opt.branch2 = branch2;
if (merge_base) {
- struct commit *base_commit;
struct tree *base_tree, *parent1_tree, *parent2_tree;
- base_commit = lookup_commit_reference_by_name(merge_base);
- if (!base_commit)
- die(_("could not lookup commit '%s'"), merge_base);
+ /*
+ * We actually only need the trees because we already
+ * have a merge base.
+ */
+ struct object_id base_oid, head_oid, merge_oid;
+
+ if (repo_get_oid_treeish(the_repository, merge_base, &base_oid))
+ die(_("could not parse as tree '%s'"), merge_base);
+ base_tree = parse_tree_indirect(&base_oid);
+ if (!base_tree)
+ die(_("unable to read tree (%s)"), oid_to_hex(&base_oid));
+ if (repo_get_oid_treeish(the_repository, branch1, &head_oid))
+ die(_("could not parse as tree '%s'"), branch1);
+ parent1_tree = parse_tree_indirect(&head_oid);
+ if (!parent1_tree)
+ die(_("unable to read tree (%s)"), oid_to_hex(&head_oid));
+ if (repo_get_oid_treeish(the_repository, branch2, &merge_oid))
+ die(_("could not parse as tree '%s'"), branch2);
+ parent2_tree = parse_tree_indirect(&merge_oid);
+ if (!parent2_tree)
+ die(_("unable to read tree (%s)"), oid_to_hex(&merge_oid));
opt.ancestor = merge_base;
- base_tree = repo_get_commit_tree(the_repository, base_commit);
- parent1_tree = repo_get_commit_tree(the_repository, parent1);
- parent2_tree = repo_get_commit_tree(the_repository, parent2);
merge_incore_nonrecursive(&opt, base_tree, parent1_tree, parent2_tree, &result);
} else {
+ parent1 = get_merge_parent(branch1);
+ if (!parent1)
+ help_unknown_ref(branch1, "merge-tree",
+ _("not something we can merge"));
+
+ parent2 = get_merge_parent(branch2);
+ if (!parent2)
+ help_unknown_ref(branch2, "merge-tree",
+ _("not something we can merge"));
+
/*
* Get the merge bases, in reverse order; see comment above
* merge_incore_recursive in merge-ort.h
*/
- merge_bases = repo_get_merge_bases(the_repository, parent1,
- parent2);
+ if (repo_get_merge_bases(the_repository, parent1,
+ parent2, &merge_bases) < 0)
+ exit(128);
if (!merge_bases && !o->allow_unrelated_histories)
die(_("refusing to merge unrelated histories"));
merge_bases = reverse_commit_list(merge_bases);
@@ -549,7 +563,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
PARSE_OPT_NONEG),
OPT_STRING(0, "merge-base",
&merge_base,
- N_("commit"),
+ N_("tree-ish"),
N_("specify a merge-base for the merge")),
OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
N_("option for selected merge strategy")),
@@ -577,7 +591,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
if (o.mode == MODE_TRIVIAL)
die(_("--trivial-merge is incompatible with all other options"));
if (merge_base)
- die(_("--merge-base is incompatible with --stdin"));
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--merge-base", "--stdin");
line_termination = '\0';
while (strbuf_getline_lf(&buf, stdin) != EOF) {
struct strbuf **split;
diff --git a/builtin/merge.c b/builtin/merge.c
index d748d46e13..6f4fec87fc 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -31,8 +31,6 @@
#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"
@@ -42,10 +40,8 @@
#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"
@@ -196,8 +192,7 @@ static struct strategy *get_strategy(const char *name)
int j, found = 0;
struct cmdname *ent = main_cmds.names[i];
for (j = 0; !found && j < ARRAY_SIZE(all_strategy); j++)
- if (!strncmp(ent->name, all_strategy[j].name, ent->len)
- && !all_strategy[j].name[ent->len])
+ if (!xstrncmpz(all_strategy[j].name, ent->name, ent->len))
found = 1;
if (!found)
add_cmdname(&not_strategies, ent->name, ent->len);
@@ -480,7 +475,7 @@ static void finish(struct commit *head_commit,
run_hooks_l("post-merge", squash ? "1" : "0", NULL);
if (new_head)
- apply_autostash(git_path_merge_autostash(the_repository));
+ apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
strbuf_release(&reflog_message);
}
@@ -682,7 +677,8 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
cache_tree_free(&the_index.cache_tree);
for (i = 0; i < nr_trees; i++) {
parse_tree(trees[i]);
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ init_tree_desc(t+i, &trees[i]->object.oid,
+ trees[i]->buffer, trees[i]->size);
}
if (unpack_trees(nr_trees, t, &opts))
return -1;
@@ -826,7 +822,7 @@ static const char scissors_editor_comment[] =
N_("An empty message aborts the commit.\n");
static const char no_scissors_editor_comment[] =
-N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
+N_("Lines starting with '%s' will be ignored, and an empty message aborts\n"
"the commit.\n");
static void write_merge_heads(struct commit_list *);
@@ -857,19 +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, comment_line_char, "\n");
+ strbuf_commented_addf(&msg, comment_line_str, "\n");
}
- strbuf_commented_addf(&msg, comment_line_char,
+ strbuf_commented_addf(&msg, comment_line_str,
_(merge_editor_comment));
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
- strbuf_commented_addf(&msg, comment_line_char,
+ strbuf_commented_addf(&msg, comment_line_str,
_(scissors_editor_comment));
else
- strbuf_commented_addf(&msg, comment_line_char,
- _(no_scissors_editor_comment), comment_line_char);
+ strbuf_commented_addf(&msg, comment_line_str,
+ _(no_scissors_editor_comment), comment_line_str);
}
if (signoff)
- append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+ append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0);
write_merge_heads(remoteheads);
write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
if (run_commit_hook(0 < option_edit, get_index_file(), NULL,
@@ -1319,7 +1315,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (abort_current_merge) {
int nargc = 2;
const char *nargv[] = {"reset", "--merge", NULL};
- struct strbuf stash_oid = STRBUF_INIT;
+ char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+ struct object_id stash_oid = {0};
if (orig_argc != 2)
usage_msg_opt(_("--abort expects no arguments"),
@@ -1328,17 +1325,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!file_exists(git_path_merge_head(the_repository)))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
- if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
- READ_ONELINER_SKIP_IF_EMPTY))
- unlink(git_path_merge_autostash(the_repository));
+ if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
+ delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
- if (stash_oid.len)
- apply_autostash_oid(stash_oid.buf);
+ if (!is_null_oid(&stash_oid)) {
+ oid_to_hex_r(stash_oid_hex, &stash_oid);
+ apply_autostash_oid(stash_oid_hex);
+ }
- strbuf_release(&stash_oid);
goto done;
}
@@ -1517,13 +1514,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!remoteheads)
; /* already up-to-date */
- else if (!remoteheads->next)
- common = repo_get_merge_bases(the_repository, head_commit,
- remoteheads->item);
- else {
+ else if (!remoteheads->next) {
+ if (repo_get_merge_bases(the_repository, head_commit,
+ remoteheads->item, &common) < 0) {
+ ret = 2;
+ goto done;
+ }
+ } else {
struct commit_list *list = remoteheads;
commit_list_insert(head_commit, &list);
- common = get_octopus_merge_bases(list);
+ if (get_octopus_merge_bases(list, &common) < 0) {
+ free(list);
+ ret = 2;
+ goto done;
+ }
free(list);
}
@@ -1567,13 +1571,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (autostash)
- create_autostash(the_repository,
- git_path_merge_autostash(the_repository));
+ create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
if (checkout_fast_forward(the_repository,
&head_commit->object.oid,
&commit->object.oid,
overwrite_ignore)) {
- apply_autostash(git_path_merge_autostash(the_repository));
+ apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
ret = 1;
goto done;
}
@@ -1631,7 +1634,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit_list *j;
for (j = remoteheads; j; j = j->next) {
- struct commit_list *common_one;
+ struct commit_list *common_one = NULL;
struct commit *common_item;
/*
@@ -1639,9 +1642,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* merge_bases again, otherwise "git merge HEAD^
* HEAD^^" would be missed.
*/
- common_one = repo_get_merge_bases(the_repository,
- head_commit,
- j->item);
+ if (repo_get_merge_bases(the_repository, head_commit,
+ j->item, &common_one) < 0)
+ exit(128);
+
common_item = common_one->item;
free_commit_list(common_one);
if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
@@ -1659,8 +1663,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
die_ff_impossible();
if (autostash)
- create_autostash(the_repository,
- git_path_merge_autostash(the_repository));
+ create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
/* We are going to make a new commit. */
git_committer_info(IDENT_STRICT);
@@ -1745,7 +1748,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
fprintf(stderr, _("Merge with strategy %s failed.\n"),
use_strategies[0]->name);
- apply_autostash(git_path_merge_autostash(the_repository));
+ apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
ret = 2;
goto done;
} else if (best_strategy == wt_strategy)
diff --git a/builtin/mktag.c b/builtin/mktag.c
index d8e0b5afc0..4767f1a97e 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -3,7 +3,6 @@
#include "hex.h"
#include "parse-options.h"
#include "strbuf.h"
-#include "tag.h"
#include "replace-object.h"
#include "object-file.h"
#include "object-store-ll.h"
diff --git a/builtin/mv.c b/builtin/mv.c
index c596515ad0..22e64fc290 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -15,7 +15,6 @@
#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"
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 2dd1807c4e..ad9930c831 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -15,6 +15,7 @@
#include "commit-slab.h"
#include "commit-graph.h"
#include "wildmatch.h"
+#include "mem-pool.h"
/*
* One day. See the 'name a rev shortly after epoch' test in t6120 when
@@ -155,30 +156,25 @@ static struct rev_name *create_or_update_name(struct commit *commit,
return name;
}
-static char *get_parent_name(const struct rev_name *name, int parent_number)
+static char *get_parent_name(const struct rev_name *name, int parent_number,
+ struct mem_pool *string_pool)
{
- struct strbuf sb = STRBUF_INIT;
size_t len;
strip_suffix(name->tip_name, "^0", &len);
if (name->generation > 0) {
- strbuf_grow(&sb, len +
- 1 + decimal_width(name->generation) +
- 1 + decimal_width(parent_number));
- strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
- name->generation, parent_number);
+ return mem_pool_strfmt(string_pool, "%.*s~%d^%d",
+ (int)len, name->tip_name,
+ name->generation, parent_number);
} else {
- strbuf_grow(&sb, len +
- 1 + decimal_width(parent_number));
- strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
- parent_number);
+ return mem_pool_strfmt(string_pool, "%.*s^%d",
+ (int)len, name->tip_name, parent_number);
}
- return strbuf_detach(&sb, NULL);
}
static void name_rev(struct commit *start_commit,
const char *tip_name, timestamp_t taggerdate,
- int from_tag, int deref)
+ int from_tag, int deref, struct mem_pool *string_pool)
{
struct prio_queue queue;
struct commit *commit;
@@ -195,9 +191,10 @@ static void name_rev(struct commit *start_commit,
if (!start_name)
return;
if (deref)
- start_name->tip_name = xstrfmt("%s^0", tip_name);
+ start_name->tip_name = mem_pool_strfmt(string_pool, "%s^0",
+ tip_name);
else
- start_name->tip_name = xstrdup(tip_name);
+ start_name->tip_name = mem_pool_strdup(string_pool, tip_name);
memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */
prio_queue_put(&queue, start_commit);
@@ -235,7 +232,8 @@ static void name_rev(struct commit *start_commit,
if (parent_number > 1)
parent_name->tip_name =
get_parent_name(name,
- parent_number);
+ parent_number,
+ string_pool);
else
parent_name->tip_name = name->tip_name;
ALLOC_GROW(parents_to_queue,
@@ -415,7 +413,7 @@ static int name_ref(const char *path, const struct object_id *oid,
return 0;
}
-static void name_tips(void)
+static void name_tips(struct mem_pool *string_pool)
{
int i;
@@ -428,7 +426,7 @@ static void name_tips(void)
struct tip_table_entry *e = &tip_table.table[i];
if (e->commit) {
name_rev(e->commit, e->refname, e->taggerdate,
- e->from_tag, e->deref);
+ e->from_tag, e->deref, string_pool);
}
}
}
@@ -561,6 +559,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
+ struct mem_pool string_pool;
struct object_array revs = OBJECT_ARRAY_INIT;
int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
@@ -587,6 +586,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ mem_pool_init(&string_pool, 0);
init_commit_rev_name(&rev_names);
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
@@ -648,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
adjust_cutoff_timestamp_for_slop();
for_each_ref(name_ref, &data);
- name_tips();
+ name_tips(&string_pool);
if (annotate_stdin) {
struct strbuf sb = STRBUF_INIT;
@@ -676,6 +676,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
always, allow_undefined, data.name_only);
}
+ UNLEAK(string_pool);
UNLEAK(revs);
return 0;
}
diff --git a/builtin/notes.c b/builtin/notes.c
index 9f38863dd5..cb011303e6 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -9,7 +9,6 @@
#include "builtin.h"
#include "config.h"
-#include "alloc.h"
#include "editor.h"
#include "environment.h"
#include "gettext.h"
@@ -19,7 +18,6 @@
#include "object-store-ll.h"
#include "path.h"
#include "repository.h"
-#include "blob.h"
#include "pretty.h"
#include "refs.h"
#include "exec-cmd.h"
@@ -181,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, comment_line_char);
+ strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_str);
write_or_die(fd, cbuf.buf, cbuf.len);
strbuf_release(&cbuf);
@@ -209,10 +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"), comment_line_char);
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
- comment_line_char);
- strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+ comment_line_str);
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_str);
write_or_die(fd, buf.buf, buf.len);
write_commented_object(fd, object);
@@ -225,7 +223,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
die(_("please supply the note contents using either -m or -F option"));
}
if (d->stripspace)
- strbuf_stripspace(&d->buf, comment_line_char);
+ strbuf_stripspace(&d->buf, comment_line_str);
}
}
@@ -266,7 +264,7 @@ static void concat_messages(struct note_data *d)
if ((d->stripspace == UNSPECIFIED &&
d->messages[i]->stripspace == STRIPSPACE) ||
d->stripspace == STRIPSPACE)
- strbuf_stripspace(&d->buf, 0);
+ strbuf_stripspace(&d->buf, NULL);
strbuf_reset(&msg);
}
strbuf_release(&msg);
@@ -718,9 +716,11 @@ static int append_edit(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
- if (prev_buf && size)
+ if (!prev_buf)
+ die(_("unable to read %s"), oid_to_hex(note));
+ if (size)
strbuf_add(&buf, prev_buf, size);
- if (d.buf.len && prev_buf && size)
+ if (d.buf.len && size)
append_separator(&buf);
strbuf_insert(&d.buf, 0, buf.buf, buf.len);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5d3c42035b..baf0090fc8 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -6,10 +6,8 @@
#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"
@@ -18,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"
@@ -1829,7 +1826,8 @@ static void add_pbase_object(struct tree_desc *tree,
tree = pbase_tree_get(&entry.oid);
if (!tree)
return;
- init_tree_desc(&sub, tree->tree_data, tree->tree_size);
+ init_tree_desc(&sub, &tree->oid,
+ tree->tree_data, tree->tree_size);
add_pbase_object(&sub, down, downlen, fullname);
pbase_tree_put(tree);
@@ -1889,7 +1887,8 @@ static void add_preferred_base_object(const char *name)
}
else {
struct tree_desc tree;
- init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
+ init_tree_desc(&tree, &it->pcache.oid,
+ it->pcache.tree_data, it->pcache.tree_size);
add_pbase_object(&tree, name, cmplen, name);
}
}
@@ -3289,7 +3288,7 @@ static int git_pack_config(const char *k, const char *v,
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
@@ -3297,6 +3296,10 @@ static int git_pack_config(const char *k, const char *v,
*/
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) ||
@@ -4395,6 +4398,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
if (sparse < 0)
sparse = the_repository->settings.pack_use_sparse;
+ if (the_repository->settings.pack_use_multi_pack_reuse)
+ allow_pack_reuse = MULTI_PACK_REUSE;
}
reset_pack_idx_option(&pack_idx_opts);
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index bcf383cac9..db40825666 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -7,24 +7,28 @@
#include "revision.h"
static char const * const pack_refs_usage[] = {
- N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
+ N_("git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]"),
NULL
};
int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
- unsigned int flags = PACK_REFS_PRUNE;
- 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 ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+ struct string_list included_refs = STRING_LIST_INIT_NODUP;
+ struct pack_refs_opts pack_refs_opts = {
+ .exclusions = &excludes,
+ .includes = &included_refs,
+ .flags = PACK_REFS_PRUNE,
+ };
+ struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
+ int pack_all = 0;
+ int ret;
struct option opts[] = {
- OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
+ OPT_BOOL(0, "all", &pack_all, N_("pack everything")),
OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+ OPT_BIT(0, "auto", &pack_refs_opts.flags, N_("auto-pack refs as needed"), PACK_REFS_AUTO),
OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
N_("references to include")),
OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
@@ -38,11 +42,16 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
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)
+ if (pack_all)
string_list_append(pack_refs_opts.includes, "*");
if (!pack_refs_opts.includes->nr)
string_list_append(pack_refs_opts.includes, "refs/tags/*");
- return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+ ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+
+ clear_ref_exclusions(&excludes);
+ string_list_clear(&included_refs, 0);
+ string_list_clear(&option_excluded_refs, 0);
+ return ret;
}
diff --git a/builtin/pull.c b/builtin/pull.c
index be2b2c9ebc..72cbb76d52 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -14,7 +14,6 @@
#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"
@@ -24,15 +23,11 @@
#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
@@ -820,7 +815,7 @@ static int get_octopus_merge_base(struct object_id *merge_base,
const struct object_id *merge_head,
const struct object_id *fork_point)
{
- struct commit_list *revs = NULL, *result;
+ struct commit_list *revs = NULL, *result = NULL;
commit_list_insert(lookup_commit_reference(the_repository, curr_head),
&revs);
@@ -830,7 +825,8 @@ static int get_octopus_merge_base(struct object_id *merge_base,
commit_list_insert(lookup_commit_reference(the_repository, fork_point),
&revs);
- result = get_octopus_merge_bases(revs);
+ if (get_octopus_merge_bases(revs, &result) < 0)
+ exit(128);
free_commit_list(revs);
reduce_heads_replace(&result);
@@ -931,6 +927,8 @@ static int get_can_ff(struct object_id *orig_head,
merge_head = lookup_commit_reference(the_repository, orig_merge_head);
ret = repo_is_descendant_of(the_repository, merge_head, list);
free_commit_list(list);
+ if (ret < 0)
+ exit(128);
return ret;
}
@@ -955,6 +953,8 @@ static int already_up_to_date(struct object_id *orig_head,
commit_list_insert(theirs, &list);
ok = repo_is_descendant_of(the_repository, ours, list);
free_commit_list(list);
+ if (ok < 0)
+ exit(128);
if (!ok)
return 0;
}
diff --git a/builtin/push.c b/builtin/push.c
index 2e708383c2..2fbb31c3ad 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -7,7 +7,6 @@
#include "config.h"
#include "environment.h"
#include "gettext.h"
-#include "refs.h"
#include "refspec.h"
#include "run-command.h"
#include "remote.h"
@@ -392,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;
}
@@ -526,26 +525,21 @@ static int git_push_config(const char *k, const char *v,
*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;
@@ -604,7 +598,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
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,
@@ -639,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/--branches/--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"));
@@ -677,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;
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index e455a4795c..f02cbac087 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -5,7 +5,6 @@
#include "range-diff.h"
#include "config.h"
#include "repository.h"
-#include "revision.h"
static const char * const builtin_range_diff_usage[] = {
N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 8196ca9dd8..6f89cec0fb 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -16,14 +16,12 @@
#include "tree-walk.h"
#include "cache-tree.h"
#include "unpack-trees.h"
-#include "dir.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;
@@ -263,8 +261,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
cache_tree_free(&the_index.cache_tree);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
- parse_tree(tree);
- init_tree_desc(t+i, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ return 128;
+ init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size);
}
if (unpack_trees(nr_trees, t, &opts))
return 128;
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 9f8192e0a5..891f28468e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -11,14 +11,10 @@
#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"
@@ -62,7 +58,7 @@ enum empty_type {
EMPTY_UNSPECIFIED = -1,
EMPTY_DROP,
EMPTY_KEEP,
- EMPTY_ASK
+ EMPTY_STOP
};
enum action {
@@ -208,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, comment_line_char);
+ strbuf_stripspace(&todo_list.buf, comment_line_str);
res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
@@ -519,7 +515,7 @@ static int finish_rebase(struct rebase_options *opts)
int ret = 0;
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
- unlink(git_path_auto_merge(the_repository));
+ delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
apply_autostash(state_dir_path("autostash", opts));
/*
* We ignore errors in 'git maintenance run --auto', since the
@@ -571,18 +567,10 @@ static int move_to_original_branch(struct rebase_options *opts)
return ret;
}
-static const char *resolvemsg =
-N_("Resolve all conflicts manually, mark them as resolved with\n"
-"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
-"You can instead skip this commit: run \"git rebase --skip\".\n"
-"To abort and get back to the state before \"git rebase\", run "
-"\"git rebase --abort\".");
-
static int run_am(struct rebase_options *opts)
{
struct child_process am = CHILD_PROCESS_INIT;
struct child_process format_patch = CHILD_PROCESS_INIT;
- struct strbuf revisions = STRBUF_INIT;
int status;
char *rebased_patches;
@@ -592,7 +580,7 @@ static int run_am(struct rebase_options *opts)
opts->reflog_action);
if (opts->action == ACTION_CONTINUE) {
strvec_push(&am.args, "--resolved");
- strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
if (opts->gpg_sign_opt)
strvec_push(&am.args, opts->gpg_sign_opt);
status = run_command(&am);
@@ -603,7 +591,7 @@ static int run_am(struct rebase_options *opts)
}
if (opts->action == ACTION_SKIP) {
strvec_push(&am.args, "--skip");
- strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
status = run_command(&am);
if (status)
return status;
@@ -615,13 +603,6 @@ static int run_am(struct rebase_options *opts)
return run_command(&am);
}
- strbuf_addf(&revisions, "%s...%s",
- oid_to_hex(opts->root ?
- /* this is now equivalent to !opts->upstream */
- &opts->onto->object.oid :
- &opts->upstream->object.oid),
- oid_to_hex(&opts->orig_head->object.oid));
-
rebased_patches = xstrdup(git_path("rebased-patches"));
format_patch.out = open(rebased_patches,
O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -629,7 +610,7 @@ static int run_am(struct rebase_options *opts)
status = error_errno(_("could not open '%s' for writing"),
rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
return status;
}
@@ -642,7 +623,12 @@ static int run_am(struct rebase_options *opts)
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));
@@ -652,7 +638,7 @@ static int run_am(struct rebase_options *opts)
struct reset_head_opts ropts = { 0 };
unlink(rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
ropts.oid = &opts->orig_head->object.oid;
ropts.branch = opts->head_name;
@@ -665,23 +651,21 @@ 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) {
status = error_errno(_("could not open '%s' for reading"),
rebased_patches);
free(rebased_patches);
- strvec_clear(&am.args);
+ child_process_clear(&am);
return status;
}
strvec_pushv(&am.args, opts->git_am_opts.v);
strvec_push(&am.args, "--rebasing");
- strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
+ strvec_pushf(&am.args, "--resolvemsg=%s", rebase_resolvemsg);
strvec_push(&am.args, "--patch-format=mboxrd");
if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
strvec_push(&am.args, "--rerere-autoupdate");
@@ -709,7 +693,6 @@ static int run_specific_rebase(struct rebase_options *opts)
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
- setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT))
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
if (opts->gpg_sign_opt) {
@@ -876,7 +859,8 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
if (!upstream)
goto done;
- merge_bases = repo_get_merge_bases(the_repository, upstream, head);
+ if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0)
+ exit(128);
if (!merge_bases || merge_bases->next)
goto done;
@@ -895,8 +879,9 @@ static void fill_branch_base(struct rebase_options *options,
{
struct commit_list *merge_bases = NULL;
- merge_bases = repo_get_merge_bases(the_repository, options->onto,
- options->orig_head);
+ if (repo_get_merge_bases(the_repository, options->onto,
+ options->orig_head, &merge_bases) < 0)
+ exit(128);
if (!merge_bases || merge_bases->next)
oidcpy(branch_base, null_oid());
else
@@ -960,10 +945,14 @@ static enum empty_type parse_empty_value(const char *value)
return EMPTY_DROP;
else if (!strcasecmp(value, "keep"))
return EMPTY_KEEP;
- else if (!strcasecmp(value, "ask"))
- return EMPTY_ASK;
+ else if (!strcasecmp(value, "stop"))
+ return EMPTY_STOP;
+ else if (!strcasecmp(value, "ask")) {
+ warning(_("--empty=ask is deprecated; use '--empty=stop' instead."));
+ return EMPTY_STOP;
+ }
- die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop\"."), value);
}
static int parse_opt_keep_empty(const struct option *opt, const char *arg,
@@ -1142,7 +1131,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|stop)",
N_("how to handle commits that become empty"),
PARSE_OPT_NONEG, parse_opt_empty),
OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
@@ -1263,7 +1252,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
if (options.action != ACTION_NONE && !in_progress)
- die(_("No rebase in progress?"));
+ die(_("no rebase in progress"));
if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
die(_("The --edit-todo action can only be used during "
@@ -1559,7 +1548,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;
+ options.empty = EMPTY_STOP;
else if (options.exec.nr > 0)
options.empty = EMPTY_KEEP;
else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 8c4f0cb90a..56d8a77ed7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -22,7 +22,6 @@
#include "connected.h"
#include "strvec.h"
#include "version.h"
-#include "tag.h"
#include "gpg-interface.h"
#include "sigchain.h"
#include "fsck.h"
@@ -142,6 +141,7 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
static int receive_pack_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ const char *msg_id;
int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
if (status)
@@ -178,12 +178,14 @@ static int receive_pack_config(const char *var, const char *value,
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;
}
@@ -591,21 +593,6 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
return strbuf_detach(&buf, NULL);
}
-static char *find_header(const char *msg, size_t len, const char *key,
- const char **next_line)
-{
- size_t out_len;
- const char *val = find_header_mem(msg, len, key, &out_len);
-
- if (!val)
- return NULL;
-
- if (next_line)
- *next_line = val + out_len + 1;
-
- return xmemdupz(val, out_len);
-}
-
/*
* Return zero if a and b are equal up to n bytes and nonzero if they are not.
* This operation is guaranteed to run in constant time to avoid leaking data.
@@ -620,13 +607,14 @@ static int constant_memequal(const char *a, const char *b, size_t n)
return res;
}
-static const char *check_nonce(const char *buf, size_t len)
+static const char *check_nonce(const char *buf)
{
- char *nonce = find_header(buf, len, "nonce", NULL);
+ size_t noncelen;
+ const char *found = find_commit_header(buf, "nonce", &noncelen);
+ char *nonce = found ? xmemdupz(found, noncelen) : NULL;
timestamp_t stamp, ostamp;
char *bohmac, *expect = NULL;
const char *retval = NONCE_BAD;
- size_t noncelen;
if (!nonce) {
retval = NONCE_MISSING;
@@ -668,7 +656,6 @@ static const char *check_nonce(const char *buf, size_t len)
goto leave;
}
- noncelen = strlen(nonce);
expect = prepare_push_cert_nonce(service_dir, stamp);
if (noncelen != strlen(expect)) {
/* This is not even the right size. */
@@ -716,35 +703,28 @@ leave:
static int check_cert_push_options(const struct string_list *push_options)
{
const char *buf = push_cert.buf;
- int len = push_cert.len;
- char *option;
- const char *next_line;
+ const char *option;
+ size_t optionlen;
int options_seen = 0;
int retval = 1;
- if (!len)
+ if (!*buf)
return 1;
- while ((option = find_header(buf, len, "push-option", &next_line))) {
- len -= (next_line - buf);
- buf = next_line;
+ while ((option = find_commit_header(buf, "push-option", &optionlen))) {
+ buf = option + optionlen + 1;
options_seen++;
if (options_seen > push_options->nr
- || strcmp(option,
- push_options->items[options_seen - 1].string)) {
- retval = 0;
- goto leave;
- }
- free(option);
+ || xstrncmpz(push_options->items[options_seen - 1].string,
+ option, optionlen))
+ return 0;
}
if (options_seen != push_options->nr)
retval = 0;
-leave:
- free(option);
return retval;
}
@@ -771,7 +751,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
check_signature(&sigcheck, push_cert.buf + bogs,
push_cert.len - bogs);
- nonce_status = check_nonce(push_cert.buf, bogs);
+ nonce_status = check_nonce(sigcheck.payload);
}
if (!is_null_oid(&push_cert_oid)) {
strvec_pushf(&proc->env, "GIT_PUSH_CERT=%s",
@@ -1546,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
starts_with(name, "refs/heads/")) {
struct object *old_object, *new_object;
struct commit *old_commit, *new_commit;
+ int ret2;
old_object = parse_object(the_repository, old_oid);
new_object = parse_object(the_repository, new_oid);
@@ -1559,7 +1540,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
- if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) {
+ ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit);
+ if (ret2 < 0)
+ exit(128);
+ if (!ret2) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
ret = "non-fast-forward";
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 6e490f83d5..060eb3377e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -7,11 +7,15 @@
#include "wildmatch.h"
#include "worktree.h"
#include "reflog.h"
+#include "refs.h"
#include "parse-options.h"
#define BUILTIN_REFLOG_SHOW_USAGE \
N_("git reflog [show] [<log-options>] [<ref>]")
+#define BUILTIN_REFLOG_LIST_USAGE \
+ N_("git reflog list")
+
#define BUILTIN_REFLOG_EXPIRE_USAGE \
N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \
" [--rewrite] [--updateref] [--stale-fix]\n" \
@@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
NULL,
};
+static const char *const reflog_list_usage[] = {
+ BUILTIN_REFLOG_LIST_USAGE,
+ NULL,
+};
+
static const char *const reflog_expire_usage[] = {
BUILTIN_REFLOG_EXPIRE_USAGE,
NULL
@@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
static const char *const reflog_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
+ BUILTIN_REFLOG_LIST_USAGE,
BUILTIN_REFLOG_EXPIRE_USAGE,
BUILTIN_REFLOG_DELETE_USAGE,
BUILTIN_REFLOG_EXISTS_USAGE,
@@ -60,8 +70,7 @@ struct worktree_reflogs {
struct string_list reflogs;
};
-static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
- int flags UNUSED, void *cb_data)
+static int collect_reflog(const char *ref, void *cb_data)
{
struct worktree_reflogs *cb = cb_data;
struct worktree *worktree = cb->worktree;
@@ -96,8 +105,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
reflog_expire_cfg_tail = &reflog_expire_cfg;
for (ent = reflog_expire_cfg; ent; ent = ent->next)
- if (!strncmp(ent->pattern, pattern, len) &&
- ent->pattern[len] == '\0')
+ if (!xstrncmpz(ent->pattern, pattern, len))
return ent;
FLEX_ALLOC_MEM(ent, pattern, pattern, len);
@@ -239,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
return cmd_log_reflog(argc, argv, prefix);
}
+static int show_reflog(const char *refname, void *cb_data UNUSED)
+{
+ printf("%s\n", refname);
+ return 0;
+}
+
+static int cmd_reflog_list(int argc, const char **argv, const char *prefix)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ struct ref_store *ref_store;
+
+ argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0);
+ if (argc)
+ return error(_("%s does not accept arguments: '%s'"),
+ "list", argv[0]);
+
+ ref_store = get_main_ref_store(the_repository);
+
+ return refs_for_each_reflog(ref_store, show_reflog, NULL);
+}
+
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
struct cmd_reflog_expire_cb cmd = { 0 };
@@ -248,7 +279,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
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"),
@@ -368,7 +399,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"),
@@ -418,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_SUBCOMMAND("show", &fn, cmd_reflog_show),
+ OPT_SUBCOMMAND("list", &fn, cmd_reflog_list),
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
diff --git a/builtin/remote.c b/builtin/remote.c
index d91bbe728d..8412d12fa5 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -150,7 +150,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
else if (!strcmp(arg, "push"))
*mirror = MIRROR_PUSH;
else
- return error(_("unknown mirror argument: %s"), arg);
+ return error(_("unknown --mirror argument: %s"), arg);
return 0;
}
diff --git a/builtin/repack.c b/builtin/repack.c
index edaee4dbec..15e4cccc45 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -8,7 +8,6 @@
#include "path.h"
#include "run-command.h"
#include "server-info.h"
-#include "sigchain.h"
#include "strbuf.h"
#include "string-list.h"
#include "strvec.h"
@@ -315,8 +314,9 @@ static int write_oid(const struct object_id *oid,
die(_("could not start pack-objects to repack promisor objects"));
}
- xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz);
- xwrite(cmd->in, "\n", 1);
+ if (write_in_full(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
+ write_in_full(cmd->in, "\n", 1) < 0)
+ die(_("failed to feed promisor objects to pack-objects"));
return 0;
}
@@ -1203,19 +1203,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()))
diff --git a/builtin/replay.c b/builtin/replay.c
new file mode 100644
index 0000000000..6bc4b47f09
--- /dev/null
+++ b/builtin/replay.c
@@ -0,0 +1,446 @@
+/*
+ * "git replay" builtin command
+ */
+
+#define USE_THE_INDEX_VARIABLE
+#include "git-compat-util.h"
+
+#include "builtin.h"
+#include "environment.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "merge-ort.h"
+#include "object-name.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "revision.h"
+#include "strmap.h"
+#include <oidset.h>
+#include <tree.h>
+
+static const char *short_commit_name(struct commit *commit)
+{
+ return repo_find_unique_abbrev(the_repository, &commit->object.oid,
+ DEFAULT_ABBREV);
+}
+
+static struct commit *peel_committish(const char *name)
+{
+ struct object *obj;
+ struct object_id oid;
+
+ if (repo_get_oid(the_repository, name, &oid))
+ return NULL;
+ obj = parse_object(the_repository, &oid);
+ return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj,
+ OBJ_COMMIT);
+}
+
+static char *get_author(const char *message)
+{
+ size_t len;
+ const char *a;
+
+ a = find_commit_header(message, "author", &len);
+ if (a)
+ return xmemdupz(a, len);
+
+ return NULL;
+}
+
+static struct commit *create_commit(struct tree *tree,
+ struct commit *based_on,
+ struct commit *parent)
+{
+ struct object_id ret;
+ struct object *obj;
+ struct commit_list *parents = NULL;
+ char *author;
+ char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
+ struct commit_extra_header *extra;
+ struct strbuf msg = STRBUF_INIT;
+ const char *out_enc = get_commit_output_encoding();
+ const char *message = repo_logmsg_reencode(the_repository, based_on,
+ NULL, out_enc);
+ const char *orig_message = NULL;
+ const char *exclude_gpgsig[] = { "gpgsig", NULL };
+
+ commit_list_insert(parent, &parents);
+ extra = read_commit_extra_headers(based_on, exclude_gpgsig);
+ find_commit_subject(message, &orig_message);
+ strbuf_addstr(&msg, orig_message);
+ author = get_author(message);
+ reset_ident_date();
+ if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
+ &ret, author, NULL, sign_commit, extra)) {
+ error(_("failed to write commit object"));
+ return NULL;
+ }
+ free(author);
+ strbuf_release(&msg);
+
+ obj = parse_object(the_repository, &ret);
+ return (struct commit *)obj;
+}
+
+struct ref_info {
+ struct commit *onto;
+ struct strset positive_refs;
+ struct strset negative_refs;
+ int positive_refexprs;
+ int negative_refexprs;
+};
+
+static void get_ref_information(struct rev_cmdline_info *cmd_info,
+ struct ref_info *ref_info)
+{
+ int i;
+
+ ref_info->onto = NULL;
+ strset_init(&ref_info->positive_refs);
+ strset_init(&ref_info->negative_refs);
+ ref_info->positive_refexprs = 0;
+ ref_info->negative_refexprs = 0;
+
+ /*
+ * When the user specifies e.g.
+ * git replay origin/main..mybranch
+ * git replay ^origin/next mybranch1 mybranch2
+ * we want to be able to determine where to replay the commits. In
+ * these examples, the branches are probably based on an old version
+ * of either origin/main or origin/next, so we want to replay on the
+ * newest version of that branch. In contrast we would want to error
+ * out if they ran
+ * git replay ^origin/master ^origin/next mybranch
+ * git replay mybranch~2..mybranch
+ * the first of those because there's no unique base to choose, and
+ * the second because they'd likely just be replaying commits on top
+ * of the same commit and not making any difference.
+ */
+ for (i = 0; i < cmd_info->nr; i++) {
+ struct rev_cmdline_entry *e = cmd_info->rev + i;
+ struct object_id oid;
+ const char *refexpr = e->name;
+ char *fullname = NULL;
+ int can_uniquely_dwim = 1;
+
+ if (*refexpr == '^')
+ refexpr++;
+ if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1)
+ can_uniquely_dwim = 0;
+
+ if (e->flags & BOTTOM) {
+ if (can_uniquely_dwim)
+ strset_add(&ref_info->negative_refs, fullname);
+ if (!ref_info->negative_refexprs)
+ ref_info->onto = lookup_commit_reference_gently(the_repository,
+ &e->item->oid, 1);
+ ref_info->negative_refexprs++;
+ } else {
+ if (can_uniquely_dwim)
+ strset_add(&ref_info->positive_refs, fullname);
+ ref_info->positive_refexprs++;
+ }
+
+ free(fullname);
+ }
+}
+
+static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
+ const char *onto_name,
+ const char **advance_name,
+ struct commit **onto,
+ struct strset **update_refs)
+{
+ struct ref_info rinfo;
+
+ get_ref_information(cmd_info, &rinfo);
+ if (!rinfo.positive_refexprs)
+ die(_("need some commits to replay"));
+ if (onto_name && *advance_name)
+ die(_("--onto and --advance are incompatible"));
+ else if (onto_name) {
+ *onto = peel_committish(onto_name);
+ if (rinfo.positive_refexprs <
+ strset_get_size(&rinfo.positive_refs))
+ die(_("all positive revisions given must be references"));
+ } else if (*advance_name) {
+ struct object_id oid;
+ char *fullname = NULL;
+
+ *onto = peel_committish(*advance_name);
+ if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name),
+ &oid, &fullname, 0) == 1) {
+ *advance_name = fullname;
+ } else {
+ die(_("argument to --advance must be a reference"));
+ }
+ if (rinfo.positive_refexprs > 1)
+ die(_("cannot advance target with multiple sources because ordering would be ill-defined"));
+ } else {
+ int positive_refs_complete = (
+ rinfo.positive_refexprs ==
+ strset_get_size(&rinfo.positive_refs));
+ int negative_refs_complete = (
+ rinfo.negative_refexprs ==
+ strset_get_size(&rinfo.negative_refs));
+ /*
+ * We need either positive_refs_complete or
+ * negative_refs_complete, but not both.
+ */
+ if (rinfo.negative_refexprs > 0 &&
+ positive_refs_complete == negative_refs_complete)
+ die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+ if (negative_refs_complete) {
+ struct hashmap_iter iter;
+ struct strmap_entry *entry;
+
+ if (rinfo.negative_refexprs == 0)
+ die(_("all positive revisions given must be references"));
+ else if (rinfo.negative_refexprs > 1)
+ die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+ else if (rinfo.positive_refexprs > 1)
+ die(_("cannot advance target with multiple source branches because ordering would be ill-defined"));
+
+ /* Only one entry, but we have to loop to get it */
+ strset_for_each_entry(&rinfo.negative_refs,
+ &iter, entry) {
+ *advance_name = entry->key;
+ }
+ } else { /* positive_refs_complete */
+ if (rinfo.negative_refexprs > 1)
+ die(_("cannot implicitly determine correct base for --onto"));
+ if (rinfo.negative_refexprs == 1)
+ *onto = rinfo.onto;
+ }
+ }
+ if (!*advance_name) {
+ *update_refs = xcalloc(1, sizeof(**update_refs));
+ **update_refs = rinfo.positive_refs;
+ memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
+ }
+ strset_clear(&rinfo.negative_refs);
+ strset_clear(&rinfo.positive_refs);
+}
+
+static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
+ struct commit *commit,
+ struct commit *fallback)
+{
+ khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
+ if (pos == kh_end(replayed_commits))
+ return fallback;
+ return kh_value(replayed_commits, pos);
+}
+
+static struct commit *pick_regular_commit(struct commit *pickme,
+ kh_oid_map_t *replayed_commits,
+ struct commit *onto,
+ struct merge_options *merge_opt,
+ struct merge_result *result)
+{
+ struct commit *base, *replayed_base;
+ struct tree *pickme_tree, *base_tree;
+
+ base = pickme->parents->item;
+ replayed_base = mapped_commit(replayed_commits, base, onto);
+
+ result->tree = repo_get_commit_tree(the_repository, replayed_base);
+ pickme_tree = repo_get_commit_tree(the_repository, pickme);
+ base_tree = repo_get_commit_tree(the_repository, base);
+
+ merge_opt->branch1 = short_commit_name(replayed_base);
+ merge_opt->branch2 = short_commit_name(pickme);
+ merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+
+ merge_incore_nonrecursive(merge_opt,
+ base_tree,
+ result->tree,
+ pickme_tree,
+ result);
+
+ free((char*)merge_opt->ancestor);
+ merge_opt->ancestor = NULL;
+ if (!result->clean)
+ return NULL;
+ return create_commit(result->tree, pickme, replayed_base);
+}
+
+int cmd_replay(int argc, const char **argv, const char *prefix)
+{
+ const char *advance_name = NULL;
+ struct commit *onto = NULL;
+ const char *onto_name = NULL;
+ int contained = 0;
+
+ struct rev_info revs;
+ struct commit *last_commit = NULL;
+ struct commit *commit;
+ struct merge_options merge_opt;
+ struct merge_result result;
+ struct strset *update_refs = NULL;
+ kh_oid_map_t *replayed_commits;
+ int ret = 0;
+
+ const char * const replay_usage[] = {
+ N_("(EXPERIMENTAL!) git replay "
+ "([--contained] --onto <newbase> | --advance <branch>) "
+ "<revision-range>..."),
+ NULL
+ };
+ struct option replay_options[] = {
+ OPT_STRING(0, "advance", &advance_name,
+ N_("branch"),
+ N_("make replay advance given branch")),
+ OPT_STRING(0, "onto", &onto_name,
+ N_("revision"),
+ N_("replay onto given commit")),
+ OPT_BOOL(0, "contained", &contained,
+ N_("advance all branches contained in revision-range")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+ if (!onto_name && !advance_name) {
+ error(_("option --onto or --advance is mandatory"));
+ usage_with_options(replay_usage, replay_options);
+ }
+
+ if (advance_name && contained)
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--advance", "--contained");
+
+ repo_init_revisions(the_repository, &revs, prefix);
+
+ /*
+ * Set desired values for rev walking options here. If they
+ * are changed by some user specified option in setup_revisions()
+ * below, we will detect that below and then warn.
+ *
+ * TODO: In the future we might want to either die(), or allow
+ * some options changing these values if we think they could
+ * be useful.
+ */
+ revs.reverse = 1;
+ revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+ revs.topo_order = 1;
+ revs.simplify_history = 0;
+
+ argc = setup_revisions(argc, argv, &revs, NULL);
+ if (argc > 1) {
+ ret = error(_("unrecognized argument: %s"), argv[1]);
+ goto cleanup;
+ }
+
+ /*
+ * Detect and warn if we override some user specified rev
+ * walking options.
+ */
+ if (revs.reverse != 1) {
+ warning(_("some rev walking options will be overridden as "
+ "'%s' bit in 'struct rev_info' will be forced"),
+ "reverse");
+ revs.reverse = 1;
+ }
+ if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
+ warning(_("some rev walking options will be overridden as "
+ "'%s' bit in 'struct rev_info' will be forced"),
+ "sort_order");
+ revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+ }
+ if (revs.topo_order != 1) {
+ warning(_("some rev walking options will be overridden as "
+ "'%s' bit in 'struct rev_info' will be forced"),
+ "topo_order");
+ revs.topo_order = 1;
+ }
+ if (revs.simplify_history != 0) {
+ warning(_("some rev walking options will be overridden as "
+ "'%s' bit in 'struct rev_info' will be forced"),
+ "simplify_history");
+ revs.simplify_history = 0;
+ }
+
+ determine_replay_mode(&revs.cmdline, onto_name, &advance_name,
+ &onto, &update_refs);
+
+ if (!onto) /* FIXME: Should handle replaying down to root commit */
+ die("Replaying down to root commit is not supported yet!");
+
+ if (prepare_revision_walk(&revs) < 0) {
+ ret = error(_("error preparing revisions"));
+ goto cleanup;
+ }
+
+ init_merge_options(&merge_opt, the_repository);
+ memset(&result, 0, sizeof(result));
+ merge_opt.show_rename_progress = 0;
+ last_commit = onto;
+ replayed_commits = kh_init_oid_map();
+ while ((commit = get_revision(&revs))) {
+ const struct name_decoration *decoration;
+ khint_t pos;
+ int hr;
+
+ if (!commit->parents)
+ die(_("replaying down to root commit is not supported yet!"));
+ if (commit->parents->next)
+ die(_("replaying merge commits is not supported yet!"));
+
+ last_commit = pick_regular_commit(commit, replayed_commits, onto,
+ &merge_opt, &result);
+ if (!last_commit)
+ break;
+
+ /* Record commit -> last_commit mapping */
+ pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr);
+ if (hr == 0)
+ BUG("Duplicate rewritten commit: %s\n",
+ oid_to_hex(&commit->object.oid));
+ kh_value(replayed_commits, pos) = last_commit;
+
+ /* Update any necessary branches */
+ if (advance_name)
+ continue;
+ decoration = get_name_decoration(&commit->object);
+ if (!decoration)
+ continue;
+ while (decoration) {
+ if (decoration->type == DECORATION_REF_LOCAL &&
+ (contained || strset_contains(update_refs,
+ decoration->name))) {
+ printf("update %s %s %s\n",
+ decoration->name,
+ oid_to_hex(&last_commit->object.oid),
+ oid_to_hex(&commit->object.oid));
+ }
+ decoration = decoration->next;
+ }
+ }
+
+ /* In --advance mode, advance the target ref */
+ if (result.clean == 1 && advance_name) {
+ printf("update %s %s %s\n",
+ advance_name,
+ oid_to_hex(&last_commit->object.oid),
+ oid_to_hex(&onto->object.oid));
+ }
+
+ merge_finalize(&merge_opt, &result);
+ kh_destroy_oid_map(replayed_commits);
+ if (update_refs) {
+ strset_clear(update_refs);
+ free(update_refs);
+ }
+ ret = result.clean;
+
+cleanup:
+ release_revisions(&revs);
+
+ /* Return */
+ if (ret < 0)
+ exit(128);
+ return ret ? 0 : 1;
+}
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 07a9d37275..b2efc6f640 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,6 +1,5 @@
#include "builtin.h"
#include "config.h"
-#include "dir.h"
#include "gettext.h"
#include "parse-options.h"
#include "repository.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 4b018d20e3..1d62ff6332 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -16,10 +16,8 @@
#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"
@@ -33,7 +31,6 @@
#include "setup.h"
#include "sparse-index.h"
#include "submodule.h"
-#include "submodule-config.h"
#include "trace.h"
#include "trace2.h"
#include "dir.h"
@@ -119,6 +116,10 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
if (reset_type == MIXED || reset_type == HARD) {
tree = parse_tree_indirect(oid);
+ if (!tree) {
+ error(_("unable to read tree (%s)"), oid_to_hex(oid));
+ goto out;
+ }
prime_cache_tree(the_repository, the_repository->index, tree);
}
@@ -284,7 +285,9 @@ static void parse_args(struct pathspec *pathspec,
verify_filename(prefix, argv[0], 1);
}
}
- *rev_ret = rev;
+
+ /* treat '@' as a shortcut for 'HEAD' */
+ *rev_ret = !strcmp("@", rev) ? "HEAD" : rev;
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 181353dcf5..77803727e0 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -7,13 +7,11 @@
#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-name.h"
#include "object-file.h"
#include "object-store-ll.h"
-#include "pack.h"
#include "pack-bitmap.h"
#include "log-tree.h"
#include "graph.h"
@@ -221,6 +219,7 @@ static void show_commit(struct commit *commit, void *data)
ctx.fmt = revs->commit_format;
ctx.output_encoding = get_log_output_encoding();
ctx.color = revs->diffopt.use_color;
+ ctx.rev = revs;
pretty_print_commit(&ctx, commit, &buf);
if (buf.len) {
if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -547,6 +546,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
*
* Let "--missing" to conditionally set fetch_if_missing.
*/
+ /*
+ * NEEDSWORK: These loops that attempt to find presence of
+ * options without understanding that the options they are
+ * skipping are broken (e.g., it would not know "--grep
+ * --exclude-promisor-objects" is not triggering
+ * "--exclude-promisor-objects" option). We really need
+ * setup_revisions() to have a mechanism to allow and disallow
+ * some sets of options for different commands (like rev-list,
+ * replay, etc). Such a mechanism should do an early parsing
+ * of options and be able to manage the `--missing=...` and
+ * `--exclude-promisor-objects` options below.
+ */
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--exclude-promisor-objects")) {
@@ -755,8 +766,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (arg_print_omitted)
oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
- if (arg_missing_action == MA_PRINT)
+ if (arg_missing_action == MA_PRINT) {
oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+ /* Add missing tips */
+ oidset_insert_from_set(&missing_objects, &revs.missing_commits);
+ oidset_clear(&revs.missing_commits);
+ }
traverse_commit_list_filtered(
&revs, show_commit, show_object, &info,
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index fde8861ca4..624182e507 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -25,6 +25,7 @@
#include "submodule.h"
#include "commit-reach.h"
#include "shallow.h"
+#include "object-file-convert.h"
#define DO_REVS 1
#define DO_NOREV 2
@@ -297,7 +298,7 @@ static int try_difference(const char *arg)
show_rev(NORMAL, &end_oid, end);
show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
if (symmetric) {
- struct commit_list *exclude;
+ struct commit_list *exclude = NULL;
struct commit *a, *b;
a = lookup_commit_reference(the_repository, &start_oid);
b = lookup_commit_reference(the_repository, &end_oid);
@@ -305,7 +306,8 @@ static int try_difference(const char *arg)
*dotdot = '.';
return 0;
}
- exclude = repo_get_merge_bases(the_repository, a, b);
+ if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
+ exit(128);
while (exclude) {
struct commit *commit = pop_commit(&exclude);
show_rev(REVERSED, &commit->object.oid, NULL);
@@ -675,6 +677,8 @@ static void print_path(const char *path, const char *prefix, enum format_type fo
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+ const struct git_hash_algo *output_algo = NULL;
+ const struct git_hash_algo *compat = NULL;
int did_repo_setup = 0;
int has_dashdash = 0;
int output_prefix = 0;
@@ -746,6 +750,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
+ compat = the_repository->compat_hash_algo;
}
if (!strcmp(arg, "--")) {
@@ -833,6 +838,22 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
flags |= GET_OID_QUIETLY;
continue;
}
+ if (opt_with_value(arg, "--output-object-format", &arg)) {
+ if (!arg)
+ die(_("no object format specified"));
+ if (!strcmp(arg, the_hash_algo->name) ||
+ !strcmp(arg, "storage")) {
+ flags |= GET_OID_HASH_ANY;
+ output_algo = the_hash_algo;
+ continue;
+ }
+ else if (compat && !strcmp(arg, compat->name)) {
+ flags |= GET_OID_HASH_ANY;
+ output_algo = compat;
+ continue;
+ }
+ else die(_("unsupported object format: %s"), arg);
+ }
if (opt_with_value(arg, "--short", &arg)) {
filter &= ~(DO_FLAGS|DO_NOREV);
verify = 1;
@@ -882,7 +903,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (skip_prefix(arg, "--disambiguate=", &arg)) {
- repo_for_each_abbrev(the_repository, arg,
+ repo_for_each_abbrev(the_repository, arg, the_hash_algo,
show_abbrev, NULL);
continue;
}
@@ -893,13 +914,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (opt_with_value(arg, "--branches", &arg)) {
if (ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --branches"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--branches");
handle_ref_opt(arg, "refs/heads/");
continue;
}
if (opt_with_value(arg, "--tags", &arg)) {
if (ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --tags"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--tags");
handle_ref_opt(arg, "refs/tags/");
continue;
}
@@ -909,7 +932,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (opt_with_value(arg, "--remotes", &arg)) {
if (ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --remotes"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--remotes");
handle_ref_opt(arg, "refs/remotes/");
continue;
}
@@ -1059,6 +1083,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))
@@ -1083,6 +1111,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
}
if (!get_oid_with_context(the_repository, name,
flags, &oid, &unused)) {
+ if (output_algo)
+ repo_oid_to_algop(the_repository, &oid,
+ output_algo, &oid);
if (verify)
revs_count++;
else
diff --git a/builtin/revert.c b/builtin/revert.c
index e6f9a1ad26..53935d2c68 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "config.h"
#include "builtin.h"
#include "parse-options.h"
#include "diff.h"
@@ -7,7 +6,6 @@
#include "repository.h"
#include "revision.h"
#include "rerere.h"
-#include "dir.h"
#include "sequencer.h"
#include "branch.h"
@@ -45,6 +43,31 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
}
+enum empty_action {
+ EMPTY_COMMIT_UNSPECIFIED = -1,
+ STOP_ON_EMPTY_COMMIT, /* output errors and stop in the middle of a cherry-pick */
+ DROP_EMPTY_COMMIT, /* skip with a notice message */
+ KEEP_EMPTY_COMMIT, /* keep recording as empty commits */
+};
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ int *opt_value = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ if (!strcmp(arg, "stop"))
+ *opt_value = STOP_ON_EMPTY_COMMIT;
+ else if (!strcmp(arg, "drop"))
+ *opt_value = DROP_EMPTY_COMMIT;
+ else if (!strcmp(arg, "keep"))
+ *opt_value = KEEP_EMPTY_COMMIT;
+ else
+ return error(_("invalid value for '%s': '%s'"), "--empty", arg);
+
+ return 0;
+}
+
static int option_parse_m(const struct option *opt,
const char *arg, int unset)
{
@@ -87,6 +110,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
const char *cleanup_arg = NULL;
+ enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED;
int cmd = 0;
struct option base_options[] = {
OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
@@ -116,7 +140,10 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
- OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
+ OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("deprecated: use --empty=keep instead")),
+ OPT_CALLBACK_F(0, "empty", &empty_opt, "(stop|drop|keep)",
+ N_("how to handle commits that become empty"),
+ PARSE_OPT_NONEG, parse_opt_empty),
OPT_END(),
};
options = parse_options_concat(options, cp_extra);
@@ -136,6 +163,11 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
+ if (opts->action == REPLAY_PICK) {
+ opts->drop_redundant_commits = (empty_opt == DROP_EMPTY_COMMIT);
+ opts->keep_redundant_commits = opts->keep_redundant_commits || (empty_opt == KEEP_EMPTY_COMMIT);
+ }
+
/* implies allow_empty */
if (opts->keep_redundant_commits)
opts->allow_empty = 1;
@@ -169,6 +201,8 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
"--ff", opts->allow_ff,
"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
"--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
+ "--keep-redundant-commits", opts->keep_redundant_commits,
+ "--empty", empty_opt != EMPTY_COMMIT_UNSPECIFIED,
NULL);
}
diff --git a/builtin/rm.c b/builtin/rm.c
index dff819ae50..fd130cea2d 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -9,7 +9,6 @@
#include "config.h"
#include "lockfile.h"
#include "dir.h"
-#include "cache-tree.h"
#include "gettext.h"
#include "hash.h"
#include "tree-walk.h"
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index cd6d9e4112..3df9eaad09 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -1,19 +1,14 @@
#include "builtin.h"
#include "config.h"
-#include "commit.h"
#include "hex.h"
-#include "refs.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"
@@ -135,21 +130,18 @@ static int send_pack_config(const char *k, const char *v,
const struct config_context *ctx, void *cb)
{
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, ctx, cb);
@@ -208,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,
@@ -341,6 +333,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
}
if (!ret && !transport_refs_pushed(remote_refs))
+ /* stable plumbing output; do not modify or localize */
fprintf(stderr, "Everything up-to-date\n");
return ret;
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 1307ed2b88..3c7cd2d6ef 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -245,7 +245,6 @@ 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 = log->date_mode;
ctx.output_encoding = get_log_output_encoding();
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 7aac525a87..1c15421e60 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -6,7 +6,6 @@
#include "object-name.h"
#include "object-store-ll.h"
#include "object.h"
-#include "tag.h"
#include "string-list.h"
#include "parse-options.h"
@@ -173,7 +172,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
while (*refs) {
struct object_id oid;
- if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) &&
+ if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
!read_ref(*refs, &oid)) {
show_one(show_one_opts, *refs, &oid);
}
@@ -239,7 +238,7 @@ static int cmd_show_ref__exists(const char **refs)
if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
&unused_oid, &unused_referent, &unused_type,
&failure_errno)) {
- if (failure_errno == ENOENT) {
+ if (failure_errno == ENOENT || failure_errno == EISDIR) {
error(_("reference does not exist"));
ret = 2;
} else {
@@ -315,9 +314,9 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, show_ref_options,
show_ref_usage, 0);
- if ((!!exclude_existing_opts.enabled + !!verify + !!exists) > 1)
- die(_("only one of '%s', '%s' or '%s' can be given"),
- "--exclude-existing", "--verify", "--exists");
+ die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
+ verify, "--verify",
+ exists, "--exists");
if (exclude_existing_opts.enabled)
return cmd_show_ref__exclude_existing(&exclude_existing_opts);
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 5c8ffb1f75..0f52e25249 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -8,14 +8,10 @@
#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"
@@ -777,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);
@@ -824,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;
@@ -835,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 {
@@ -996,8 +990,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_check_rules_options,
- builtin_sparse_checkout_check_rules_usage,
- PARSE_OPT_KEEP_UNKNOWN_OPT);
+ 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;
diff --git a/builtin/stash.c b/builtin/stash.c
index 4a6771c9f4..062be1fbc0 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -26,7 +26,6 @@
#include "sparse-index.h"
#include "log-tree.h"
#include "diffcore.h"
-#include "exec-cmd.h"
#include "reflog.h"
#include "add-interactive.h"
@@ -285,7 +284,7 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
if (parse_tree(tree))
return -1;
- init_tree_desc(t, tree->buffer, tree->size);
+ init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
opts.head_idx = 1;
opts.src_index = &the_index;
@@ -521,7 +520,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
- die(_("Unable to write index."));
+ die(_("could not write index"));
}
static int do_apply_stash(const char *prefix, struct stash_info *info,
@@ -538,7 +537,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
repo_read_index_preload(the_repository, NULL, 0);
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
NULL, NULL, NULL))
- return -1;
+ return error(_("could not write index"));
if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
NULL))
@@ -871,7 +870,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
tree[i] = parse_tree_indirect(oid[i]);
if (parse_tree(tree[i]) < 0)
die(_("failed to parse tree"));
- init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
+ init_tree_desc(&tree_desc[i], &tree[i]->object.oid,
+ tree[i]->buffer, tree[i]->size);
}
unpack_tree_opt.head_idx = -1;
@@ -1365,7 +1365,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
repo_read_index_preload(the_repository, NULL, 0);
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
NULL, NULL, NULL) < 0) {
- ret = -1;
+ ret = error(_("could not write index"));
goto done;
}
@@ -1556,7 +1556,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
if (repo_refresh_and_write_index(the_repository, REFRESH_QUIET, 0, 0,
NULL, NULL, NULL)) {
- ret = -1;
+ ret = error(_("could not write index"));
goto done;
}
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 7b700a9fb1..e5626e5126 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -13,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, comment_line_char);
+ strbuf_add_commented_lines(buf, msg, len, comment_line_str);
free(msg);
}
@@ -59,7 +59,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
strbuf_stripspace(&buf,
- mode == STRIP_COMMENTS ? comment_line_char : '\0');
+ mode == STRIP_COMMENTS ? comment_line_str : NULL);
else
comment_lines(&buf);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cce46450ab..e4e18adb57 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -22,7 +22,6 @@
#include "remote.h"
#include "refs.h"
#include "refspec.h"
-#include "connect.h"
#include "revision.h"
#include "diffcore.h"
#include "diff.h"
@@ -1284,7 +1283,7 @@ static void sync_submodule(const char *path, const char *prefix,
submodule_to_gitdir(&sb, path);
strbuf_addstr(&sb, "/config");
- if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+ if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
die(_("failed to update remote for submodule '%s'"),
path);
diff --git a/builtin/tag.c b/builtin/tag.c
index 2528d499dd..9a33cb50b4 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -18,7 +18,6 @@
#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"
@@ -28,6 +27,7 @@
#include "ref-filter.h"
#include "date.h"
#include "write-or-die.h"
+#include "object-file-convert.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
@@ -152,18 +152,52 @@ static int verify_tag(const char *name, const char *ref UNUSED,
return 0;
}
-static int do_sign(struct strbuf *buffer)
+static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
+ struct object_id *compat_oid_buf)
{
- return sign_buffer(buffer, buffer, get_signing_key());
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+ struct strbuf compat_buf = STRBUF_INIT;
+ const char *keyid = get_signing_key();
+ int ret = -1;
+
+ if (sign_buffer(buffer, &sig, keyid))
+ return -1;
+
+ if (compat) {
+ const struct git_hash_algo *algo = the_repository->hash_algo;
+
+ if (convert_object_file(&compat_buf, algo, compat,
+ buffer->buf, buffer->len, OBJ_TAG, 1))
+ goto out;
+ if (sign_buffer(&compat_buf, &compat_sig, keyid))
+ goto out;
+ add_header_signature(&compat_buf, &sig, algo);
+ strbuf_addbuf(&compat_buf, &compat_sig);
+ hash_object_file(compat, compat_buf.buf, compat_buf.len,
+ OBJ_TAG, compat_oid_buf);
+ *compat_oid = compat_oid_buf;
+ }
+
+ if (compat_sig.len)
+ add_header_signature(buffer, &compat_sig, compat);
+
+ strbuf_addbuf(buffer, &sig);
+ ret = 0;
+out:
+ strbuf_release(&sig);
+ strbuf_release(&compat_sig);
+ strbuf_release(&compat_buf);
+ return ret;
}
static const char tag_template[] =
N_("\nWrite a message for tag:\n %s\n"
- "Lines starting with '%c' will be ignored.\n");
+ "Lines starting with '%s' will be ignored.\n");
static const char tag_template_nocleanup[] =
N_("\nWrite a message for tag:\n %s\n"
- "Lines starting with '%c' will be kept; you may remove them"
+ "Lines starting with '%s' will be kept; you may remove them"
" yourself if you want to.\n");
static int git_tag_config(const char *var, const char *value,
@@ -227,9 +261,11 @@ static void write_tag_body(int fd, const struct object_id *oid)
static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
{
- if (sign && do_sign(buf) < 0)
+ struct object_id *compat_oid = NULL, compat_oid_buf;
+ if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0)
return error(_("unable to sign the tag"));
- if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
+ if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result,
+ compat_oid, 0) < 0)
return error(_("unable to write tag file"));
return 0;
}
@@ -292,11 +328,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, comment_line_char,
- _(tag_template), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_str,
+ _(tag_template), tag, comment_line_str);
else
- strbuf_commented_addf(&buf, comment_line_char,
- _(tag_template_nocleanup), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_str,
+ _(tag_template_nocleanup), tag, comment_line_str);
write_or_die(fd, buf.buf, buf.len);
strbuf_release(&buf);
}
@@ -311,7 +347,7 @@ 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 ? comment_line_char : '\0');
+ opt->cleanup_mode == CLEANUP_ALL ? comment_line_str : NULL);
if (!opt->message_given && !buf->len)
die(_("no tag message?"));
@@ -531,7 +567,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct column_options copts;
memset(&copts, 0, sizeof(copts));
copts.padding = 2;
- run_column_filter(colopts, &copts);
+ if (run_column_filter(colopts, &copts))
+ die(_("could not start 'git column'"));
}
filter.name_patterns = argv;
ret = list_tags(&filter, sorting, &format);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index fef7423448..f1c85a00ae 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -10,12 +10,8 @@
#include "delta.h"
#include "pack.h"
#include "blob.h"
-#include "commit.h"
#include "replace-object.h"
#include "strbuf.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
#include "progress.h"
#include "decorate.h"
#include "fsck.h"
@@ -683,13 +679,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
use(the_hash_algo->rawsz);
/* Write the last part of the buffer to stdout */
- while (len) {
- int ret = xwrite(1, buffer + offset, len);
- if (ret <= 0)
- break;
- len -= ret;
- offset += ret;
- }
+ write_in_full(1, buffer + offset, len);
/* All done */
return has_errors;
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index c0c4e65e6f..e46afbc46d 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -7,11 +7,10 @@
#include "parse-options.h"
#include "quote.h"
#include "repository.h"
-#include "strvec.h"
static const char * const git_update_ref_usage[] = {
- N_("git update-ref [<options>] -d <refname> [<old-val>]"),
- N_("git update-ref [<options>] <refname> <new-val> [<old-val>]"),
+ N_("git update-ref [<options>] -d <refname> [<old-oid>]"),
+ N_("git update-ref [<options>] <refname> <new-oid> [<old-oid>]"),
N_("git update-ref [<options>] --stdin [-z]"),
NULL
};
@@ -78,14 +77,14 @@ static char *parse_refname(const char **next)
}
/*
- * The value being parsed is <oldvalue> (as opposed to <newvalue>; the
+ * The value being parsed is <old-oid> (as opposed to <new-oid>; the
* difference affects which error messages are generated):
*/
#define PARSE_SHA1_OLD 0x01
/*
* For backwards compatibility, accept an empty string for update's
- * <newvalue> in binary mode to be equivalent to specifying zeros.
+ * <new-oid> in binary mode to be equivalent to specifying zeros.
*/
#define PARSE_SHA1_ALLOW_EMPTY 0x02
@@ -141,7 +140,7 @@ static int parse_next_oid(const char **next, const char *end,
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
- warning("%s %s: missing <newvalue>, treating as zero",
+ warning("%s %s: missing <new-oid>, treating as zero",
command, refname);
oidclr(oid);
} else {
@@ -159,14 +158,14 @@ static int parse_next_oid(const char **next, const char *end,
invalid:
die(flags & PARSE_SHA1_OLD ?
- "%s %s: invalid <oldvalue>: %s" :
- "%s %s: invalid <newvalue>: %s",
+ "%s %s: invalid <old-oid>: %s" :
+ "%s %s: invalid <new-oid>: %s",
command, refname, arg.buf);
eof:
die(flags & PARSE_SHA1_OLD ?
- "%s %s: unexpected end of input when reading <oldvalue>" :
- "%s %s: unexpected end of input when reading <newvalue>",
+ "%s %s: unexpected end of input when reading <old-oid>" :
+ "%s %s: unexpected end of input when reading <new-oid>",
command, refname);
}
@@ -195,7 +194,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
if (parse_next_oid(&next, end, &new_oid, "update", refname,
PARSE_SHA1_ALLOW_EMPTY))
- die("update %s: missing <newvalue>", refname);
+ die("update %s: missing <new-oid>", refname);
have_old = !parse_next_oid(&next, end, &old_oid, "update", refname,
PARSE_SHA1_OLD);
@@ -226,10 +225,10 @@ static void parse_cmd_create(struct ref_transaction *transaction,
die("create: missing <ref>");
if (parse_next_oid(&next, end, &new_oid, "create", refname, 0))
- die("create %s: missing <newvalue>", refname);
+ die("create %s: missing <new-oid>", refname);
if (is_null_oid(&new_oid))
- die("create %s: zero <newvalue>", refname);
+ die("create %s: zero <new-oid>", refname);
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
@@ -261,7 +260,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
have_old = 0;
} else {
if (is_null_oid(&old_oid))
- die("delete %s: zero <oldvalue>", refname);
+ die("delete %s: zero <old-oid>", refname);
have_old = 1;
}
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 9b021ef026..15afb97260 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -8,6 +8,7 @@
#include "replace-object.h"
#include "upload-pack.h"
#include "serve.h"
+#include "commit.h"
static const char * const upload_pack_usage[] = {
N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -37,6 +38,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
packet_trace_identity("upload-pack");
disable_replace_refs();
+ save_commit_buffer = 0;
argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
diff --git a/builtin/var.c b/builtin/var.c
index 8cf7dd9e2e..cf5567208a 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -90,7 +90,7 @@ static char *git_config_val_global(int ident_flag UNUSED)
char *user, *xdg;
size_t unused;
- git_global_config(&user, &xdg);
+ git_global_config_paths(&user, &xdg);
if (xdg && *xdg) {
normalize_path_copy(xdg, xdg);
strbuf_addf(&buf, "%s\n", xdg);
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 9680b58701..0d2b9aea2a 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -9,10 +9,8 @@
#include "config.h"
#include "gettext.h"
#include "object-name.h"
-#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
-#include "run-command.h"
#include "parse-options.h"
#include "gpg-interface.h"
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index d8753270eb..c731e2f87b 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -9,7 +9,6 @@
#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"
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 62b7e26f4b..7c6c72536b 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -49,14 +49,14 @@
_("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 orphan branch\n" \
+ _("If you meant to create a worktree containing a new unborn branch\n" \
"(branch with no commits) for this repository, you can do so\n" \
"using the --orphan flag:\n" \
"\n" \
" git worktree add --orphan -b %s %s\n")
#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
- _("If you meant to create a worktree containing a new orphan branch\n" \
+ _("If you meant to create a worktree containing a new unborn branch\n" \
"(branch with no commits) for this repository, you can do so\n" \
"using the --orphan flag:\n" \
"\n" \
@@ -365,12 +365,12 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
bare &&
git_config_set_multivar_in_file_gently(
- to_file, "core.bare", NULL, "true", 0))
+ to_file, "core.bare", NULL, "true", NULL, 0))
error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file);
if (!git_configset_get(&cs, "core.worktree") &&
git_config_set_in_file_gently(to_file,
- "core.worktree", NULL))
+ "core.worktree", NULL, NULL))
error(_("failed to unset '%s' in '%s'"),
"core.worktree", to_file);
@@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
const char *name;
- struct child_process cp = CHILD_PROCESS_INIT;
struct strvec child_env = STRVEC_INIT;
unsigned int counter = 0;
int len, ret;
@@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
struct commit *commit = NULL;
int is_branch = 0;
struct strbuf sb_name = STRBUF_INIT;
- struct worktree **worktrees;
+ struct worktree **worktrees, *wt = NULL;
+ struct ref_store *wt_refs;
worktrees = get_worktrees();
check_candidate_path(path, opts->force, worktrees, "add");
@@ -495,21 +495,33 @@ static int add_worktree(const char *path, const char *refname,
strbuf_realpath(&realpath, get_git_common_dir(), 1);
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
realpath.buf, name);
- /*
- * This is to keep resolve_ref() happy. We need a valid HEAD
- * or is_git_directory() will reject the directory. Any value which
- * looks like an object ID will do since it will be immediately
- * replaced by the symbolic-ref or update-ref invocation in the new
- * worktree.
- */
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
- write_file(sb.buf, "%s", oid_to_hex(null_oid()));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
/*
+ * Set up the ref store of the worktree and create the HEAD reference.
+ */
+ wt = get_linked_worktree(name, 1);
+ if (!wt) {
+ ret = error(_("could not find created worktree '%s'"), name);
+ goto done;
+ }
+ wt_refs = get_worktree_ref_store(wt);
+
+ ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+ if (ret)
+ goto done;
+
+ if (!is_branch && commit)
+ ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
+ NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+ else
+ ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+ if (ret)
+ goto done;
+
+ /*
* If the current worktree has sparse-checkout enabled, then copy
* the sparse-checkout patterns from the current worktree.
*/
@@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
- cp.git_cmd = 1;
-
- if (!is_branch && commit) {
- strvec_pushl(&cp.args, "update-ref", "HEAD",
- oid_to_hex(&commit->object.oid), NULL);
- } else {
- strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
- symref.buf, NULL);
- if (opts->quiet)
- strvec_push(&cp.args, "--quiet");
- }
-
- strvec_pushv(&cp.env, child_env.v);
- ret = run_command(&cp);
- if (ret)
- goto done;
if (opts->orphan &&
(ret = make_worktree_orphan(refname, opts, &child_env)))
@@ -587,6 +583,7 @@ done:
strbuf_release(&sb_git);
strbuf_release(&sb_name);
strbuf_release(&realpath);
+ free_worktree(wt);
return ret;
}
@@ -660,7 +657,7 @@ static int can_use_local_refs(const struct add_opts *opts)
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_stripspace(&contents, NULL);
strbuf_strip_suffix(&contents, "\n");
warning(_("HEAD points to an invalid (or orphaned) reference.\n"
@@ -730,11 +727,11 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
}
if (opt_track) {
- die(_("'%s' and '%s' cannot be used together"), "--orphan",
- "--track");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--orphan", "--track");
} else if (!opts->checkout) {
- die(_("'%s' and '%s' cannot be used together"), "--orphan",
- "--no-checkout");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--orphan", "--no-checkout");
}
return 1;
}
@@ -784,7 +781,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/orphaned 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")),
@@ -806,16 +803,17 @@ static int add(int ac, const char **av, const char *prefix)
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"),
+ die(_("options '%s' and '%s' cannot be used together"),
"--orphan", "--detach");
if (opts.orphan && opt_track)
- die(_("'%s' and '%s' cannot be used together"), "--orphan", "--track");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--orphan", "--track");
if (opts.orphan && !opts.checkout)
- die(_("'%s' and '%s' cannot be used together"), "--orphan",
- "--no-checkout");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--orphan", "--no-checkout");
if (opts.orphan && ac == 2)
- die(_("'%s' and '%s' cannot be used together"), "--orphan",
- _("<commit-ish>"));
+ 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)
@@ -850,21 +848,21 @@ static int add(int ac, const char **av, const char *prefix)
const char *s = worktree_basename(path, &n);
new_branch = xstrndup(s, n);
} else if (opts.orphan) {
- // No-op
+ ; /* no-op */
} else if (opts.detach) {
- // Check HEAD
+ /* Check HEAD */
if (!strcmp(branch, "HEAD"))
can_use_local_refs(&opts);
} else if (ac < 2 && new_branch) {
- // DWIM: Infer --orphan when repo has no refs.
+ /* DWIM: Infer --orphan when repo has no refs. */
opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
} else if (ac < 2) {
- // DWIM: Guess branch name from path.
+ /* DWIM: Guess branch name from path. */
const char *s = dwim_branch(path, &new_branch);
if (s)
branch = s;
- // DWIM: Infer --orphan when repo has no refs.
+ /* DWIM: Infer --orphan when repo has no refs. */
opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
} else if (ac == 2) {
struct object_id oid;
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 6ce62999e5..eb46b88637 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -11,7 +11,6 @@
#include "csum-file.h"
#include "pack.h"
#include "strbuf.h"
-#include "string-list.h"
#include "tmp-objdir.h"
#include "packfile.h"
#include "object-file.h"
diff --git a/bundle-uri.c b/bundle-uri.c
index 8492fffd2f..ca32050a78 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -4,7 +4,6 @@
#include "copy.h"
#include "environment.h"
#include "gettext.h"
-#include "object-store-ll.h"
#include "refs.h"
#include "run-command.h"
#include "hashmap.h"
diff --git a/cache-tree.c b/cache-tree.c
index 641427ed41..387c0a3e5b 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -11,7 +11,6 @@
#include "read-cache-ll.h"
#include "replace-object.h"
#include "promisor-remote.h"
-#include "sparse-index.h"
#include "trace.h"
#include "trace2.h"
@@ -448,7 +447,7 @@ static int update_one(struct cache_tree *it,
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
OBJ_TREE, &it->oid);
} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
- &it->oid, flags & WRITE_TREE_SILENT
+ &it->oid, NULL, flags & WRITE_TREE_SILENT
? HASH_SILENT : 0)) {
strbuf_release(&buffer);
return -1;
@@ -770,7 +769,7 @@ static void prime_cache_tree_rec(struct repository *r,
oidcpy(&it->oid, &tree->object.oid);
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
if (!S_ISDIR(entry.mode))
@@ -779,8 +778,8 @@ static void prime_cache_tree_rec(struct repository *r,
struct cache_tree_sub *sub;
struct tree *subtree = lookup_tree(r, &entry.oid);
- if (!subtree->object.parsed)
- parse_tree(subtree);
+ if (parse_tree(subtree) < 0)
+ exit(128);
sub = cache_tree_sub(it, entry.path);
sub->cache_tree = cache_tree();
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 4f407530d3..b4e22de3cb 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -37,15 +37,13 @@ macos-*)
test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
- mkdir -p $HOME/bin
- (
- cd $HOME/bin
+
+ mkdir -p "$P4_PATH"
+ pushd "$P4_PATH"
wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
tar -xf helix-core-server.tgz &&
sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
- )
- PATH="$PATH:${HOME}/bin"
- export PATH
+ popd
if test -n "$CC_PACKAGE"
then
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
index 48c43f0f90..eb2c9e1eca 100755
--- a/ci/install-docker-dependencies.sh
+++ b/ci/install-docker-dependencies.sh
@@ -21,7 +21,7 @@ linux-musl)
apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
;;
-linux-*)
+linux-*|StaticAnalysis)
# Required so that apt doesn't wait for user input on certain packages.
export DEBIAN_FRONTEND=noninteractive
@@ -31,6 +31,11 @@ linux-*)
perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
apache2 cvs cvsps gnupg libcgi-pm-perl subversion
+
+ if test "$jobname" = StaticAnalysis
+ then
+ apt install -q -y coccinelle
+ fi
;;
pedantic)
dnf -yq update >/dev/null &&
diff --git a/ci/lib.sh b/ci/lib.sh
index c749b21366..0a73fc7bd1 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -252,7 +252,14 @@ then
CI_COMMIT="$CI_COMMIT_SHA"
case "$CI_JOB_IMAGE" in
macos-*)
- CI_OS_NAME=osx;;
+ # GitLab CI has Python installed via multiple package managers,
+ # most notably via asdf and Homebrew. Ensure that our builds
+ # pick up the Homebrew one by prepending it to our PATH as the
+ # asdf one breaks tests.
+ export PATH="$(brew --prefix)/bin:$PATH"
+
+ CI_OS_NAME=osx
+ ;;
alpine:*|fedora:*|ubuntu:*)
CI_OS_NAME=linux;;
*)
@@ -344,6 +351,9 @@ macos-*)
then
MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
fi
+
+ P4_PATH="$HOME/custom/p4"
+ export PATH="$P4_PATH:$PATH"
;;
esac
@@ -357,7 +367,7 @@ linux-musl)
MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes"
MAKEFLAGS="$MAKEFLAGS GIT_TEST_UTF8_LOCALE=C.UTF-8"
;;
-linux-leaks)
+linux-leaks|linux-reftable-leaks)
export SANITIZE=leak
export GIT_TEST_PASSING_SANITIZE_LEAK=true
export GIT_TEST_SANITIZE_LEAK_LOG=true
diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh
index c33ad4e3a2..b1f80aeac3 100755
--- a/ci/print-test-failures.sh
+++ b/ci/print-test-failures.sh
@@ -8,7 +8,7 @@
# Tracing executed commands would produce too much noise in the loop below.
set +x
-cd t/
+cd "${TEST_OUTPUT_DIRECTORY:-t/}"
if ! ls test-results/*.exit >/dev/null 2>/dev/null
then
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
new file mode 100755
index 0000000000..a51076d18d
--- /dev/null
+++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Build and test Git's fuzzers
+#
+
+. ${0%/*}/lib.sh
+
+group "Build fuzzers" make \
+ CC=clang \
+ CXX=clang++ \
+ CFLAGS="-fsanitize=fuzzer-no-link,address" \
+ LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
+ fuzz-all
+
+for fuzzer in commit-graph config date pack-headers pack-idx ; do
+ begin_group "fuzz-$fuzzer"
+ ./oss-fuzz/fuzz-$fuzzer -verbosity=0 -runs=1 || exit 1
+ end_group "fuzz-$fuzzer"
+done
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 7a1466b868..c192bd613c 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -37,6 +37,9 @@ linux-clang)
linux-sha256)
export GIT_TEST_DEFAULT_HASH=sha256
;;
+linux-reftable|linux-reftable-leaks|osx-reftable)
+ export GIT_TEST_DEFAULT_REF_FORMAT=reftable
+ ;;
pedantic)
# Don't run the tests; we only care about whether Git can be
# built.
diff --git a/column.c b/column.c
index ff2f0abf39..50bbccc92e 100644
--- a/column.c
+++ b/column.c
@@ -182,6 +182,8 @@ void print_columns(const struct string_list *list, unsigned int colopts,
{
struct column_options nopts;
+ if (opts && (0 > opts->padding))
+ BUG("padding must be non-negative");
if (!list->nr)
return;
assert((colopts & COL_ENABLE_MASK) != COL_AUTO);
@@ -361,6 +363,8 @@ int run_column_filter(int colopts, const struct column_options *opts)
{
struct strvec *argv;
+ if (opts && (0 > opts->padding))
+ BUG("padding must be non-negative");
if (fd_out != -1)
return -1;
diff --git a/combine-diff.c b/combine-diff.c
index f90f442482..d6d6fa1689 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -2,7 +2,6 @@
#include "object-store-ll.h"
#include "commit.h"
#include "convert.h"
-#include "blob.h"
#include "diff.h"
#include "diffcore.h"
#include "environment.h"
@@ -338,6 +337,8 @@ static char *grab_blob(struct repository *r,
free_filespec(df);
} else {
blob = repo_read_object_file(r, oid, &type, size);
+ if (!blob)
+ die(_("unable to read %s"), oid_to_hex(oid));
if (type != OBJ_BLOB)
die("object '%s' is not a blob!", oid_to_hex(oid));
}
diff --git a/command-list.txt b/command-list.txt
index 54b2a50f5f..c4cd0f352b 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -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
diff --git a/commit-graph.c b/commit-graph.c
index acac9bf6e1..45417d7412 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,14 +1,13 @@
#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-file.h"
@@ -807,6 +806,9 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
void close_commit_graph(struct raw_object_store *o)
{
+ if (!o->commit_graph)
+ return;
+
clear_commit_graph_data_slab(&commit_graph_data_slab);
free_commit_graph(o->commit_graph);
o->commit_graph = NULL;
@@ -1005,7 +1007,7 @@ struct commit *lookup_commit_in_graph(struct repository *repo, const struct obje
uint32_t pos;
if (commit_graph_paranoia == -1)
- commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
+ commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
if (!prepare_commit_graph(repo))
return NULL;
@@ -2617,19 +2619,16 @@ cleanup:
oid_array_clear(&ctx->oids);
clear_topo_level_slab(&topo_levels);
- if (ctx->commit_graph_filenames_after) {
- for (i = 0; i < ctx->num_commit_graphs_after; i++) {
- free(ctx->commit_graph_filenames_after[i]);
- free(ctx->commit_graph_hash_after[i]);
- }
-
- for (i = 0; i < ctx->num_commit_graphs_before; i++)
- free(ctx->commit_graph_filenames_before[i]);
+ for (i = 0; i < ctx->num_commit_graphs_before; i++)
+ free(ctx->commit_graph_filenames_before[i]);
+ free(ctx->commit_graph_filenames_before);
- free(ctx->commit_graph_filenames_after);
- free(ctx->commit_graph_filenames_before);
- free(ctx->commit_graph_hash_after);
+ for (i = 0; i < ctx->num_commit_graphs_after; i++) {
+ free(ctx->commit_graph_filenames_after[i]);
+ free(ctx->commit_graph_hash_after[i]);
}
+ free(ctx->commit_graph_filenames_after);
+ free(ctx->commit_graph_hash_after);
free(ctx);
diff --git a/commit-reach.c b/commit-reach.c
index a868a575ea..8f9b008f87 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -4,7 +4,6 @@
#include "decorate.h"
#include "hex.h"
#include "prio-queue.h"
-#include "tree.h"
#include "ref-filter.h"
#include "revision.h"
#include "tag.h"
@@ -50,13 +49,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
}
/* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct repository *r,
- struct commit *one, int n,
- struct commit **twos,
- timestamp_t min_generation)
+static int paint_down_to_common(struct repository *r,
+ struct commit *one, int n,
+ struct commit **twos,
+ timestamp_t min_generation,
+ int ignore_missing_commits,
+ struct commit_list **result)
{
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
- struct commit_list *result = NULL;
int i;
timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
@@ -65,8 +65,8 @@ static struct commit_list *paint_down_to_common(struct repository *r,
one->object.flags |= PARENT1;
if (!n) {
- commit_list_append(one, &result);
- return result;
+ commit_list_append(one, result);
+ return 0;
}
prio_queue_put(&queue, one);
@@ -94,7 +94,7 @@ static struct commit_list *paint_down_to_common(struct repository *r,
if (flags == (PARENT1 | PARENT2)) {
if (!(commit->object.flags & RESULT)) {
commit->object.flags |= RESULT;
- commit_list_insert_by_date(commit, &result);
+ commit_list_insert_by_date(commit, result);
}
/* Mark parents of a found merge stale */
flags |= STALE;
@@ -105,67 +105,97 @@ static struct commit_list *paint_down_to_common(struct repository *r,
parents = parents->next;
if ((p->object.flags & flags) == flags)
continue;
- if (repo_parse_commit(r, p))
- return NULL;
+ if (repo_parse_commit(r, p)) {
+ clear_prio_queue(&queue);
+ free_commit_list(*result);
+ *result = NULL;
+ /*
+ * At this stage, we know that the commit is
+ * missing: `repo_parse_commit()` uses
+ * `OBJECT_INFO_DIE_IF_CORRUPT` and therefore
+ * corrupt commits would already have been
+ * dispatched with a `die()`.
+ */
+ if (ignore_missing_commits)
+ return 0;
+ return error(_("could not parse commit %s"),
+ oid_to_hex(&p->object.oid));
+ }
p->object.flags |= flags;
prio_queue_put(&queue, p);
}
}
clear_prio_queue(&queue);
- return result;
+ return 0;
}
-static struct commit_list *merge_bases_many(struct repository *r,
- struct commit *one, int n,
- struct commit **twos)
+static int merge_bases_many(struct repository *r,
+ struct commit *one, int n,
+ struct commit **twos,
+ struct commit_list **result)
{
struct commit_list *list = NULL;
- struct commit_list *result = NULL;
int i;
for (i = 0; i < n; i++) {
- if (one == twos[i])
+ if (one == twos[i]) {
/*
* We do not mark this even with RESULT so we do not
* have to clean it up.
*/
- return commit_list_insert(one, &result);
+ *result = commit_list_insert(one, result);
+ return 0;
+ }
}
+ if (!one)
+ return 0;
if (repo_parse_commit(r, one))
- return NULL;
+ return error(_("could not parse commit %s"),
+ oid_to_hex(&one->object.oid));
for (i = 0; i < n; i++) {
+ if (!twos[i])
+ return 0;
if (repo_parse_commit(r, twos[i]))
- return NULL;
+ return error(_("could not parse commit %s"),
+ oid_to_hex(&twos[i]->object.oid));
}
- list = paint_down_to_common(r, one, n, twos, 0);
+ if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) {
+ free_commit_list(list);
+ return -1;
+ }
while (list) {
struct commit *commit = pop_commit(&list);
if (!(commit->object.flags & STALE))
- commit_list_insert_by_date(commit, &result);
+ commit_list_insert_by_date(commit, result);
}
- return result;
+ return 0;
}
-struct commit_list *get_octopus_merge_bases(struct commit_list *in)
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result)
{
- struct commit_list *i, *j, *k, *ret = NULL;
+ struct commit_list *i, *j, *k;
if (!in)
- return ret;
+ return 0;
- commit_list_insert(in->item, &ret);
+ commit_list_insert(in->item, result);
for (i = in->next; i; i = i->next) {
struct commit_list *new_commits = NULL, *end = NULL;
- for (j = ret; j; j = j->next) {
- struct commit_list *bases;
- bases = repo_get_merge_bases(the_repository, i->item,
- j->item);
+ for (j = *result; j; j = j->next) {
+ struct commit_list *bases = NULL;
+ if (repo_get_merge_bases(the_repository, i->item,
+ j->item, &bases) < 0) {
+ free_commit_list(bases);
+ free_commit_list(*result);
+ *result = NULL;
+ return -1;
+ }
if (!new_commits)
new_commits = bases;
else
@@ -173,10 +203,10 @@ 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;
+ free_commit_list(*result);
+ *result = new_commits;
}
- return ret;
+ return 0;
}
static int remove_redundant_no_gen(struct repository *r,
@@ -194,7 +224,7 @@ static int remove_redundant_no_gen(struct repository *r,
for (i = 0; i < cnt; i++)
repo_parse_commit(r, array[i]);
for (i = 0; i < cnt; i++) {
- struct commit_list *common;
+ struct commit_list *common = NULL;
timestamp_t min_generation = commit_graph_generation(array[i]);
if (redundant[i])
@@ -210,8 +240,16 @@ static int remove_redundant_no_gen(struct repository *r,
if (curr_generation < min_generation)
min_generation = curr_generation;
}
- common = paint_down_to_common(r, array[i], filled,
- work, min_generation);
+ if (paint_down_to_common(r, array[i], filled,
+ work, min_generation, 0, &common)) {
+ clear_commit_marks(array[i], all_flags);
+ clear_commit_marks_many(filled, work, all_flags);
+ free_commit_list(common);
+ free(work);
+ free(redundant);
+ free(filled_index);
+ return -1;
+ }
if (array[i]->object.flags & PARENT2)
redundant[i] = 1;
for (j = 0; j < filled; j++)
@@ -376,69 +414,77 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
return remove_redundant_no_gen(r, array, cnt);
}
-static struct commit_list *get_merge_bases_many_0(struct repository *r,
- struct commit *one,
- int n,
- struct commit **twos,
- int cleanup)
+static int get_merge_bases_many_0(struct repository *r,
+ struct commit *one,
+ int n,
+ struct commit **twos,
+ int cleanup,
+ struct commit_list **result)
{
struct commit_list *list;
struct commit **rslt;
- struct commit_list *result;
int cnt, i;
- result = merge_bases_many(r, one, n, twos);
+ if (merge_bases_many(r, one, n, twos, result) < 0)
+ return -1;
for (i = 0; i < n; i++) {
if (one == twos[i])
- return result;
+ return 0;
}
- if (!result || !result->next) {
+ if (!*result || !(*result)->next) {
if (cleanup) {
clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags);
}
- return result;
+ return 0;
}
/* There are more than one */
- cnt = commit_list_count(result);
+ cnt = commit_list_count(*result);
CALLOC_ARRAY(rslt, cnt);
- for (list = result, i = 0; list; list = list->next)
+ for (list = *result, i = 0; list; list = list->next)
rslt[i++] = list->item;
- free_commit_list(result);
+ free_commit_list(*result);
+ *result = NULL;
clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags);
cnt = remove_redundant(r, rslt, cnt);
- result = NULL;
+ if (cnt < 0) {
+ free(rslt);
+ return -1;
+ }
for (i = 0; i < cnt; i++)
- commit_list_insert_by_date(rslt[i], &result);
+ commit_list_insert_by_date(rslt[i], result);
free(rslt);
- return result;
+ return 0;
}
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
- struct commit *one,
- int n,
- struct commit **twos)
+int repo_get_merge_bases_many(struct repository *r,
+ struct commit *one,
+ int n,
+ struct commit **twos,
+ struct commit_list **result)
{
- return get_merge_bases_many_0(r, one, n, twos, 1);
+ return get_merge_bases_many_0(r, one, n, twos, 1, result);
}
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
- struct commit *one,
- int n,
- struct commit **twos)
+int repo_get_merge_bases_many_dirty(struct repository *r,
+ struct commit *one,
+ int n,
+ struct commit **twos,
+ struct commit_list **result)
{
- return get_merge_bases_many_0(r, one, n, twos, 0);
+ return get_merge_bases_many_0(r, one, n, twos, 0, result);
}
-struct commit_list *repo_get_merge_bases(struct repository *r,
- struct commit *one,
- struct commit *two)
+int repo_get_merge_bases(struct repository *r,
+ struct commit *one,
+ struct commit *two,
+ struct commit_list **result)
{
- return get_merge_bases_many_0(r, one, 1, &two, 1);
+ return get_merge_bases_many_0(r, one, 1, &two, 1, result);
}
/*
@@ -461,11 +507,13 @@ int repo_is_descendant_of(struct repository *r,
} else {
while (with_commit) {
struct commit *other;
+ int ret;
other = with_commit->item;
with_commit = with_commit->next;
- if (repo_in_merge_bases_many(r, other, 1, &commit))
- return 1;
+ ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
+ if (ret)
+ return ret;
}
return 0;
}
@@ -475,17 +523,18 @@ int repo_is_descendant_of(struct repository *r,
* Is "commit" an ancestor of one of the "references"?
*/
int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
- int nr_reference, struct commit **reference)
+ int nr_reference, struct commit **reference,
+ int ignore_missing_commits)
{
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
int ret = 0, i;
timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
if (repo_parse_commit(r, commit))
- return ret;
+ return ignore_missing_commits ? 0 : -1;
for (i = 0; i < nr_reference; i++) {
if (repo_parse_commit(r, reference[i]))
- return ret;
+ return ignore_missing_commits ? 0 : -1;
generation = commit_graph_generation(reference[i]);
if (generation > max_generation)
@@ -496,10 +545,11 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
if (generation > max_generation)
return ret;
- bases = paint_down_to_common(r, commit,
- nr_reference, reference,
- generation);
- if (commit->object.flags & PARENT2)
+ if (paint_down_to_common(r, commit,
+ nr_reference, reference,
+ generation, ignore_missing_commits, &bases))
+ ret = -1;
+ else if (commit->object.flags & PARENT2)
ret = 1;
clear_commit_marks(commit, all_flags);
clear_commit_marks_many(nr_reference, reference, all_flags);
@@ -552,6 +602,10 @@ struct commit_list *reduce_heads(struct commit_list *heads)
}
}
num_head = remove_redundant(the_repository, array, num_head);
+ if (num_head < 0) {
+ free(array);
+ return NULL;
+ }
for (i = 0; i < num_head; i++)
tail = &commit_list_insert(array[i], tail)->next;
free(array);
@@ -594,6 +648,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
commit_list_insert(old_commit, &old_commit_list);
ret = repo_is_descendant_of(the_repository,
new_commit, old_commit_list);
+ if (ret < 0)
+ exit(128);
free_commit_list(old_commit_list);
return ret;
}
diff --git a/commit-reach.h b/commit-reach.h
index 35c4da4948..bf63cc468f 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -9,18 +9,21 @@ struct ref_filter;
struct object_id;
struct object_array;
-struct commit_list *repo_get_merge_bases(struct repository *r,
- struct commit *rev1,
- struct commit *rev2);
-struct commit_list *repo_get_merge_bases_many(struct repository *r,
- struct commit *one, int n,
- struct commit **twos);
+int repo_get_merge_bases(struct repository *r,
+ struct commit *rev1,
+ struct commit *rev2,
+ struct commit_list **result);
+int repo_get_merge_bases_many(struct repository *r,
+ struct commit *one, int n,
+ struct commit **twos,
+ struct commit_list **result);
/* To be used only when object flags after this call no longer matter */
-struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
- struct commit *one, int n,
- struct commit **twos);
+int repo_get_merge_bases_many_dirty(struct repository *r,
+ struct commit *one, int n,
+ struct commit **twos,
+ struct commit_list **result);
-struct commit_list *get_octopus_merge_bases(struct commit_list *in);
+int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result);
int repo_is_descendant_of(struct repository *r,
struct commit *commit,
@@ -30,7 +33,8 @@ int repo_in_merge_bases(struct repository *r,
struct commit *reference);
int repo_in_merge_bases_many(struct repository *r,
struct commit *commit,
- int nr_reference, struct commit **reference);
+ int nr_reference, struct commit **reference,
+ int ignore_missing_commits);
/*
* Takes a list of commits and returns a new list where those
diff --git a/commit.c b/commit.c
index 8405d7c3fc..1a479a997c 100644
--- a/commit.c
+++ b/commit.c
@@ -8,7 +8,6 @@
#include "repository.h"
#include "object-name.h"
#include "object-store-ll.h"
-#include "pkt-line.h"
#include "utf8.h"
#include "diff.h"
#include "revision.h"
@@ -23,12 +22,12 @@
#include "advice.h"
#include "refs.h"
#include "commit-reach.h"
-#include "run-command.h"
#include "setup.h"
#include "shallow.h"
#include "tree.h"
#include "hook.h"
#include "parse.h"
+#include "object-file-convert.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -577,7 +576,7 @@ int repo_parse_commit_internal(struct repository *r,
static int commit_graph_paranoia = -1;
if (commit_graph_paranoia == -1)
- commit_graph_paranoia = git_env_bool(GIT_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);
@@ -1054,7 +1053,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
{
struct object_id oid;
struct rev_collect revs;
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
int i;
struct commit *ret = NULL;
char *full_refname;
@@ -1079,8 +1078,9 @@ 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 = repo_get_merge_bases_many(the_repository, commit, revs.nr,
- revs.commit);
+ if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
+ revs.commit, &bases) < 0)
+ exit(128);
/*
* There should be one and only one merge base, when we found
@@ -1114,12 +1114,11 @@ static const char *gpg_sig_headers[] = {
"gpgsig-sha256",
};
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
{
- struct strbuf sig = STRBUF_INIT;
int inspos, copypos;
const char *eoh;
- const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+ const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
int gpg_sig_header_len = strlen(gpg_sig_header);
/* find the end of the header */
@@ -1129,15 +1128,8 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
else
inspos = eoh - buf->buf + 1;
- if (!keyid || !*keyid)
- keyid = get_signing_key();
- if (sign_buffer(buf, &sig, keyid)) {
- strbuf_release(&sig);
- return -1;
- }
-
- for (copypos = 0; sig.buf[copypos]; ) {
- const char *bol = sig.buf + copypos;
+ for (copypos = 0; sig->buf[copypos]; ) {
+ const char *bol = sig->buf + copypos;
const char *eol = strchrnul(bol, '\n');
int len = (eol - bol) + !!*eol;
@@ -1150,11 +1142,17 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
inspos += len;
copypos += len;
}
- strbuf_release(&sig);
return 0;
}
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+ if (!keyid || !*keyid)
+ keyid = get_signing_key();
+ if (sign_buffer(buf, sig, keyid))
+ return -1;
+ return 0;
+}
int parse_signed_commit(const struct commit *commit,
struct strbuf *payload, struct strbuf *signature,
@@ -1370,6 +1368,39 @@ void append_merge_tag_headers(struct commit_list *parents,
}
}
+static int convert_commit_extra_headers(struct commit_extra_header *orig,
+ struct commit_extra_header **result)
+{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ const struct git_hash_algo *algo = the_repository->hash_algo;
+ struct commit_extra_header *extra = NULL, **tail = &extra;
+ struct strbuf out = STRBUF_INIT;
+ while (orig) {
+ struct commit_extra_header *new;
+ CALLOC_ARRAY(new, 1);
+ if (!strcmp(orig->key, "mergetag")) {
+ if (convert_object_file(&out, algo, compat,
+ orig->value, orig->len,
+ OBJ_TAG, 1)) {
+ free(new);
+ free_commit_extra_headers(extra);
+ return -1;
+ }
+ new->key = xstrdup("mergetag");
+ new->value = strbuf_detach(&out, &new->len);
+ } else {
+ new->key = xstrdup(orig->key);
+ new->len = orig->len;
+ new->value = xmemdupz(orig->value, orig->len);
+ }
+ *tail = new;
+ tail = &new->next;
+ orig = orig->next;
+ }
+ *result = extra;
+ return 0;
+}
+
static void add_extra_header(struct strbuf *buffer,
struct commit_extra_header *extra)
{
@@ -1613,70 +1644,169 @@ N_("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");
-int commit_tree_extended(const char *msg, size_t msg_len,
- const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
- const char *author, const char *committer,
- const char *sign_commit,
- struct commit_extra_header *extra)
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ const struct object_id *parents, size_t parents_len,
+ const char *author, const char *committer,
+ struct commit_extra_header *extra)
{
- int result;
int encoding_is_utf8;
- struct strbuf buffer;
-
- assert_oid_type(tree, OBJ_TREE);
-
- if (memchr(msg, '\0', msg_len))
- return error("a NUL byte in commit log message not allowed.");
+ size_t i;
/* Not having i18n.commitencoding is the same as having utf-8 */
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
+ strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
/*
* NOTE! This ordering means that the same exact tree merged with a
* different order of parents will be a _different_ changeset even
* if everything else stays the same.
*/
- while (parents) {
- struct commit *parent = pop_commit(&parents);
- strbuf_addf(&buffer, "parent %s\n",
- oid_to_hex(&parent->object.oid));
- }
+ for (i = 0; i < parents_len; i++)
+ strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
/* Person/date information */
if (!author)
author = git_author_info(IDENT_STRICT);
- strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(buffer, "author %s\n", author);
if (!committer)
committer = git_committer_info(IDENT_STRICT);
- strbuf_addf(&buffer, "committer %s\n", committer);
+ strbuf_addf(buffer, "committer %s\n", committer);
if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+ strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
while (extra) {
- add_extra_header(&buffer, extra);
+ add_extra_header(buffer, extra);
extra = extra->next;
}
- strbuf_addch(&buffer, '\n');
+ strbuf_addch(buffer, '\n');
/* And add the comment */
- strbuf_add(&buffer, msg, msg_len);
+ strbuf_add(buffer, msg, msg_len);
+}
- /* And check the encoding */
- if (encoding_is_utf8 && !verify_utf8(&buffer))
- fprintf(stderr, _(commit_utf8_warn));
+int commit_tree_extended(const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ struct commit_list *parents, struct object_id *ret,
+ const char *author, const char *committer,
+ const char *sign_commit,
+ struct commit_extra_header *extra)
+{
+ struct repository *r = the_repository;
+ int result = 0;
+ int encoding_is_utf8;
+ struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
+ struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+ struct object_id *parent_buf = NULL, *compat_oid = NULL;
+ struct object_id compat_oid_buf;
+ size_t i, nparents;
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ assert_oid_type(tree, OBJ_TREE);
- if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+ if (memchr(msg, '\0', msg_len))
+ return error("a NUL byte in commit log message not allowed.");
+
+ nparents = commit_list_count(parents);
+ CALLOC_ARRAY(parent_buf, nparents);
+ i = 0;
+ while (parents) {
+ struct commit *parent = pop_commit(&parents);
+ oidcpy(&parent_buf[i++], &parent->object.oid);
+ }
+
+ write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+ if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
result = -1;
goto out;
}
+ if (r->compat_hash_algo) {
+ struct commit_extra_header *compat_extra = NULL;
+ struct object_id mapped_tree;
+ struct object_id *mapped_parents;
+
+ CALLOC_ARRAY(mapped_parents, nparents);
- result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+ if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ for (i = 0; i < nparents; i++)
+ if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ if (convert_commit_extra_headers(extra, &compat_extra)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+ mapped_parents, nparents, author, committer, compat_extra);
+ free_commit_extra_headers(compat_extra);
+ free(mapped_parents);
+
+ if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+ result = -1;
+ goto out;
+ }
+ }
+
+ if (sign_commit) {
+ struct sig_pairs {
+ struct strbuf *sig;
+ const struct git_hash_algo *algo;
+ } bufs [2] = {
+ { &compat_sig, r->compat_hash_algo },
+ { &sig, r->hash_algo },
+ };
+ int i;
+
+ /*
+ * We write algorithms in the order they were implemented in
+ * Git to produce a stable hash when multiple algorithms are
+ * used.
+ */
+ if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+ SWAP(bufs[0], bufs[1]);
+
+ /*
+ * We traverse each algorithm in order, and apply the signature
+ * to each buffer.
+ */
+ for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+ if (!bufs[i].algo)
+ continue;
+ add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+ if (r->compat_hash_algo)
+ add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+ }
+ }
+
+ /* And check the encoding. */
+ if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
+ fprintf(stderr, _(commit_utf8_warn));
+
+ if (r->compat_hash_algo) {
+ hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
+ OBJ_COMMIT, &compat_oid_buf);
+ compat_oid = &compat_oid_buf;
+ }
+
+ result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+ ret, compat_oid, 0);
out:
+ free(parent_buf);
strbuf_release(&buffer);
+ strbuf_release(&compat_buffer);
+ strbuf_release(&sig);
+ strbuf_release(&compat_sig);
return result;
}
@@ -1783,7 +1913,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;
@@ -1798,7 +1928,8 @@ size_t ignore_non_trailer(const char *buf, size_t len)
else
next_line++;
- if (buf[bol] == comment_line_char || buf[bol] == '\n') {
+ if (starts_with_mem(buf + bol, cutoff - bol, comment_line_str) ||
+ buf[bol] == '\n') {
/* is this the first of the run of comments? */
if (!boc)
boc = bol;
diff --git a/commit.h b/commit.h
index 28928833c5..62fe0d77a7 100644
--- a/commit.h
+++ b/commit.h
@@ -294,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);
@@ -370,5 +370,6 @@ int parse_buffer_signed_by_header(const char *buffer,
struct strbuf *payload,
struct strbuf *signature,
const struct git_hash_algo *algop);
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo);
#endif /* COMMIT_H */
diff --git a/compat/compiler.h b/compat/compiler.h
index 10dbb65937..e9ad9db84f 100644
--- a/compat/compiler.h
+++ b/compat/compiler.h
@@ -1,7 +1,6 @@
#ifndef COMPILER_H
#define COMPILER_H
-#include "git-compat-util.h"
#include "strbuf.h"
#ifdef __GLIBC__
diff --git a/compat/disk.h b/compat/disk.h
index 6c979c27d8..23bc1bef86 100644
--- a/compat/disk.h
+++ b/compat/disk.h
@@ -1,7 +1,6 @@
#ifndef COMPAT_DISK_H
#define COMPAT_DISK_H
-#include "git-compat-util.h"
#include "abspath.h"
#include "gettext.h"
diff --git a/compat/fsmonitor/fsm-health-win32.c b/compat/fsmonitor/fsm-health-win32.c
index 2d4e245beb..2aa8c219ac 100644
--- a/compat/fsmonitor/fsm-health-win32.c
+++ b/compat/fsmonitor/fsm-health-win32.c
@@ -4,6 +4,7 @@
#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-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index 11b56d3ef1..2fc67442eb 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -29,6 +29,7 @@
#include "fsmonitor--daemon.h"
#include "fsmonitor-path-utils.h"
#include "gettext.h"
+#include "simple-ipc.h"
#include "string-list.h"
#include "trace.h"
diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c
index 90a2412284..5a21dade7b 100644
--- a/compat/fsmonitor/fsm-listen-win32.c
+++ b/compat/fsmonitor/fsm-listen-win32.c
@@ -4,6 +4,7 @@
#include "fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "gettext.h"
+#include "simple-ipc.h"
#include "trace2.h"
/*
diff --git a/compat/mingw.c b/compat/mingw.c
index ec5280da16..4876344b5b 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -255,6 +255,8 @@ int mingw_core_config(const char *var, const char *value,
}
if (!strcmp(var, "core.unsetenvvars")) {
+ if (!value)
+ return config_error_nonbool(var);
free(unset_environment_variables);
unset_environment_variables = xstrdup(value);
return 0;
@@ -705,13 +707,24 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
{
ssize_t result = write(fd, buf, len);
- if (result < 0 && errno == EINVAL && buf) {
+ if (result < 0 && (errno == EINVAL || errno == ENOSPC) && buf) {
+ int orig = errno;
+
/* check if fd is a pipe */
HANDLE h = (HANDLE) _get_osfhandle(fd);
- if (GetFileType(h) == FILE_TYPE_PIPE)
+ if (GetFileType(h) != FILE_TYPE_PIPE)
+ errno = orig;
+ else if (orig == EINVAL)
errno = EPIPE;
- else
- errno = EINVAL;
+ else {
+ DWORD buf_size;
+
+ if (!GetNamedPipeInfo(h, NULL, NULL, &buf_size, NULL))
+ buf_size = 4096;
+ if (len > buf_size)
+ return write(fd, buf, buf_size);
+ errno = orig;
+ }
}
return result;
@@ -2682,6 +2695,30 @@ static PSID get_current_user_sid(void)
return result;
}
+static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
+{
+ SID_NAME_USE pe_use;
+ DWORD len_user = 0, len_domain = 0;
+ BOOL translate_sid_to_user;
+
+ /*
+ * returns only FALSE, because the string pointers are NULL
+ */
+ LookupAccountSidA(NULL, sid, NULL, &len_user, NULL, &len_domain,
+ &pe_use);
+ /*
+ * Alloc needed space of the strings
+ */
+ ALLOC_ARRAY((*str), (size_t)len_domain + (size_t)len_user);
+ translate_sid_to_user = LookupAccountSidA(NULL, sid,
+ (*str) + len_domain, &len_user, *str, &len_domain, &pe_use);
+ if (!translate_sid_to_user)
+ FREE_AND_NULL(*str);
+ else
+ (*str)[len_domain] = '/';
+ return translate_sid_to_user;
+}
+
static int acls_supported(const char *path)
{
size_t offset = offset_1st_component(path);
@@ -2763,27 +2800,47 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
strbuf_addf(report, "'%s' is on a file system that does "
"not record ownership\n", path);
} else if (report) {
- LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
+ LPSTR str1, str2, str3, str4, to_free1 = NULL,
+ to_free3 = NULL, to_local_free2 = NULL,
+ to_local_free4 = NULL;
- if (ConvertSidToStringSidA(sid, &str1))
+ if (user_sid_to_user_name(sid, &str1))
to_free1 = str1;
else
str1 = "(inconvertible)";
-
- if (!current_user_sid)
- str2 = "(none)";
- else if (!IsValidSid(current_user_sid))
- str2 = "(invalid)";
- else if (ConvertSidToStringSidA(current_user_sid, &str2))
- to_free2 = str2;
+ if (ConvertSidToStringSidA(sid, &str2))
+ to_local_free2 = str2;
else
str2 = "(inconvertible)";
+
+ if (!current_user_sid) {
+ str3 = "(none)";
+ str4 = "(none)";
+ }
+ else if (!IsValidSid(current_user_sid)) {
+ str3 = "(invalid)";
+ str4 = "(invalid)";
+ } else {
+ if (user_sid_to_user_name(current_user_sid,
+ &str3))
+ to_free3 = str3;
+ else
+ str3 = "(inconvertible)";
+ if (ConvertSidToStringSidA(current_user_sid,
+ &str4))
+ to_local_free4 = str4;
+ else
+ str4 = "(inconvertible)";
+ }
strbuf_addf(report,
"'%s' is owned by:\n"
- "\t'%s'\nbut the current user is:\n"
- "\t'%s'\n", path, str1, str2);
- LocalFree(to_free1);
- LocalFree(to_free2);
+ "\t%s (%s)\nbut the current user is:\n"
+ "\t%s (%s)\n",
+ path, str1, str2, str3, str4);
+ free(to_free1);
+ LocalFree(to_local_free2);
+ free(to_free3);
+ LocalFree(to_local_free4);
}
}
@@ -3101,3 +3158,22 @@ int uname(struct utsname *buf)
"%u", (v >> 16) & 0x7fff);
return 0;
}
+
+int mingw_have_unix_sockets(void)
+{
+ SC_HANDLE scm, srvc;
+ SERVICE_STATUS_PROCESS status;
+ DWORD bytes;
+ int ret = 0;
+ scm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
+ if (scm) {
+ srvc = OpenServiceA(scm, "afunix", SERVICE_QUERY_STATUS);
+ if (srvc) {
+ if(QueryServiceStatusEx(srvc, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes))
+ ret = status.dwCurrentState == SERVICE_RUNNING;
+ CloseServiceHandle(srvc);
+ }
+ CloseServiceHandle(scm);
+ }
+ return ret;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 6aec50e412..27b61284f4 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -631,3 +631,9 @@ void open_in_gdb(void);
* Used by Pthread API implementation for Windows
*/
int err_win_to_posix(DWORD winerr);
+
+#ifndef NO_UNIX_SOCKETS
+int mingw_have_unix_sockets(void);
+#undef have_unix_sockets
+#define have_unix_sockets mingw_have_unix_sockets
+#endif
diff --git a/compat/simple-ipc/ipc-shared.c b/compat/simple-ipc/ipc-shared.c
index e5e1dda8cc..cb176d966f 100644
--- a/compat/simple-ipc/ipc-shared.c
+++ b/compat/simple-ipc/ipc-shared.c
@@ -1,8 +1,5 @@
#include "git-compat-util.h"
#include "simple-ipc.h"
-#include "strbuf.h"
-#include "pkt-line.h"
-#include "thread-utils.h"
#ifndef SUPPORTS_SIMPLE_IPC
/*
diff --git a/compat/simple-ipc/ipc-unix-socket.c b/compat/simple-ipc/ipc-unix-socket.c
index b2f4f22ce4..9b3f2cdf8c 100644
--- a/compat/simple-ipc/ipc-unix-socket.c
+++ b/compat/simple-ipc/ipc-unix-socket.c
@@ -2,7 +2,6 @@
#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"
diff --git a/config.c b/config.c
index b330c7adb4..ae3652b08f 100644
--- a/config.c
+++ b/config.c
@@ -30,15 +30,12 @@
#include "pager.h"
#include "path.h"
#include "utf8.h"
-#include "dir.h"
#include "color.h"
-#include "replace-object.h"
#include "refs.h"
#include "setup.h"
#include "strvec.h"
#include "trace2.h"
#include "wildmatch.h"
-#include "worktree.h"
#include "ws.h"
#include "write-or-die.h"
@@ -98,7 +95,6 @@ static long config_file_ftell(struct config_source *conf)
return ftell(conf->u.file);
}
-
static int config_buf_fgetc(struct config_source *conf)
{
if (conf->u.buf.pos < conf->u.buf.len)
@@ -821,7 +817,8 @@ static int get_next_char(struct config_source *cs)
static char *parse_value(struct config_source *cs)
{
- int quote = 0, comment = 0, space = 0;
+ int quote = 0, comment = 0;
+ size_t trim_len = 0;
strbuf_reset(&cs->value);
for (;;) {
@@ -831,13 +828,17 @@ static char *parse_value(struct config_source *cs)
cs->linenr--;
return NULL;
}
+ if (trim_len)
+ strbuf_setlen(&cs->value, trim_len);
return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
+ if (!trim_len)
+ trim_len = cs->value.len;
if (cs->value.len)
- space++;
+ strbuf_addch(&cs->value, c);
continue;
}
if (!quote) {
@@ -846,8 +847,8 @@ static char *parse_value(struct config_source *cs)
continue;
}
}
- for (; space; space--)
- strbuf_addch(&cs->value, ' ');
+ if (trim_len)
+ trim_len = 0;
if (c == '\\') {
c = get_next_char(cs);
switch (c) {
@@ -873,7 +874,7 @@ static char *parse_value(struct config_source *cs)
continue;
}
if (c == '"') {
- quote = 1-quote;
+ quote = 1 - quote;
continue;
}
strbuf_addch(&cs->value, c);
@@ -1386,10 +1387,15 @@ static int git_default_core_config(const char *var, const char *value,
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")) {
@@ -1546,12 +1552,12 @@ static int git_default_core_config(const char *var, const char *value,
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;
}
@@ -1559,24 +1565,29 @@ static int git_default_core_config(const char *var, const char *value,
if (!strcmp(var, "core.editor"))
return git_config_string(&editor_program, var, value);
- if (!strcmp(var, "core.commentchar")) {
+ if (!strcmp(var, "core.commentchar") ||
+ !strcmp(var, "core.commentstring")) {
if (!value)
return config_error_nonbool(var);
else if (!strcasecmp(value, "auto"))
auto_comment_line_char = 1;
- else if (value[0] && !value[1]) {
- comment_line_char = value[0];
+ else if (value[0]) {
+ if (strchr(value, '\n'))
+ return error(_("%s cannot contain newline"), var);
+ comment_line_str = xstrdup(value);
auto_comment_line_char = 0;
} else
- return error(_("core.commentChar should only be one ASCII character"));
+ return error(_("%s must have at least one character"), var);
return 0;
}
if (!strcmp(var, "core.askpass"))
return git_config_string(&askpass_program, var, value);
- if (!strcmp(var, "core.excludesfile"))
+ if (!strcmp(var, "core.excludesfile")) {
+ free((char *)excludes_file);
return git_config_pathname(&excludes_file, var, value);
+ }
if (!strcmp(var, "core.whitespace")) {
if (!value)
@@ -1619,6 +1630,8 @@ static int git_default_core_config(const char *var, const char *value,
}
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"))
@@ -1984,7 +1997,27 @@ char *git_system_config(void)
return system_config;
}
-void git_global_config(char **user_out, char **xdg_out)
+char *git_global_config(void)
+{
+ char *user_config, *xdg_config;
+
+ git_global_config_paths(&user_config, &xdg_config);
+ if (!user_config) {
+ free(xdg_config);
+ return NULL;
+ }
+
+ if (access_or_warn(user_config, R_OK, 0) && xdg_config &&
+ !access_or_warn(xdg_config, R_OK, 0)) {
+ free(user_config);
+ return xdg_config;
+ } else {
+ free(xdg_config);
+ return user_config;
+ }
+}
+
+void git_global_config_paths(char **user_out, char **xdg_out)
{
char *user_config = xstrdup_or_null(getenv("GIT_CONFIG_GLOBAL"));
char *xdg_config = NULL;
@@ -2037,7 +2070,7 @@ static int do_git_config_sequence(const struct config_options *opts,
data, CONFIG_SCOPE_SYSTEM,
NULL);
- git_global_config(&user_config, &xdg_config);
+ git_global_config_paths(&user_config, &xdg_config);
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file_with_options(fn, xdg_config, data,
@@ -2978,6 +3011,7 @@ static ssize_t write_section(int fd, const char *key,
}
static ssize_t write_pair(int fd, const char *key, const char *value,
+ const char *comment,
const struct config_store_data *store)
{
int i;
@@ -3018,7 +3052,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
strbuf_addch(&sb, value[i]);
break;
}
- strbuf_addf(&sb, "%s\n", quote);
+
+ if (comment)
+ strbuf_addf(&sb, "%s%s\n", quote, comment);
+ else
+ strbuf_addf(&sb, "%s\n", quote);
ret = write_in_full(fd, sb.buf, sb.len);
strbuf_release(&sb);
@@ -3107,9 +3145,9 @@ static void maybe_remove_section(struct config_store_data *store,
}
int git_config_set_in_file_gently(const char *config_filename,
- const char *key, const char *value)
+ const char *key, const char *comment, const char *value)
{
- return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
+ return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
}
void git_config_set_in_file(const char *config_filename,
@@ -3130,7 +3168,7 @@ int repo_config_set_worktree_gently(struct repository *r,
if (r->repository_format_worktree_config) {
char *file = repo_git_path(r, "config.worktree");
int ret = git_config_set_multivar_in_file_gently(
- file, key, value, NULL, 0);
+ file, key, value, NULL, NULL, 0);
free(file);
return ret;
}
@@ -3145,6 +3183,62 @@ void git_config_set(const char *key, const char *value)
}
/*
+ * The ownership rule is that the caller will own the string
+ * if it receives a piece of memory different from what it passed
+ * as the parameter.
+ */
+const char *git_config_prepare_comment_string(const char *comment)
+{
+ size_t leading_blanks;
+
+ if (!comment)
+ return NULL;
+
+ if (strchr(comment, '\n'))
+ die(_("no multi-line comment allowed: '%s'"), comment);
+
+ /*
+ * If it begins with one or more leading whitespace characters
+ * followed by '#", the comment string is used as-is.
+ *
+ * If it begins with '#', a SP is inserted between the comment
+ * and the value the comment is about.
+ *
+ * Otherwise, the value is followed by a SP followed by '#'
+ * followed by SP and then the comment string comes.
+ */
+
+ leading_blanks = strspn(comment, " \t");
+ if (leading_blanks && comment[leading_blanks] == '#')
+ ; /* use it as-is */
+ else if (comment[0] == '#')
+ comment = xstrfmt(" %s", comment);
+ else
+ comment = xstrfmt(" # %s", comment);
+
+ return comment;
+}
+
+static void validate_comment_string(const char *comment)
+{
+ size_t leading_blanks;
+
+ if (!comment)
+ return;
+ /*
+ * The front-end must have massaged the comment string
+ * properly before calling us.
+ */
+ if (strchr(comment, '\n'))
+ BUG("multi-line comments are not permitted: '%s'", comment);
+
+ leading_blanks = strspn(comment, " \t");
+ if (!leading_blanks || comment[leading_blanks] != '#')
+ BUG("comment must begin with one or more SP followed by '#': '%s'",
+ comment);
+}
+
+/*
* If value==NULL, unset in (remove from) config,
* if value_pattern!=NULL, disregard key/value pairs where value does not match.
* if value_pattern==CONFIG_REGEX_NONE, do not match any existing values
@@ -3172,6 +3266,7 @@ void git_config_set(const char *key, const char *value)
int git_config_set_multivar_in_file_gently(const char *config_filename,
const char *key, const char *value,
const char *value_pattern,
+ const char *comment,
unsigned flags)
{
int fd = -1, in_fd = -1;
@@ -3182,6 +3277,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
size_t contents_sz;
struct config_store_data store = CONFIG_STORE_INIT;
+ validate_comment_string(comment);
+
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
if (ret)
@@ -3222,7 +3319,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
free(store.key);
store.key = xstrdup(key);
if (write_section(fd, key, &store) < 0 ||
- write_pair(fd, key, value, &store) < 0)
+ write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out;
} else {
struct stat st;
@@ -3376,7 +3473,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
if (write_section(fd, key, &store) < 0)
goto write_err_out;
}
- if (write_pair(fd, key, value, &store) < 0)
+ if (write_pair(fd, key, value, comment, &store) < 0)
goto write_err_out;
}
@@ -3414,7 +3511,6 @@ out_free:
write_err_out:
ret = write_error(get_lock_file_path(&lock));
goto out_free;
-
}
void git_config_set_multivar_in_file(const char *config_filename,
@@ -3422,7 +3518,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
const char *value_pattern, unsigned flags)
{
if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
- value_pattern, flags))
+ value_pattern, NULL, flags))
return;
if (value)
die(_("could not set '%s' to '%s'"), key, value);
@@ -3445,7 +3541,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
int res = git_config_set_multivar_in_file_gently(file,
key, value,
value_pattern,
- flags);
+ NULL, flags);
free(file);
return res;
}
diff --git a/config.h b/config.h
index 14f881ecfa..f4966e3749 100644
--- a/config.h
+++ b/config.h
@@ -290,7 +290,7 @@ int git_config_pathname(const char **, const char *, const char *);
int git_config_expiry_date(timestamp_t *, const char *, const char *);
int git_config_color(char *, const char *, const char *);
-int git_config_set_in_file_gently(const char *, const char *, const char *);
+int git_config_set_in_file_gently(const char *, const char *, const char *, const char *);
/**
* write config values to a specific config file, takes a key/value pair as
@@ -336,7 +336,9 @@ int git_config_parse_key(const char *, char **, size_t *);
int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
void git_config_set_multivar(const char *, const char *, const char *, unsigned);
int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
-int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
+
+const char *git_config_prepare_comment_string(const char *);
/**
* takes four parameters:
@@ -382,7 +384,8 @@ int config_error_nonbool(const char *);
#endif
char *git_system_config(void);
-void git_global_config(char **user, char **xdg);
+char *git_global_config(void);
+void git_global_config_paths(char **user, char **xdg);
int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
diff --git a/config.mak.uname b/config.mak.uname
index 3bb03f423a..a7607a5676 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -65,9 +65,9 @@ ifeq ($(uname_S),Linux)
HAVE_PLATFORM_PROCINFO = YesPlease
COMPAT_OBJS += compat/linux/procinfo.o
# centos7/rhel7 provides gcc 4.8.5 and zlib 1.2.7.
- ifneq ($(findstring .el7.,$(uname_R)),)
+ ifneq ($(findstring .el7.,$(uname_R)),)
BASIC_CFLAGS += -std=c99
- endif
+ endif
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
@@ -95,13 +95,13 @@ ifeq ($(uname_S),UnixWare)
NO_MEMMEM = YesPlease
endif
ifeq ($(uname_S),SCO_SV)
- ifeq ($(uname_R),3.2)
+ ifeq ($(uname_R),3.2)
CFLAGS = -O2
- endif
- ifeq ($(uname_R),5)
+ endif
+ ifeq ($(uname_R),5)
CC = cc
BASIC_CFLAGS += -Kthread
- endif
+ endif
NEEDS_SOCKET = YesPlease
NEEDS_NSL = YesPlease
NEEDS_SSL_WITH_CRYPTO = YesPlease
@@ -124,19 +124,19 @@ ifeq ($(uname_S),Darwin)
# - MacOS 10.0.* and MacOS 10.1.0 = Darwin 1.*
# - MacOS 10.x.* = Darwin (x+4).* for (1 <= x)
# i.e. "begins with [15678] and a dot" means "10.4.* or older".
- ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
OLD_ICONV = UnfortunatelyYes
NO_APPLE_COMMON_CRYPTO = YesPlease
- endif
- ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
+ endif
+ ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
NO_STRLCPY = YesPlease
- endif
- ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
+ endif
+ ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
HAVE_GETDELIM = YesPlease
- endif
- ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
+ endif
+ ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
OPEN_RETURNS_EINTR = UnfortunatelyYes
- endif
+ endif
NO_MEMMEM = YesPlease
USE_ST_TIMESPEC = YesPlease
HAVE_DEV_TTY = YesPlease
@@ -152,22 +152,35 @@ ifeq ($(uname_S),Darwin)
# Workaround for `gettext` being keg-only and not even being linked via
# `brew link --force gettext`, should be obsolete as of
# https://github.com/Homebrew/homebrew-core/pull/53489
- ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
+ ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
BASIC_CFLAGS += -I/usr/local/include -I/usr/local/opt/gettext/include
BASIC_LDFLAGS += -L/usr/local/lib -L/usr/local/opt/gettext/lib
- ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
+ ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
MSGFMT = /usr/local/opt/gettext/bin/msgfmt
- endif
- endif
+ endif
+ # On newer ARM-based machines the default installation path has changed to
+ # /opt/homebrew. Include it in our search paths so that the user does not
+ # have to configure this manually.
+ #
+ # Note that we do not employ the same workaround as above where we manually
+ # add gettext. The issue was fixed more than three years ago by now, and at
+ # that point there haven't been any ARM-based Macs yet.
+ else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
+ BASIC_CFLAGS += -I/opt/homebrew/include
+ BASIC_LDFLAGS += -L/opt/homebrew/lib
+ ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
+ MSGFMT = /opt/homebrew/bin/msgfmt
+ endif
+ endif
# The builtin FSMonitor on MacOS builds upon Simple-IPC. Both require
# Unix domain sockets and PThreads.
- ifndef NO_PTHREADS
- ifndef NO_UNIX_SOCKETS
+ ifndef NO_PTHREADS
+ ifndef NO_UNIX_SOCKETS
FSMONITOR_DAEMON_BACKEND = darwin
FSMONITOR_OS_SETTINGS = darwin
- endif
- endif
+ endif
+ endif
BASIC_LDFLAGS += -framework CoreServices
endif
@@ -183,7 +196,7 @@ ifeq ($(uname_S),SunOS)
NO_REGEX = YesPlease
NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
HAVE_DEV_TTY = YesPlease
- ifeq ($(uname_R),5.6)
+ ifeq ($(uname_R),5.6)
SOCKLEN_T = int
NO_HSTRERROR = YesPlease
NO_IPV6 = YesPlease
@@ -193,8 +206,8 @@ ifeq ($(uname_S),SunOS)
NO_STRLCPY = YesPlease
NO_STRTOUMAX = YesPlease
GIT_TEST_CMP = cmp
- endif
- ifeq ($(uname_R),5.7)
+ endif
+ ifeq ($(uname_R),5.7)
NEEDS_RESOLV = YesPlease
NO_IPV6 = YesPlease
NO_SOCKADDR_STORAGE = YesPlease
@@ -203,25 +216,25 @@ ifeq ($(uname_S),SunOS)
NO_STRLCPY = YesPlease
NO_STRTOUMAX = YesPlease
GIT_TEST_CMP = cmp
- endif
- ifeq ($(uname_R),5.8)
+ endif
+ ifeq ($(uname_R),5.8)
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
NO_STRTOUMAX = YesPlease
GIT_TEST_CMP = cmp
- endif
- ifeq ($(uname_R),5.9)
+ endif
+ ifeq ($(uname_R),5.9)
NO_UNSETENV = YesPlease
NO_SETENV = YesPlease
NO_STRTOUMAX = YesPlease
GIT_TEST_CMP = cmp
- endif
+ endif
INSTALL = /usr/ucb/install
TAR = gtar
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
endif
ifeq ($(uname_O),Cygwin)
- ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
+ ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
NO_D_TYPE_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
@@ -232,9 +245,9 @@ ifeq ($(uname_O),Cygwin)
# On some boxes NO_MMAP is needed, and not so elsewhere.
# Try commenting this out if you suspect MMAP is more efficient
NO_MMAP = YesPlease
- else
+ else
NO_REGEX = UnfortunatelyYes
- endif
+ endif
HAVE_ALLOCA_H = YesPlease
NEEDS_LIBICONV = YesPlease
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
@@ -250,25 +263,25 @@ ifeq ($(uname_S),FreeBSD)
NEEDS_LIBICONV = YesPlease
# Versions up to 10.1 require OLD_ICONV; 10.2 and beyond don't.
# A typical version string looks like "10.2-RELEASE".
- ifeq ($(shell expr "$(uname_R)" : '[1-9]\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '[1-9]\.'),2)
OLD_ICONV = YesPlease
- endif
- ifeq ($(firstword $(subst -, ,$(uname_R))),10.0)
+ endif
+ ifeq ($(firstword $(subst -, ,$(uname_R))),10.0)
OLD_ICONV = YesPlease
- endif
- ifeq ($(firstword $(subst -, ,$(uname_R))),10.1)
+ endif
+ ifeq ($(firstword $(subst -, ,$(uname_R))),10.1)
OLD_ICONV = YesPlease
- endif
+ endif
NO_MEMMEM = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
USE_ST_TIMESPEC = YesPlease
- ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
PTHREAD_LIBS = -pthread
NO_UINTMAX_T = YesPlease
NO_STRTOUMAX = YesPlease
- endif
+ endif
PYTHON_PATH = /usr/local/bin/python
PERL_PATH = /usr/local/bin/perl
HAVE_PATHS_H = YesPlease
@@ -304,9 +317,9 @@ ifeq ($(uname_S),MirBSD)
CSPRNG_METHOD = arc4random
endif
ifeq ($(uname_S),NetBSD)
- ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
NEEDS_LIBICONV = YesPlease
- endif
+ endif
BASIC_CFLAGS += -I/usr/pkg/include
BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
USE_ST_TIMESPEC = YesPlease
@@ -330,14 +343,14 @@ ifeq ($(uname_S),AIX)
BASIC_CFLAGS += -D_LARGE_FILES
FILENO_IS_A_MACRO = UnfortunatelyYes
NEED_ACCESS_ROOT_HANDLER = UnfortunatelyYes
- ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
+ ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
NO_PTHREADS = YesPlease
- else
+ else
PTHREAD_LIBS = -lpthread
- endif
- ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
+ endif
+ ifeq ($(shell expr "$(uname_V).$(uname_R)" : '5\.1'),3)
INLINE = ''
- endif
+ endif
GIT_TEST_CMP = cmp
endif
ifeq ($(uname_S),GNU)
@@ -397,29 +410,29 @@ ifeq ($(uname_S),HP-UX)
NO_SYS_SELECT_H = YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
NO_NSEC = YesPlease
- ifeq ($(uname_R),B.11.00)
+ ifeq ($(uname_R),B.11.00)
NO_INET_NTOP = YesPlease
NO_INET_PTON = YesPlease
- endif
- ifeq ($(uname_R),B.10.20)
+ endif
+ ifeq ($(uname_R),B.10.20)
# Override HP-UX 11.x setting:
INLINE =
SOCKLEN_T = size_t
NO_PREAD = YesPlease
NO_INET_NTOP = YesPlease
NO_INET_PTON = YesPlease
- endif
+ endif
GIT_TEST_CMP = cmp
endif
ifeq ($(uname_S),Windows)
GIT_VERSION := $(GIT_VERSION).MSVC
pathsep = ;
# Assume that this is built in Git for Windows' SDK
- ifeq (MINGW32,$(MSYSTEM))
+ ifeq (MINGW32,$(MSYSTEM))
prefix = /mingw32
- else
+ else
prefix = /mingw64
- endif
+ endif
# Prepend MSVC 64-bit tool-chain to PATH.
#
# A regular Git Bash *does not* have cl.exe in its $PATH. As there is a
@@ -434,7 +447,6 @@ ifeq ($(uname_S),Windows)
NO_POLL = YesPlease
NO_SYMLINK_HEAD = YesPlease
NO_IPV6 = YesPlease
- NO_UNIX_SOCKETS = YesPlease
NO_SETENV = YesPlease
NO_STRCASESTR = YesPlease
NO_STRLCPY = YesPlease
@@ -537,16 +549,16 @@ ifeq ($(uname_S),Interix)
NO_MKDTEMP = YesPlease
NO_STRTOUMAX = YesPlease
NO_NSEC = YesPlease
- ifeq ($(uname_R),3.5)
+ ifeq ($(uname_R),3.5)
NO_INET_NTOP = YesPlease
NO_INET_PTON = YesPlease
NO_SOCKADDR_STORAGE = YesPlease
- endif
- ifeq ($(uname_R),5.2)
+ endif
+ ifeq ($(uname_R),5.2)
NO_INET_NTOP = YesPlease
NO_INET_PTON = YesPlease
NO_SOCKADDR_STORAGE = YesPlease
- endif
+ endif
endif
ifeq ($(uname_S),Minix)
NO_IPV6 = YesPlease
@@ -566,12 +578,12 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
# still not compile in c89 mode, due to non-const array initializations.
CC = cc -c99
# Build down-rev compatible objects that don't use our new getopt_long.
- ifeq ($(uname_R).$(uname_V),J06.21)
+ ifeq ($(uname_R).$(uname_V),J06.21)
CC += -WRVU=J06.20
- endif
- ifeq ($(uname_R).$(uname_V),L17.02)
+ endif
+ ifeq ($(uname_R).$(uname_V),L17.02)
CC += -WRVU=L16.05
- endif
+ endif
# Disable all optimization, seems to result in bad code, with -O or -O2
# or even -O1 (default), /usr/local/libexec/git-core/git-pack-objects
# abends on "git push". Needs more investigation.
@@ -625,10 +637,22 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
SHELL_PATH = /usr/coreutils/bin/bash
endif
+ifeq ($(uname_S),OS/390)
+ NO_SYS_POLL_H = YesPlease
+ NO_STRCASESTR = YesPlease
+ NO_REGEX = YesPlease
+ NO_MMAP = YesPlease
+ NO_NSEC = YesPlease
+ NO_STRLCPY = YesPlease
+ NO_MEMMEM = YesPlease
+ NO_GECOS_IN_PWENT = YesPlease
+ HAVE_STRINGS_H = YesPlease
+ NEEDS_MODE_TRANSLATION = YesPlease
+endif
ifeq ($(uname_S),MINGW)
- ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
+ ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
$(error "Building with MSys is no longer supported")
- endif
+ endif
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
@@ -636,7 +660,6 @@ ifeq ($(uname_S),MINGW)
NO_LIBGEN_H = YesPlease
NO_POLL = YesPlease
NO_SYMLINK_HEAD = YesPlease
- NO_UNIX_SOCKETS = YesPlease
NO_SETENV = YesPlease
NO_STRCASESTR = YesPlease
NO_STRLCPY = YesPlease
@@ -687,22 +710,22 @@ ifeq ($(uname_S),MINGW)
# Enable DEP
BASIC_LDFLAGS += -Wl,--nxcompat
# Enable ASLR (unless debugging)
- ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+ ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
BASIC_LDFLAGS += -Wl,--dynamicbase
- endif
- ifeq (MINGW32,$(MSYSTEM))
+ endif
+ ifeq (MINGW32,$(MSYSTEM))
prefix = /mingw32
HOST_CPU = i686
BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
- endif
- ifeq (MINGW64,$(MSYSTEM))
+ endif
+ ifeq (MINGW64,$(MSYSTEM))
prefix = /mingw64
HOST_CPU = x86_64
BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
- else
+ else
COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
BASIC_LDFLAGS += -Wl,--large-address-aware
- endif
+ endif
CC = gcc
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
-fstack-protector-strong
@@ -714,11 +737,11 @@ ifeq ($(uname_S),MINGW)
USE_GETTEXT_SCHEME = fallthrough
USE_LIBPCRE = YesPlease
USE_NED_ALLOCATOR = YesPlease
- ifeq (/mingw64,$(subst 32,64,$(prefix)))
+ 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
diff --git a/contrib/coccinelle/xstrncmpz.cocci b/contrib/coccinelle/xstrncmpz.cocci
new file mode 100644
index 0000000000..ccb39e2bc0
--- /dev/null
+++ b/contrib/coccinelle/xstrncmpz.cocci
@@ -0,0 +1,28 @@
+@@
+expression S, T, L;
+@@
+(
+- strncmp(S, T, L) || S[L]
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || S[L] != '\0'
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || T[L]
++ !!xstrncmpz(T, S, L)
+|
+- strncmp(S, T, L) || T[L] != '\0'
++ !!xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && !S[L]
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && S[L] == '\0'
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && !T[L]
++ !xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && T[L] == '\0'
++ !xstrncmpz(T, S, L)
+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 13a39ebd2e..75193ded4b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -122,6 +122,40 @@ __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
+ local head
+
+ __git_find_repo_path
+
+ # If the reftable is in use, we have to shell out to 'git rev-parse'
+ # to determine whether the ref exists instead of looking directly in
+ # the filesystem to determine whether the ref exists. Otherwise, use
+ # Bash builtins since executing Git commands are expensive on some
+ # platforms.
+ if __git_eread "$__git_repo_path/HEAD" head; then
+ if [ "$head" == "ref: refs/heads/.invalid" ]; then
+ __git show-ref --exists "$ref"
+ return $?
+ fi
+ fi
+
+ [ -f "$__git_repo_path/$ref" ]
+}
+
# Removes backslash escaping, single quotes and double quotes from a word,
# stores the result in the variable $dequoted_word.
# 1: The word to dequote.
@@ -420,16 +454,18 @@ fi
# This function is equivalent to
#
-# __gitcomp "$(git xxx --git-completion-helper) ..."
+# ___git_resolved_builtins=$(git xxx --git-completion-helper)
#
-# except that the output is cached. Accept 1-3 arguments:
+# except that the result of the execution is cached.
+#
+# Accept 1-3 arguments:
# 1: the git command to execute, this is also the cache key
+# (use "_" when the command contains spaces, e.g. "remote add"
+# becomes "remote_add")
# 2: extra options to be added on top (e.g. negative forms)
# 3: options to be excluded
-__gitcomp_builtin ()
+__git_resolve_builtins ()
{
- # spaces must be replaced with underscore for multi-word
- # commands, e.g. "git remote add" becomes remote_add.
local cmd="$1"
local incl="${2-}"
local excl="${3-}"
@@ -455,7 +491,24 @@ __gitcomp_builtin ()
eval "$var=\"$options\""
fi
- __gitcomp "$options"
+ ___git_resolved_builtins="$options"
+}
+
+# This function is equivalent to
+#
+# __gitcomp "$(git xxx --git-completion-helper) ..."
+#
+# except that the output is cached. Accept 1-3 arguments:
+# 1: the git command to execute, this is also the cache key
+# (use "_" when the command contains spaces, e.g. "remote add"
+# becomes "remote_add")
+# 2: extra options to be added on top (e.g. negative forms)
+# 3: options to be excluded
+__gitcomp_builtin ()
+{
+ __git_resolve_builtins "$1" "$2" "$3"
+
+ __gitcomp "$___git_resolved_builtins"
}
# Variation of __gitcomp_nl () that appends to the existing list of
@@ -522,6 +575,26 @@ __gitcomp_file ()
true
}
+# Find the current subcommand for commands that follow the syntax:
+#
+# git <command> <subcommand>
+#
+# 1: List of possible subcommands.
+# 2: Optional subcommand to return when none is found.
+__git_find_subcommand ()
+{
+ local subcommand subcommands="$1" default_subcommand="$2"
+
+ for subcommand in $subcommands; do
+ if [ "$subcommand" = "${words[__git_cmd_idx+1]}" ]; then
+ echo $subcommand
+ return
+ fi
+ done
+
+ echo $default_subcommand
+}
+
# Execute 'git ls-files', unless the --committable option is specified, in
# which case it runs 'git diff-index' to find out the files that can be
# committed. It return paths relative to the directory specified in the first
@@ -1449,12 +1522,32 @@ _git_bisect ()
{
__git_has_doubledash && return
- local subcommands="start bad good skip reset visualize replay log run"
- local subcommand="$(__git_find_on_cmdline "$subcommands")"
+ __git_find_repo_path
+
+ # If a bisection is in progress get the terms being used.
+ local term_bad term_good
+ if [ -f "$__git_repo_path"/BISECT_TERMS ]; then
+ term_bad=$(__git bisect terms --term-bad)
+ term_good=$(__git bisect terms --term-good)
+ fi
+
+ # We will complete any custom terms, but still always complete the
+ # more usual bad/new/good/old because git bisect gives a good error
+ # message if these are given when not in use, and that's better than
+ # silent refusal to complete if the user is confused.
+ #
+ # We want to recognize 'view' but not complete it, because it overlaps
+ # with 'visualize' too much and is just an alias for it.
+ #
+ local completable_subcommands="start bad new $term_bad good old $term_good terms skip reset visualize replay log run help"
+ local all_subcommands="$completable_subcommands view"
+
+ local subcommand="$(__git_find_on_cmdline "$all_subcommands")"
+
if [ -z "$subcommand" ]; then
__git_find_repo_path
if [ -f "$__git_repo_path"/BISECT_START ]; then
- __gitcomp "$subcommands"
+ __gitcomp "$completable_subcommands"
else
__gitcomp "replay start"
fi
@@ -1462,7 +1555,26 @@ _git_bisect ()
fi
case "$subcommand" in
- bad|good|reset|skip|start)
+ start)
+ case "$cur" in
+ --*)
+ __gitcomp "--first-parent --no-checkout --term-new --term-bad --term-old --term-good"
+ return
+ ;;
+ *)
+ __git_complete_refs
+ ;;
+ esac
+ ;;
+ terms)
+ __gitcomp "--term-good --term-old --term-bad --term-new"
+ return
+ ;;
+ visualize|view)
+ __git_complete_log_opts
+ return
+ ;;
+ bad|new|"$term_bad"|good|old|"$term_good"|reset|skip)
__git_complete_refs
;;
*)
@@ -1624,8 +1736,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
@@ -1775,7 +1886,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--output= --output-indicator-context=
--output-indicator-new= --output-indicator-old=
--ws-error-highlight=
- --pickaxe-all --pickaxe-regex
+ --pickaxe-all --pickaxe-regex --patch-with-raw
"
# Options for diff/difftool
@@ -2039,6 +2150,16 @@ __git_log_common_options="
--min-age= --until= --before=
--min-parents= --max-parents=
--no-min-parents --no-max-parents
+ --alternate-refs --ancestry-path
+ --author-date-order --basic-regexp
+ --bisect --boundary --exclude-first-parent-only
+ --exclude-hidden --extended-regexp
+ --fixed-strings --grep-reflog
+ --ignore-missing --left-only --perl-regexp
+ --reflog --regexp-ignore-case --remove-empty
+ --right-only --show-linear-break
+ --show-notes-by-default --show-pulls
+ --since-as-filter --single-worktree
"
# Options that go well for log and gitk (not shortlog)
__git_log_gitk_options="
@@ -2054,6 +2175,7 @@ __git_log_shortlog_options="
# Options accepted by log and show
__git_log_show_options="
--diff-merges --diff-merges= --no-diff-merges --dd --remerge-diff
+ --encoding=
"
__git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-combined cc remerge r"
@@ -2061,13 +2183,15 @@ __git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-c
__git_log_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd"
__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix auto: format:"
-_git_log ()
+# Complete porcelain (i.e. not git-rev-list) options and at least some
+# option arguments accepted by git-log. Note that this same set of options
+# are also accepted by some other git commands besides git-log.
+__git_complete_log_opts ()
{
- __git_has_doubledash && return
- __git_find_repo_path
+ COMPREPLY=()
local merge=""
- if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+ if __git_pseudoref_exists MERGE_HEAD; then
merge="--merge"
fi
case "$prev,$cur" in
@@ -2137,6 +2261,8 @@ _git_log ()
--no-walk --no-walk= --do-walk
--parents --children
--expand-tabs --expand-tabs= --no-expand-tabs
+ --clear-decorations --decorate-refs=
+ --decorate-refs-exclude=
$merge
$__git_diff_common_options
"
@@ -2158,6 +2284,16 @@ _git_log ()
return
;;
esac
+}
+
+_git_log ()
+{
+ __git_has_doubledash && return
+ __git_find_repo_path
+
+ __git_complete_log_opts
+ [ ${#COMPREPLY[@]} -eq 0 ] || return
+
__git_complete_revlist
}
@@ -2374,13 +2510,30 @@ _git_rebase ()
_git_reflog ()
{
- local subcommands="show delete expire"
- local subcommand="$(__git_find_on_cmdline "$subcommands")"
+ local subcommands subcommand
- if [ -z "$subcommand" ]; then
- __gitcomp "$subcommands"
- else
- __git_complete_refs
+ __git_resolve_builtins "reflog"
+
+ subcommands="$___git_resolved_builtins"
+ subcommand="$(__git_find_subcommand "$subcommands" "show")"
+
+ case "$subcommand,$cur" in
+ show,--*)
+ __gitcomp "
+ $__git_log_common_options
+ "
+ return
+ ;;
+ $subcommand,--*)
+ __gitcomp_builtin "reflog_$subcommand"
+ return
+ ;;
+ esac
+
+ __git_complete_refs
+
+ if [ $((cword - __git_cmd_idx)) -eq 1 ]; then
+ __gitcompappend "$subcommands" "" "$cur" " "
fi
}
@@ -2563,6 +2716,33 @@ __git_compute_config_vars ()
__git_config_vars="$(git help --config-for-completion)"
}
+__git_config_vars_all=
+__git_compute_config_vars_all ()
+{
+ test -n "$__git_config_vars_all" ||
+ __git_config_vars_all="$(git --no-pager help --config)"
+}
+
+__git_compute_first_level_config_vars_for_section ()
+{
+ local section="$1"
+ __git_compute_config_vars
+ local this_section="__git_first_level_config_vars_for_section_${section}"
+ test -n "${!this_section}" ||
+ printf -v "__git_first_level_config_vars_for_section_${section}" %s \
+ "$(echo "$__git_config_vars" | awk -F. "/^${section}\.[a-z]/ { print \$2 }")"
+}
+
+__git_compute_second_level_config_vars_for_section ()
+{
+ local section="$1"
+ __git_compute_config_vars_all
+ local this_section="__git_second_level_config_vars_for_section_${section}"
+ test -n "${!this_section}" ||
+ printf -v "__git_second_level_config_vars_for_section_${section}" %s \
+ "$(echo "$__git_config_vars_all" | awk -F. "/^${section}\.</ { print \$3 }")"
+}
+
__git_config_sections=
__git_compute_config_sections ()
{
@@ -2707,73 +2887,50 @@ __git_complete_config_variable_name ()
done
case "$cur_" in
- branch.*.*)
+ branch.*.*|guitool.*.*|difftool.*.*|man.*.*|mergetool.*.*|remote.*.*|submodule.*.*|url.*.*)
local pfx="${cur_%.*}."
cur_="${cur_##*.}"
- __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx"
+ local section="${pfx%.*.}"
+ __git_compute_second_level_config_vars_for_section "${section}"
+ local this_section="__git_second_level_config_vars_for_section_${section}"
+ __gitcomp "${!this_section}" "$pfx" "$cur_" "$sfx"
return
;;
branch.*)
local pfx="${cur_%.*}."
cur_="${cur_#*.}"
+ local section="${pfx%.}"
__gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
- __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "${sfx- }"
- return
- ;;
- guitool.*.*)
- local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "
- argPrompt cmd confirm needsFile noConsole noRescan
- prompt revPrompt revUnmerged title
- " "$pfx" "$cur_" "$sfx"
- return
- ;;
- difftool.*.*)
- local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
- return
- ;;
- man.*.*)
- local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
- return
- ;;
- mergetool.*.*)
- local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx"
+ __git_compute_first_level_config_vars_for_section "${section}"
+ local this_section="__git_first_level_config_vars_for_section_${section}"
+ __gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
return
;;
pager.*)
local pfx="${cur_%.*}."
cur_="${cur_#*.}"
__git_compute_all_commands
- __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "${sfx- }"
- return
- ;;
- remote.*.*)
- local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "
- url proxy fetch push mirror skipDefaultUpdate
- receivepack uploadpack tagOpt pushurl
- " "$pfx" "$cur_" "$sfx"
+ __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "${sfx:- }"
return
;;
remote.*)
local pfx="${cur_%.*}."
cur_="${cur_#*.}"
+ local section="${pfx%.}"
__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
- __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "${sfx- }"
+ __git_compute_first_level_config_vars_for_section "${section}"
+ local this_section="__git_first_level_config_vars_for_section_${section}"
+ __gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
return
;;
- url.*.*)
+ submodule.*)
local pfx="${cur_%.*}."
- cur_="${cur_##*.}"
- __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx"
+ cur_="${cur_#*.}"
+ local section="${pfx%.}"
+ __gitcomp_nl "$(__git config -f "$(__git rev-parse --show-toplevel)/.gitmodules" --get-regexp 'submodule.*.path' | awk -F. '{print $2}')" "$pfx" "$cur_" "."
+ __git_compute_first_level_config_vars_for_section "${section}"
+ local this_section="__git_first_level_config_vars_for_section_${section}"
+ __gitcomp_nl_append "${!this_section}" "$pfx" "$cur_" "${sfx:- }"
return
;;
*.*)
@@ -2952,7 +3109,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
@@ -2962,8 +3119,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
@@ -3084,12 +3240,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
}
@@ -3097,6 +3360,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
@@ -3107,9 +3371,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
}
@@ -3356,7 +3629,7 @@ __git_complete_worktree_paths ()
# Generate completion reply from worktree list skipping the first
# entry: it's the path of the main worktree, which can't be moved,
# removed, locked, etc.
- __gitcomp_nl "$(git worktree list --porcelain |
+ __gitcomp_nl "$(__git worktree list --porcelain |
sed -n -e '2,$ s/^worktree //p')"
}
@@ -3592,7 +3865,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
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 2c030050ae..5330e769a7 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -141,7 +141,7 @@ __git_ps1_show_upstream ()
# parse configuration values
local option
- for option in ${GIT_PS1_SHOWUPSTREAM}; do
+ for option in ${GIT_PS1_SHOWUPSTREAM-}; do
case "$option" in
git|svn) upstream_type="$option" ;;
verbose) verbose=1 ;;
@@ -408,7 +408,7 @@ __git_ps1 ()
local repo_info rev_parse_exit_code
repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
- --is-bare-repository --is-inside-work-tree \
+ --is-bare-repository --is-inside-work-tree --show-ref-format \
--short HEAD 2>/dev/null)"
rev_parse_exit_code="$?"
@@ -421,6 +421,8 @@ __git_ps1 ()
short_sha="${repo_info##*$'\n'}"
repo_info="${repo_info%$'\n'*}"
fi
+ local ref_format="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
local inside_worktree="${repo_info##*$'\n'}"
repo_info="${repo_info%$'\n'*}"
local bare_repo="${repo_info##*$'\n'}"
@@ -479,12 +481,25 @@ __git_ps1 ()
b="$(git symbolic-ref HEAD 2>/dev/null)"
else
local head=""
- if ! __git_eread "$g/HEAD" head; then
- return $exit
- fi
- # is it a symbolic ref?
- b="${head#ref: }"
- if [ "$head" = "$b" ]; then
+
+ case "$ref_format" in
+ files)
+ if ! __git_eread "$g/HEAD" head; then
+ return $exit
+ fi
+
+ if [[ $head == "ref: "* ]]; then
+ head="${head#ref: }"
+ else
+ head=""
+ fi
+ ;;
+ *)
+ head="$(git symbolic-ref HEAD 2>/dev/null)"
+ ;;
+ esac
+
+ if test -z "$head"; then
detached=yes
b="$(
case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -502,6 +517,8 @@ __git_ps1 ()
b="$short_sha..."
b="($b)"
+ else
+ b="$head"
fi
fi
fi
@@ -511,7 +528,7 @@ __git_ps1 ()
fi
local conflict="" # state indicator for unresolved conflicts
- if [[ "${GIT_PS1_SHOWCONFLICTSTATE}" == "yes" ]] &&
+ if [[ "${GIT_PS1_SHOWCONFLICTSTATE-}" == "yes" ]] &&
[[ $(git ls-files --unmerged 2>/dev/null) ]]; then
conflict="|CONFLICT"
fi
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
index 4ec419f900..6ce9603568 100755
--- a/contrib/coverage-diff.sh
+++ b/contrib/coverage-diff.sh
@@ -74,8 +74,7 @@ do
sort >uncovered_lines.txt
comm -12 uncovered_lines.txt new_lines.txt |
- sed -e 's/$/\)/' |
- sed -e 's/^/ /' >uncovered_new_lines.txt
+ sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
echo $file >>coverage-data.txt &&
@@ -91,11 +90,7 @@ cat coverage-data.txt
echo "Commits introducing uncovered code:"
-commit_list=$(cat coverage-data.txt |
- grep -E '^[0-9a-f]{7,} ' |
- awk '{print $1;}' |
- sort |
- uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
(
for commit in $commit_list
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index 215a81d8ba..90034d0cf1 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -164,6 +164,9 @@ static int keyring_get(struct credential *c)
if (g_strv_length(parts) >= 1) {
g_free(c->password);
c->password = g_strdup(parts[0]);
+ } else {
+ g_free(c->password);
+ c->password = g_strdup("");
}
for (int i = 1; i < g_strv_length(parts); i++) {
if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 4b3a08a2ba..238f5f8c36 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -8,7 +8,8 @@ CFLAGS = -g -O2 -Wall
-include ../../../config.mak
git-credential-osxkeychain: git-credential-osxkeychain.o
- $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) \
+ -framework Security -framework CoreFoundation
git-credential-osxkeychain.o: git-credential-osxkeychain.c
$(CC) -c $(CFLAGS) $<
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 5f2e5f16c8..6a40917b1e 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -3,14 +3,51 @@
#include <stdlib.h>
#include <Security/Security.h>
-static SecProtocolType protocol;
-static char *host;
-static char *path;
-static char *username;
-static char *password;
-static UInt16 port;
-
-__attribute__((format (printf, 1, 2)))
+#define ENCODING kCFStringEncodingUTF8
+static CFStringRef protocol; /* Stores constant strings - not memory managed */
+static CFStringRef host;
+static CFNumberRef port;
+static CFStringRef path;
+static CFStringRef username;
+static CFDataRef password;
+static CFDataRef password_expiry_utc;
+static CFDataRef oauth_refresh_token;
+
+static void clear_credential(void)
+{
+ if (host) {
+ CFRelease(host);
+ host = NULL;
+ }
+ if (port) {
+ CFRelease(port);
+ port = NULL;
+ }
+ if (path) {
+ CFRelease(path);
+ path = NULL;
+ }
+ if (username) {
+ CFRelease(username);
+ username = NULL;
+ }
+ if (password) {
+ CFRelease(password);
+ password = NULL;
+ }
+ if (password_expiry_utc) {
+ CFRelease(password_expiry_utc);
+ password_expiry_utc = NULL;
+ }
+ if (oauth_refresh_token) {
+ CFRelease(oauth_refresh_token);
+ oauth_refresh_token = NULL;
+ }
+}
+
+#define STRING_WITH_LENGTH(s) s, sizeof(s) - 1
+
+__attribute__((format (printf, 1, 2), __noreturn__))
static void die(const char *err, ...)
{
char msg[4096];
@@ -19,70 +56,199 @@ static void die(const char *err, ...)
vsnprintf(msg, sizeof(msg), err, params);
fprintf(stderr, "%s\n", msg);
va_end(params);
+ clear_credential();
exit(1);
}
-static void *xstrdup(const char *s1)
+static void *xmalloc(size_t len)
{
- void *ret = strdup(s1);
+ void *ret = malloc(len);
if (!ret)
die("Out of memory");
return ret;
}
-#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
-#define KEYCHAIN_ARGS \
- NULL, /* default keychain */ \
- KEYCHAIN_ITEM(host), \
- 0, NULL, /* account domain */ \
- KEYCHAIN_ITEM(username), \
- KEYCHAIN_ITEM(path), \
- port, \
- protocol, \
- kSecAuthenticationTypeDefault
-
-static void write_item(const char *what, const char *buf, int len)
+static CFDictionaryRef create_dictionary(CFAllocatorRef allocator, ...)
+{
+ va_list args;
+ const void *key;
+ CFMutableDictionaryRef result;
+
+ result = CFDictionaryCreateMutable(allocator,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+
+ va_start(args, allocator);
+ while ((key = va_arg(args, const void *)) != NULL) {
+ const void *value;
+ value = va_arg(args, const void *);
+ if (value)
+ CFDictionarySetValue(result, key, value);
+ }
+ va_end(args);
+
+ return result;
+}
+
+#define CREATE_SEC_ATTRIBUTES(...) \
+ create_dictionary(kCFAllocatorDefault, \
+ kSecClass, kSecClassInternetPassword, \
+ kSecAttrServer, host, \
+ kSecAttrAccount, username, \
+ kSecAttrPath, path, \
+ kSecAttrPort, port, \
+ kSecAttrProtocol, protocol, \
+ kSecAttrAuthenticationType, \
+ kSecAttrAuthenticationTypeDefault, \
+ __VA_ARGS__);
+
+static void write_item(const char *what, const char *buf, size_t len)
{
printf("%s=", what);
fwrite(buf, 1, len, stdout);
putchar('\n');
}
-static void find_username_in_item(SecKeychainItemRef item)
+static void find_username_in_item(CFDictionaryRef item)
{
- SecKeychainAttributeList list;
- SecKeychainAttribute attr;
+ CFStringRef account_ref;
+ char *username_buf;
+ CFIndex buffer_len;
- list.count = 1;
- list.attr = &attr;
- attr.tag = kSecAccountItemAttr;
+ account_ref = CFDictionaryGetValue(item, kSecAttrAccount);
+ if (!account_ref)
+ {
+ write_item("username", "", 0);
+ return;
+ }
- if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
+ username_buf = (char *)CFStringGetCStringPtr(account_ref, ENCODING);
+ if (username_buf)
+ {
+ write_item("username", username_buf, strlen(username_buf));
return;
+ }
- write_item("username", attr.data, attr.length);
- SecKeychainItemFreeContent(&list, NULL);
+ /* If we can't get a CString pointer then
+ * we need to allocate our own buffer */
+ buffer_len = CFStringGetMaximumSizeForEncoding(
+ CFStringGetLength(account_ref), ENCODING) + 1;
+ username_buf = xmalloc(buffer_len);
+ if (CFStringGetCString(account_ref,
+ username_buf,
+ buffer_len,
+ ENCODING)) {
+ write_item("username", username_buf, buffer_len - 1);
+ }
+ free(username_buf);
}
-static void find_internet_password(void)
+static OSStatus find_internet_password(void)
{
- void *buf;
- UInt32 len;
- SecKeychainItemRef item;
+ CFDictionaryRef attrs;
+ CFDictionaryRef item;
+ CFDataRef data;
+ OSStatus result;
- if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
- return;
+ attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitOne,
+ kSecReturnAttributes, kCFBooleanTrue,
+ kSecReturnData, kCFBooleanTrue,
+ NULL);
+ result = SecItemCopyMatching(attrs, (CFTypeRef *)&item);
+ if (result) {
+ goto out;
+ }
+
+ data = CFDictionaryGetValue(item, kSecValueData);
- write_item("password", buf, len);
+ write_item("password",
+ (const char *)CFDataGetBytePtr(data),
+ CFDataGetLength(data));
if (!username)
find_username_in_item(item);
- SecKeychainItemFreeContent(NULL, buf);
+ CFRelease(item);
+
+out:
+ CFRelease(attrs);
+
+ /* We consider not found to not be an error */
+ if (result == errSecItemNotFound)
+ result = errSecSuccess;
+
+ return result;
+}
+
+static OSStatus delete_ref(const void *itemRef)
+{
+ CFArrayRef item_ref_list;
+ CFDictionaryRef delete_query;
+ OSStatus result;
+
+ item_ref_list = CFArrayCreate(kCFAllocatorDefault,
+ &itemRef,
+ 1,
+ &kCFTypeArrayCallBacks);
+ delete_query = create_dictionary(kCFAllocatorDefault,
+ kSecClass, kSecClassInternetPassword,
+ kSecMatchItemList, item_ref_list,
+ NULL);
+
+ if (password) {
+ /* We only want to delete items with a matching password */
+ CFIndex capacity;
+ CFMutableDictionaryRef query;
+ CFDataRef data;
+
+ capacity = CFDictionaryGetCount(delete_query) + 1;
+ query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
+ capacity,
+ delete_query);
+ CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
+ result = SecItemCopyMatching(query, (CFTypeRef *)&data);
+ if (!result) {
+ CFDataRef kc_password;
+ const UInt8 *raw_data;
+ const UInt8 *line;
+
+ /* Don't match appended metadata */
+ raw_data = CFDataGetBytePtr(data);
+ line = memchr(raw_data, '\n', CFDataGetLength(data));
+ if (line)
+ kc_password = CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault,
+ raw_data,
+ line - raw_data,
+ kCFAllocatorNull);
+ else
+ kc_password = data;
+
+ if (CFEqual(kc_password, password))
+ result = SecItemDelete(delete_query);
+
+ if (line)
+ CFRelease(kc_password);
+ CFRelease(data);
+ }
+
+ CFRelease(query);
+ } else {
+ result = SecItemDelete(delete_query);
+ }
+
+ CFRelease(delete_query);
+ CFRelease(item_ref_list);
+
+ return result;
}
-static void delete_internet_password(void)
+static OSStatus delete_internet_password(void)
{
- SecKeychainItemRef item;
+ CFDictionaryRef attrs;
+ CFArrayRef refs;
+ OSStatus result;
/*
* Require at least a protocol and host for removal, which is what git
@@ -90,25 +256,69 @@ static void delete_internet_password(void)
* Keychain manager.
*/
if (!protocol || !host)
- return;
+ return -1;
- if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
- return;
+ attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll,
+ kSecReturnRef, kCFBooleanTrue,
+ NULL);
+ result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs);
+ CFRelease(attrs);
+
+ if (!result) {
+ for (CFIndex i = 0; !result && i < CFArrayGetCount(refs); i++)
+ result = delete_ref(CFArrayGetValueAtIndex(refs, i));
- SecKeychainItemDelete(item);
+ CFRelease(refs);
+ }
+
+ /* We consider not found to not be an error */
+ if (result == errSecItemNotFound)
+ result = errSecSuccess;
+
+ return result;
}
-static void add_internet_password(void)
+static OSStatus add_internet_password(void)
{
+ CFMutableDataRef data;
+ CFDictionaryRef attrs;
+ OSStatus result;
+
/* Only store complete credentials */
if (!protocol || !host || !username || !password)
- return;
+ return -1;
- if (SecKeychainAddInternetPassword(
- KEYCHAIN_ARGS,
- KEYCHAIN_ITEM(password),
- NULL))
- return;
+ data = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, password);
+ if (password_expiry_utc) {
+ CFDataAppendBytes(data,
+ (const UInt8 *)STRING_WITH_LENGTH("\npassword_expiry_utc="));
+ CFDataAppendBytes(data,
+ CFDataGetBytePtr(password_expiry_utc),
+ CFDataGetLength(password_expiry_utc));
+ }
+ if (oauth_refresh_token) {
+ CFDataAppendBytes(data,
+ (const UInt8 *)STRING_WITH_LENGTH("\noauth_refresh_token="));
+ CFDataAppendBytes(data,
+ CFDataGetBytePtr(oauth_refresh_token),
+ CFDataGetLength(oauth_refresh_token));
+ }
+
+ attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, data,
+ NULL);
+
+ result = SecItemAdd(attrs, NULL);
+ if (result == errSecDuplicateItem) {
+ CFDictionaryRef query;
+ query = CREATE_SEC_ATTRIBUTES(NULL);
+ result = SecItemUpdate(query, attrs);
+ CFRelease(query);
+ }
+
+ CFRelease(data);
+ CFRelease(attrs);
+
+ return result;
}
static void read_credential(void)
@@ -131,36 +341,60 @@ static void read_credential(void)
if (!strcmp(buf, "protocol")) {
if (!strcmp(v, "imap"))
- protocol = kSecProtocolTypeIMAP;
+ protocol = kSecAttrProtocolIMAP;
else if (!strcmp(v, "imaps"))
- protocol = kSecProtocolTypeIMAPS;
+ protocol = kSecAttrProtocolIMAPS;
else if (!strcmp(v, "ftp"))
- protocol = kSecProtocolTypeFTP;
+ protocol = kSecAttrProtocolFTP;
else if (!strcmp(v, "ftps"))
- protocol = kSecProtocolTypeFTPS;
+ protocol = kSecAttrProtocolFTPS;
else if (!strcmp(v, "https"))
- protocol = kSecProtocolTypeHTTPS;
+ protocol = kSecAttrProtocolHTTPS;
else if (!strcmp(v, "http"))
- protocol = kSecProtocolTypeHTTP;
+ protocol = kSecAttrProtocolHTTP;
else if (!strcmp(v, "smtp"))
- protocol = kSecProtocolTypeSMTP;
- else /* we don't yet handle other protocols */
+ protocol = kSecAttrProtocolSMTP;
+ else {
+ /* we don't yet handle other protocols */
+ clear_credential();
exit(0);
+ }
}
else if (!strcmp(buf, "host")) {
char *colon = strchr(v, ':');
if (colon) {
+ UInt16 port_i;
*colon++ = '\0';
- port = atoi(colon);
+ port_i = atoi(colon);
+ port = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberShortType,
+ &port_i);
}
- host = xstrdup(v);
+ host = CFStringCreateWithCString(kCFAllocatorDefault,
+ v,
+ ENCODING);
}
else if (!strcmp(buf, "path"))
- path = xstrdup(v);
+ path = CFStringCreateWithCString(kCFAllocatorDefault,
+ v,
+ ENCODING);
else if (!strcmp(buf, "username"))
- username = xstrdup(v);
+ username = CFStringCreateWithCString(
+ kCFAllocatorDefault,
+ v,
+ ENCODING);
else if (!strcmp(buf, "password"))
- password = xstrdup(v);
+ password = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
+ else if (!strcmp(buf, "password_expiry_utc"))
+ password_expiry_utc = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
+ else if (!strcmp(buf, "oauth_refresh_token"))
+ oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
@@ -173,6 +407,7 @@ static void read_credential(void)
int main(int argc, const char **argv)
{
+ OSStatus result = 0;
const char *usage =
"usage: git credential-osxkeychain <get|store|erase>";
@@ -182,12 +417,17 @@ int main(int argc, const char **argv)
read_credential();
if (!strcmp(argv[1], "get"))
- find_internet_password();
+ result = find_internet_password();
else if (!strcmp(argv[1], "store"))
- add_internet_password();
+ result = add_internet_password();
else if (!strcmp(argv[1], "erase"))
- delete_internet_password();
+ result = delete_internet_password();
/* otherwise, ignore unknown action */
+ if (result)
+ die("failed to %s: %d", argv[1], (int)result);
+
+ clear_credential();
+
return 0;
}
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
index 4cd56c42e2..4be0d58cd8 100644
--- a/contrib/credential/wincred/git-credential-wincred.c
+++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -35,7 +35,7 @@ static void *xmalloc(size_t size)
}
static WCHAR *wusername, *password, *protocol, *host, *path, target[1024],
- *password_expiry_utc;
+ *password_expiry_utc, *oauth_refresh_token;
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
{
@@ -140,6 +140,11 @@ static void get_credential(void)
DWORD num_creds;
int i;
CREDENTIAL_ATTRIBUTEW *attr;
+ WCHAR *secret;
+ WCHAR *line;
+ WCHAR *remaining_lines;
+ WCHAR *part;
+ WCHAR *remaining_parts;
if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
return;
@@ -149,9 +154,24 @@ static void get_credential(void)
if (match_cred(creds[i], 0)) {
write_item("username", creds[i]->UserName,
creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
- write_item("password",
- (LPCWSTR)creds[i]->CredentialBlob,
- creds[i]->CredentialBlobSize / sizeof(WCHAR));
+ if (creds[i]->CredentialBlobSize > 0) {
+ secret = xmalloc(creds[i]->CredentialBlobSize);
+ wcsncpy_s(secret, creds[i]->CredentialBlobSize, (LPCWSTR)creds[i]->CredentialBlob, creds[i]->CredentialBlobSize / sizeof(WCHAR));
+ line = wcstok_s(secret, L"\r\n", &remaining_lines);
+ write_item("password", line, line ? wcslen(line) : 0);
+ while(line != NULL) {
+ part = wcstok_s(line, L"=", &remaining_parts);
+ if (!wcscmp(part, L"oauth_refresh_token")) {
+ write_item("oauth_refresh_token", remaining_parts, remaining_parts ? wcslen(remaining_parts) : 0);
+ }
+ line = wcstok_s(NULL, L"\r\n", &remaining_lines);
+ }
+ free(secret);
+ } else {
+ write_item("password",
+ (LPCWSTR)creds[i]->CredentialBlob,
+ creds[i]->CredentialBlobSize / sizeof(WCHAR));
+ }
for (int j = 0; j < creds[i]->AttributeCount; j++) {
attr = creds[i]->Attributes + j;
if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
@@ -170,16 +190,26 @@ static void store_credential(void)
{
CREDENTIALW cred;
CREDENTIAL_ATTRIBUTEW expiry_attr;
+ WCHAR *secret;
+ int wlen;
if (!wusername || !password)
return;
+ if (oauth_refresh_token) {
+ wlen = _scwprintf(L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token);
+ secret = xmalloc(sizeof(WCHAR) * wlen);
+ _snwprintf_s(secret, sizeof(WCHAR) * wlen, wlen, L"%s\r\noauth_refresh_token=%s", password, oauth_refresh_token);
+ } else {
+ secret = _wcsdup(password);
+ }
+
cred.Flags = 0;
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = target;
cred.Comment = L"saved by git-credential-wincred";
- cred.CredentialBlobSize = (wcslen(password)) * sizeof(WCHAR);
- cred.CredentialBlob = (LPVOID)password;
+ cred.CredentialBlobSize = wcslen(secret) * sizeof(WCHAR);
+ cred.CredentialBlob = (LPVOID)_wcsdup(secret);
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
cred.AttributeCount = 0;
cred.Attributes = NULL;
@@ -194,6 +224,8 @@ static void store_credential(void)
cred.TargetAlias = NULL;
cred.UserName = wusername;
+ free(secret);
+
if (!CredWriteW(&cred, 0))
die("CredWrite failed");
}
@@ -265,6 +297,8 @@ static void read_credential(void)
password = utf8_to_utf16_dup(v);
else if (!strcmp(buf, "password_expiry_utc"))
password_expiry_utc = utf8_to_utf16_dup(v);
+ else if (!strcmp(buf, "oauth_refresh_token"))
+ oauth_refresh_token = utf8_to_utf16_dup(v);
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
deleted file mode 100755
index 7eb1b24cc7..0000000000
--- a/contrib/hg-to-git/hg-to-git.py
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env python
-
-""" hg-to-git.py - A Mercurial to GIT converter
-
- Copyright (C)2007 Stelian Pop <stelian@popies.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-"""
-
-import os, os.path, sys
-import tempfile, pickle, getopt
-import re
-
-if sys.hexversion < 0x02030000:
- # The behavior of the pickle module changed significantly in 2.3
- sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n")
- sys.exit(1)
-
-# Maps hg version -> git version
-hgvers = {}
-# List of children for each hg revision
-hgchildren = {}
-# List of parents for each hg revision
-hgparents = {}
-# Current branch for each hg revision
-hgbranch = {}
-# Number of new changesets converted from hg
-hgnewcsets = 0
-
-#------------------------------------------------------------------------------
-
-def usage():
-
- print("""\
-%s: [OPTIONS] <hgprj>
-
-options:
- -s, --gitstate=FILE: name of the state to be saved/read
- for incrementals
- -n, --nrepack=INT: number of changesets that will trigger
- a repack (default=0, -1 to deactivate)
- -v, --verbose: be verbose
-
-required:
- hgprj: name of the HG project to import (directory)
-""" % sys.argv[0])
-
-#------------------------------------------------------------------------------
-
-def getgitenv(user, date):
- env = ''
- elems = re.compile('(.*?)\s+<(.*)>').match(user)
- if elems:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
- env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
- else:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % user
- env += 'export GIT_COMMITTER_NAME="%s" ;' % user
- env += 'export GIT_AUTHOR_EMAIL= ;'
- env += 'export GIT_COMMITTER_EMAIL= ;'
-
- env += 'export GIT_AUTHOR_DATE="%s" ;' % date
- env += 'export GIT_COMMITTER_DATE="%s" ;' % date
- return env
-
-#------------------------------------------------------------------------------
-
-state = ''
-opt_nrepack = 0
-verbose = False
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
- for o, a in opts:
- if o in ('-s', '--gitstate'):
- state = a
- state = os.path.abspath(state)
- if o in ('-n', '--nrepack'):
- opt_nrepack = int(a)
- if o in ('-v', '--verbose'):
- verbose = True
- if len(args) != 1:
- raise Exception('params')
-except:
- usage()
- sys.exit(1)
-
-hgprj = args[0]
-os.chdir(hgprj)
-
-if state:
- if os.path.exists(state):
- if verbose:
- print('State does exist, reading')
- f = open(state, 'r')
- hgvers = pickle.load(f)
- else:
- print('State does not exist, first run')
-
-sock = os.popen('hg tip --template "{rev}"')
-tip = sock.read()
-if sock.close():
- sys.exit(1)
-if verbose:
- print('tip is', tip)
-
-# Calculate the branches
-if verbose:
- print('analysing the branches...')
-hgchildren["0"] = ()
-hgparents["0"] = (None, None)
-hgbranch["0"] = "master"
-for cset in range(1, int(tip) + 1):
- hgchildren[str(cset)] = ()
- prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
- prnts = map(lambda x: x[:x.find(':')], prnts)
- if prnts[0] != '':
- parent = prnts[0].strip()
- else:
- parent = str(cset - 1)
- hgchildren[parent] += ( str(cset), )
- if len(prnts) > 1:
- mparent = prnts[1].strip()
- hgchildren[mparent] += ( str(cset), )
- else:
- mparent = None
-
- hgparents[str(cset)] = (parent, mparent)
-
- if mparent:
- # For merge changesets, take either one, preferably the 'master' branch
- if hgbranch[mparent] == 'master':
- hgbranch[str(cset)] = 'master'
- else:
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- # Normal changesets
- # For first children, take the parent branch, for the others create a new branch
- if hgchildren[parent][0] == str(cset):
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- hgbranch[str(cset)] = "branch-" + str(cset)
-
-if "0" not in hgvers:
- print('creating repository')
- os.system('git init')
-
-# loop through every hg changeset
-for cset in range(int(tip) + 1):
-
- # incremental, already seen
- if str(cset) in hgvers:
- continue
- hgnewcsets += 1
-
- # get info
- log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
- tag = log_data[0].strip()
- date = log_data[1].strip()
- user = log_data[2].strip()
- parent = hgparents[str(cset)][0]
- mparent = hgparents[str(cset)][1]
-
- #get comment
- (fdcomment, filecomment) = tempfile.mkstemp()
- csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
- os.write(fdcomment, csetcomment)
- os.close(fdcomment)
-
- print('-----------------------------------------')
- print('cset:', cset)
- print('branch:', hgbranch[str(cset)])
- print('user:', user)
- print('date:', date)
- print('comment:', csetcomment)
- if parent:
- print('parent:', parent)
- if mparent:
- print('mparent:', mparent)
- if tag:
- print('tag:', tag)
- print('-----------------------------------------')
-
- # checkout the parent if necessary
- if cset != 0:
- if hgbranch[str(cset)] == "branch-" + str(cset):
- print('creating new branch', hgbranch[str(cset)])
- os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
- else:
- print('checking out branch', hgbranch[str(cset)])
- os.system('git checkout %s' % hgbranch[str(cset)])
-
- # merge
- if mparent:
- if hgbranch[parent] == hgbranch[str(cset)]:
- otherbranch = hgbranch[mparent]
- else:
- otherbranch = hgbranch[parent]
- print('merging', otherbranch, 'into', hgbranch[str(cset)])
- os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
-
- # remove everything except .git and .hg directories
- os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
-
- # repopulate with checkouted files
- os.system('hg update -C %d' % cset)
-
- # add new files
- os.system('git ls-files -x .hg --others | git update-index --add --stdin')
- # delete removed files
- os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
-
- # commit
- os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
- os.unlink(filecomment)
-
- # tag
- if tag and tag != 'tip':
- os.system(getgitenv(user, date) + 'git tag %s' % tag)
-
- # delete branch if not used anymore...
- if mparent and len(hgchildren[str(cset)]):
- print("Deleting unused branch:", otherbranch)
- os.system('git branch -d %s' % otherbranch)
-
- # retrieve and record the version
- vvv = os.popen('git show --quiet --pretty=format:%H').read()
- print('record', cset, '->', vvv)
- hgvers[str(cset)] = vvv
-
-if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
- os.system('git repack -a -d')
-
-# write the state for incrementals
-if state:
- if verbose:
- print('Writing state')
- f = open(state, 'w')
- pickle.dump(hgvers, f)
-
-# vim: et ts=8 sw=4 sts=4
diff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt
deleted file mode 100644
index 91f8fe6410..0000000000
--- a/contrib/hg-to-git/hg-to-git.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-hg-to-git.py is able to convert a Mercurial repository into a git one,
-and preserves the branches in the process (unlike tailor)
-
-hg-to-git.py can probably be greatly improved (it's a rather crude
-combination of shell and python) but it does already work quite well for
-me. Features:
- - supports incremental conversion
- (for keeping a git repo in sync with a hg one)
- - supports hg branches
- - converts hg tags
-
-Note that the git repository will be created 'in place' (at the same
-location as the source hg repo). You will have to manually remove the
-'.hg' directory after the conversion.
-
-Also note that the incremental conversion uses 'simple' hg changesets
-identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
-are not stable across different repositories the hg-to-git.py state file
-is forever tied to one hg repository.
-
-Stelian Pop <stelian@popies.net>
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 3028029ac2..5dab3f506c 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -787,6 +787,22 @@ ensure_valid_ref_format () {
die "fatal: '$1' does not look like a ref"
}
+# Usage: check if a commit from another subtree should be
+# ignored from processing for splits
+should_ignore_subtree_split_commit () {
+ assert test $# = 1
+ local rev="$1"
+ if test -n "$(git log -1 --grep="git-subtree-dir:" $rev)"
+ then
+ if test -z "$(git log -1 --grep="git-subtree-mainline:" $rev)" &&
+ test -z "$(git log -1 --grep="git-subtree-dir: $arg_prefix$" $rev)"
+ then
+ return 0
+ fi
+ fi
+ return 1
+}
+
# Usage: process_split_commit REV PARENTS
process_split_commit () {
assert test $# = 2
@@ -972,7 +988,19 @@ cmd_split () {
eval "$grl" |
while read rev parents
do
- process_split_commit "$rev" "$parents"
+ if should_ignore_subtree_split_commit "$rev"
+ then
+ continue
+ fi
+ parsedparents=''
+ for parent in $parents
+ do
+ if ! should_ignore_subtree_split_commit "$parent"
+ then
+ parsedparents="$parsedparents$parent "
+ fi
+ done
+ process_split_commit "$rev" "$parsedparents"
done || exit $?
latest_new=$(cache_get latest_new) || exit $?
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 49a21dd7c9..c3bd2a58b9 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -63,7 +63,7 @@ test_create_pre2_32_repo () {
git -C "$1" log -1 --format=%B HEAD^2 >msg &&
test_commit -C "$1-sub" --annotate sub2 &&
git clone --no-local "$1" "$1-clone" &&
- new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+ new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
git -C "$1-clone" replace HEAD^2 $new_commit
}
@@ -385,6 +385,46 @@ test_expect_success 'split sub dir/ with --rejoin' '
)
'
+# Tests that commits from other subtrees are not processed as
+# part of a split.
+#
+# This test performs the following:
+# - Creates Repo with subtrees 'subA' and 'subB'
+# - Creates commits in the repo including changes to subtrees
+# - Runs the following 'split' and commit' commands in order:
+# - Perform 'split' on subtree A
+# - Perform 'split' on subtree B
+# - Create new commits with changes to subtree A and B
+# - Perform split on subtree A
+# - Check that the commits in subtree B are not processed
+# as part of the subtree A split
+test_expect_success 'split with multiple subtrees' '
+ subtree_test_create_repo "$test_count" &&
+ subtree_test_create_repo "$test_count/subA" &&
+ subtree_test_create_repo "$test_count/subB" &&
+ test_create_commit "$test_count" main1 &&
+ test_create_commit "$test_count/subA" subA1 &&
+ test_create_commit "$test_count/subA" subA2 &&
+ test_create_commit "$test_count/subA" subA3 &&
+ test_create_commit "$test_count/subB" subB1 &&
+ git -C "$test_count" fetch ./subA HEAD &&
+ git -C "$test_count" subtree add --prefix=subADir FETCH_HEAD &&
+ git -C "$test_count" fetch ./subB HEAD &&
+ git -C "$test_count" subtree add --prefix=subBDir FETCH_HEAD &&
+ test_create_commit "$test_count" subADir/main-subA1 &&
+ test_create_commit "$test_count" subBDir/main-subB1 &&
+ git -C "$test_count" subtree split --prefix=subADir \
+ --squash --rejoin -m "Sub A Split 1" &&
+ git -C "$test_count" subtree split --prefix=subBDir \
+ --squash --rejoin -m "Sub B Split 1" &&
+ test_create_commit "$test_count" subADir/main-subA2 &&
+ test_create_commit "$test_count" subBDir/main-subB2 &&
+ git -C "$test_count" subtree split --prefix=subADir \
+ --squash --rejoin -m "Sub A Split 2" &&
+ test "$(git -C "$test_count" subtree split --prefix=subBDir \
+ --squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = ""
+'
+
test_expect_success 'split sub dir/ with --rejoin from scratch' '
subtree_test_create_repo "$test_count" &&
test_create_commit "$test_count" main1 &&
diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh
index 521d303722..f2d61bb0e6 100755
--- a/contrib/vscode/init.sh
+++ b/contrib/vscode/init.sh
@@ -92,7 +92,6 @@ cat >.vscode/settings.json.new <<\EOF ||
"isexe",
"iskeychar",
"kompare",
- "mksnpath",
"mktag",
"mktree",
"mmblob",
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 888c34a521..989197aace 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -79,7 +79,7 @@ trap cleanup $siglist
# create the links to the original repo. explicitly exclude index, HEAD and
# logs/HEAD from the list since they are purely related to the current working
# directory, and should not be shared.
-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable
do
# create a containing directory if needed
case $x in
diff --git a/convert.c b/convert.c
index a8870baff3..35b25eb3cb 100644
--- a/convert.c
+++ b/convert.c
@@ -1028,7 +1028,7 @@ static int read_convert_config(const char *var, const char *value,
if (parse_config_key(var, "filter", &name, &namelen, &key) < 0 || !name)
return 0;
for (drv = user_convert; drv; drv = drv->next)
- if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
+ if (!xstrncmpz(drv->name, name, namelen))
break;
if (!drv) {
CALLOC_ARRAY(drv, 1);
diff --git a/convert.h b/convert.h
index d925589444..ab8b4fa68d 100644
--- a/convert.h
+++ b/convert.h
@@ -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/date.c b/date.c
index 619ada5b20..7365a4ad24 100644
--- a/date.c
+++ b/date.c
@@ -207,13 +207,13 @@ void show_date_relative(timestamp_t time, struct strbuf *timebuf)
(diff + 183) / 365);
}
-struct date_mode *date_mode_from_type(enum date_mode_type type)
+struct date_mode date_mode_from_type(enum date_mode_type type)
{
- static struct date_mode mode = DATE_MODE_INIT;
+ struct date_mode mode = DATE_MODE_INIT;
if (type == DATE_STRFTIME)
BUG("cannot create anonymous strftime date_mode struct");
mode.type = type;
- return &mode;
+ return mode;
}
static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm, int tz, struct tm *human_tm, int human_tz, int local)
@@ -283,7 +283,7 @@ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm
strbuf_addf(buf, " %+05d", tz);
}
-const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
+const char *show_date(timestamp_t time, int tz, struct date_mode mode)
{
struct tm *tm;
struct tm tmbuf = { 0 };
@@ -291,13 +291,13 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
int human_tz = -1;
static struct strbuf timebuf = STRBUF_INIT;
- if (mode->type == DATE_UNIX) {
+ if (mode.type == DATE_UNIX) {
strbuf_reset(&timebuf);
strbuf_addf(&timebuf, "%"PRItime, time);
return timebuf.buf;
}
- if (mode->type == DATE_HUMAN) {
+ if (mode.type == DATE_HUMAN) {
struct timeval now;
get_time(&now);
@@ -306,22 +306,22 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
human_tz = local_time_tzoffset(now.tv_sec, &human_tm);
}
- if (mode->local)
+ if (mode.local)
tz = local_tzoffset(time);
- if (mode->type == DATE_RAW) {
+ if (mode.type == DATE_RAW) {
strbuf_reset(&timebuf);
strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz);
return timebuf.buf;
}
- if (mode->type == DATE_RELATIVE) {
+ if (mode.type == DATE_RELATIVE) {
strbuf_reset(&timebuf);
show_date_relative(time, &timebuf);
return timebuf.buf;
}
- if (mode->local)
+ if (mode.local)
tm = time_to_tm_local(time, &tmbuf);
else
tm = time_to_tm(time, tz, &tmbuf);
@@ -331,35 +331,39 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
}
strbuf_reset(&timebuf);
- if (mode->type == DATE_SHORT)
+ if (mode.type == DATE_SHORT)
strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday);
- else if (mode->type == DATE_ISO8601)
+ else if (mode.type == DATE_ISO8601)
strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tz);
- else if (mode->type == DATE_ISO8601_STRICT) {
- char sign = (tz >= 0) ? '+' : '-';
- tz = abs(tz);
- strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
+ else if (mode.type == DATE_ISO8601_STRICT) {
+ strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- sign, tz / 100, tz % 100);
- } else if (mode->type == DATE_RFC2822)
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ if (tz == 0) {
+ strbuf_addch(&timebuf, 'Z');
+ } else {
+ strbuf_addch(&timebuf, tz >= 0 ? '+' : '-');
+ tz = abs(tz);
+ strbuf_addf(&timebuf, "%02d:%02d", tz / 100, tz % 100);
+ }
+ } else if (mode.type == DATE_RFC2822)
strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
- else if (mode->type == DATE_STRFTIME)
- strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz,
- !mode->local);
+ else if (mode.type == DATE_STRFTIME)
+ strbuf_addftime(&timebuf, mode.strftime_fmt, tm, tz,
+ !mode.local);
else
- show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode->local);
+ show_date_normal(&timebuf, time, tm, tz, &human_tm, human_tz, mode.local);
return timebuf.buf;
}
diff --git a/date.h b/date.h
index 6136212a19..0747864fd7 100644
--- a/date.h
+++ b/date.h
@@ -22,8 +22,8 @@ enum date_mode_type {
struct date_mode {
enum date_mode_type type;
- const char *strftime_fmt;
int local;
+ const char *strftime_fmt;
};
#define DATE_MODE_INIT { \
@@ -36,14 +36,14 @@ struct date_mode {
* show_date(t, tz, DATE_MODE(NORMAL));
*/
#define DATE_MODE(t) date_mode_from_type(DATE_##t)
-struct date_mode *date_mode_from_type(enum date_mode_type type);
+struct date_mode date_mode_from_type(enum date_mode_type type);
/**
* Format <'time', 'timezone'> into static memory according to 'mode'
* and return it. The mode is an initialized "struct date_mode"
* (usually from the DATE_MODE() macro).
*/
-const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+const char *show_date(timestamp_t time, int timezone, struct date_mode mode);
/**
* Parse a date format for later use with show_date().
diff --git a/delta-islands.c b/delta-islands.c
index 5de5759f3f..f7e079425f 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -1,18 +1,13 @@
#include "git-compat-util.h"
-#include "attr.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"
@@ -289,7 +284,7 @@ void resolve_tree_islands(struct repository *r,
if (!tree || parse_tree(tree) < 0)
die(_("bad tree object %s"), oid_to_hex(&ent->idx.oid));
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj;
diff --git a/diff-lib.c b/diff-lib.c
index 0e9ec4f68a..683f11e509 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -2,7 +2,6 @@
* Copyright (C) 2005 Junio C Hamano
*/
#include "git-compat-util.h"
-#include "quote.h"
#include "commit.h"
#include "diff.h"
#include "diffcore.h"
@@ -38,7 +37,13 @@
*/
static int check_removed(const struct cache_entry *ce, struct stat *st)
{
- if (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;
@@ -122,7 +127,16 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
if (diff_can_quit_early(&revs->diffopt))
break;
- if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
+ /*
+ * NEEDSWORK:
+ * Here we filter with pathspec but the result is further
+ * filtered out when --relative is in effect. To end-users,
+ * a pathspec element that matched only to paths outside the
+ * current directory is like not matching anything at all;
+ * the handling of ps_matched[] here may become problematic
+ * if/when we add the "--error-unmatch" option to "git diff".
+ */
+ if (!ce_path_match(istate, ce, &revs->prune_data, revs->ps_matched))
continue;
if (revs->diffopt.prefix &&
@@ -557,7 +571,7 @@ static int diff_cache(struct rev_info *revs,
opts.pathspec = &revs->diffopt.pathspec;
opts.pathspec->recursive = 1;
- init_tree_desc(&t, tree->buffer, tree->size);
+ init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
return unpack_trees(1, &t, &opts);
}
@@ -565,7 +579,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
{
int i;
struct commit *mb_child[2] = {0};
- struct commit_list *merge_bases;
+ struct commit_list *merge_bases = NULL;
for (i = 0; i < revs->pending.nr; i++) {
struct object *obj = revs->pending.objects[i].item;
@@ -592,7 +606,8 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
mb_child[1] = lookup_commit_reference(the_repository, &oid);
}
- merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]);
+ if (repo_get_merge_bases(the_repository, mb_child[0], mb_child[1], &merge_bases) < 0)
+ exit(128);
if (!merge_bases)
die(_("no merge base found"));
if (merge_bases->next)
diff --git a/diff-no-index.c b/diff-no-index.c
index e7041b89e3..3a8965672c 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -8,13 +8,10 @@
#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 "parse-options.h"
#include "string-list.h"
#include "dir.h"
diff --git a/diff.c b/diff.c
index 2c602df10a..108c187577 100644
--- a/diff.c
+++ b/diff.c
@@ -16,12 +16,10 @@
#include "hex.h"
#include "xdiff-interface.h"
#include "color.h"
-#include "attr.h"
#include "run-command.h"
#include "utf8.h"
#include "object-store-ll.h"
#include "userdiff.h"
-#include "submodule-config.h"
#include "submodule.h"
#include "hashmap.h"
#include "mem-pool.h"
@@ -64,6 +62,8 @@ static const char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static const char *diff_src_prefix = "a/";
+static const char *diff_dst_prefix = "b/";
static int diff_relative;
static int diff_stat_name_width;
static int diff_stat_graph_width;
@@ -372,7 +372,10 @@ int git_diff_ui_config(const char *var, const char *value,
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;
@@ -407,6 +410,12 @@ int git_diff_ui_config(const char *var, const char *value,
diff_no_prefix = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.srcprefix")) {
+ return git_config_string(&diff_src_prefix, var, value);
+ }
+ if (!strcmp(var, "diff.dstprefix")) {
+ return git_config_string(&diff_dst_prefix, var, value);
+ }
if (!strcmp(var, "diff.relative")) {
diff_relative = git_config_bool(var, value);
return 0;
@@ -426,10 +435,15 @@ int git_diff_ui_config(const char *var, const char *value,
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);
@@ -437,9 +451,12 @@ int git_diff_ui_config(const char *var, const char *value,
}
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;
}
@@ -473,9 +490,13 @@ int git_diff_basic_config(const char *var, const char *value,
}
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;
}
@@ -490,6 +511,8 @@ int git_diff_basic_config(const char *var, const char *value,
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"),
@@ -3410,8 +3433,8 @@ void diff_set_noprefix(struct diff_options *options)
void diff_set_default_prefix(struct diff_options *options)
{
- options->a_prefix = "a/";
- options->b_prefix = "b/";
+ options->a_prefix = diff_src_prefix;
+ options->b_prefix = diff_dst_prefix;
}
struct userdiff_driver *get_textconv(struct repository *r,
@@ -4369,7 +4392,8 @@ static void run_external_diff(const char *pgm,
add_external_diff_name(o->repo, &cmd.args, two);
if (other) {
strvec_push(&cmd.args, other);
- strvec_push(&cmd.args, xfrm_msg);
+ if (xfrm_msg)
+ strvec_push(&cmd.args, xfrm_msg);
}
}
@@ -5346,6 +5370,8 @@ static int diff_opt_default_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
+ diff_src_prefix = "a/";
+ diff_dst_prefix = "b/";
diff_set_default_prefix(options);
return 0;
}
@@ -5574,7 +5600,7 @@ struct option *add_diff_options(const struct option *opts,
OPT_BITOP(0, "shortstat", &options->output_format,
N_("output only the last line of --stat"),
DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
- OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
+ OPT_CALLBACK_F('X', "dirstat", options, N_("<param1>,<param2>..."),
N_("output the distribution of relative amount of changes for each sub-directory"),
PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
diff_opt_dirstat),
@@ -5582,8 +5608,8 @@ struct option *add_diff_options(const struct option *opts,
N_("synonym for --dirstat=cumulative"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG,
diff_opt_dirstat),
- OPT_CALLBACK_F(0, "dirstat-by-file", options, N_("<param1,param2>..."),
- N_("synonym for --dirstat=files,param1,param2..."),
+ OPT_CALLBACK_F(0, "dirstat-by-file", options, N_("<param1>,<param2>..."),
+ N_("synonym for --dirstat=files,<param1>,<param2>..."),
PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
diff_opt_dirstat),
OPT_BIT_F(0, "check", &options->output_format,
diff --git a/diffcore-break.c b/diffcore-break.c
index f57ece2757..49ba38aa7c 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -2,7 +2,6 @@
* Copyright (C) 2005 Junio C Hamano
*/
#include "git-compat-util.h"
-#include "diff.h"
#include "diffcore.h"
#include "hash.h"
#include "object.h"
diff --git a/diffcore-delta.c b/diffcore-delta.c
index c30b56e983..ba6cbee76b 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "diff.h"
#include "diffcore.h"
/*
@@ -159,6 +158,10 @@ static struct spanhash_top *hash_chars(struct repository *r,
n = 0;
accum1 = accum2 = 0;
}
+ if (n > 0) {
+ hashval = (accum1 + accum2 * 0x61) % HASHBASE;
+ hash = add_spanhash(hash, hashval, n);
+ }
QSORT(hash->data, (size_t)1ul << hash->alloc_log2, spanhash_cmp);
return hash;
}
diff --git a/dir-iterator.c b/dir-iterator.c
index 278b04243a..de619846f2 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -2,11 +2,20 @@
#include "dir.h"
#include "iterator.h"
#include "dir-iterator.h"
+#include "string-list.h"
struct dir_iterator_level {
DIR *dir;
/*
+ * The directory entries of the current level. This list will only be
+ * populated when the iterator is ordered. In that case, `dir` will be
+ * set to `NULL`.
+ */
+ struct string_list entries;
+ size_t entries_idx;
+
+ /*
* The length of the directory part of path at this level
* (including a trailing '/'):
*/
@@ -43,6 +52,31 @@ struct dir_iterator_int {
unsigned int flags;
};
+static int next_directory_entry(DIR *dir, const char *path,
+ struct dirent **out)
+{
+ struct dirent *de;
+
+repeat:
+ errno = 0;
+ de = readdir(dir);
+ if (!de) {
+ if (errno) {
+ warning_errno("error reading directory '%s'",
+ path);
+ return -1;
+ }
+
+ return 1;
+ }
+
+ if (is_dot_or_dotdot(de->d_name))
+ goto repeat;
+
+ *out = de;
+ return 0;
+}
+
/*
* Push a level in the iter stack and initialize it with information from
* the directory pointed by iter->base->path. It is assumed that this
@@ -72,6 +106,35 @@ static int push_level(struct dir_iterator_int *iter)
return -1;
}
+ string_list_init_dup(&level->entries);
+ level->entries_idx = 0;
+
+ /*
+ * When the iterator is sorted we read and sort all directory entries
+ * directly.
+ */
+ if (iter->flags & DIR_ITERATOR_SORTED) {
+ struct dirent *de;
+
+ while (1) {
+ int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+ if (ret < 0) {
+ if (errno != ENOENT &&
+ iter->flags & DIR_ITERATOR_PEDANTIC)
+ return -1;
+ continue;
+ } else if (ret > 0) {
+ break;
+ }
+
+ string_list_append(&level->entries, de->d_name);
+ }
+ string_list_sort(&level->entries);
+
+ closedir(level->dir);
+ level->dir = NULL;
+ }
+
return 0;
}
@@ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter)
warning_errno("error closing directory '%s'",
iter->base.path.buf);
level->dir = NULL;
+ string_list_clear(&level->entries, 0);
return --iter->levels_nr;
}
/*
* Populate iter->base with the necessary information on the next iteration
- * entry, represented by the given dirent de. Return 0 on success and -1
+ * entry, represented by the given name. Return 0 on success and -1
* otherwise, setting errno accordingly.
*/
static int prepare_next_entry_data(struct dir_iterator_int *iter,
- struct dirent *de)
+ const char *name)
{
int err, saved_errno;
- strbuf_addstr(&iter->base.path, de->d_name);
+ strbuf_addstr(&iter->base.path, name);
/*
* We have to reset these because the path strbuf might have
* been realloc()ed at the previous strbuf_addstr().
@@ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
struct dirent *de;
struct dir_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
+ const char *name;
strbuf_setlen(&iter->base.path, level->prefix_len);
- errno = 0;
- de = readdir(level->dir);
- if (!de) {
- if (errno) {
- warning_errno("error reading directory '%s'",
- iter->base.path.buf);
+ if (level->dir) {
+ int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
+ if (ret < 0) {
if (iter->flags & DIR_ITERATOR_PEDANTIC)
goto error_out;
- } else if (pop_level(iter) == 0) {
- return dir_iterator_abort(dir_iterator);
+ continue;
+ } else if (ret > 0) {
+ if (pop_level(iter) == 0)
+ return dir_iterator_abort(dir_iterator);
+ continue;
}
- continue;
- }
- if (is_dot_or_dotdot(de->d_name))
- continue;
+ name = de->d_name;
+ } else {
+ if (level->entries_idx >= level->entries.nr) {
+ if (pop_level(iter) == 0)
+ return dir_iterator_abort(dir_iterator);
+ continue;
+ }
- if (prepare_next_entry_data(iter, de)) {
+ name = level->entries.items[level->entries_idx++].string;
+ }
+
+ if (prepare_next_entry_data(iter, name)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
goto error_out;
continue;
@@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
warning_errno("error closing directory '%s'",
iter->base.path.buf);
}
+
+ string_list_clear(&level->entries, 0);
}
free(iter->levels);
diff --git a/dir-iterator.h b/dir-iterator.h
index 479e1ec784..6d438809b6 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -54,8 +54,11 @@
* and ITER_ERROR is returned immediately. In both cases, a meaningful
* warning is emitted. Note: ENOENT errors are always ignored so that
* the API users may remove files during iteration.
+ *
+ * - DIR_ITERATOR_SORTED: sort directory entries alphabetically.
*/
#define DIR_ITERATOR_PEDANTIC (1 << 0)
+#define DIR_ITERATOR_SORTED (1 << 1)
struct dir_iterator {
/* The current path: */
diff --git a/dir.c b/dir.c
index 4d1cd039be..20ebe4cba2 100644
--- a/dir.c
+++ b/dir.c
@@ -16,7 +16,6 @@
#include "object-file.h"
#include "object-store-ll.h"
#include "path.h"
-#include "attr.h"
#include "refs.h"
#include "wildmatch.h"
#include "pathspec.h"
@@ -3919,6 +3918,26 @@ void untracked_cache_invalidate_path(struct index_state *istate,
path, strlen(path));
}
+void untracked_cache_invalidate_trimmed_path(struct index_state *istate,
+ const char *path,
+ int safe_path)
+{
+ size_t len = strlen(path);
+
+ if (!len)
+ BUG("untracked_cache_invalidate_trimmed_path given zero length path");
+
+ if (path[len - 1] != '/') {
+ untracked_cache_invalidate_path(istate, path, safe_path);
+ } else {
+ struct strbuf tmp = STRBUF_INIT;
+
+ strbuf_add(&tmp, path, len - 1);
+ untracked_cache_invalidate_path(istate, tmp.buf, safe_path);
+ strbuf_release(&tmp);
+ }
+}
+
void untracked_cache_remove_from_index(struct index_state *istate,
const char *path)
{
diff --git a/dir.h b/dir.h
index 98aa85fcc0..45a7b9ec5f 100644
--- a/dir.h
+++ b/dir.h
@@ -576,6 +576,13 @@ int cmp_dir_entry(const void *p1, const void *p2);
int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
+/*
+ * Invalidate the untracked-cache for this path, but first strip
+ * off a trailing slash, if present.
+ */
+void untracked_cache_invalidate_trimmed_path(struct index_state *,
+ const char *path,
+ int safe_path);
void untracked_cache_remove_from_index(struct index_state *, const char *);
void untracked_cache_add_to_index(struct index_state *, const char *);
diff --git a/editor.c b/editor.c
index b67b802ddf..d1ba2d7c34 100644
--- a/editor.c
+++ b/editor.c
@@ -104,16 +104,15 @@ static int launch_specified_editor(const char *editor, const char *path,
sigchain_pop(SIGQUIT);
if (sig == SIGINT || sig == SIGQUIT)
raise(sig);
- if (ret)
- return error("There was a problem with the editor '%s'.",
- editor);
-
if (print_waiting_for_editor && !is_terminal_dumb())
/*
* Erase the entire line to avoid wasting the
* vertical space.
*/
term_clear_line();
+ if (ret)
+ return error("there was a problem with the editor '%s'",
+ editor);
}
if (!buffer)
diff --git a/entry.c b/entry.c
index 076e97eb89..f918a3a78e 100644
--- a/entry.c
+++ b/entry.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "blob.h"
#include "object-store-ll.h"
#include "dir.h"
#include "environment.h"
diff --git a/environment.c b/environment.c
index 9e37bf58c0..a73ba9c12c 100644
--- a/environment.c
+++ b/environment.c
@@ -64,7 +64,7 @@ const char *excludes_file;
enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
enum eol core_eol = EOL_UNSET;
int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-char *check_roundtrip_encoding = "SHIFT-JIS";
+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;
@@ -110,7 +110,7 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT;
* The character that begins a commented line in user-editable file
* that is subject to stripspace.
*/
-char comment_line_char = '#';
+const char *comment_line_str = "#";
int auto_comment_line_char;
/* Parallel index stat data preload? */
@@ -207,6 +207,9 @@ void setup_git_env(const char *git_dir)
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
if (shallow_file)
set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+ if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+ fetch_if_missing = 0;
}
int is_bare_repository(void)
diff --git a/environment.h b/environment.h
index e5351c9dd9..05fd94d7be 100644
--- a/environment.h
+++ b/environment.h
@@ -8,7 +8,7 @@ struct strvec;
* The character that begins a commented line in user-editable file
* that is subject to stripspace.
*/
-extern char comment_line_char;
+extern const char *comment_line_str;
extern int auto_comment_line_char;
/*
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
#define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE"
+#define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH"
#define GITATTRIBUTES_FILE ".gitattributes"
#define INFOATTRIBUTES_FILE "info/attributes"
#define ATTRIBUTE_MACRO_PREFIX "[attr]"
diff --git a/exec-cmd.c b/exec-cmd.c
index 1d597e84ea..909777f61f 100644
--- a/exec-cmd.c
+++ b/exec-cmd.c
@@ -4,7 +4,6 @@
#include "exec-cmd.h"
#include "gettext.h"
#include "path.h"
-#include "quote.h"
#include "run-command.h"
#include "strvec.h"
#include "trace.h"
diff --git a/fetch-pack.c b/fetch-pack.c
index 26999e3b65..091f9a80a9 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -10,7 +10,6 @@
#include "pkt-line.h"
#include "commit.h"
#include "tag.h"
-#include "exec-cmd.h"
#include "pack.h"
#include "sideband.h"
#include "fetch-pack.h"
@@ -18,7 +17,6 @@
#include "run-command.h"
#include "connect.h"
#include "trace2.h"
-#include "transport.h"
#include "version.h"
#include "oid-array.h"
#include "oidset.h"
@@ -1862,6 +1860,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
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;
@@ -1873,12 +1873,14 @@ static int fetch_pack_config_cb(const char *var, const char *value,
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;
}
@@ -2214,7 +2216,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
the_repository, "%d",
negotiation_round);
}
- trace2_region_enter("fetch-pack", "negotiate_using_fetch", the_repository);
+ trace2_region_leave("fetch-pack", "negotiate_using_fetch", the_repository);
trace2_data_intmax("negotiate_using_fetch", the_repository,
"total_rounds", negotiation_round);
clear_common_flag(acked_commits);
diff --git a/fetch-pack.h b/fetch-pack.h
index 8c7752fc82..6775d26517 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -2,7 +2,6 @@
#define FETCH_PACK_H
#include "string-list.h"
-#include "run-command.h"
#include "protocol.h"
#include "list-objects-filter-options.h"
#include "oidset.h"
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 66e47449a0..ae201e21db 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -321,7 +321,7 @@ static void credit_people(struct strbuf *out,
skip_prefix(me, them->items->string, &me) &&
starts_with(me, " <")))
return;
- strbuf_addf(out, "\n%c %s ", comment_line_char, label);
+ strbuf_addf(out, "\n%s %s ", comment_line_str, label);
add_people_count(out, them);
}
@@ -510,7 +510,7 @@ static void fmt_tag_signature(struct strbuf *tagbuf,
if (sig->len) {
strbuf_addch(tagbuf, '\n');
strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
- comment_line_char);
+ comment_line_str);
}
}
@@ -557,7 +557,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
strbuf_add_commented_lines(&tagline,
origins.items[first_tag].string,
strlen(origins.items[first_tag].string),
- comment_line_char);
+ comment_line_str);
strbuf_insert(&tagbuf, 0, tagline.buf,
tagline.len);
strbuf_release(&tagline);
@@ -566,7 +566,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
strbuf_add_commented_lines(&tagbuf,
origins.items[i].string,
strlen(origins.items[i].string),
- comment_line_char);
+ comment_line_str);
fmt_tag_signature(&tagbuf, &sig, buf, len);
}
strbuf_release(&payload);
diff --git a/fsck.c b/fsck.c
index 6a0bbc5087..78af29d264 100644
--- a/fsck.c
+++ b/fsck.c
@@ -16,12 +16,10 @@
#include "refs.h"
#include "url.h"
#include "utf8.h"
-#include "decorate.h"
#include "oidset.h"
#include "packfile.h"
#include "submodule-config.h"
#include "config.h"
-#include "credential.h"
#include "help.h"
static ssize_t max_tree_entry_len = 4096;
@@ -329,7 +327,8 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+ if (init_tree_desc_gently(&desc, &tree->object.oid,
+ tree->buffer, tree->size, 0))
return -1;
while (tree_entry_gently(&desc, &entry)) {
struct object *obj;
@@ -600,7 +599,8 @@ static int fsck_tree(const struct object_id *tree_oid,
const char *o_name;
struct name_stack df_dup_candidates = { NULL };
- if (init_tree_desc_gently(&desc, buffer, size, TREE_DESC_RAW_MODES)) {
+ if (init_tree_desc_gently(&desc, tree_oid, buffer, size,
+ TREE_DESC_RAW_MODES)) {
retval += report(options, tree_oid, OBJ_TREE,
FSCK_MSG_BAD_TREE,
"cannot be parsed as a tree");
@@ -1048,138 +1048,6 @@ done:
return ret;
}
-static int starts_with_dot_slash(const char *const path)
-{
- return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
- PATH_MATCH_XPLATFORM);
-}
-
-static int starts_with_dot_dot_slash(const char *const path)
-{
- return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
- PATH_MATCH_XPLATFORM);
-}
-
-static int submodule_url_is_relative(const char *url)
-{
- return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
-}
-
-/*
- * Count directory components that a relative submodule URL should chop
- * from the remote_url it is to be resolved against.
- *
- * In other words, this counts "../" components at the start of a
- * submodule URL.
- *
- * Returns the number of directory components to chop and writes a
- * pointer to the next character of url after all leading "./" and
- * "../" components to out.
- */
-static int count_leading_dotdots(const char *url, const char **out)
-{
- int result = 0;
- while (1) {
- if (starts_with_dot_dot_slash(url)) {
- result++;
- url += strlen("../");
- continue;
- }
- if (starts_with_dot_slash(url)) {
- url += strlen("./");
- continue;
- }
- *out = url;
- return result;
- }
-}
-/*
- * Check whether a transport is implemented by git-remote-curl.
- *
- * If it is, returns 1 and writes the URL that would be passed to
- * git-remote-curl to the "out" parameter.
- *
- * Otherwise, returns 0 and leaves "out" untouched.
- *
- * Examples:
- * http::https://example.com/repo.git -> 1, https://example.com/repo.git
- * https://example.com/repo.git -> 1, https://example.com/repo.git
- * git://example.com/repo.git -> 0
- *
- * This is for use in checking for previously exploitable bugs that
- * required a submodule URL to be passed to git-remote-curl.
- */
-static int url_to_curl_url(const char *url, const char **out)
-{
- /*
- * We don't need to check for case-aliases, "http.exe", and so
- * on because in the default configuration, is_transport_allowed
- * prevents URLs with those schemes from being cloned
- * automatically.
- */
- if (skip_prefix(url, "http::", out) ||
- skip_prefix(url, "https::", out) ||
- skip_prefix(url, "ftp::", out) ||
- skip_prefix(url, "ftps::", out))
- return 1;
- if (starts_with(url, "http://") ||
- starts_with(url, "https://") ||
- starts_with(url, "ftp://") ||
- starts_with(url, "ftps://")) {
- *out = url;
- return 1;
- }
- return 0;
-}
-
-static int check_submodule_url(const char *url)
-{
- const char *curl_url;
-
- if (looks_like_command_line_option(url))
- return -1;
-
- if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
- char *decoded;
- const char *next;
- int has_nl;
-
- /*
- * This could be appended to an http URL and url-decoded;
- * check for malicious characters.
- */
- decoded = url_decode(url);
- has_nl = !!strchr(decoded, '\n');
-
- free(decoded);
- if (has_nl)
- return -1;
-
- /*
- * URLs which escape their root via "../" can overwrite
- * the host field and previous components, resolving to
- * URLs like https::example.com/submodule.git and
- * https:///example.com/submodule.git that were
- * susceptible to CVE-2020-11008.
- */
- if (count_leading_dotdots(url, &next) > 0 &&
- (*next == ':' || *next == '/'))
- return -1;
- }
-
- else if (url_to_curl_url(url, &curl_url)) {
- struct credential c = CREDENTIAL_INIT;
- int ret = 0;
- if (credential_from_url_gently(&c, curl_url, 1) ||
- !*c.host)
- ret = -1;
- credential_clear(&c);
- return ret;
- }
-
- return 0;
-}
-
struct fsck_gitmodules_data {
const struct object_id *oid;
struct fsck_options *options;
@@ -1403,6 +1271,8 @@ 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;
@@ -1416,8 +1286,10 @@ int git_fsck_config(const char *var, const char *value,
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;
}
diff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h
index 673f80d2aa..5cbbec8d94 100644
--- a/fsmonitor--daemon.h
+++ b/fsmonitor--daemon.h
@@ -3,9 +3,7 @@
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
-#include "dir.h"
-#include "run-command.h"
-#include "simple-ipc.h"
+#include "hashmap.h"
#include "thread-utils.h"
#include "fsmonitor-path-utils.h"
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index 153918cf76..45471b5b74 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "fsmonitor-ll.h"
#include "gettext.h"
#include "simple-ipc.h"
#include "fsmonitor-ipc.h"
diff --git a/fsmonitor.c b/fsmonitor.c
index f670c50937..2b17d60bbb 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -5,6 +5,7 @@
#include "ewah/ewok.h"
#include "fsmonitor.h"
#include "fsmonitor-ipc.h"
+#include "name-hash.h"
#include "run-command.h"
#include "strbuf.h"
#include "trace2.h"
@@ -183,79 +184,282 @@ static int query_fsmonitor_hook(struct repository *r,
return result;
}
-static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
+/*
+ * Invalidate the FSM bit on this CE. This is like mark_fsmonitor_invalid()
+ * but we've already handled the untracked-cache, so let's not repeat that
+ * work. This also lets us have a different trace message so that we can
+ * see everything that was done as part of the refresh-callback.
+ */
+static void invalidate_ce_fsm(struct cache_entry *ce)
{
- int i, len = strlen(name);
- int pos = index_name_pos(istate, name, len);
+ if (ce->ce_flags & CE_FSMONITOR_VALID) {
+ trace_printf_key(&trace_fsmonitor,
+ "fsmonitor_refresh_callback INV: '%s'",
+ ce->name);
+ ce->ce_flags &= ~CE_FSMONITOR_VALID;
+ }
+}
+
+static size_t handle_path_with_trailing_slash(
+ struct index_state *istate, const char *name, int pos);
+
+/*
+ * Use the name-hash to do a case-insensitive cache-entry lookup with
+ * the pathname and invalidate the cache-entry.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_name_hash_icase(
+ struct index_state *istate, const char *name)
+{
+ struct cache_entry *ce = NULL;
+
+ ce = index_file_exists(istate, name, strlen(name), 1);
+ if (!ce)
+ return 0;
+ /*
+ * A case-insensitive search in the name-hash using the
+ * observed pathname found a cache-entry, so the observed path
+ * is case-incorrect. Invalidate the cache-entry and use the
+ * correct spelling from the cache-entry to invalidate the
+ * untracked-cache. Since we now have sparse-directories in
+ * the index, the observed pathname may represent a regular
+ * file or a sparse-index directory.
+ *
+ * Note that we should not have seen FSEvents for a
+ * sparse-index directory, but we handle it just in case.
+ *
+ * Either way, we know that there are not any cache-entries for
+ * children inside the cone of the directory, so we don't need to
+ * do the usual scan.
+ */
trace_printf_key(&trace_fsmonitor,
- "fsmonitor_refresh_callback '%s' (pos %d)",
- name, pos);
+ "fsmonitor_refresh_callback MAP: '%s' '%s'",
+ name, ce->name);
- if (name[len - 1] == '/') {
- /*
- * The daemon can decorate directory events, such as
- * moves or renames, with a trailing slash if the OS
- * FS Event contains sufficient information, such as
- * MacOS.
- *
- * Use this to invalidate the entire cone under that
- * directory.
- *
- * We do not expect an exact match because the index
- * does not normally contain directory entries, so we
- * start at the insertion point and scan.
- */
- if (pos < 0)
- pos = -pos - 1;
+ /*
+ * NEEDSWORK: We used the name-hash to find the correct
+ * case-spelling of the pathname in the cache-entry[], so
+ * technically this is a tracked file or a sparse-directory.
+ * It should not have any entries in the untracked-cache, so
+ * we should not need to use the case-corrected spelling to
+ * invalidate the the untracked-cache. So we may not need to
+ * do this. For now, I'm going to be conservative and always
+ * do it; we can revisit this later.
+ */
+ untracked_cache_invalidate_trimmed_path(istate, ce->name, 0);
- /* Mark all entries for the folder invalid */
- for (i = pos; i < istate->cache_nr; i++) {
- if (!starts_with(istate->cache[i]->name, name))
- break;
- istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
- }
+ invalidate_ce_fsm(ce);
+ return 1;
+}
+
+/*
+ * Use the dir-name-hash to find the correct-case spelling of the
+ * directory. Use the canonical spelling to invalidate all of the
+ * cache-entries within the matching cone.
+ *
+ * Returns the number of cache-entries that we invalidated.
+ */
+static size_t handle_using_dir_name_hash_icase(
+ struct index_state *istate, const char *name)
+{
+ struct strbuf canonical_path = STRBUF_INIT;
+ int pos;
+ size_t len = strlen(name);
+ size_t nr_in_cone;
+
+ if (name[len - 1] == '/')
+ len--;
+
+ if (!index_dir_find(istate, name, len, &canonical_path))
+ return 0; /* name is untracked */
+ if (!memcmp(name, canonical_path.buf, canonical_path.len)) {
+ strbuf_release(&canonical_path);
/*
- * We need to remove the traling "/" from the path
- * for the untracked cache.
+ * NEEDSWORK: Our caller already tried an exact match
+ * and failed to find one. They called us to do an
+ * ICASE match, so we should never get an exact match,
+ * so we could promote this to a BUG() here if we
+ * wanted to. It doesn't hurt anything to just return
+ * 0 and go on because we should never get here. Or we
+ * could just get rid of the memcmp() and this "if"
+ * clause completely.
*/
- name[len - 1] = '\0';
- } else if (pos >= 0) {
+ BUG("handle_using_dir_name_hash_icase(%s) did not exact match",
+ name);
+ }
+
+ trace_printf_key(&trace_fsmonitor,
+ "fsmonitor_refresh_callback MAP: '%s' '%s'",
+ name, canonical_path.buf);
+
+ /*
+ * The dir-name-hash only tells us the corrected spelling of
+ * the prefix. We have to use this canonical path to do a
+ * lookup in the cache-entry array so that we repeat the
+ * original search using the case-corrected spelling.
+ */
+ strbuf_addch(&canonical_path, '/');
+ pos = index_name_pos(istate, canonical_path.buf,
+ canonical_path.len);
+ nr_in_cone = handle_path_with_trailing_slash(
+ istate, canonical_path.buf, pos);
+ strbuf_release(&canonical_path);
+ return nr_in_cone;
+}
+
+/*
+ * The daemon sent an observed pathname without a trailing slash.
+ * (This is the normal case.) We do not know if it is a tracked or
+ * untracked file, a sparse-directory, or a populated directory (on a
+ * platform such as Windows where FSEvents are not qualified).
+ *
+ * The pathname contains the observed case reported by the FS. We
+ * do not know it is case-correct or -incorrect.
+ *
+ * Assume it is case-correct and try an exact match.
+ *
+ * Return the number of cache-entries that we invalidated.
+ */
+static size_t handle_path_without_trailing_slash(
+ struct index_state *istate, const char *name, int pos)
+{
+ /*
+ * Mark the untracked cache dirty for this path (regardless of
+ * whether or not we find an exact match for it in the index).
+ * Since the path is unqualified (no trailing slash hint in the
+ * FSEvent), it may refer to a file or directory. So we should
+ * not assume one or the other and should always let the untracked
+ * cache decide what needs to invalidated.
+ */
+ untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+ if (pos >= 0) {
/*
- * We have an exact match for this path and can just
- * invalidate it.
+ * An exact match on a tracked file. We assume that we
+ * do not need to scan forward for a sparse-directory
+ * cache-entry with the same pathname, nor for a cone
+ * at that directory. (That is, assume no D/F conflicts.)
*/
- istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
+ invalidate_ce_fsm(istate->cache[pos]);
+ return 1;
} else {
+ size_t nr_in_cone;
+ struct strbuf work_path = STRBUF_INIT;
+
/*
- * The path is not a tracked file -or- it is a
- * directory event on a platform that cannot
- * distinguish between file and directory events in
- * the event handler, such as Windows.
- *
- * Scan as if it is a directory and invalidate the
- * cone under it. (But remember to ignore items
- * between "name" and "name/", such as "name-" and
- * "name.".
+ * The negative "pos" gives us the suggested insertion
+ * point for the pathname (without the trailing slash).
+ * We need to see if there is a directory with that
+ * prefix, but there can be lots of pathnames between
+ * "foo" and "foo/" like "foo-" or "foo-bar", so we
+ * don't want to do our own scan.
*/
+ strbuf_add(&work_path, name, strlen(name));
+ strbuf_addch(&work_path, '/');
+ pos = index_name_pos(istate, work_path.buf, work_path.len);
+ nr_in_cone = handle_path_with_trailing_slash(
+ istate, work_path.buf, pos);
+ strbuf_release(&work_path);
+ return nr_in_cone;
+ }
+}
+
+/*
+ * The daemon can decorate directory events, such as a move or rename,
+ * by adding a trailing slash to the observed name. Use this to
+ * explicitly invalidate the entire cone under that directory.
+ *
+ * The daemon can only reliably do that if the OS FSEvent contains
+ * sufficient information in the event.
+ *
+ * macOS FSEvents have enough information.
+ *
+ * Other platforms may or may not be able to do it (and it might
+ * depend on the type of event (for example, a daemon could lstat() an
+ * observed pathname after a rename, but not after a delete)).
+ *
+ * If we find an exact match in the index for a path with a trailing
+ * slash, it means that we matched a sparse-index directory in a
+ * cone-mode sparse-checkout (since that's the only time we have
+ * directories in the index). We should never see this in practice
+ * (because sparse directories should not be present and therefore
+ * not generating FS events). Either way, we can treat them in the
+ * same way and just invalidate the cache-entry and the untracked
+ * cache (and in this case, the forward cache-entry scan won't find
+ * anything and it doesn't hurt to let it run).
+ *
+ * Return the number of cache-entries that we invalidated. We will
+ * use this later to determine if we need to attempt a second
+ * case-insensitive search on case-insensitive file systems. That is,
+ * if the search using the observed-case in the FSEvent yields any
+ * results, we assume the prefix is case-correct. If there are no
+ * matches, we still don't know if the observed path is simply
+ * untracked or case-incorrect.
+ */
+static size_t handle_path_with_trailing_slash(
+ struct index_state *istate, const char *name, int pos)
+{
+ int i;
+ size_t nr_in_cone = 0;
+
+ /*
+ * Mark the untracked cache dirty for this directory path
+ * (regardless of whether or not we find an exact match for it
+ * in the index or find it to be proper prefix of one or more
+ * files in the index), since the FSEvent is hinting that
+ * there may be changes on or within the directory.
+ */
+ untracked_cache_invalidate_trimmed_path(istate, name, 0);
+
+ if (pos < 0)
pos = -pos - 1;
- for (i = pos; i < istate->cache_nr; i++) {
- if (!starts_with(istate->cache[i]->name, name))
- break;
- if ((unsigned char)istate->cache[i]->name[len] > '/')
- break;
- if (istate->cache[i]->name[len] == '/')
- istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
- }
+ /* Mark all entries for the folder invalid */
+ for (i = pos; i < istate->cache_nr; i++) {
+ if (!starts_with(istate->cache[i]->name, name))
+ break;
+ invalidate_ce_fsm(istate->cache[i]);
+ nr_in_cone++;
}
+ return nr_in_cone;
+}
+
+static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
+{
+ int len = strlen(name);
+ int pos = index_name_pos(istate, name, len);
+ size_t nr_in_cone;
+
+ trace_printf_key(&trace_fsmonitor,
+ "fsmonitor_refresh_callback '%s' (pos %d)",
+ name, pos);
+
+ if (name[len - 1] == '/')
+ nr_in_cone = handle_path_with_trailing_slash(istate, name, pos);
+ else
+ nr_in_cone = handle_path_without_trailing_slash(istate, name, pos);
+
/*
- * Mark the untracked cache dirty even if it wasn't found in the index
- * as it could be a new untracked file.
+ * If we did not find an exact match for this pathname or any
+ * cache-entries with this directory prefix and we're on a
+ * case-insensitive file system, try again using the name-hash
+ * and dir-name-hash.
*/
- untracked_cache_invalidate_path(istate, name, 0);
+ if (!nr_in_cone && ignore_case) {
+ nr_in_cone = handle_using_name_hash_icase(istate, name);
+ if (!nr_in_cone)
+ nr_in_cone = handle_using_dir_name_hash_icase(
+ istate, name);
+ }
+
+ if (nr_in_cone)
+ trace_printf_key(&trace_fsmonitor,
+ "fsmonitor_refresh_callback CNT: %d",
+ (int)nr_in_cone);
}
/*
diff --git a/gettext.c b/gettext.c
index f27e94407b..57facbc21e 100644
--- a/gettext.c
+++ b/gettext.c
@@ -7,9 +7,7 @@
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
-#include "strbuf.h"
#include "utf8.h"
-#include "config.h"
#ifndef NO_GETTEXT
# include <libintl.h>
diff --git a/git-compat-util.h b/git-compat-util.h
index c3b6c2c226..ca7678a379 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -218,6 +218,18 @@ struct strbuf;
#define GIT_WINDOWS_NATIVE
#endif
+#if defined(NO_UNIX_SOCKETS) || !defined(GIT_WINDOWS_NATIVE)
+static inline int _have_unix_sockets(void)
+{
+#if defined(NO_UNIX_SOCKETS)
+ return 0;
+#else
+ return 1;
+#endif
+}
+#define have_unix_sockets _have_unix_sockets
+#endif
+
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -225,6 +237,7 @@ 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() */
@@ -390,6 +403,7 @@ char *gitdirname(char *);
#ifndef NO_OPENSSL
#ifdef __APPLE__
+#undef __AVAILABILITY_MACROS_USES_AVAILABILITY
#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0
#include <AvailabilityMacros.h>
#undef DEPRECATED_ATTRIBUTE
@@ -684,11 +698,11 @@ report_fn get_warn_routine(void);
void set_die_is_recursing_routine(int (*routine)(void));
/*
- * 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:
*
@@ -699,57 +713,58 @@ void set_die_is_recursing_routine(int (*routine)(void));
* [skip prefix if present, otherwise use whole string]
* skip_prefix(name, "refs/heads/", &name);
*/
-static inline 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;
+ 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);
diff --git a/git-curl-compat.h b/git-curl-compat.h
index fd96b3cdff..e1d0bdd273 100644
--- a/git-curl-compat.h
+++ b/git-curl-compat.h
@@ -127,6 +127,15 @@
#endif
/**
+ * Versions before curl 7.66.0 (September 2019) required manually setting the
+ * transfer-encoding for a streaming POST; after that this is handled
+ * automatically.
+ */
+#if LIBCURL_VERSION_NUM < 0x074200
+#define GIT_CURL_NEED_TRANSFER_ENCODING_HEADER
+#endif
+
+/**
* CURLOPT_PROTOCOLS_STR and CURLOPT_REDIR_PROTOCOLS_STR were added in 7.85.0,
* released in August 2022.
*/
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 07ea3443f7..211ec8459a 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -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;
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index e4e820e680..dd0c9a5b7f 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -91,6 +91,19 @@ then
# ignore the error from the above --- run_merge_tool
# will diagnose unusable tool by itself
run_merge_tool "$merge_tool" false
+
+ status=$?
+ if test $status -ge 126
+ then
+ # Command not found (127), not executable (126) or
+ # exited via a signal (>= 128).
+ exit $status
+ fi
+
+ if test "$GIT_DIFFTOOL_TRUST_EXIT_CODE" = true
+ then
+ exit $status
+ fi
else
# Launch the merge tool on each path provided by 'git diff'
while test $# -gt 6
diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes
index 59cd41dbff..118d56cfbd 100644
--- a/git-gui/.gitattributes
+++ b/git-gui/.gitattributes
@@ -3,3 +3,4 @@
git-gui.sh encoding=UTF-8
/po/*.po encoding=UTF-8
/GIT-VERSION-GEN eol=lf
+Makefile whitespace=!indent,trail,space
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 3f80435436..667c39ed56 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -107,12 +107,12 @@ endif
ifeq ($(uname_S),Darwin)
TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app
- ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n)
+ ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n)
TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish.app
- ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n)
+ ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n)
TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app
- endif
- endif
+ endif
+ endif
TKEXECUTABLE = $(shell basename "$(TKFRAMEWORK)" .app)
endif
@@ -143,9 +143,9 @@ ifeq ($(exedir),$(gg_libdir))
endif
gg_libdir_sed_in := $(gg_libdir)
ifeq ($(uname_S),Darwin)
- ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
+ ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y)
GITGUI_MACOSXAPP := YesPlease
- endif
+ endif
endif
ifneq (,$(findstring MINGW,$(uname_S)))
ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
@@ -220,9 +220,9 @@ ifdef NO_MSGFMT
MSGFMT ?= $(TCL_PATH) po/po2msg.sh
else
MSGFMT ?= msgfmt
- ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+ ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
MSGFMT := $(TCL_PATH) po/po2msg.sh
- endif
+ endif
endif
msgsdir = $(gg_libdir)/msgs
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 3e5907a460..507fb2b682 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -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/>.}]
######################################################################
##
@@ -2367,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 .
@@ -3052,7 +3052,7 @@ if {$doc_path ne {}} {
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} {
diff --git a/git-gui/lib/encoding.tcl b/git-gui/lib/encoding.tcl
index 32668fc9c6..d2e0fa60c3 100644
--- a/git-gui/lib/encoding.tcl
+++ b/git-gui/lib/encoding.tcl
@@ -3,7 +3,7 @@
# (Copied from gitk, commit fd8ccbec4f0161)
# This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
# Not all of them are supported by Tcl.
set encoding_aliases {
{ ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
diff --git a/git-gui/po/README b/git-gui/po/README
index 2514bc22ab..116233100d 100644
--- a/git-gui/po/README
+++ b/git-gui/po/README
@@ -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
diff --git a/git-instaweb.sh b/git-instaweb.sh
index c68f49454c..994431c887 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -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;
diff --git a/git-p4.py b/git-p4.py
index 0eb3bb4c47..28ab12c72b 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -689,8 +689,8 @@ def setP4ExecBit(file, mode):
if not isModeExec(mode):
p4Type = getP4OpenedType(file)
- p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
- p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
+ p4Type = re.sub(r'^([cku]?)x(.*)', r'\1\2', p4Type)
+ p4Type = re.sub(r'(.*?\+.*?)x(.*?)', r'\1\2', p4Type)
if p4Type[-1] == "+":
p4Type = p4Type[0:-1]
@@ -701,7 +701,7 @@ def getP4OpenedType(file):
"""Returns the perforce file type for the given file."""
result = p4_read_pipe(["opened", wildcard_encode(file)])
- match = re.match(".*\((.+)\)( \*exclusive\*)?\r?$", result)
+ match = re.match(r".*\((.+)\)( \*exclusive\*)?\r?$", result)
if match:
return match.group(1)
else:
@@ -757,7 +757,7 @@ def parseDiffTreeEntry(entry):
global _diff_tree_pattern
if not _diff_tree_pattern:
- _diff_tree_pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
+ _diff_tree_pattern = re.compile(r':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
match = _diff_tree_pattern.match(entry)
if match:
@@ -918,9 +918,9 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
if len(result) > 0:
data = result[0].get('data')
if data:
- m = re.search('Too many rows scanned \(over (\d+)\)', data)
+ m = re.search(r'Too many rows scanned \(over (\d+)\)', data)
if not m:
- m = re.search('Request too large \(over (\d+)\)', data)
+ m = re.search(r'Request too large \(over (\d+)\)', data)
if m:
limit = int(m.group(1))
@@ -1452,7 +1452,7 @@ def wildcard_encode(path):
def wildcard_present(path):
- m = re.search("[*#@%]", path)
+ m = re.search(r"[*#@%]", path)
return m is not None
@@ -3048,7 +3048,7 @@ class P4Sync(Command, P4UserMap):
# Preserve everything in relative path name except leading
# //depot/; just look at first prefix as they all should
# be in the same depot.
- depot = re.sub("^(//[^/]+/).*", r'\1', prefixes[0])
+ depot = re.sub(r"^(//[^/]+/).*", r'\1', prefixes[0])
if p4PathStartsWith(path, depot):
path = path[len(depot):]
@@ -3603,7 +3603,7 @@ class P4Sync(Command, P4UserMap):
commitFound = True
else:
gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
- "--reverse", ":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
+ "--reverse", r":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
if len(gitCommit) == 0:
print("importing label %s: could not find git commit for changelist %d" % (name, changelist))
else:
@@ -4182,7 +4182,7 @@ class P4Sync(Command, P4UserMap):
if len(self.changesFile) == 0:
revision = "#head"
- p = re.sub("\.\.\.$", "", p)
+ p = re.sub(r"\.\.\.$", "", p)
if not p.endswith("/"):
p += "/"
@@ -4251,7 +4251,8 @@ class P4Sync(Command, P4UserMap):
if self.tempBranches != []:
for branch in self.tempBranches:
read_pipe(["git", "update-ref", "-d", branch])
- os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+ if len(read_pipe(["git", "for-each-ref", self.tempBranchLocation])) > 0:
+ die("There are unexpected temporary branches")
# Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
# a convenient shortcut refname "p4".
@@ -4291,7 +4292,7 @@ class P4Rebase(Command):
die("Cannot find upstream branchpoint for rebase")
# the branchpoint may be p4/foo~3, so strip off the parent
- upstream = re.sub("~[0-9]+$", "", upstream)
+ upstream = re.sub(r"~[0-9]+$", "", upstream)
print("Rebasing the current branch onto %s" % upstream)
oldHead = read_pipe(["git", "rev-parse", "HEAD"]).strip()
@@ -4320,8 +4321,8 @@ class P4Clone(P4Sync):
def defaultDestination(self, args):
# TODO: use common prefix of args?
depotPath = args[0]
- depotDir = re.sub("(@[^@]*)$", "", depotPath)
- depotDir = re.sub("(#[^#]*)$", "", depotDir)
+ depotDir = re.sub(r"(@[^@]*)$", "", depotPath)
+ depotDir = re.sub(r"(#[^#]*)$", "", depotDir)
depotDir = re.sub(r"\.\.\.$", "", depotDir)
depotDir = re.sub(r"/$", "", depotDir)
return os.path.split(depotDir)[1]
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index e3d3909743..eb34cda409 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -148,7 +148,7 @@ do
if [ -z "$dry_run" ] ; then
git apply --index -C1 ${level:+"$level"} "$tmp_patch" &&
tree=$(git write-tree) &&
- commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) &&
+ commit=$( { echo "$SUBJECT"; echo; cat "$tmp_msg"; } | git commit-tree $tree -p $commit) &&
git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
done 3<"$QUILT_SERIES"
diff --git a/git.c b/git.c
index c67e44dd82..654d615a18 100644
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
#include "exec-cmd.h"
#include "gettext.h"
#include "help.h"
+#include "object-file.h"
#include "pager.h"
#include "read-cache-ll.h"
#include "run-command.h"
@@ -186,6 +187,11 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
use_pager = 0;
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--no-lazy-fetch")) {
+ fetch_if_missing = 0;
+ setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
+ if (envchanged)
+ *envchanged = 1;
} else if (!strcmp(cmd, "--no-replace-objects")) {
disable_replace_refs();
setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
@@ -373,8 +379,6 @@ static int handle_alias(int *argcp, const char ***argv)
strvec_pushv(&child.args, (*argv) + 1);
trace2_cmd_alias(alias_command, child.args.v);
- trace2_cmd_list_config();
- trace2_cmd_list_env_vars();
trace2_cmd_name("_run_shell_alias_");
ret = run_command(&child);
@@ -411,8 +415,6 @@ static int handle_alias(int *argcp, const char ***argv)
COPY_ARRAY(new_argv + count, *argv + 1, *argcp);
trace2_cmd_alias(alias_command, new_argv);
- trace2_cmd_list_config();
- trace2_cmd_list_env_vars();
*argv = new_argv;
*argcp += count - 1;
@@ -462,8 +464,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
trace_argv_printf(argv, "trace: built-in: git");
trace2_cmd_name(p->cmd);
- trace2_cmd_list_config();
- trace2_cmd_list_env_vars();
validate_cache_entries(the_repository->index);
status = p->fn(argc, argv, prefix);
@@ -594,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 },
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index 5bdd52a6eb..e1f0aff4a1 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -33,9 +33,9 @@ ifdef NO_MSGFMT
MSGFMT ?= $(TCL_PATH) po/po2msg.sh
else
MSGFMT ?= msgfmt
- ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
+ ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0)
MSGFMT := $(TCL_PATH) po/po2msg.sh
- endif
+ endif
endif
PO_TEMPLATE = po/gitk.pot
diff --git a/gitk-git/gitk b/gitk-git/gitk
index df3ba2ea99..7a087f123d 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -11956,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
@@ -12472,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]
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index dadc6efa81..5bfa4968c4 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -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]
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 55e7c6567e..ccd14e0e30 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -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',
@@ -728,9 +728,11 @@ our $per_request_config = 1;
sub read_config_file {
my $filename = shift;
return unless defined $filename;
- # die if there are errors parsing config file
if (-e $filename) {
do $filename;
+ # die if there is a problem accessing the file
+ die $! if $!;
+ # die if there are errors parsing config file
die $@ if $@;
return 1;
}
@@ -8193,7 +8195,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");
}
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index 3212601032..48d2e51015 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -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: */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index 018bbb7d4c..99e3eb8c3d 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -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
diff --git a/gpg-interface.c b/gpg-interface.c
index 48f43c5a21..1ff94266d2 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -12,7 +12,6 @@
#include "sigchain.h"
#include "tempfile.h"
#include "alias.h"
-#include "environment.h"
static int git_gpg_config(const char *, const char *,
const struct config_context *, void *);
@@ -484,7 +483,7 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
if (sigc->payload_timestamp)
strbuf_addf(&verify_time, "-Overify-time=%s",
- show_date(sigc->payload_timestamp, 0, &verify_date_mode));
+ show_date(sigc->payload_timestamp, 0, verify_date_mode));
/* Find the principal from the signers */
strvec_pushl(&ssh_keygen.args, fmt->program,
@@ -587,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, NULL);
+ strbuf_stripspace(&ssh_keygen_err, NULL);
/* Add stderr outputs to show the user actual ssh-keygen errors */
strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
@@ -762,23 +761,14 @@ static int git_gpg_config(const char *var, const char *value,
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";
@@ -1088,7 +1078,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
if (strstr(signer_stderr.buf, "usage:"))
error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)"));
- error("%s", signer_stderr.buf);
+ ret = error("%s", signer_stderr.buf);
goto out;
}
diff --git a/gpg-interface.h b/gpg-interface.h
index 143cdc1c02..7cd98161f7 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -66,7 +66,7 @@ size_t parse_signed_buffer(const char *buf, size_t size);
* Create a detached signature for the contents of "buffer" and append
* it after "signature"; "buffer" and "signature" can be the same
* strbuf instance, which would cause the detached signature appended
- * at the end.
+ * at the end. Returns 0 on success, non-zero on failure.
*/
int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
const char *signing_key);
diff --git a/graph.h b/graph.h
index e88632a014..3fd1dcb2e9 100644
--- 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 fc2d0c837a..ac34bfeafb 100644
--- a/grep.c
+++ b/grep.c
@@ -9,7 +9,6 @@
#include "xdiff-interface.h"
#include "diff.h"
#include "diffcore.h"
-#include "commit.h"
#include "quote.h"
#include "help.h"
@@ -622,7 +621,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
*list = p->next;
x = compile_pattern_or(list);
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
- die("unmatched parenthesis");
+ die("unmatched ( for expression group");
*list = (*list)->next;
return x;
default:
@@ -793,7 +792,7 @@ void compile_grep_patterns(struct grep_opt *opt)
if (p)
opt->pattern_expression = compile_pattern_expr(&p);
if (p)
- die("incomplete pattern expression: %s", p->pattern);
+ die("incomplete pattern expression group: %s", p->pattern);
if (opt->no_body_match && opt->pattern_expression)
opt->pattern_expression = grep_not_expr(opt->pattern_expression);
diff --git a/hash-ll.h b/hash-ll.h
index 10d84cc208..2cfde63ae1 100644
--- a/hash-ll.h
+++ b/hash-ll.h
@@ -145,6 +145,7 @@ struct object_id {
#define GET_OID_RECORD_PATH 0200
#define GET_OID_ONLY_TO_DIE 04000
#define GET_OID_REQUIRE_PATH 010000
+#define GET_OID_HASH_ANY 020000
#define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
diff --git a/hash.h b/hash.h
index 615ae0691d..e064807c17 100644
--- a/hash.h
+++ b/hash.h
@@ -73,10 +73,15 @@ static inline void oidclr(struct object_id *oid)
oid->algo = hash_algo_by_ptr(the_hash_algo);
}
+static inline void oidread_algop(struct object_id *oid, const unsigned char *hash, const struct git_hash_algo *algop)
+{
+ memcpy(oid->hash, hash, algop->rawsz);
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
static inline void oidread(struct object_id *oid, const unsigned char *hash)
{
- memcpy(oid->hash, hash, the_hash_algo->rawsz);
- oid->algo = hash_algo_by_ptr(the_hash_algo);
+ oidread_algop(oid, hash, the_hash_algo);
}
static inline int is_empty_blob_sha1(const unsigned char *sha1)
diff --git a/help.c b/help.c
index 6d2ebfbd2a..2dbe57b413 100644
--- a/help.c
+++ b/help.c
@@ -464,8 +464,11 @@ static int get_alias(const char *var, const char *value,
{
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;
}
diff --git a/http-backend.c b/http-backend.c
index ff07b87e64..1ed1e29d07 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -38,6 +38,7 @@ struct rpc_service {
static struct rpc_service rpc_service[] = {
{ "upload-pack", "uploadpack", 1, 1 },
{ "receive-pack", "receivepack", 0, -1 },
+ { "upload-archive", "uploadarchive", 0, -1 },
};
static struct string_list *get_parameters(void)
@@ -639,10 +640,15 @@ static void check_content_type(struct strbuf *hdr, const char *accepted_type)
static void service_rpc(struct strbuf *hdr, char *service_name)
{
- const char *argv[] = {NULL, "--stateless-rpc", ".", NULL};
+ struct strvec argv = STRVEC_INIT;
struct rpc_service *svc = select_service(hdr, service_name);
struct strbuf buf = STRBUF_INIT;
+ strvec_push(&argv, svc->name);
+ if (strcmp(service_name, "git-upload-archive"))
+ strvec_push(&argv, "--stateless-rpc");
+ strvec_push(&argv, ".");
+
strbuf_reset(&buf);
strbuf_addf(&buf, "application/x-git-%s-request", svc->name);
check_content_type(hdr, buf.buf);
@@ -655,9 +661,9 @@ static void service_rpc(struct strbuf *hdr, char *service_name)
end_headers(hdr);
- argv[0] = svc->name;
- run_service(argv, svc->buffer_input);
+ run_service(argv.v, svc->buffer_input);
strbuf_release(&buf);
+ strvec_clear(&argv);
}
static int dead;
@@ -723,6 +729,7 @@ static struct service_cmd {
{"GET", "/objects/pack/pack-[0-9a-f]{64}\\.idx$", get_idx_file},
{"POST", "/git-upload-pack$", service_rpc},
+ {"POST", "/git-upload-archive$", service_rpc},
{"POST", "/git-receive-pack$", service_rpc}
};
diff --git a/http-fetch.c b/http-fetch.c
index fffda59267..bec94988bb 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -1,12 +1,12 @@
#include "git-compat-util.h"
#include "config.h"
-#include "exec-cmd.h"
#include "gettext.h"
#include "hex.h"
#include "http.h"
#include "walker.h"
#include "setup.h"
#include "strvec.h"
+#include "url.h"
#include "urlmatch.h"
#include "trace2.h"
diff --git a/http-push.c b/http-push.c
index a704f490fd..1fe51226fd 100644
--- a/http-push.c
+++ b/http-push.c
@@ -6,10 +6,8 @@
#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"
@@ -17,6 +15,7 @@
#include "strvec.h"
#include "tree.h"
#include "tree-walk.h"
+#include "url.h"
#include "packfile.h"
#include "object-store-ll.h"
#include "commit-reach.h"
@@ -1308,7 +1307,7 @@ static struct object_list **process_tree(struct tree *tree,
obj->flags |= SEEN;
p = add_one_object(obj, p);
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry))
switch (object_type(entry.mode)) {
@@ -1576,8 +1575,11 @@ static int verify_merge_base(struct object_id *head_oid, struct ref *remote)
struct commit *head = lookup_commit_or_die(head_oid, "HEAD");
struct commit *branch = lookup_commit_or_die(&remote->old_oid,
remote->name);
+ int ret = repo_in_merge_bases(the_repository, branch, head);
- return repo_in_merge_bases(the_repository, branch, head);
+ if (ret < 0)
+ exit(128);
+ return ret;
}
static int delete_remote_branch(const char *pattern, int force)
@@ -1852,6 +1854,7 @@ int cmd_main(int argc, const char **argv)
if (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {
if (push_verbosely)
+ /* stable plumbing output; do not modify or localize */
fprintf(stderr, "'%s': up-to-date\n", ref->name);
if (helper_status)
printf("ok %s up to date\n", ref->name);
@@ -1872,6 +1875,7 @@ int cmd_main(int argc, const char **argv)
* commits at the remote end and likely
* we were not up to date to begin with.
*/
+ /* stable plumbing output; do not modify or localize */
error("remote '%s' is not an ancestor of\n"
"local '%s'.\n"
"Maybe you are not up-to-date and "
diff --git a/http-walker.c b/http-walker.c
index 78d99f7c4b..b395ef1327 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -1,6 +1,5 @@
#include "git-compat-util.h"
#include "repository.h"
-#include "commit.h"
#include "hex.h"
#include "walker.h"
#include "http.h"
diff --git a/http.c b/http.c
index 8f71bf00d8..3d80bd6116 100644
--- a/http.c
+++ b/http.c
@@ -4,7 +4,6 @@
#include "http.h"
#include "config.h"
#include "pack.h"
-#include "sideband.h"
#include "run-command.h"
#include "url.h"
#include "urlmatch.h"
@@ -15,7 +14,6 @@
#include "trace.h"
#include "transport.h"
#include "packfile.h"
-#include "protocol.h"
#include "string-list.h"
#include "object-file.h"
#include "object-store-ll.h"
@@ -1454,6 +1452,7 @@ struct active_request_slot *get_active_slot(void)
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
+ curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, -1L);
curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
@@ -1902,7 +1901,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;
diff --git a/http.h b/http.h
index 3a409bccd4..3af19a8bf5 100644
--- a/http.h
+++ b/http.h
@@ -10,7 +10,6 @@ struct packed_git;
#include "strbuf.h"
#include "remote.h"
-#include "url.h"
#define DEFAULT_MAX_REQUESTS 5
diff --git a/imap-send.c b/imap-send.c
index 996651e4f8..4caa8668e6 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -18,13 +18,12 @@
* 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 "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"
@@ -69,9 +68,6 @@ static void imap_warn(const char *, ...);
static char *next_arg(char **);
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
-
static int nfvasprintf(char **strp, const char *fmt, va_list ap)
{
int len;
@@ -501,19 +497,6 @@ static char *next_arg(char **s)
return ret;
}
-__attribute__((format (printf, 3, 4)))
-static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
-{
- int ret;
- va_list va;
-
- va_start(va, fmt);
- if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
- BUG("buffer too small. Please report a bug.");
- va_end(va);
- return ret;
-}
-
static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
struct imap_cmd_cb *cb,
const char *fmt, va_list ap)
@@ -536,11 +519,11 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
get_cmd_result(ctx, NULL);
if (!cmd->cb.data)
- bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+ bufl = xsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
else
- bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
- cmd->tag, cmd->cmd, cmd->cb.dlen,
- CAP(LITERALPLUS) ? "+" : "");
+ bufl = xsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+ cmd->tag, cmd->cmd, cmd->cb.dlen,
+ CAP(LITERALPLUS) ? "+" : "");
if (0 < verbosity) {
if (imap->num_in_progress)
@@ -860,7 +843,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)
{
@@ -1346,7 +1329,7 @@ static int git_imap_config(const char *var, const char *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;
diff --git a/json-writer.h b/json-writer.h
index 209355e0f1..04413bd1af 100644
--- a/json-writer.h
+++ b/json-writer.h
@@ -3,8 +3,8 @@
/*
* JSON data structures are defined at:
- * [1] http://www.ietf.org/rfc/rfc7159.txt
- * [2] http://json.org/
+ * [1] https://www.ietf.org/rfc/rfc7159.txt
+ * [2] https://www.json.org/
*
* The JSON-writer API allows one to build JSON data structures using a
* simple wrapper around a "struct strbuf" buffer. It is intended as a
diff --git a/kwset.c b/kwset.c
index bbfcf815a5..695e47b7cc 100644
--- a/kwset.c
+++ b/kwset.c
@@ -18,7 +18,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>. */
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
/* Written August 1989 by Mike Haertel.
The author may be reached (Email) at the address mike@ai.mit.edu,
diff --git a/kwset.h b/kwset.h
index d42a793a30..c722664e5a 100644
--- a/kwset.h
+++ b/kwset.h
@@ -20,7 +20,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,
diff --git a/line-log.c b/line-log.c
index 24a1ecb677..8ff6ccb772 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1,8 +1,8 @@
#include "git-compat-util.h"
+#include "diffcore.h"
#include "line-range.h"
#include "hex.h"
#include "tag.h"
-#include "blob.h"
#include "tree.h"
#include "diff.h"
#include "commit.h"
@@ -12,8 +12,6 @@
#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"
diff --git a/line-log.h b/line-log.h
index 4291da8d79..e9dadbc1a5 100644
--- a/line-log.h
+++ b/line-log.h
@@ -1,8 +1,6 @@
#ifndef LINE_LOG_H
#define LINE_LOG_H
-#include "diffcore.h"
-
struct rev_info;
struct commit;
struct string_list;
diff --git a/line-range.c b/line-range.c
index 47bf0d6f1a..60f0e5ada8 100644
--- a/line-range.c
+++ b/line-range.c
@@ -1,7 +1,6 @@
#include "git-compat-util.h"
#include "line-range.h"
#include "xdiff-interface.h"
-#include "strbuf.h"
#include "userdiff.h"
/*
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index 8a08b7af49..c5f363ca6f 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -1,11 +1,6 @@
#include "git-compat-util.h"
-#include "commit.h"
#include "config.h"
#include "gettext.h"
-#include "revision.h"
-#include "strvec.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
#include "promisor-remote.h"
#include "trace.h"
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 9327ccd505..4346f8da45 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -2,14 +2,9 @@
#include "dir.h"
#include "gettext.h"
#include "hex.h"
-#include "tag.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"
@@ -716,15 +711,6 @@ static void filter_combine__free(void *filter_data)
free(d);
}
-static void add_all(struct oidset *dest, struct oidset *src) {
- struct oidset_iter iter;
- struct object_id *src_oid;
-
- oidset_iter_init(src, &iter);
- while ((src_oid = oidset_iter_next(&iter)) != NULL)
- oidset_insert(dest, src_oid);
-}
-
static void filter_combine__finalize_omits(
struct oidset *omits,
void *filter_data)
@@ -733,7 +719,7 @@ static void filter_combine__finalize_omits(
size_t sub;
for (sub = 0; sub < d->nr; sub++) {
- add_all(omits, &d->sub[sub].omits);
+ oidset_insert_from_set(omits, &d->sub[sub].omits);
oidset_clear(&d->sub[sub].omits);
}
}
diff --git a/list-objects.c b/list-objects.c
index f39b68faf5..11ad8be411 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -102,7 +102,7 @@ static void process_tree_contents(struct traversal_context *ctx,
enum interesting match = ctx->revs->diffopt.pathspec.nr == 0 ?
all_entries_interesting : entry_not_interesting;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (match != all_entries_interesting) {
diff --git a/list.h b/list.h
index 362a4cd7f5..98428010f4 100644
--- a/list.h
+++ b/list.h
@@ -19,7 +19,7 @@
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
+ * <https://www.gnu.org/licenses/>.
*/
#ifndef LIST_H
diff --git a/lockfile.h b/lockfile.h
index 90af4e66b2..1bb9926497 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -321,11 +321,11 @@ static inline int commit_lock_file_to(struct lock_file *lk, const char *path)
* Roll back `lk`: close the file descriptor and/or file pointer and
* remove the lockfile. It is a NOOP to call `rollback_lock_file()`
* for a `lock_file` object that has already been committed or rolled
- * back.
+ * back. No error will be returned in this case.
*/
-static inline void rollback_lock_file(struct lock_file *lk)
+static inline int rollback_lock_file(struct lock_file *lk)
{
- delete_tempfile(&lk->tempfile);
+ return delete_tempfile(&lk->tempfile);
}
#endif /* LOCKFILE_H */
diff --git a/log-tree.c b/log-tree.c
index 504da6b519..16031b44e7 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -2,6 +2,7 @@
#include "commit-reach.h"
#include "config.h"
#include "diff.h"
+#include "diffcore.h"
#include "environment.h"
#include "hex.h"
#include "object-name.h"
@@ -469,16 +470,19 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
}
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
- const char **extra_headers_p,
+ char **extra_headers_p,
int *need_8bit_cte_p,
int maybe_multipart)
{
- const char *extra_headers = opt->extra_headers;
+ struct strbuf headers = STRBUF_INIT;
const char *name = oid_to_hex(opt->zero_commit ?
null_oid() : &commit->object.oid);
*need_8bit_cte_p = 0; /* unknown */
+ if (opt->extra_headers && *opt->extra_headers)
+ strbuf_addstr(&headers, opt->extra_headers);
+
fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
graph_show_oneline(opt->graph);
if (opt->message_id) {
@@ -495,16 +499,13 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
graph_show_oneline(opt->graph);
}
if (opt->mime_boundary && maybe_multipart) {
- static struct strbuf subject_buffer = STRBUF_INIT;
static struct strbuf buffer = STRBUF_INIT;
struct strbuf filename = STRBUF_INIT;
*need_8bit_cte_p = -1; /* NEVER */
- strbuf_reset(&subject_buffer);
strbuf_reset(&buffer);
- strbuf_addf(&subject_buffer,
- "%s"
+ strbuf_addf(&headers,
"MIME-Version: 1.0\n"
"Content-Type: multipart/mixed;"
" boundary=\"%s%s\"\n"
@@ -515,10 +516,8 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
"Content-Type: text/plain; "
"charset=UTF-8; format=fixed\n"
"Content-Transfer-Encoding: 8bit\n\n",
- extra_headers ? extra_headers : "",
mime_boundary_leader, opt->mime_boundary,
mime_boundary_leader, opt->mime_boundary);
- extra_headers = subject_buffer.buf;
if (opt->numbered_files)
strbuf_addf(&filename, "%d", opt->nr);
@@ -538,7 +537,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
opt->diffopt.stat_sep = buffer.buf;
strbuf_release(&filename);
}
- *extra_headers_p = extra_headers;
+ *extra_headers_p = headers.len ? strbuf_detach(&headers, NULL) : NULL;
}
static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
@@ -677,7 +676,6 @@ void show_log(struct rev_info *opt)
struct log_info *log = opt->loginfo;
struct commit *commit = log->commit, *parent = log->parent;
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
- const char *extra_headers = opt->extra_headers;
struct pretty_print_context ctx = {0};
opt->loginfo = NULL;
@@ -738,10 +736,9 @@ void show_log(struct rev_info *opt)
*/
if (cmit_fmt_is_mail(opt->commit_format)) {
- log_write_email_headers(opt, commit, &extra_headers,
+ log_write_email_headers(opt, commit, &ctx.after_subject,
&ctx.need_8bit_cte, 1);
ctx.rev = opt;
- ctx.print_email_subject = 1;
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
if (opt->commit_format != CMIT_FMT_ONELINE)
@@ -776,7 +773,7 @@ void show_log(struct rev_info *opt)
*/
show_reflog_message(opt->reflog_info,
opt->commit_format == CMIT_FMT_ONELINE,
- &opt->date_mode,
+ opt->date_mode,
opt->date_mode_explicit);
if (opt->commit_format == CMIT_FMT_ONELINE)
return;
@@ -807,7 +804,6 @@ void show_log(struct rev_info *opt)
ctx.date_mode = opt->date_mode;
ctx.date_mode_explicit = opt->date_mode_explicit;
ctx.abbrev = opt->diffopt.abbrev;
- ctx.after_subject = extra_headers;
ctx.preserve_subject = opt->preserve_subject;
ctx.encode_email_headers = opt->encode_email_headers;
ctx.reflog_info = opt->reflog_info;
@@ -856,6 +852,7 @@ void show_log(struct rev_info *opt)
strbuf_release(&msgbuf);
free(ctx.notes_message);
+ free(ctx.after_subject);
if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
struct diff_queue_struct dq;
@@ -1010,7 +1007,7 @@ static int do_remerge_diff(struct rev_info *opt,
struct object_id *oid)
{
struct merge_options o;
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
struct merge_result res = {0};
struct pretty_print_context ctx = {0};
struct commit *parent1 = parents->item;
@@ -1035,7 +1032,8 @@ static int do_remerge_diff(struct rev_info *opt,
/* Parse the relevant commits and get the merge bases */
parse_commit_or_die(parent1);
parse_commit_or_die(parent2);
- bases = repo_get_merge_bases(the_repository, parent1, parent2);
+ if (repo_get_merge_bases(the_repository, parent1, parent2, &bases) < 0)
+ exit(128);
/* Re-merge the parents */
merge_incore_recursive(&o, bases, parent1, parent2, &res);
diff --git a/log-tree.h b/log-tree.h
index 41c776fea5..94978e2c83 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -29,7 +29,7 @@ void format_decorations(struct strbuf *sb, const struct commit *commit,
int use_color, const struct decoration_options *opts);
void show_decorations(struct rev_info *opt, struct commit *commit);
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
- const char **extra_headers_p,
+ char **extra_headers_p,
int *need_8bit_cte_p,
int maybe_multipart);
void load_ref_decorations(struct decoration_filter *filter, int flags);
diff --git a/loose.c b/loose.c
new file mode 100644
index 0000000000..f6faa6216a
--- /dev/null
+++ b/loose.c
@@ -0,0 +1,259 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "path.h"
+#include "object-store.h"
+#include "hex.h"
+#include "wrapper.h"
+#include "gettext.h"
+#include "loose.h"
+#include "lockfile.h"
+#include "oidtree.h"
+
+static const char *loose_object_header = "# loose-object-idx\n";
+
+static inline int should_use_loose_object_map(struct repository *repo)
+{
+ return repo->compat_hash_algo && repo->gitdir;
+}
+
+void loose_object_map_init(struct loose_object_map **map)
+{
+ struct loose_object_map *m;
+ m = xmalloc(sizeof(**map));
+ m->to_compat = kh_init_oid_map();
+ m->to_storage = kh_init_oid_map();
+ *map = m;
+}
+
+static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
+{
+ khiter_t pos;
+ int ret;
+ struct object_id *stored;
+
+ pos = kh_put_oid_map(map, *key, &ret);
+
+ /* This item already exists in the map. */
+ if (ret == 0)
+ return 0;
+
+ stored = xmalloc(sizeof(*stored));
+ oidcpy(stored, value);
+ kh_value(map, pos) = stored;
+ return 1;
+}
+
+static int insert_loose_map(struct object_directory *odb,
+ const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ struct loose_object_map *map = odb->loose_map;
+ int inserted = 0;
+
+ inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
+ inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
+ if (inserted)
+ oidtree_insert(odb->loose_objects_cache, compat_oid);
+
+ return inserted;
+}
+
+static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
+{
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+ FILE *fp;
+
+ if (!dir->loose_map)
+ loose_object_map_init(&dir->loose_map);
+ if (!dir->loose_objects_cache) {
+ ALLOC_ARRAY(dir->loose_objects_cache, 1);
+ oidtree_init(dir->loose_objects_cache);
+ }
+
+ insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
+ insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
+ insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ fp = fopen(path.buf, "rb");
+ if (!fp) {
+ strbuf_release(&path);
+ return 0;
+ }
+
+ errno = 0;
+ if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
+ goto err;
+ while (!strbuf_getline_lf(&buf, fp)) {
+ const char *p;
+ struct object_id oid, compat_oid;
+ if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
+ *p++ != ' ' ||
+ parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
+ p != buf.buf + buf.len)
+ goto err;
+ insert_loose_map(dir, &oid, &compat_oid);
+ }
+
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return errno ? -1 : 0;
+err:
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+int repo_read_loose_object_map(struct repository *repo)
+{
+ struct object_directory *dir;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ prepare_alt_odb(repo);
+
+ for (dir = repo->objects->odb; dir; dir = dir->next) {
+ if (load_one_loose_object_map(repo, dir) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int repo_write_loose_object_map(struct repository *repo)
+{
+ kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
+ struct lock_file lock;
+ int fd;
+ khiter_t iter;
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+ iter = kh_begin(map);
+ if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+ goto errout;
+
+ for (; iter != kh_end(map); iter++) {
+ if (kh_exist(map, iter)) {
+ if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
+ oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
+ continue;
+ strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ goto errout;
+ strbuf_reset(&buf);
+ }
+ }
+ strbuf_release(&buf);
+ if (commit_lock_file(&lock) < 0) {
+ error_errno(_("could not write loose object index %s"), path.buf);
+ strbuf_release(&path);
+ return -1;
+ }
+ strbuf_release(&path);
+ return 0;
+errout:
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ error_errno(_("failed to write loose object index %s\n"), path.buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+static int write_one_object(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ struct lock_file lock;
+ int fd;
+ struct stat st;
+ struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
+
+ strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
+
+ fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (fd < 0)
+ goto errout;
+ if (fstat(fd, &st) < 0)
+ goto errout;
+ if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
+ goto errout;
+
+ strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ goto errout;
+ if (close(fd))
+ goto errout;
+ adjust_shared_perm(path.buf);
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return 0;
+errout:
+ error_errno(_("failed to write loose object index %s\n"), path.buf);
+ close(fd);
+ rollback_lock_file(&lock);
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return -1;
+}
+
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid)
+{
+ int inserted = 0;
+
+ if (!should_use_loose_object_map(repo))
+ return 0;
+
+ inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
+ if (inserted)
+ return write_one_object(repo, oid, compat_oid);
+ return 0;
+}
+
+int repo_loose_object_map_oid(struct repository *repo,
+ const struct object_id *src,
+ const struct git_hash_algo *to,
+ struct object_id *dest)
+{
+ struct object_directory *dir;
+ kh_oid_map_t *map;
+ khiter_t pos;
+
+ for (dir = repo->objects->odb; dir; dir = dir->next) {
+ struct loose_object_map *loose_map = dir->loose_map;
+ if (!loose_map)
+ continue;
+ map = (to == repo->compat_hash_algo) ?
+ loose_map->to_compat :
+ loose_map->to_storage;
+ pos = kh_get_oid_map(map, *src);
+ if (pos < kh_end(map)) {
+ oidcpy(dest, kh_value(map, pos));
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void loose_object_map_clear(struct loose_object_map **map)
+{
+ struct loose_object_map *m = *map;
+ struct object_id *oid;
+
+ if (!m)
+ return;
+
+ kh_foreach_value(m->to_compat, oid, free(oid));
+ kh_foreach_value(m->to_storage, oid, free(oid));
+ kh_destroy_oid_map(m->to_compat);
+ kh_destroy_oid_map(m->to_storage);
+ free(m);
+ *map = NULL;
+}
diff --git a/loose.h b/loose.h
new file mode 100644
index 0000000000..2c2957072c
--- /dev/null
+++ b/loose.h
@@ -0,0 +1,22 @@
+#ifndef LOOSE_H
+#define LOOSE_H
+
+#include "khash.h"
+
+struct loose_object_map {
+ kh_oid_map_t *to_compat;
+ kh_oid_map_t *to_storage;
+};
+
+void loose_object_map_init(struct loose_object_map **map);
+void loose_object_map_clear(struct loose_object_map **map);
+int repo_loose_object_map_oid(struct repository *repo,
+ const struct object_id *src,
+ const struct git_hash_algo *dest_algo,
+ struct object_id *dest);
+int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
+ const struct object_id *compat_oid);
+int repo_read_loose_object_map(struct repository *repo);
+int repo_write_loose_object_map(struct repository *repo);
+
+#endif
diff --git a/ls-refs.c b/ls-refs.c
index 0e49b932c3..819cbefee3 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -5,7 +5,6 @@
#include "hex.h"
#include "repository.h"
#include "refs.h"
-#include "remote.h"
#include "strvec.h"
#include "ls-refs.h"
#include "pkt-line.h"
diff --git a/mailinfo.c b/mailinfo.c
index a07d2da16d..94b9b0abf2 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -58,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 {
@@ -72,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;
}
}
@@ -88,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 {
@@ -1253,6 +1257,8 @@ static int git_mailinfo_config(const char *var, const char *value,
return 0;
}
if (!strcmp(var, "mailinfo.quotedcr")) {
+ if (!value)
+ return config_error_nonbool(var);
if (mailinfo_parse_quoted_cr_action(value, &mi->quoted_cr) != 0)
return error(_("bad action '%s' for '%s'"), value, var);
return 0;
diff --git a/match-trees.c b/match-trees.c
index 0885ac681c..3412b6a140 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -63,7 +63,7 @@ static void *fill_tree_desc_strict(struct tree_desc *desc,
die("unable to read tree (%s)", oid_to_hex(hash));
if (type != OBJ_TREE)
die("%s is not a tree", oid_to_hex(hash));
- init_tree_desc(desc, buffer, size);
+ init_tree_desc(desc, hash, buffer, size);
return buffer;
}
@@ -194,7 +194,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
buf = repo_read_object_file(the_repository, oid1, &type, &sz);
if (!buf)
die("cannot read tree %s", oid_to_hex(oid1));
- init_tree_desc(&desc, buf, sz);
+ init_tree_desc(&desc, oid1, buf, sz);
rewrite_here = NULL;
while (desc.size) {
diff --git a/mem-pool.c b/mem-pool.c
index c34846d176..3065b12b23 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -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;
@@ -109,6 +107,47 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
return r;
}
+static char *mem_pool_strvfmt(struct mem_pool *pool, const char *fmt,
+ va_list ap)
+{
+ struct mp_block *block = pool->mp_block;
+ char *next_free = block ? block->next_free : NULL;
+ size_t available = block ? block->end - block->next_free : 0;
+ va_list cp;
+ int len, len2;
+ size_t size;
+ char *ret;
+
+ va_copy(cp, ap);
+ len = vsnprintf(next_free, available, fmt, cp);
+ va_end(cp);
+ if (len < 0)
+ BUG("your vsnprintf is broken (returned %d)", len);
+
+ size = st_add(len, 1); /* 1 for NUL */
+ ret = mem_pool_alloc(pool, size);
+
+ /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
+ if (ret == next_free)
+ return ret;
+
+ len2 = vsnprintf(ret, size, fmt, ap);
+ if (len2 != len)
+ BUG("your vsnprintf is broken (returns inconsistent lengths)");
+ return ret;
+}
+
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = mem_pool_strvfmt(pool, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size)
{
size_t len = st_mult(count, size);
diff --git a/mem-pool.h b/mem-pool.h
index fe7507f022..d1c66413ec 100644
--- a/mem-pool.h
+++ b/mem-pool.h
@@ -48,6 +48,11 @@ char *mem_pool_strdup(struct mem_pool *pool, const char *str);
char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
/*
+ * Allocate memory from the memory pool and format a string into it.
+ */
+char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...);
+
+/*
* Move the memory associated with the 'src' pool to the 'dst' pool. The 'src'
* pool will be empty and not contain any memory. It still needs to be free'd
* with a call to `mem_pool_discard`.
diff --git a/merge-blobs.c b/merge-blobs.c
index 9293cbf75c..2f659fd014 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -1,6 +1,4 @@
#include "git-compat-util.h"
-#include "run-command.h"
-#include "xdiff-interface.h"
#include "merge-ll.h"
#include "blob.h"
#include "merge-blobs.h"
diff --git a/merge-ll.c b/merge-ll.c
index 8fcf2d3710..bf1077ae09 100644
--- a/merge-ll.c
+++ b/merge-ll.c
@@ -128,7 +128,9 @@ static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unuse
xmp.level = XDL_MERGE_ZEALOUS;
xmp.favor = opts->variant;
xmp.xpp.flags = opts->xdl_opts;
- if (git_xmerge_style >= 0)
+ if (opts->conflict_style >= 0)
+ xmp.style = opts->conflict_style;
+ else if (git_xmerge_style >= 0)
xmp.style = git_xmerge_style;
if (marker_size > 0)
xmp.marker_size = marker_size;
@@ -185,9 +187,9 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
mmbuffer_t *result,
const char *path,
- mmfile_t *orig, const char *orig_name UNUSED,
- mmfile_t *src1, const char *name1 UNUSED,
- mmfile_t *src2, const char *name2 UNUSED,
+ mmfile_t *orig, const char *orig_name,
+ mmfile_t *src1, const char *name1,
+ mmfile_t *src2, const char *name2,
const struct ll_merge_options *opts,
int marker_size)
{
@@ -222,6 +224,12 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
strbuf_addf(&cmd, "%d", marker_size);
else if (skip_prefix(format, "P", &format))
sq_quote_buf(&cmd, path);
+ else if (skip_prefix(format, "S", &format))
+ sq_quote_buf(&cmd, orig_name ? orig_name : "");
+ else if (skip_prefix(format, "X", &format))
+ sq_quote_buf(&cmd, name1 ? name1 : "");
+ else if (skip_prefix(format, "Y", &format))
+ sq_quote_buf(&cmd, name2 ? name2 : "");
else
strbuf_addch(&cmd, '%');
}
@@ -286,7 +294,7 @@ static int read_merge_config(const char *var, const char *value,
* after seeing merge.<name>.var1.
*/
for (fn = ll_user_merge; fn; fn = fn->next)
- if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+ if (!xstrncmpz(fn->name, name, namelen))
break;
if (!fn) {
CALLOC_ARRAY(fn, 1);
@@ -301,7 +309,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:
*
@@ -315,7 +323,12 @@ static int read_merge_config(const char *var, const char *value,
* %B - temporary file name for the other branches' version.
* %L - conflict marker length
* %P - the original path (safely quoted for the shell)
+ * %S - the revision for the merge base
+ * %X - the revision for our version
+ * %Y - the revision for their version
*
+ * If the file is not named indentically in all versions, then each
+ * revision is joined with the corresponding path, separated by a colon.
* The external merge driver should write the results in the
* file named by %A, and signal that it has done with zero exit
* status.
@@ -390,7 +403,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
const struct ll_merge_options *opts)
{
struct attr_check *check = load_merge_attributes();
- static const struct ll_merge_options default_opts;
+ static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT;
const char *ll_driver_name = NULL;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
const struct ll_merge_driver *driver;
diff --git a/merge-ll.h b/merge-ll.h
index e4a20e81a3..d038ee0c1e 100644
--- a/merge-ll.h
+++ b/merge-ll.h
@@ -78,10 +78,15 @@ struct ll_merge_options {
*/
unsigned extra_marker_size;
+ /* Override the global conflict style. */
+ int conflict_style;
+
/* Extra xpparam_t flags as defined in xdiff/xdiff.h. */
long xdl_opts;
};
+#define LL_MERGE_OPTIONS_INIT { .conflict_style = -1 }
+
enum ll_merge_result {
LL_MERGE_ERROR = -1,
LL_MERGE_OK = 0,
diff --git a/merge-ort.c b/merge-ort.c
index 6491070d96..eaede6cead 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -18,8 +18,8 @@
#include "merge-ort.h"
#include "alloc.h"
+#include "advice.h"
#include "attr.h"
-#include "blob.h"
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
@@ -39,11 +39,10 @@
#include "path.h"
#include "promisor-remote.h"
#include "read-cache-ll.h"
+#include "refs.h"
#include "revision.h"
#include "sparse-index.h"
#include "strmap.h"
-#include "submodule-config.h"
-#include "submodule.h"
#include "trace2.h"
#include "tree.h"
#include "unpack-trees.h"
@@ -544,6 +543,7 @@ enum conflict_and_info_types {
CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
+ CONFLICT_SUBMODULE_CORRUPT,
/* Keep this entry _last_ in the list */
NB_CONFLICT_TYPES,
@@ -596,7 +596,9 @@ static const char *type_short_descriptions[] = {
[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
"CONFLICT (submodule may have rewinds)",
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
- "CONFLICT (submodule lacks merge base)"
+ "CONFLICT (submodule lacks merge base)",
+ [CONFLICT_SUBMODULE_CORRUPT] =
+ "CONFLICT (submodule corrupt)"
};
struct logical_conflict_info {
@@ -1659,12 +1661,14 @@ static int collect_merge_info(struct merge_options *opt,
info.data = opt;
info.show_all_errors = 1;
- parse_tree(merge_base);
- parse_tree(side1);
- parse_tree(side2);
- init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
- init_tree_desc(t + 1, side1->buffer, side1->size);
- init_tree_desc(t + 2, side2->buffer, side2->size);
+ if (parse_tree(merge_base) < 0 ||
+ parse_tree(side1) < 0 ||
+ parse_tree(side2) < 0)
+ return -1;
+ init_tree_desc(t + 0, &merge_base->object.oid,
+ merge_base->buffer, merge_base->size);
+ init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+ init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
trace2_region_enter("merge", "traverse_trees", opt->repo);
ret = traverse_trees(NULL, 3, t, &info);
@@ -1710,7 +1714,14 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
- if (repo_in_merge_bases(repo, b, commit))
+ int ret = repo_in_merge_bases(repo, b, commit);
+
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1725,9 +1736,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
- if (i != j && repo_in_merge_bases(repo, m2, m1)) {
- contains_another = 1;
- break;
+ if (i != j) {
+ int ret = repo_in_merge_bases(repo, m2, m1);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0) {
+ contains_another = 1;
+ break;
+ }
}
}
@@ -1749,7 +1768,7 @@ static int merge_submodule(struct merge_options *opt,
{
struct repository subrepo;
struct strbuf sb = STRBUF_INIT;
- int ret = 0;
+ int ret = 0, ret2;
struct commit *commit_o, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@@ -1796,8 +1815,28 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
- if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
- !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
+ if (ret2 < 0) {
+ path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("Failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0)
+ ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
+ if (ret2 < 0) {
+ path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("Failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (!ret2) {
path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
@@ -1807,7 +1846,17 @@ static int merge_submodule(struct merge_options *opt,
}
/* Case #1: a is contained in b or vice versa */
- if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+ if (ret2 < 0) {
+ path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("Failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0) {
oidcpy(result, b);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@@ -1816,7 +1865,17 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
- if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+ if (ret2 < 0) {
+ path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("Failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0) {
oidcpy(result, a);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@@ -1841,6 +1900,14 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
&merges);
switch (parent_count) {
+ case -1:
+ path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("Failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ break;
case 0:
path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
path, NULL, NULL, NULL,
@@ -1958,7 +2025,7 @@ static int merge_3way(struct merge_options *opt,
mmbuffer_t *result_buf)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
enum ll_merge_result merge_status;
@@ -1968,6 +2035,7 @@ static int merge_3way(struct merge_options *opt,
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
@@ -2644,7 +2712,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
oidcpy(&ci->stages[i].oid, null_oid());
}
- // Now we want to focus on new_ci, so reassign ci to it
+ /* Now we want to focus on new_ci, so reassign ci to it. */
ci = new_ci;
}
@@ -4378,10 +4446,12 @@ static int checkout(struct merge_options *opt,
unpack_opts.verbose_update = (opt->verbosity > 2);
unpack_opts.fn = twoway_merge;
unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
- parse_tree(prev);
- init_tree_desc(&trees[0], prev->buffer, prev->size);
- parse_tree(next);
- init_tree_desc(&trees[1], next->buffer, next->size);
+ if (parse_tree(prev) < 0)
+ return -1;
+ init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
+ if (parse_tree(next) < 0)
+ return -1;
+ init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
ret = unpack_trees(2, trees, &unpack_opts);
clear_unpack_trees_porcelain(&unpack_opts);
@@ -4558,7 +4628,7 @@ static void print_submodule_conflict_suggestion(struct string_list *csub) {
" - commit the resulting index in the superproject\n"),
tmp.buf, subs.buf);
- printf("%s", msg.buf);
+ advise_if_enabled(ADVICE_SUBMODULE_MERGE_CONFLICT, "%s", msg.buf);
strbuf_release(&subs);
strbuf_release(&tmp);
@@ -4662,9 +4732,6 @@ void merge_switch_to_result(struct merge_options *opt,
{
assert(opt->priv == NULL);
if (result->clean >= 0 && update_worktree_and_index) {
- const char *filename;
- FILE *fp;
-
trace2_region_enter("merge", "checkout", opt->repo);
if (checkout(opt, head, result->tree)) {
/* failure to function */
@@ -4690,10 +4757,17 @@ void merge_switch_to_result(struct merge_options *opt,
trace2_region_leave("merge", "record_conflicted", opt->repo);
trace2_region_enter("merge", "write_auto_merge", opt->repo);
- filename = git_path_auto_merge(opt->repo);
- fp = xfopen(filename, "w");
- fprintf(fp, "%s\n", oid_to_hex(&result->tree->object.oid));
- fclose(fp);
+ if (refs_update_ref(get_main_ref_store(opt->repo), "", "AUTO_MERGE",
+ &result->tree->object.oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_MSG_ON_ERR)) {
+ /* failure to function */
+ opt->priv = NULL;
+ result->clean = -1;
+ merge_finalize(opt, result);
+ trace2_region_leave("merge", "write_auto_merge",
+ opt->repo);
+ return;
+ }
trace2_region_leave("merge", "write_auto_merge", opt->repo);
}
if (display_update_msgs)
@@ -4980,6 +5054,9 @@ redo:
if (result->clean >= 0) {
result->tree = parse_tree_indirect(&working_tree_oid);
+ if (!result->tree)
+ die(_("unable to read tree (%s)"),
+ oid_to_hex(&working_tree_oid));
/* existence of conflicted entries implies unclean */
result->clean &= strmap_empty(&opt->priv->conflicted);
}
@@ -5005,7 +5082,11 @@ static void merge_ort_internal(struct merge_options *opt,
struct strbuf merge_base_abbrev = STRBUF_INIT;
if (!merge_bases) {
- merge_bases = repo_get_merge_bases(the_repository, h1, h2);
+ if (repo_get_merge_bases(the_repository, h1, h2,
+ &merge_bases) < 0) {
+ result->clean = -1;
+ return;
+ }
/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
merge_bases = reverse_commit_list(merge_bases);
}
diff --git a/merge-recursive.c b/merge-recursive.c
index e3beb0801b..8ff29ed09e 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -6,10 +6,7 @@
#include "git-compat-util.h"
#include "merge-recursive.h"
-#include "advice.h"
#include "alloc.h"
-#include "attr.h"
-#include "blob.h"
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
@@ -32,8 +29,6 @@
#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"
@@ -410,8 +405,9 @@ static inline int merge_detect_rename(struct merge_options *opt)
static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
{
- parse_tree(tree);
- init_tree_desc(desc, tree->buffer, tree->size);
+ if (parse_tree(tree) < 0)
+ exit(128);
+ init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
}
static int unpack_trees_start(struct merge_options *opt,
@@ -1052,13 +1048,14 @@ static int merge_3way(struct merge_options *opt,
const int extra_marker_size)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
enum ll_merge_result merge_status;
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
@@ -1144,7 +1141,13 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
- if (repo_in_merge_bases(repo, b, commit))
+ int ret = repo_in_merge_bases(repo, b, commit);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1159,9 +1162,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
- if (i != j && repo_in_merge_bases(repo, m2, m1)) {
- contains_another = 1;
- break;
+ if (i != j) {
+ int ret = repo_in_merge_bases(repo, m2, m1);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0) {
+ contains_another = 1;
+ break;
+ }
}
}
@@ -1197,7 +1208,7 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *b)
{
struct repository subrepo;
- int ret = 0;
+ int ret = 0, ret2;
struct commit *commit_base, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@@ -1234,14 +1245,32 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
- if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
- !repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0)
+ ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (!ret2) {
output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
goto cleanup;
}
/* Case #1: a is contained in b or vice versa */
- if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2) {
oidcpy(result, b);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1254,7 +1283,13 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
- if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+ if (ret2 < 0) {
+ output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2) {
oidcpy(result, a);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1283,6 +1318,10 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, &merges, path,
commit_a, commit_b);
switch (parent_count) {
+ case -1:
+ output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
+ ret = -1;
+ break;
case 0:
output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
break;
@@ -1397,11 +1436,14 @@ static int merge_mode_and_contents(struct merge_options *opt,
/* FIXME: bug, what if modes didn't match? */
result->clean = (merge_status == 0);
} else if (S_ISGITLINK(a->mode)) {
- result->clean = merge_submodule(opt, &result->blob.oid,
- o->path,
- &o->oid,
- &a->oid,
- &b->oid);
+ int clean = merge_submodule(opt, &result->blob.oid,
+ o->path,
+ &o->oid,
+ &a->oid,
+ &b->oid);
+ if (clean < 0)
+ return -1;
+ result->clean = clean;
} else if (S_ISLNK(a->mode)) {
switch (opt->recursive_variant) {
case MERGE_VARIANT_NORMAL:
@@ -3602,7 +3644,9 @@ static int merge_recursive_internal(struct merge_options *opt,
}
if (!merge_bases) {
- merge_bases = repo_get_merge_bases(the_repository, h1, h2);
+ if (repo_get_merge_bases(the_repository, h1, h2,
+ &merge_bases) < 0)
+ return -1;
merge_bases = reverse_commit_list(merge_bases);
}
@@ -3904,6 +3948,8 @@ void init_merge_options(struct merge_options *opt,
opt->renormalize = 0;
+ opt->conflict_style = -1;
+
merge_recursive_config(opt);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
diff --git a/merge-recursive.h b/merge-recursive.h
index 3d3b3e3c29..e67d38c303 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -31,6 +31,7 @@ struct merge_options {
/* xdiff-related options (patience, ignore whitespace, ours/theirs) */
long xdl_opts;
+ int conflict_style;
enum {
MERGE_VARIANT_NORMAL = 0,
MERGE_VARIANT_OURS,
diff --git a/merge.c b/merge.c
index b60925459c..752a937fa9 100644
--- a/merge.c
+++ b/merge.c
@@ -1,6 +1,4 @@
#include "git-compat-util.h"
-#include "diff.h"
-#include "diffcore.h"
#include "gettext.h"
#include "hash.h"
#include "hex.h"
@@ -13,7 +11,6 @@
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
-#include "dir.h"
static const char *merge_argument(struct commit *commit)
{
@@ -80,8 +77,12 @@ int checkout_fast_forward(struct repository *r,
return -1;
}
for (i = 0; i < nr_trees; i++) {
- parse_tree(trees[i]);
- init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+ if (parse_tree(trees[i]) < 0) {
+ rollback_lock_file(&lock_file);
+ return -1;
+ }
+ init_tree_desc(t+i, &trees[i]->object.oid,
+ trees[i]->buffer, trees[i]->size);
}
memset(&opts, 0, sizeof(opts));
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index 06937acbf5..97e376329b 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -371,9 +371,17 @@ diff_cmd_help () {
merge_cmd () {
- layout=$(git config mergetool.vimdiff.layout)
+ TOOL=$1
- case "$1" in
+ layout=$(git config "mergetool.$TOOL.layout")
+
+ # backward compatibility:
+ if test -z "$layout"
+ then
+ layout=$(git config mergetool.vimdiff.layout)
+ fi
+
+ case "$TOOL" in
*vimdiff)
if test -z "$layout"
then
diff --git a/midx-write.c b/midx-write.c
new file mode 100644
index 0000000000..65e69d2de7
--- /dev/null
+++ b/midx-write.c
@@ -0,0 +1,1525 @@
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "config.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "packfile.h"
+#include "object-file.h"
+#include "hash-lookup.h"
+#include "midx.h"
+#include "progress.h"
+#include "trace2.h"
+#include "run-command.h"
+#include "chunk-format.h"
+#include "pack-bitmap.h"
+#include "refs.h"
+#include "revision.h"
+#include "list-objects.h"
+
+#define PACK_EXPIRED UINT_MAX
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+#define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
+#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+
+extern int midx_checksum_valid(struct multi_pack_index *m);
+extern void clear_midx_files_ext(const char *object_dir, const char *ext,
+ unsigned char *keep_hash);
+extern int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+ const char *idx_name);
+
+static size_t write_midx_header(struct hashfile *f,
+ unsigned char num_chunks,
+ uint32_t num_packs)
+{
+ hashwrite_be32(f, MIDX_SIGNATURE);
+ hashwrite_u8(f, MIDX_VERSION);
+ hashwrite_u8(f, oid_version(the_hash_algo));
+ hashwrite_u8(f, num_chunks);
+ hashwrite_u8(f, 0); /* unused */
+ hashwrite_be32(f, num_packs);
+
+ return MIDX_HEADER_SIZE;
+}
+
+struct pack_info {
+ uint32_t orig_pack_int_id;
+ char *pack_name;
+ struct packed_git *p;
+
+ uint32_t bitmap_pos;
+ uint32_t bitmap_nr;
+
+ unsigned expired : 1;
+};
+
+static void fill_pack_info(struct pack_info *info,
+ struct packed_git *p, const char *pack_name,
+ uint32_t orig_pack_int_id)
+{
+ memset(info, 0, sizeof(struct pack_info));
+
+ info->orig_pack_int_id = orig_pack_int_id;
+ info->pack_name = xstrdup(pack_name);
+ info->p = p;
+ info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
+static int pack_info_compare(const void *_a, const void *_b)
+{
+ struct pack_info *a = (struct pack_info *)_a;
+ struct pack_info *b = (struct pack_info *)_b;
+ return strcmp(a->pack_name, b->pack_name);
+}
+
+static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
+{
+ const char *pack_name = _va;
+ const struct pack_info *compar = _vb;
+
+ return cmp_idx_or_pack_name(pack_name, compar->pack_name);
+}
+
+struct write_midx_context {
+ struct pack_info *info;
+ size_t nr;
+ size_t alloc;
+ struct multi_pack_index *m;
+ struct progress *progress;
+ unsigned pack_paths_checked;
+
+ struct pack_midx_entry *entries;
+ size_t entries_nr;
+
+ uint32_t *pack_perm;
+ uint32_t *pack_order;
+ unsigned large_offsets_needed:1;
+ uint32_t num_large_offsets;
+
+ int preferred_pack_idx;
+
+ struct string_list *to_include;
+};
+
+static void add_pack_to_midx(const char *full_path, size_t full_path_len,
+ const char *file_name, void *data)
+{
+ struct write_midx_context *ctx = data;
+ struct packed_git *p;
+
+ if (ends_with(file_name, ".idx")) {
+ display_progress(ctx->progress, ++ctx->pack_paths_checked);
+ /*
+ * Note that at most one of ctx->m and ctx->to_include are set,
+ * so we are testing midx_contains_pack() and
+ * string_list_has_string() independently (guarded by the
+ * appropriate NULL checks).
+ *
+ * We could support passing to_include while reusing an existing
+ * MIDX, but don't currently since the reuse process drags
+ * forward all packs from an existing MIDX (without checking
+ * whether or not they appear in the to_include list).
+ *
+ * If we added support for that, these next two conditional
+ * should be performed independently (likely checking
+ * to_include before the existing MIDX).
+ */
+ if (ctx->m && midx_contains_pack(ctx->m, file_name))
+ return;
+ else if (ctx->to_include &&
+ !string_list_has_string(ctx->to_include, file_name))
+ return;
+
+ ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
+
+ p = add_packed_git(full_path, full_path_len, 0);
+ if (!p) {
+ warning(_("failed to add packfile '%s'"),
+ full_path);
+ return;
+ }
+
+ if (open_pack_index(p)) {
+ warning(_("failed to open pack-index '%s'"),
+ full_path);
+ close_pack(p);
+ free(p);
+ return;
+ }
+
+ fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
+ ctx->nr++;
+ }
+}
+
+struct pack_midx_entry {
+ struct object_id oid;
+ uint32_t pack_int_id;
+ time_t pack_mtime;
+ uint64_t offset;
+ unsigned preferred : 1;
+};
+
+static int midx_oid_compare(const void *_a, const void *_b)
+{
+ const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
+ const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
+ int cmp = oidcmp(&a->oid, &b->oid);
+
+ if (cmp)
+ return cmp;
+
+ /* Sort objects in a preferred pack first when multiple copies exist. */
+ if (a->preferred > b->preferred)
+ return -1;
+ if (a->preferred < b->preferred)
+ return 1;
+
+ if (a->pack_mtime > b->pack_mtime)
+ return -1;
+ else if (a->pack_mtime < b->pack_mtime)
+ return 1;
+
+ return a->pack_int_id - b->pack_int_id;
+}
+
+static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
+ struct pack_midx_entry *e,
+ uint32_t pos)
+{
+ if (pos >= m->num_objects)
+ return 1;
+
+ nth_midxed_object_oid(&e->oid, m, pos);
+ e->pack_int_id = nth_midxed_pack_int_id(m, pos);
+ e->offset = nth_midxed_offset(m, pos);
+
+ /* consider objects in midx to be from "old" packs */
+ e->pack_mtime = 0;
+ return 0;
+}
+
+static void fill_pack_entry(uint32_t pack_int_id,
+ struct packed_git *p,
+ uint32_t cur_object,
+ struct pack_midx_entry *entry,
+ int preferred)
+{
+ if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
+ die(_("failed to locate object %d in packfile"), cur_object);
+
+ entry->pack_int_id = pack_int_id;
+ entry->pack_mtime = p->mtime;
+
+ entry->offset = nth_packed_object_offset(p, cur_object);
+ entry->preferred = !!preferred;
+}
+
+struct midx_fanout {
+ struct pack_midx_entry *entries;
+ size_t nr, alloc;
+};
+
+static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
+{
+ if (nr < fanout->nr)
+ BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
+ (uintmax_t)nr, (uintmax_t)fanout->nr);
+ ALLOC_GROW(fanout->entries, nr, fanout->alloc);
+}
+
+static void midx_fanout_sort(struct midx_fanout *fanout)
+{
+ QSORT(fanout->entries, fanout->nr, midx_oid_compare);
+}
+
+static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
+ struct multi_pack_index *m,
+ uint32_t cur_fanout,
+ int preferred_pack)
+{
+ uint32_t start = 0, end;
+ uint32_t cur_object;
+
+ if (cur_fanout)
+ start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
+ end = ntohl(m->chunk_oid_fanout[cur_fanout]);
+
+ for (cur_object = start; cur_object < end; cur_object++) {
+ if ((preferred_pack > -1) &&
+ (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
+ /*
+ * Objects from preferred packs are added
+ * separately.
+ */
+ continue;
+ }
+
+ midx_fanout_grow(fanout, fanout->nr + 1);
+ nth_midxed_pack_midx_entry(m,
+ &fanout->entries[fanout->nr],
+ cur_object);
+ fanout->entries[fanout->nr].preferred = 0;
+ fanout->nr++;
+ }
+}
+
+static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
+ struct pack_info *info,
+ uint32_t cur_pack,
+ int preferred,
+ uint32_t cur_fanout)
+{
+ struct packed_git *pack = info[cur_pack].p;
+ uint32_t start = 0, end;
+ uint32_t cur_object;
+
+ if (cur_fanout)
+ start = get_pack_fanout(pack, cur_fanout - 1);
+ end = get_pack_fanout(pack, cur_fanout);
+
+ for (cur_object = start; cur_object < end; cur_object++) {
+ midx_fanout_grow(fanout, fanout->nr + 1);
+ fill_pack_entry(cur_pack,
+ info[cur_pack].p,
+ cur_object,
+ &fanout->entries[fanout->nr],
+ preferred);
+ fanout->nr++;
+ }
+}
+
+/*
+ * It is possible to artificially get into a state where there are many
+ * duplicate copies of objects. That can create high memory pressure if
+ * we are to create a list of all objects before de-duplication. To reduce
+ * this memory pressure without a significant performance drop, automatically
+ * group objects by the first byte of their object id. Use the IDX fanout
+ * tables to group the data, copy to a local array, then sort.
+ *
+ * Copy only the de-duplicated entries (selected by most-recent modified time
+ * of a packfile containing the object).
+ */
+static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
+ struct pack_info *info,
+ uint32_t nr_packs,
+ size_t *nr_objects,
+ int preferred_pack)
+{
+ uint32_t cur_fanout, cur_pack, cur_object;
+ size_t alloc_objects, total_objects = 0;
+ struct midx_fanout fanout = { 0 };
+ struct pack_midx_entry *deduplicated_entries = NULL;
+ uint32_t start_pack = m ? m->num_packs : 0;
+
+ for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
+ total_objects = st_add(total_objects,
+ info[cur_pack].p->num_objects);
+
+ /*
+ * As we de-duplicate by fanout value, we expect the fanout
+ * slices to be evenly distributed, with some noise. Hence,
+ * allocate slightly more than one 256th.
+ */
+ alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
+
+ ALLOC_ARRAY(fanout.entries, fanout.alloc);
+ ALLOC_ARRAY(deduplicated_entries, alloc_objects);
+ *nr_objects = 0;
+
+ for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
+ fanout.nr = 0;
+
+ if (m)
+ midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
+ preferred_pack);
+
+ for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
+ int preferred = cur_pack == preferred_pack;
+ midx_fanout_add_pack_fanout(&fanout,
+ info, cur_pack,
+ preferred, cur_fanout);
+ }
+
+ if (-1 < preferred_pack && preferred_pack < start_pack)
+ midx_fanout_add_pack_fanout(&fanout, info,
+ preferred_pack, 1,
+ cur_fanout);
+
+ midx_fanout_sort(&fanout);
+
+ /*
+ * The batch is now sorted by OID and then mtime (descending).
+ * Take only the first duplicate.
+ */
+ for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
+ if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
+ &fanout.entries[cur_object].oid))
+ continue;
+
+ ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
+ alloc_objects);
+ memcpy(&deduplicated_entries[*nr_objects],
+ &fanout.entries[cur_object],
+ sizeof(struct pack_midx_entry));
+ (*nr_objects)++;
+ }
+ }
+
+ free(fanout.entries);
+ return deduplicated_entries;
+}
+
+static int write_midx_pack_names(struct hashfile *f, void *data)
+{
+ struct write_midx_context *ctx = data;
+ uint32_t i;
+ unsigned char padding[MIDX_CHUNK_ALIGNMENT];
+ size_t written = 0;
+
+ for (i = 0; i < ctx->nr; i++) {
+ size_t writelen;
+
+ if (ctx->info[i].expired)
+ continue;
+
+ if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
+ BUG("incorrect pack-file order: %s before %s",
+ ctx->info[i - 1].pack_name,
+ ctx->info[i].pack_name);
+
+ writelen = strlen(ctx->info[i].pack_name) + 1;
+ hashwrite(f, ctx->info[i].pack_name, writelen);
+ written += writelen;
+ }
+
+ /* add padding to be aligned */
+ i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
+ if (i < MIDX_CHUNK_ALIGNMENT) {
+ memset(padding, 0, sizeof(padding));
+ hashwrite(f, padding, i);
+ }
+
+ return 0;
+}
+
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+ struct write_midx_context *ctx = data;
+ size_t i;
+
+ for (i = 0; i < ctx->nr; i++) {
+ struct pack_info *pack = &ctx->info[i];
+ if (pack->expired)
+ continue;
+
+ if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+ BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+ pack->pack_name, pack->bitmap_nr);
+
+ hashwrite_be32(f, pack->bitmap_pos);
+ hashwrite_be32(f, pack->bitmap_nr);
+ }
+ return 0;
+}
+
+static int write_midx_oid_fanout(struct hashfile *f,
+ void *data)
+{
+ struct write_midx_context *ctx = data;
+ struct pack_midx_entry *list = ctx->entries;
+ struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
+ uint32_t count = 0;
+ uint32_t i;
+
+ /*
+ * Write the first-level table (the list is sorted,
+ * but we use a 256-entry lookup to be able to avoid
+ * having to do eight extra binary search iterations).
+ */
+ for (i = 0; i < 256; i++) {
+ struct pack_midx_entry *next = list;
+
+ while (next < last && next->oid.hash[0] == i) {
+ count++;
+ next++;
+ }
+
+ hashwrite_be32(f, count);
+ list = next;
+ }
+
+ return 0;
+}
+
+static int write_midx_oid_lookup(struct hashfile *f,
+ void *data)
+{
+ struct write_midx_context *ctx = data;
+ unsigned char hash_len = the_hash_algo->rawsz;
+ struct pack_midx_entry *list = ctx->entries;
+ uint32_t i;
+
+ for (i = 0; i < ctx->entries_nr; i++) {
+ struct pack_midx_entry *obj = list++;
+
+ if (i < ctx->entries_nr - 1) {
+ struct pack_midx_entry *next = list;
+ if (oidcmp(&obj->oid, &next->oid) >= 0)
+ BUG("OIDs not in order: %s >= %s",
+ oid_to_hex(&obj->oid),
+ oid_to_hex(&next->oid));
+ }
+
+ hashwrite(f, obj->oid.hash, (int)hash_len);
+ }
+
+ return 0;
+}
+
+static int write_midx_object_offsets(struct hashfile *f,
+ void *data)
+{
+ struct write_midx_context *ctx = data;
+ struct pack_midx_entry *list = ctx->entries;
+ uint32_t i, nr_large_offset = 0;
+
+ for (i = 0; i < ctx->entries_nr; i++) {
+ struct pack_midx_entry *obj = list++;
+
+ if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
+ BUG("object %s is in an expired pack with int-id %d",
+ oid_to_hex(&obj->oid),
+ obj->pack_int_id);
+
+ hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
+
+ if (ctx->large_offsets_needed && obj->offset >> 31)
+ hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
+ else if (!ctx->large_offsets_needed && obj->offset >> 32)
+ BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
+ oid_to_hex(&obj->oid),
+ obj->offset);
+ else
+ hashwrite_be32(f, (uint32_t)obj->offset);
+ }
+
+ return 0;
+}
+
+static int write_midx_large_offsets(struct hashfile *f,
+ void *data)
+{
+ struct write_midx_context *ctx = data;
+ struct pack_midx_entry *list = ctx->entries;
+ struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
+ uint32_t nr_large_offset = ctx->num_large_offsets;
+
+ while (nr_large_offset) {
+ struct pack_midx_entry *obj;
+ uint64_t offset;
+
+ if (list >= end)
+ BUG("too many large-offset objects");
+
+ obj = list++;
+ offset = obj->offset;
+
+ if (!(offset >> 31))
+ continue;
+
+ hashwrite_be64(f, offset);
+
+ nr_large_offset--;
+ }
+
+ return 0;
+}
+
+static int write_midx_revindex(struct hashfile *f,
+ void *data)
+{
+ struct write_midx_context *ctx = data;
+ uint32_t i;
+
+ for (i = 0; i < ctx->entries_nr; i++)
+ hashwrite_be32(f, ctx->pack_order[i]);
+
+ return 0;
+}
+
+struct midx_pack_order_data {
+ uint32_t nr;
+ uint32_t pack;
+ off_t offset;
+};
+
+static int midx_pack_order_cmp(const void *va, const void *vb)
+{
+ const struct midx_pack_order_data *a = va, *b = vb;
+ if (a->pack < b->pack)
+ return -1;
+ else if (a->pack > b->pack)
+ return 1;
+ else if (a->offset < b->offset)
+ return -1;
+ else if (a->offset > b->offset)
+ return 1;
+ else
+ return 0;
+}
+
+static uint32_t *midx_pack_order(struct write_midx_context *ctx)
+{
+ struct midx_pack_order_data *data;
+ uint32_t *pack_order;
+ uint32_t i;
+
+ trace2_region_enter("midx", "midx_pack_order", the_repository);
+
+ ALLOC_ARRAY(data, ctx->entries_nr);
+ for (i = 0; i < ctx->entries_nr; i++) {
+ struct pack_midx_entry *e = &ctx->entries[i];
+ data[i].nr = i;
+ data[i].pack = ctx->pack_perm[e->pack_int_id];
+ if (!e->preferred)
+ data[i].pack |= (1U << 31);
+ data[i].offset = e->offset;
+ }
+
+ QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
+
+ ALLOC_ARRAY(pack_order, ctx->entries_nr);
+ for (i = 0; i < ctx->entries_nr; i++) {
+ struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+ struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+ if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+ pack->bitmap_pos = i;
+ pack->bitmap_nr++;
+ pack_order[i] = data[i].nr;
+ }
+ for (i = 0; i < ctx->nr; i++) {
+ struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+ if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+ pack->bitmap_pos = 0;
+ }
+ free(data);
+
+ trace2_region_leave("midx", "midx_pack_order", the_repository);
+
+ return pack_order;
+}
+
+static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
+ struct write_midx_context *ctx)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *tmp_file;
+
+ trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
+
+ strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
+
+ tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
+ midx_hash, WRITE_REV);
+
+ if (finalize_object_file(tmp_file, buf.buf))
+ die(_("cannot store reverse index file"));
+
+ strbuf_release(&buf);
+
+ trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
+}
+
+static void prepare_midx_packing_data(struct packing_data *pdata,
+ struct write_midx_context *ctx)
+{
+ uint32_t i;
+
+ trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
+
+ memset(pdata, 0, sizeof(struct packing_data));
+ prepare_packing_data(the_repository, pdata);
+
+ for (i = 0; i < ctx->entries_nr; i++) {
+ struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
+ struct object_entry *to = packlist_alloc(pdata, &from->oid);
+
+ oe_set_in_pack(pdata, to,
+ ctx->info[ctx->pack_perm[from->pack_int_id]].p);
+ }
+
+ trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
+}
+
+static int add_ref_to_pending(const char *refname,
+ const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ struct rev_info *revs = (struct rev_info*)cb_data;
+ struct object_id peeled;
+ struct object *object;
+
+ if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
+ warning("symbolic ref is dangling: %s", refname);
+ return 0;
+ }
+
+ if (!peel_iterated_oid(oid, &peeled))
+ oid = &peeled;
+
+ object = parse_object_or_die(oid, refname);
+ if (object->type != OBJ_COMMIT)
+ return 0;
+
+ add_pending_object(revs, object, "");
+ if (bitmap_is_preferred_refname(revs->repo, refname))
+ object->flags |= NEEDS_BITMAP;
+ return 0;
+}
+
+struct bitmap_commit_cb {
+ struct commit **commits;
+ size_t commits_nr, commits_alloc;
+
+ struct write_midx_context *ctx;
+};
+
+static const struct object_id *bitmap_oid_access(size_t index,
+ const void *_entries)
+{
+ const struct pack_midx_entry *entries = _entries;
+ return &entries[index].oid;
+}
+
+static void bitmap_show_commit(struct commit *commit, void *_data)
+{
+ struct bitmap_commit_cb *data = _data;
+ int pos = oid_pos(&commit->object.oid, data->ctx->entries,
+ data->ctx->entries_nr,
+ bitmap_oid_access);
+ if (pos < 0)
+ return;
+
+ ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
+ data->commits[data->commits_nr++] = commit;
+}
+
+static int read_refs_snapshot(const char *refs_snapshot,
+ struct rev_info *revs)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct object_id oid;
+ FILE *f = xfopen(refs_snapshot, "r");
+
+ while (strbuf_getline(&buf, f) != EOF) {
+ struct object *object;
+ int preferred = 0;
+ char *hex = buf.buf;
+ const char *end = NULL;
+
+ if (buf.len && *buf.buf == '+') {
+ preferred = 1;
+ hex = &buf.buf[1];
+ }
+
+ if (parse_oid_hex(hex, &oid, &end) < 0)
+ die(_("could not parse line: %s"), buf.buf);
+ if (*end)
+ die(_("malformed line: %s"), buf.buf);
+
+ object = parse_object_or_die(&oid, NULL);
+ if (preferred)
+ object->flags |= NEEDS_BITMAP;
+
+ add_pending_object(revs, object, "");
+ }
+
+ fclose(f);
+ strbuf_release(&buf);
+ return 0;
+}
+static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
+ const char *refs_snapshot,
+ struct write_midx_context *ctx)
+{
+ struct rev_info revs;
+ struct bitmap_commit_cb cb = {0};
+
+ trace2_region_enter("midx", "find_commits_for_midx_bitmap",
+ the_repository);
+
+ cb.ctx = ctx;
+
+ repo_init_revisions(the_repository, &revs, NULL);
+ if (refs_snapshot) {
+ read_refs_snapshot(refs_snapshot, &revs);
+ } else {
+ setup_revisions(0, NULL, &revs, NULL);
+ for_each_ref(add_ref_to_pending, &revs);
+ }
+
+ /*
+ * Skipping promisor objects here is intentional, since it only excludes
+ * them from the list of reachable commits that we want to select from
+ * when computing the selection of MIDX'd commits to receive bitmaps.
+ *
+ * Reachability bitmaps do require that their objects be closed under
+ * reachability, but fetching any objects missing from promisors at this
+ * point is too late. But, if one of those objects can be reached from
+ * an another object that is included in the bitmap, then we will
+ * complain later that we don't have reachability closure (and fail
+ * appropriately).
+ */
+ fetch_if_missing = 0;
+ revs.exclude_promisor_objects = 1;
+
+ if (prepare_revision_walk(&revs))
+ die(_("revision walk setup failed"));
+
+ traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
+ if (indexed_commits_nr_p)
+ *indexed_commits_nr_p = cb.commits_nr;
+
+ release_revisions(&revs);
+
+ trace2_region_leave("midx", "find_commits_for_midx_bitmap",
+ the_repository);
+
+ return cb.commits;
+}
+
+static int write_midx_bitmap(const char *midx_name,
+ const unsigned char *midx_hash,
+ struct packing_data *pdata,
+ struct commit **commits,
+ uint32_t commits_nr,
+ uint32_t *pack_order,
+ unsigned flags)
+{
+ int ret, i;
+ uint16_t options = 0;
+ struct pack_idx_entry **index;
+ char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
+ hash_to_hex(midx_hash));
+
+ trace2_region_enter("midx", "write_midx_bitmap", the_repository);
+
+ if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
+ options |= BITMAP_OPT_HASH_CACHE;
+
+ if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
+ options |= BITMAP_OPT_LOOKUP_TABLE;
+
+ /*
+ * Build the MIDX-order index based on pdata.objects (which is already
+ * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
+ * this order).
+ */
+ ALLOC_ARRAY(index, pdata->nr_objects);
+ for (i = 0; i < pdata->nr_objects; i++)
+ index[i] = &pdata->objects[i].idx;
+
+ bitmap_writer_show_progress(flags & MIDX_PROGRESS);
+ bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
+
+ /*
+ * bitmap_writer_finish expects objects in lex order, but pack_order
+ * gives us exactly that. use it directly instead of re-sorting the
+ * array.
+ *
+ * This changes the order of objects in 'index' between
+ * bitmap_writer_build_type_index and bitmap_writer_finish.
+ *
+ * The same re-ordering takes place in the single-pack bitmap code via
+ * write_idx_file(), which is called by finish_tmp_packfile(), which
+ * happens between bitmap_writer_build_type_index() and
+ * bitmap_writer_finish().
+ */
+ for (i = 0; i < pdata->nr_objects; i++)
+ index[pack_order[i]] = &pdata->objects[i].idx;
+
+ bitmap_writer_select_commits(commits, commits_nr, -1);
+ ret = bitmap_writer_build(pdata);
+ if (ret < 0)
+ goto cleanup;
+
+ bitmap_writer_set_checksum(midx_hash);
+ bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
+
+cleanup:
+ free(index);
+ free(bitmap_name);
+
+ trace2_region_leave("midx", "write_midx_bitmap", the_repository);
+
+ return ret;
+}
+
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+ const char *object_dir)
+{
+ struct multi_pack_index *result = NULL;
+ struct multi_pack_index *cur;
+ char *obj_dir_real = real_pathdup(object_dir, 1);
+ struct strbuf cur_path_real = STRBUF_INIT;
+
+ /* Ensure the given object_dir is local, or a known alternate. */
+ find_odb(r, obj_dir_real);
+
+ for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+ strbuf_realpath(&cur_path_real, cur->object_dir, 1);
+ if (!strcmp(obj_dir_real, cur_path_real.buf)) {
+ result = cur;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ free(obj_dir_real);
+ strbuf_release(&cur_path_real);
+ return result;
+}
+
+static int write_midx_internal(const char *object_dir,
+ struct string_list *packs_to_include,
+ struct string_list *packs_to_drop,
+ const char *preferred_pack_name,
+ const char *refs_snapshot,
+ unsigned flags)
+{
+ struct strbuf midx_name = STRBUF_INIT;
+ unsigned char midx_hash[GIT_MAX_RAWSZ];
+ uint32_t i;
+ struct hashfile *f = NULL;
+ struct lock_file lk;
+ struct write_midx_context ctx = { 0 };
+ int bitmapped_packs_concat_len = 0;
+ int pack_name_concat_len = 0;
+ int dropped_packs = 0;
+ int result = 0;
+ struct chunkfile *cf;
+
+ trace2_region_enter("midx", "write_midx_internal", the_repository);
+
+ get_midx_filename(&midx_name, object_dir);
+ if (safe_create_leading_directories(midx_name.buf))
+ die_errno(_("unable to create leading directories of %s"),
+ midx_name.buf);
+
+ if (!packs_to_include) {
+ /*
+ * Only reference an existing MIDX when not filtering which
+ * packs to include, since all packs and objects are copied
+ * blindly from an existing MIDX if one is present.
+ */
+ ctx.m = lookup_multi_pack_index(the_repository, object_dir);
+ }
+
+ if (ctx.m && !midx_checksum_valid(ctx.m)) {
+ warning(_("ignoring existing multi-pack-index; checksum mismatch"));
+ ctx.m = NULL;
+ }
+
+ ctx.nr = 0;
+ ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
+ ctx.info = NULL;
+ ALLOC_ARRAY(ctx.info, ctx.alloc);
+
+ if (ctx.m) {
+ for (i = 0; i < ctx.m->num_packs; i++) {
+ ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
+
+ if (flags & MIDX_WRITE_REV_INDEX) {
+ /*
+ * If generating a reverse index, need to have
+ * packed_git's loaded to compare their
+ * mtimes and object count.
+ */
+ if (prepare_midx_pack(the_repository, ctx.m, i)) {
+ error(_("could not load pack"));
+ result = 1;
+ goto cleanup;
+ }
+
+ if (open_pack_index(ctx.m->packs[i]))
+ die(_("could not open index for %s"),
+ ctx.m->packs[i]->pack_name);
+ }
+
+ fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+ ctx.m->pack_names[i], i);
+ }
+ }
+
+ ctx.pack_paths_checked = 0;
+ if (flags & MIDX_PROGRESS)
+ ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
+ else
+ ctx.progress = NULL;
+
+ ctx.to_include = packs_to_include;
+
+ for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
+ stop_progress(&ctx.progress);
+
+ if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
+ !(packs_to_include || packs_to_drop)) {
+ struct bitmap_index *bitmap_git;
+ int bitmap_exists;
+ int want_bitmap = flags & MIDX_WRITE_BITMAP;
+
+ bitmap_git = prepare_midx_bitmap_git(ctx.m);
+ bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
+ free_bitmap_index(bitmap_git);
+
+ if (bitmap_exists || !want_bitmap) {
+ /*
+ * The correct MIDX already exists, and so does a
+ * corresponding bitmap (or one wasn't requested).
+ */
+ if (!want_bitmap)
+ clear_midx_files_ext(object_dir, ".bitmap",
+ NULL);
+ goto cleanup;
+ }
+ }
+
+ if (preferred_pack_name) {
+ ctx.preferred_pack_idx = -1;
+
+ for (i = 0; i < ctx.nr; i++) {
+ if (!cmp_idx_or_pack_name(preferred_pack_name,
+ ctx.info[i].pack_name)) {
+ ctx.preferred_pack_idx = i;
+ break;
+ }
+ }
+
+ if (ctx.preferred_pack_idx == -1)
+ warning(_("unknown preferred pack: '%s'"),
+ preferred_pack_name);
+ } else if (ctx.nr &&
+ (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
+ struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
+ ctx.preferred_pack_idx = 0;
+
+ if (packs_to_drop && packs_to_drop->nr)
+ BUG("cannot write a MIDX bitmap during expiration");
+
+ /*
+ * set a preferred pack when writing a bitmap to ensure that
+ * the pack from which the first object is selected in pseudo
+ * pack-order has all of its objects selected from that pack
+ * (and not another pack containing a duplicate)
+ */
+ for (i = 1; i < ctx.nr; i++) {
+ struct packed_git *p = ctx.info[i].p;
+
+ if (!oldest->num_objects || p->mtime < oldest->mtime) {
+ oldest = p;
+ ctx.preferred_pack_idx = i;
+ }
+ }
+
+ if (!oldest->num_objects) {
+ /*
+ * If all packs are empty; unset the preferred index.
+ * This is acceptable since there will be no duplicate
+ * objects to resolve, so the preferred value doesn't
+ * matter.
+ */
+ ctx.preferred_pack_idx = -1;
+ }
+ } else {
+ /*
+ * otherwise don't mark any pack as preferred to avoid
+ * interfering with expiration logic below
+ */
+ ctx.preferred_pack_idx = -1;
+ }
+
+ if (ctx.preferred_pack_idx > -1) {
+ struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
+ if (!preferred->num_objects) {
+ error(_("cannot select preferred pack %s with no objects"),
+ preferred->pack_name);
+ result = 1;
+ goto cleanup;
+ }
+ }
+
+ ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
+ ctx.preferred_pack_idx);
+
+ ctx.large_offsets_needed = 0;
+ for (i = 0; i < ctx.entries_nr; i++) {
+ if (ctx.entries[i].offset > 0x7fffffff)
+ ctx.num_large_offsets++;
+ if (ctx.entries[i].offset > 0xffffffff)
+ ctx.large_offsets_needed = 1;
+ }
+
+ QSORT(ctx.info, ctx.nr, pack_info_compare);
+
+ if (packs_to_drop && packs_to_drop->nr) {
+ int drop_index = 0;
+ int missing_drops = 0;
+
+ for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
+ int cmp = strcmp(ctx.info[i].pack_name,
+ packs_to_drop->items[drop_index].string);
+
+ if (!cmp) {
+ drop_index++;
+ ctx.info[i].expired = 1;
+ } else if (cmp > 0) {
+ error(_("did not see pack-file %s to drop"),
+ packs_to_drop->items[drop_index].string);
+ drop_index++;
+ missing_drops++;
+ i--;
+ } else {
+ ctx.info[i].expired = 0;
+ }
+ }
+
+ if (missing_drops) {
+ result = 1;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * pack_perm stores a permutation between pack-int-ids from the
+ * previous multi-pack-index to the new one we are writing:
+ *
+ * pack_perm[old_id] = new_id
+ */
+ ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
+ for (i = 0; i < ctx.nr; i++) {
+ if (ctx.info[i].expired) {
+ dropped_packs++;
+ ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
+ } else {
+ ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
+ }
+ }
+
+ for (i = 0; i < ctx.nr; i++) {
+ if (ctx.info[i].expired)
+ continue;
+ pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+ bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
+ }
+
+ /* Check that the preferred pack wasn't expired (if given). */
+ if (preferred_pack_name) {
+ struct pack_info *preferred = bsearch(preferred_pack_name,
+ ctx.info, ctx.nr,
+ sizeof(*ctx.info),
+ idx_or_pack_name_cmp);
+ if (preferred) {
+ uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
+ if (perm == PACK_EXPIRED)
+ warning(_("preferred pack '%s' is expired"),
+ preferred_pack_name);
+ }
+ }
+
+ if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
+ pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
+ (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
+
+ hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
+ f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+
+ if (ctx.nr - dropped_packs == 0) {
+ error(_("no pack files to index."));
+ result = 1;
+ goto cleanup;
+ }
+
+ if (!ctx.entries_nr) {
+ if (flags & MIDX_WRITE_BITMAP)
+ warning(_("refusing to write multi-pack .bitmap without any objects"));
+ flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
+ }
+
+ cf = init_chunkfile(f);
+
+ add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
+ write_midx_pack_names);
+ add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
+ write_midx_oid_fanout);
+ add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
+ st_mult(ctx.entries_nr, the_hash_algo->rawsz),
+ write_midx_oid_lookup);
+ add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
+ st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
+ write_midx_object_offsets);
+
+ if (ctx.large_offsets_needed)
+ add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
+ st_mult(ctx.num_large_offsets,
+ MIDX_CHUNK_LARGE_OFFSET_WIDTH),
+ write_midx_large_offsets);
+
+ if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
+ ctx.pack_order = midx_pack_order(&ctx);
+ add_chunk(cf, MIDX_CHUNKID_REVINDEX,
+ st_mult(ctx.entries_nr, sizeof(uint32_t)),
+ write_midx_revindex);
+ add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+ bitmapped_packs_concat_len,
+ write_midx_bitmapped_packs);
+ }
+
+ write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
+ write_chunkfile(cf, &ctx);
+
+ finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
+ CSUM_FSYNC | CSUM_HASH_IN_STREAM);
+ free_chunkfile(cf);
+
+ if (flags & MIDX_WRITE_REV_INDEX &&
+ git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
+ write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+
+ if (flags & MIDX_WRITE_BITMAP) {
+ struct packing_data pdata;
+ struct commit **commits;
+ uint32_t commits_nr;
+
+ if (!ctx.entries_nr)
+ BUG("cannot write a bitmap without any objects");
+
+ prepare_midx_packing_data(&pdata, &ctx);
+
+ commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
+
+ /*
+ * The previous steps translated the information from
+ * 'entries' into information suitable for constructing
+ * bitmaps. We no longer need that array, so clear it to
+ * reduce memory pressure.
+ */
+ FREE_AND_NULL(ctx.entries);
+ ctx.entries_nr = 0;
+
+ if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
+ commits, commits_nr, ctx.pack_order,
+ flags) < 0) {
+ error(_("could not write multi-pack bitmap"));
+ result = 1;
+ clear_packing_data(&pdata);
+ free(commits);
+ goto cleanup;
+ }
+
+ clear_packing_data(&pdata);
+ free(commits);
+ }
+ /*
+ * NOTE: Do not use ctx.entries beyond this point, since it might
+ * have been freed in the previous if block.
+ */
+
+ if (ctx.m)
+ close_object_store(the_repository->objects);
+
+ if (commit_lock_file(&lk) < 0)
+ die_errno(_("could not write multi-pack-index"));
+
+ clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
+ clear_midx_files_ext(object_dir, ".rev", midx_hash);
+
+cleanup:
+ for (i = 0; i < ctx.nr; i++) {
+ if (ctx.info[i].p) {
+ close_pack(ctx.info[i].p);
+ free(ctx.info[i].p);
+ }
+ free(ctx.info[i].pack_name);
+ }
+
+ free(ctx.info);
+ free(ctx.entries);
+ free(ctx.pack_perm);
+ free(ctx.pack_order);
+ strbuf_release(&midx_name);
+
+ trace2_region_leave("midx", "write_midx_internal", the_repository);
+
+ return result;
+}
+
+int write_midx_file(const char *object_dir,
+ const char *preferred_pack_name,
+ const char *refs_snapshot,
+ unsigned flags)
+{
+ return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
+ refs_snapshot, flags);
+}
+
+int write_midx_file_only(const char *object_dir,
+ struct string_list *packs_to_include,
+ const char *preferred_pack_name,
+ const char *refs_snapshot,
+ unsigned flags)
+{
+ return write_midx_internal(object_dir, packs_to_include, NULL,
+ preferred_pack_name, refs_snapshot, flags);
+}
+
+int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
+{
+ uint32_t i, *count, result = 0;
+ struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
+ struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+ struct progress *progress = NULL;
+
+ if (!m)
+ return 0;
+
+ CALLOC_ARRAY(count, m->num_packs);
+
+ if (flags & MIDX_PROGRESS)
+ progress = start_delayed_progress(_("Counting referenced objects"),
+ m->num_objects);
+ for (i = 0; i < m->num_objects; i++) {
+ int pack_int_id = nth_midxed_pack_int_id(m, i);
+ count[pack_int_id]++;
+ display_progress(progress, i + 1);
+ }
+ stop_progress(&progress);
+
+ if (flags & MIDX_PROGRESS)
+ progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
+ m->num_packs);
+ for (i = 0; i < m->num_packs; i++) {
+ char *pack_name;
+ display_progress(progress, i + 1);
+
+ if (count[i])
+ continue;
+
+ if (prepare_midx_pack(r, m, i))
+ continue;
+
+ if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
+ continue;
+
+ pack_name = xstrdup(m->packs[i]->pack_name);
+ close_pack(m->packs[i]);
+
+ string_list_insert(&packs_to_drop, m->pack_names[i]);
+ unlink_pack_path(pack_name, 0);
+ free(pack_name);
+ }
+ stop_progress(&progress);
+
+ free(count);
+
+ if (packs_to_drop.nr)
+ result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
+
+ string_list_clear(&packs_to_drop, 0);
+
+ return result;
+}
+
+struct repack_info {
+ timestamp_t mtime;
+ uint32_t referenced_objects;
+ uint32_t pack_int_id;
+};
+
+static int compare_by_mtime(const void *a_, const void *b_)
+{
+ const struct repack_info *a, *b;
+
+ a = (const struct repack_info *)a_;
+ b = (const struct repack_info *)b_;
+
+ if (a->mtime < b->mtime)
+ return -1;
+ if (a->mtime > b->mtime)
+ return 1;
+ return 0;
+}
+
+static int want_included_pack(struct repository *r,
+ struct multi_pack_index *m,
+ int pack_kept_objects,
+ uint32_t pack_int_id)
+{
+ struct packed_git *p;
+ if (prepare_midx_pack(r, m, pack_int_id))
+ return 0;
+ p = m->packs[pack_int_id];
+ if (!pack_kept_objects && p->pack_keep)
+ return 0;
+ if (p->is_cruft)
+ return 0;
+ if (open_pack_index(p) || !p->num_objects)
+ return 0;
+ return 1;
+}
+
+static void fill_included_packs_all(struct repository *r,
+ struct multi_pack_index *m,
+ unsigned char *include_pack)
+{
+ uint32_t i;
+ int pack_kept_objects = 0;
+
+ repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+ for (i = 0; i < m->num_packs; i++) {
+ if (!want_included_pack(r, m, pack_kept_objects, i))
+ continue;
+
+ include_pack[i] = 1;
+ }
+}
+
+static void fill_included_packs_batch(struct repository *r,
+ struct multi_pack_index *m,
+ unsigned char *include_pack,
+ size_t batch_size)
+{
+ uint32_t i;
+ size_t total_size;
+ struct repack_info *pack_info;
+ int pack_kept_objects = 0;
+
+ CALLOC_ARRAY(pack_info, m->num_packs);
+
+ repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+ for (i = 0; i < m->num_packs; i++) {
+ pack_info[i].pack_int_id = i;
+
+ if (prepare_midx_pack(r, m, i))
+ continue;
+
+ pack_info[i].mtime = m->packs[i]->mtime;
+ }
+
+ for (i = 0; i < m->num_objects; i++) {
+ uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
+ pack_info[pack_int_id].referenced_objects++;
+ }
+
+ QSORT(pack_info, m->num_packs, compare_by_mtime);
+
+ total_size = 0;
+ for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
+ int pack_int_id = pack_info[i].pack_int_id;
+ struct packed_git *p = m->packs[pack_int_id];
+ size_t expected_size;
+
+ if (!want_included_pack(r, m, pack_kept_objects, pack_int_id))
+ continue;
+
+ expected_size = st_mult(p->pack_size,
+ pack_info[i].referenced_objects);
+ expected_size /= p->num_objects;
+
+ if (expected_size >= batch_size)
+ continue;
+
+ total_size += expected_size;
+ include_pack[pack_int_id] = 1;
+ }
+
+ free(pack_info);
+}
+
+int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
+{
+ int result = 0;
+ uint32_t i, packs_to_repack = 0;
+ unsigned char *include_pack;
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ FILE *cmd_in;
+ struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
+
+ /*
+ * When updating the default for these configuration
+ * variables in builtin/repack.c, these must be adjusted
+ * to match.
+ */
+ int delta_base_offset = 1;
+ int use_delta_islands = 0;
+
+ if (!m)
+ return 0;
+
+ CALLOC_ARRAY(include_pack, m->num_packs);
+
+ if (batch_size)
+ fill_included_packs_batch(r, m, include_pack, batch_size);
+ else
+ fill_included_packs_all(r, m, include_pack);
+
+ for (i = 0; i < m->num_packs; i++) {
+ if (include_pack[i])
+ packs_to_repack++;
+ }
+ if (packs_to_repack <= 1)
+ goto cleanup;
+
+ repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
+ repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
+
+ strvec_pushl(&cmd.args, "pack-objects", "--stdin-packs", "--non-empty",
+ NULL);
+
+ strvec_pushf(&cmd.args, "%s/pack/pack", object_dir);
+
+ if (delta_base_offset)
+ strvec_push(&cmd.args, "--delta-base-offset");
+ if (use_delta_islands)
+ strvec_push(&cmd.args, "--delta-islands");
+
+ if (flags & MIDX_PROGRESS)
+ strvec_push(&cmd.args, "--progress");
+ else
+ strvec_push(&cmd.args, "-q");
+
+ cmd.git_cmd = 1;
+ cmd.in = cmd.out = -1;
+
+ if (start_command(&cmd)) {
+ error(_("could not start pack-objects"));
+ result = 1;
+ goto cleanup;
+ }
+
+ cmd_in = xfdopen(cmd.in, "w");
+ for (i = 0; i < m->num_packs; i++) {
+ struct packed_git *p = m->packs[i];
+ if (!p)
+ continue;
+
+ if (include_pack[i])
+ fprintf(cmd_in, "%s\n", pack_basename(p));
+ else
+ fprintf(cmd_in, "^%s\n", pack_basename(p));
+ }
+ fclose(cmd_in);
+
+ if (finish_command(&cmd)) {
+ error(_("could not finish pack-objects"));
+ result = 1;
+ goto cleanup;
+ }
+
+ result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
+
+cleanup:
+ free(include_pack);
+ return result;
+}
diff --git a/midx.c b/midx.c
index d2e357fa0f..6f07de3688 100644
--- a/midx.c
+++ b/midx.c
@@ -1,52 +1,22 @@
#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-file.h"
-#include "object-store-ll.h"
#include "hash-lookup.h"
#include "midx.h"
#include "progress.h"
#include "trace2.h"
-#include "run-command.h"
-#include "repository.h"
#include "chunk-format.h"
-#include "pack.h"
#include "pack-bitmap.h"
-#include "refs.h"
-#include "revision.h"
-#include "list-objects.h"
#include "pack-revindex.h"
-#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
-#define MIDX_VERSION 1
-#define MIDX_BYTE_FILE_VERSION 4
-#define MIDX_BYTE_HASH_VERSION 5
-#define MIDX_BYTE_NUM_CHUNKS 6
-#define MIDX_BYTE_NUM_PACKS 8
-#define MIDX_HEADER_SIZE 12
-#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
-
-#define MIDX_CHUNK_ALIGNMENT 4
-#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
-#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
-#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
-#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
-#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
-#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
-#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
-#define MIDX_CHUNK_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
+int midx_checksum_valid(struct multi_pack_index *m);
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+ unsigned char *keep_hash);
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+ const char *idx_name);
const unsigned char *get_midx_checksum(struct multi_pack_index *m)
{
@@ -115,6 +85,8 @@ static int midx_read_object_offsets(const unsigned char *chunk_start,
return 0;
}
+#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
+
struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local)
{
struct multi_pack_index *m = NULL;
@@ -295,6 +267,8 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t
return 0;
}
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
+
int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
struct bitmapped_pack *bp, uint32_t pack_int_id)
{
@@ -401,8 +375,8 @@ int fill_midx_entry(struct repository *r,
}
/* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */
-static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
- const char *idx_name)
+int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+ const char *idx_name)
{
/* Skip past any initial matching prefix. */
while (*idx_name && *idx_name == *idx_or_pack_name) {
@@ -509,1262 +483,11 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
return 0;
}
-static size_t write_midx_header(struct hashfile *f,
- unsigned char num_chunks,
- uint32_t num_packs)
-{
- hashwrite_be32(f, MIDX_SIGNATURE);
- hashwrite_u8(f, MIDX_VERSION);
- hashwrite_u8(f, oid_version(the_hash_algo));
- hashwrite_u8(f, num_chunks);
- hashwrite_u8(f, 0); /* unused */
- hashwrite_be32(f, num_packs);
-
- return MIDX_HEADER_SIZE;
-}
-
-#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;
- struct pack_info *b = (struct pack_info *)_b;
- return strcmp(a->pack_name, b->pack_name);
-}
-
-static int idx_or_pack_name_cmp(const void *_va, const void *_vb)
-{
- const char *pack_name = _va;
- const struct pack_info *compar = _vb;
-
- return cmp_idx_or_pack_name(pack_name, compar->pack_name);
-}
-
-struct write_midx_context {
- struct pack_info *info;
- size_t nr;
- size_t alloc;
- struct multi_pack_index *m;
- struct progress *progress;
- unsigned pack_paths_checked;
-
- struct pack_midx_entry *entries;
- size_t entries_nr;
-
- uint32_t *pack_perm;
- uint32_t *pack_order;
- unsigned large_offsets_needed:1;
- uint32_t num_large_offsets;
-
- int preferred_pack_idx;
-
- struct string_list *to_include;
-};
-
-static void add_pack_to_midx(const char *full_path, size_t full_path_len,
- const char *file_name, void *data)
-{
- struct write_midx_context *ctx = data;
- struct packed_git *p;
-
- if (ends_with(file_name, ".idx")) {
- display_progress(ctx->progress, ++ctx->pack_paths_checked);
- /*
- * Note that at most one of ctx->m and ctx->to_include are set,
- * so we are testing midx_contains_pack() and
- * string_list_has_string() independently (guarded by the
- * appropriate NULL checks).
- *
- * We could support passing to_include while reusing an existing
- * MIDX, but don't currently since the reuse process drags
- * forward all packs from an existing MIDX (without checking
- * whether or not they appear in the to_include list).
- *
- * If we added support for that, these next two conditional
- * should be performed independently (likely checking
- * to_include before the existing MIDX).
- */
- if (ctx->m && midx_contains_pack(ctx->m, file_name))
- return;
- else if (ctx->to_include &&
- !string_list_has_string(ctx->to_include, file_name))
- return;
-
- ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
-
- p = add_packed_git(full_path, full_path_len, 0);
- if (!p) {
- warning(_("failed to add packfile '%s'"),
- full_path);
- return;
- }
-
- if (open_pack_index(p)) {
- warning(_("failed to open pack-index '%s'"),
- full_path);
- close_pack(p);
- free(p);
- return;
- }
-
- fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
- ctx->nr++;
- }
-}
-
-struct pack_midx_entry {
- struct object_id oid;
- uint32_t pack_int_id;
- time_t pack_mtime;
- uint64_t offset;
- unsigned preferred : 1;
-};
-
-static int midx_oid_compare(const void *_a, const void *_b)
-{
- const struct pack_midx_entry *a = (const struct pack_midx_entry *)_a;
- const struct pack_midx_entry *b = (const struct pack_midx_entry *)_b;
- int cmp = oidcmp(&a->oid, &b->oid);
-
- if (cmp)
- return cmp;
-
- /* Sort objects in a preferred pack first when multiple copies exist. */
- if (a->preferred > b->preferred)
- return -1;
- if (a->preferred < b->preferred)
- return 1;
-
- if (a->pack_mtime > b->pack_mtime)
- return -1;
- else if (a->pack_mtime < b->pack_mtime)
- return 1;
-
- return a->pack_int_id - b->pack_int_id;
-}
-
-static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,
- struct pack_midx_entry *e,
- uint32_t pos)
-{
- if (pos >= m->num_objects)
- return 1;
-
- nth_midxed_object_oid(&e->oid, m, pos);
- e->pack_int_id = nth_midxed_pack_int_id(m, pos);
- e->offset = nth_midxed_offset(m, pos);
-
- /* consider objects in midx to be from "old" packs */
- e->pack_mtime = 0;
- return 0;
-}
-
-static void fill_pack_entry(uint32_t pack_int_id,
- struct packed_git *p,
- uint32_t cur_object,
- struct pack_midx_entry *entry,
- int preferred)
-{
- if (nth_packed_object_id(&entry->oid, p, cur_object) < 0)
- die(_("failed to locate object %d in packfile"), cur_object);
-
- entry->pack_int_id = pack_int_id;
- entry->pack_mtime = p->mtime;
-
- entry->offset = nth_packed_object_offset(p, cur_object);
- entry->preferred = !!preferred;
-}
-
-struct midx_fanout {
- struct pack_midx_entry *entries;
- size_t nr, alloc;
-};
-
-static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr)
-{
- if (nr < fanout->nr)
- BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")",
- (uintmax_t)nr, (uintmax_t)fanout->nr);
- ALLOC_GROW(fanout->entries, nr, fanout->alloc);
-}
-
-static void midx_fanout_sort(struct midx_fanout *fanout)
-{
- QSORT(fanout->entries, fanout->nr, midx_oid_compare);
-}
-
-static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout,
- struct multi_pack_index *m,
- uint32_t cur_fanout,
- int preferred_pack)
-{
- uint32_t start = 0, end;
- uint32_t cur_object;
-
- if (cur_fanout)
- start = ntohl(m->chunk_oid_fanout[cur_fanout - 1]);
- end = ntohl(m->chunk_oid_fanout[cur_fanout]);
-
- for (cur_object = start; cur_object < end; cur_object++) {
- if ((preferred_pack > -1) &&
- (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) {
- /*
- * Objects from preferred packs are added
- * separately.
- */
- continue;
- }
-
- midx_fanout_grow(fanout, fanout->nr + 1);
- nth_midxed_pack_midx_entry(m,
- &fanout->entries[fanout->nr],
- cur_object);
- fanout->entries[fanout->nr].preferred = 0;
- fanout->nr++;
- }
-}
-
-static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout,
- struct pack_info *info,
- uint32_t cur_pack,
- int preferred,
- uint32_t cur_fanout)
-{
- struct packed_git *pack = info[cur_pack].p;
- uint32_t start = 0, end;
- uint32_t cur_object;
-
- if (cur_fanout)
- start = get_pack_fanout(pack, cur_fanout - 1);
- end = get_pack_fanout(pack, cur_fanout);
-
- for (cur_object = start; cur_object < end; cur_object++) {
- midx_fanout_grow(fanout, fanout->nr + 1);
- fill_pack_entry(cur_pack,
- info[cur_pack].p,
- cur_object,
- &fanout->entries[fanout->nr],
- preferred);
- fanout->nr++;
- }
-}
-
-/*
- * It is possible to artificially get into a state where there are many
- * duplicate copies of objects. That can create high memory pressure if
- * we are to create a list of all objects before de-duplication. To reduce
- * this memory pressure without a significant performance drop, automatically
- * group objects by the first byte of their object id. Use the IDX fanout
- * tables to group the data, copy to a local array, then sort.
- *
- * Copy only the de-duplicated entries (selected by most-recent modified time
- * of a packfile containing the object).
- */
-static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
- struct pack_info *info,
- uint32_t nr_packs,
- size_t *nr_objects,
- int preferred_pack)
-{
- uint32_t cur_fanout, cur_pack, cur_object;
- size_t alloc_objects, total_objects = 0;
- struct midx_fanout fanout = { 0 };
- struct pack_midx_entry *deduplicated_entries = NULL;
- uint32_t start_pack = m ? m->num_packs : 0;
-
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++)
- total_objects = st_add(total_objects,
- info[cur_pack].p->num_objects);
-
- /*
- * As we de-duplicate by fanout value, we expect the fanout
- * slices to be evenly distributed, with some noise. Hence,
- * allocate slightly more than one 256th.
- */
- alloc_objects = fanout.alloc = total_objects > 3200 ? total_objects / 200 : 16;
-
- ALLOC_ARRAY(fanout.entries, fanout.alloc);
- ALLOC_ARRAY(deduplicated_entries, alloc_objects);
- *nr_objects = 0;
-
- for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) {
- fanout.nr = 0;
-
- if (m)
- midx_fanout_add_midx_fanout(&fanout, m, cur_fanout,
- preferred_pack);
-
- for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) {
- int preferred = cur_pack == preferred_pack;
- midx_fanout_add_pack_fanout(&fanout,
- info, cur_pack,
- preferred, cur_fanout);
- }
-
- if (-1 < preferred_pack && preferred_pack < start_pack)
- midx_fanout_add_pack_fanout(&fanout, info,
- preferred_pack, 1,
- cur_fanout);
-
- midx_fanout_sort(&fanout);
-
- /*
- * The batch is now sorted by OID and then mtime (descending).
- * Take only the first duplicate.
- */
- for (cur_object = 0; cur_object < fanout.nr; cur_object++) {
- if (cur_object && oideq(&fanout.entries[cur_object - 1].oid,
- &fanout.entries[cur_object].oid))
- continue;
-
- ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1),
- alloc_objects);
- memcpy(&deduplicated_entries[*nr_objects],
- &fanout.entries[cur_object],
- sizeof(struct pack_midx_entry));
- (*nr_objects)++;
- }
- }
-
- free(fanout.entries);
- return deduplicated_entries;
-}
-
-static int write_midx_pack_names(struct hashfile *f, void *data)
-{
- struct write_midx_context *ctx = data;
- uint32_t i;
- unsigned char padding[MIDX_CHUNK_ALIGNMENT];
- size_t written = 0;
-
- for (i = 0; i < ctx->nr; i++) {
- size_t writelen;
-
- if (ctx->info[i].expired)
- continue;
-
- if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
- BUG("incorrect pack-file order: %s before %s",
- ctx->info[i - 1].pack_name,
- ctx->info[i].pack_name);
-
- writelen = strlen(ctx->info[i].pack_name) + 1;
- hashwrite(f, ctx->info[i].pack_name, writelen);
- written += writelen;
- }
-
- /* add padding to be aligned */
- i = MIDX_CHUNK_ALIGNMENT - (written % MIDX_CHUNK_ALIGNMENT);
- if (i < MIDX_CHUNK_ALIGNMENT) {
- memset(padding, 0, sizeof(padding));
- hashwrite(f, padding, i);
- }
-
- return 0;
-}
-
-static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
-{
- struct write_midx_context *ctx = data;
- size_t i;
-
- for (i = 0; i < ctx->nr; i++) {
- struct pack_info *pack = &ctx->info[i];
- if (pack->expired)
- continue;
-
- if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
- BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
- pack->pack_name, pack->bitmap_nr);
-
- hashwrite_be32(f, pack->bitmap_pos);
- hashwrite_be32(f, pack->bitmap_nr);
- }
- return 0;
-}
-
-static int write_midx_oid_fanout(struct hashfile *f,
- void *data)
-{
- struct write_midx_context *ctx = data;
- struct pack_midx_entry *list = ctx->entries;
- struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
- uint32_t count = 0;
- uint32_t i;
-
- /*
- * Write the first-level table (the list is sorted,
- * but we use a 256-entry lookup to be able to avoid
- * having to do eight extra binary search iterations).
- */
- for (i = 0; i < 256; i++) {
- struct pack_midx_entry *next = list;
-
- while (next < last && next->oid.hash[0] == i) {
- count++;
- next++;
- }
-
- hashwrite_be32(f, count);
- list = next;
- }
-
- return 0;
-}
-
-static int write_midx_oid_lookup(struct hashfile *f,
- void *data)
-{
- struct write_midx_context *ctx = data;
- unsigned char hash_len = the_hash_algo->rawsz;
- struct pack_midx_entry *list = ctx->entries;
- uint32_t i;
-
- for (i = 0; i < ctx->entries_nr; i++) {
- struct pack_midx_entry *obj = list++;
-
- if (i < ctx->entries_nr - 1) {
- struct pack_midx_entry *next = list;
- if (oidcmp(&obj->oid, &next->oid) >= 0)
- BUG("OIDs not in order: %s >= %s",
- oid_to_hex(&obj->oid),
- oid_to_hex(&next->oid));
- }
-
- hashwrite(f, obj->oid.hash, (int)hash_len);
- }
-
- return 0;
-}
-
-static int write_midx_object_offsets(struct hashfile *f,
- void *data)
-{
- struct write_midx_context *ctx = data;
- struct pack_midx_entry *list = ctx->entries;
- uint32_t i, nr_large_offset = 0;
-
- for (i = 0; i < ctx->entries_nr; i++) {
- struct pack_midx_entry *obj = list++;
-
- if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
- BUG("object %s is in an expired pack with int-id %d",
- oid_to_hex(&obj->oid),
- obj->pack_int_id);
-
- hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
-
- if (ctx->large_offsets_needed && obj->offset >> 31)
- hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
- else if (!ctx->large_offsets_needed && obj->offset >> 32)
- BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
- oid_to_hex(&obj->oid),
- obj->offset);
- else
- hashwrite_be32(f, (uint32_t)obj->offset);
- }
-
- return 0;
-}
-
-static int write_midx_large_offsets(struct hashfile *f,
- void *data)
-{
- struct write_midx_context *ctx = data;
- struct pack_midx_entry *list = ctx->entries;
- struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
- uint32_t nr_large_offset = ctx->num_large_offsets;
-
- while (nr_large_offset) {
- struct pack_midx_entry *obj;
- uint64_t offset;
-
- if (list >= end)
- BUG("too many large-offset objects");
-
- obj = list++;
- offset = obj->offset;
-
- if (!(offset >> 31))
- continue;
-
- hashwrite_be64(f, offset);
-
- nr_large_offset--;
- }
-
- return 0;
-}
-
-static int write_midx_revindex(struct hashfile *f,
- void *data)
-{
- struct write_midx_context *ctx = data;
- uint32_t i;
-
- for (i = 0; i < ctx->entries_nr; i++)
- hashwrite_be32(f, ctx->pack_order[i]);
-
- return 0;
-}
-
-struct midx_pack_order_data {
- uint32_t nr;
- uint32_t pack;
- off_t offset;
-};
-
-static int midx_pack_order_cmp(const void *va, const void *vb)
-{
- const struct midx_pack_order_data *a = va, *b = vb;
- if (a->pack < b->pack)
- return -1;
- else if (a->pack > b->pack)
- return 1;
- else if (a->offset < b->offset)
- return -1;
- else if (a->offset > b->offset)
- return 1;
- else
- return 0;
-}
-
-static uint32_t *midx_pack_order(struct write_midx_context *ctx)
-{
- struct midx_pack_order_data *data;
- uint32_t *pack_order;
- uint32_t i;
-
- trace2_region_enter("midx", "midx_pack_order", the_repository);
-
- ALLOC_ARRAY(data, ctx->entries_nr);
- for (i = 0; i < ctx->entries_nr; i++) {
- struct pack_midx_entry *e = &ctx->entries[i];
- data[i].nr = i;
- data[i].pack = ctx->pack_perm[e->pack_int_id];
- if (!e->preferred)
- data[i].pack |= (1U << 31);
- data[i].offset = e->offset;
- }
-
- QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
-
- ALLOC_ARRAY(pack_order, ctx->entries_nr);
- for (i = 0; i < ctx->entries_nr; i++) {
- struct pack_midx_entry *e = &ctx->entries[data[i].nr];
- struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
- if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
- pack->bitmap_pos = i;
- pack->bitmap_nr++;
- pack_order[i] = data[i].nr;
- }
- for (i = 0; i < ctx->nr; i++) {
- struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
- if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
- pack->bitmap_pos = 0;
- }
- free(data);
-
- trace2_region_leave("midx", "midx_pack_order", the_repository);
-
- return pack_order;
-}
-
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
- struct write_midx_context *ctx)
-{
- struct strbuf buf = STRBUF_INIT;
- const char *tmp_file;
-
- trace2_region_enter("midx", "write_midx_reverse_index", the_repository);
-
- strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex(midx_hash));
-
- tmp_file = write_rev_file_order(NULL, ctx->pack_order, ctx->entries_nr,
- midx_hash, WRITE_REV);
-
- if (finalize_object_file(tmp_file, buf.buf))
- die(_("cannot store reverse index file"));
-
- strbuf_release(&buf);
-
- trace2_region_leave("midx", "write_midx_reverse_index", the_repository);
-}
-
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
- unsigned char *keep_hash);
-
-static int midx_checksum_valid(struct multi_pack_index *m)
+int midx_checksum_valid(struct multi_pack_index *m)
{
return hashfile_checksum_valid(m->data, m->data_len);
}
-static void prepare_midx_packing_data(struct packing_data *pdata,
- struct write_midx_context *ctx)
-{
- uint32_t i;
-
- trace2_region_enter("midx", "prepare_midx_packing_data", the_repository);
-
- memset(pdata, 0, sizeof(struct packing_data));
- prepare_packing_data(the_repository, pdata);
-
- for (i = 0; i < ctx->entries_nr; i++) {
- struct pack_midx_entry *from = &ctx->entries[ctx->pack_order[i]];
- struct object_entry *to = packlist_alloc(pdata, &from->oid);
-
- oe_set_in_pack(pdata, to,
- ctx->info[ctx->pack_perm[from->pack_int_id]].p);
- }
-
- trace2_region_leave("midx", "prepare_midx_packing_data", the_repository);
-}
-
-static int add_ref_to_pending(const char *refname,
- const struct object_id *oid,
- int flag, void *cb_data)
-{
- struct rev_info *revs = (struct rev_info*)cb_data;
- struct object_id peeled;
- struct object *object;
-
- if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
- warning("symbolic ref is dangling: %s", refname);
- return 0;
- }
-
- if (!peel_iterated_oid(oid, &peeled))
- oid = &peeled;
-
- object = parse_object_or_die(oid, refname);
- if (object->type != OBJ_COMMIT)
- return 0;
-
- add_pending_object(revs, object, "");
- if (bitmap_is_preferred_refname(revs->repo, refname))
- object->flags |= NEEDS_BITMAP;
- return 0;
-}
-
-struct bitmap_commit_cb {
- struct commit **commits;
- size_t commits_nr, commits_alloc;
-
- struct write_midx_context *ctx;
-};
-
-static const struct object_id *bitmap_oid_access(size_t index,
- const void *_entries)
-{
- const struct pack_midx_entry *entries = _entries;
- return &entries[index].oid;
-}
-
-static void bitmap_show_commit(struct commit *commit, void *_data)
-{
- struct bitmap_commit_cb *data = _data;
- int pos = oid_pos(&commit->object.oid, data->ctx->entries,
- data->ctx->entries_nr,
- bitmap_oid_access);
- if (pos < 0)
- return;
-
- ALLOC_GROW(data->commits, data->commits_nr + 1, data->commits_alloc);
- data->commits[data->commits_nr++] = commit;
-}
-
-static int read_refs_snapshot(const char *refs_snapshot,
- struct rev_info *revs)
-{
- struct strbuf buf = STRBUF_INIT;
- struct object_id oid;
- FILE *f = xfopen(refs_snapshot, "r");
-
- while (strbuf_getline(&buf, f) != EOF) {
- struct object *object;
- int preferred = 0;
- char *hex = buf.buf;
- const char *end = NULL;
-
- if (buf.len && *buf.buf == '+') {
- preferred = 1;
- hex = &buf.buf[1];
- }
-
- if (parse_oid_hex(hex, &oid, &end) < 0)
- die(_("could not parse line: %s"), buf.buf);
- if (*end)
- die(_("malformed line: %s"), buf.buf);
-
- object = parse_object_or_die(&oid, NULL);
- if (preferred)
- object->flags |= NEEDS_BITMAP;
-
- add_pending_object(revs, object, "");
- }
-
- fclose(f);
- strbuf_release(&buf);
- return 0;
-}
-
-static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr_p,
- const char *refs_snapshot,
- struct write_midx_context *ctx)
-{
- struct rev_info revs;
- struct bitmap_commit_cb cb = {0};
-
- trace2_region_enter("midx", "find_commits_for_midx_bitmap",
- the_repository);
-
- cb.ctx = ctx;
-
- repo_init_revisions(the_repository, &revs, NULL);
- if (refs_snapshot) {
- read_refs_snapshot(refs_snapshot, &revs);
- } else {
- setup_revisions(0, NULL, &revs, NULL);
- for_each_ref(add_ref_to_pending, &revs);
- }
-
- /*
- * Skipping promisor objects here is intentional, since it only excludes
- * them from the list of reachable commits that we want to select from
- * when computing the selection of MIDX'd commits to receive bitmaps.
- *
- * Reachability bitmaps do require that their objects be closed under
- * reachability, but fetching any objects missing from promisors at this
- * point is too late. But, if one of those objects can be reached from
- * an another object that is included in the bitmap, then we will
- * complain later that we don't have reachability closure (and fail
- * appropriately).
- */
- fetch_if_missing = 0;
- revs.exclude_promisor_objects = 1;
-
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
-
- traverse_commit_list(&revs, bitmap_show_commit, NULL, &cb);
- if (indexed_commits_nr_p)
- *indexed_commits_nr_p = cb.commits_nr;
-
- release_revisions(&revs);
-
- trace2_region_leave("midx", "find_commits_for_midx_bitmap",
- the_repository);
-
- return cb.commits;
-}
-
-static int write_midx_bitmap(const char *midx_name,
- const unsigned char *midx_hash,
- struct packing_data *pdata,
- struct commit **commits,
- uint32_t commits_nr,
- uint32_t *pack_order,
- unsigned flags)
-{
- int ret, i;
- uint16_t options = 0;
- struct pack_idx_entry **index;
- char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
- hash_to_hex(midx_hash));
-
- trace2_region_enter("midx", "write_midx_bitmap", the_repository);
-
- if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
- options |= BITMAP_OPT_HASH_CACHE;
-
- if (flags & MIDX_WRITE_BITMAP_LOOKUP_TABLE)
- options |= BITMAP_OPT_LOOKUP_TABLE;
-
- /*
- * Build the MIDX-order index based on pdata.objects (which is already
- * in MIDX order; c.f., 'midx_pack_order_cmp()' for the definition of
- * this order).
- */
- ALLOC_ARRAY(index, pdata->nr_objects);
- for (i = 0; i < pdata->nr_objects; i++)
- index[i] = &pdata->objects[i].idx;
-
- bitmap_writer_show_progress(flags & MIDX_PROGRESS);
- bitmap_writer_build_type_index(pdata, index, pdata->nr_objects);
-
- /*
- * bitmap_writer_finish expects objects in lex order, but pack_order
- * gives us exactly that. use it directly instead of re-sorting the
- * array.
- *
- * This changes the order of objects in 'index' between
- * bitmap_writer_build_type_index and bitmap_writer_finish.
- *
- * The same re-ordering takes place in the single-pack bitmap code via
- * write_idx_file(), which is called by finish_tmp_packfile(), which
- * happens between bitmap_writer_build_type_index() and
- * bitmap_writer_finish().
- */
- for (i = 0; i < pdata->nr_objects; i++)
- index[pack_order[i]] = &pdata->objects[i].idx;
-
- bitmap_writer_select_commits(commits, commits_nr, -1);
- ret = bitmap_writer_build(pdata);
- if (ret < 0)
- goto cleanup;
-
- bitmap_writer_set_checksum(midx_hash);
- bitmap_writer_finish(index, pdata->nr_objects, bitmap_name, options);
-
-cleanup:
- free(index);
- free(bitmap_name);
-
- trace2_region_leave("midx", "write_midx_bitmap", the_repository);
-
- return ret;
-}
-
-static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
- const char *object_dir)
-{
- struct multi_pack_index *result = NULL;
- struct multi_pack_index *cur;
- char *obj_dir_real = real_pathdup(object_dir, 1);
- struct strbuf cur_path_real = STRBUF_INIT;
-
- /* Ensure the given object_dir is local, or a known alternate. */
- find_odb(r, obj_dir_real);
-
- for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
- strbuf_realpath(&cur_path_real, cur->object_dir, 1);
- if (!strcmp(obj_dir_real, cur_path_real.buf)) {
- result = cur;
- goto cleanup;
- }
- }
-
-cleanup:
- free(obj_dir_real);
- strbuf_release(&cur_path_real);
- return result;
-}
-
-static int write_midx_internal(const char *object_dir,
- struct string_list *packs_to_include,
- struct string_list *packs_to_drop,
- const char *preferred_pack_name,
- const char *refs_snapshot,
- unsigned flags)
-{
- struct strbuf midx_name = STRBUF_INIT;
- unsigned char midx_hash[GIT_MAX_RAWSZ];
- uint32_t i;
- struct hashfile *f = NULL;
- struct lock_file lk;
- struct write_midx_context ctx = { 0 };
- int bitmapped_packs_concat_len = 0;
- int pack_name_concat_len = 0;
- int dropped_packs = 0;
- int result = 0;
- struct chunkfile *cf;
-
- trace2_region_enter("midx", "write_midx_internal", the_repository);
-
- get_midx_filename(&midx_name, object_dir);
- if (safe_create_leading_directories(midx_name.buf))
- die_errno(_("unable to create leading directories of %s"),
- midx_name.buf);
-
- if (!packs_to_include) {
- /*
- * Only reference an existing MIDX when not filtering which
- * packs to include, since all packs and objects are copied
- * blindly from an existing MIDX if one is present.
- */
- ctx.m = lookup_multi_pack_index(the_repository, object_dir);
- }
-
- if (ctx.m && !midx_checksum_valid(ctx.m)) {
- warning(_("ignoring existing multi-pack-index; checksum mismatch"));
- ctx.m = NULL;
- }
-
- ctx.nr = 0;
- ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
- ctx.info = NULL;
- ALLOC_ARRAY(ctx.info, ctx.alloc);
-
- if (ctx.m) {
- for (i = 0; i < ctx.m->num_packs; i++) {
- ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
-
- if (flags & MIDX_WRITE_REV_INDEX) {
- /*
- * If generating a reverse index, need to have
- * packed_git's loaded to compare their
- * mtimes and object count.
- */
- if (prepare_midx_pack(the_repository, ctx.m, i)) {
- error(_("could not load pack"));
- result = 1;
- goto cleanup;
- }
-
- if (open_pack_index(ctx.m->packs[i]))
- die(_("could not open index for %s"),
- ctx.m->packs[i]->pack_name);
- }
-
- fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
- ctx.m->pack_names[i], i);
- }
- }
-
- ctx.pack_paths_checked = 0;
- if (flags & MIDX_PROGRESS)
- ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
- else
- ctx.progress = NULL;
-
- ctx.to_include = packs_to_include;
-
- for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
- stop_progress(&ctx.progress);
-
- if ((ctx.m && ctx.nr == ctx.m->num_packs) &&
- !(packs_to_include || packs_to_drop)) {
- struct bitmap_index *bitmap_git;
- int bitmap_exists;
- int want_bitmap = flags & MIDX_WRITE_BITMAP;
-
- bitmap_git = prepare_midx_bitmap_git(ctx.m);
- bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git);
- free_bitmap_index(bitmap_git);
-
- if (bitmap_exists || !want_bitmap) {
- /*
- * The correct MIDX already exists, and so does a
- * corresponding bitmap (or one wasn't requested).
- */
- if (!want_bitmap)
- clear_midx_files_ext(object_dir, ".bitmap",
- NULL);
- goto cleanup;
- }
- }
-
- if (preferred_pack_name) {
- ctx.preferred_pack_idx = -1;
-
- for (i = 0; i < ctx.nr; i++) {
- if (!cmp_idx_or_pack_name(preferred_pack_name,
- ctx.info[i].pack_name)) {
- ctx.preferred_pack_idx = i;
- break;
- }
- }
-
- if (ctx.preferred_pack_idx == -1)
- warning(_("unknown preferred pack: '%s'"),
- preferred_pack_name);
- } else if (ctx.nr &&
- (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) {
- struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p;
- ctx.preferred_pack_idx = 0;
-
- if (packs_to_drop && packs_to_drop->nr)
- BUG("cannot write a MIDX bitmap during expiration");
-
- /*
- * set a preferred pack when writing a bitmap to ensure that
- * the pack from which the first object is selected in pseudo
- * pack-order has all of its objects selected from that pack
- * (and not another pack containing a duplicate)
- */
- for (i = 1; i < ctx.nr; i++) {
- struct packed_git *p = ctx.info[i].p;
-
- if (!oldest->num_objects || p->mtime < oldest->mtime) {
- oldest = p;
- ctx.preferred_pack_idx = i;
- }
- }
-
- if (!oldest->num_objects) {
- /*
- * If all packs are empty; unset the preferred index.
- * This is acceptable since there will be no duplicate
- * objects to resolve, so the preferred value doesn't
- * matter.
- */
- ctx.preferred_pack_idx = -1;
- }
- } else {
- /*
- * otherwise don't mark any pack as preferred to avoid
- * interfering with expiration logic below
- */
- ctx.preferred_pack_idx = -1;
- }
-
- if (ctx.preferred_pack_idx > -1) {
- struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p;
- if (!preferred->num_objects) {
- error(_("cannot select preferred pack %s with no objects"),
- preferred->pack_name);
- result = 1;
- goto cleanup;
- }
- }
-
- ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr,
- ctx.preferred_pack_idx);
-
- ctx.large_offsets_needed = 0;
- for (i = 0; i < ctx.entries_nr; i++) {
- if (ctx.entries[i].offset > 0x7fffffff)
- ctx.num_large_offsets++;
- if (ctx.entries[i].offset > 0xffffffff)
- ctx.large_offsets_needed = 1;
- }
-
- QSORT(ctx.info, ctx.nr, pack_info_compare);
-
- if (packs_to_drop && packs_to_drop->nr) {
- int drop_index = 0;
- int missing_drops = 0;
-
- for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
- int cmp = strcmp(ctx.info[i].pack_name,
- packs_to_drop->items[drop_index].string);
-
- if (!cmp) {
- drop_index++;
- ctx.info[i].expired = 1;
- } else if (cmp > 0) {
- error(_("did not see pack-file %s to drop"),
- packs_to_drop->items[drop_index].string);
- drop_index++;
- missing_drops++;
- i--;
- } else {
- ctx.info[i].expired = 0;
- }
- }
-
- if (missing_drops) {
- result = 1;
- goto cleanup;
- }
- }
-
- /*
- * pack_perm stores a permutation between pack-int-ids from the
- * previous multi-pack-index to the new one we are writing:
- *
- * pack_perm[old_id] = new_id
- */
- ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
- for (i = 0; i < ctx.nr; i++) {
- if (ctx.info[i].expired) {
- dropped_packs++;
- ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
- } else {
- ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
- }
- }
-
- for (i = 0; i < ctx.nr; i++) {
- if (ctx.info[i].expired)
- continue;
- pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
- bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
- }
-
- /* Check that the preferred pack wasn't expired (if given). */
- if (preferred_pack_name) {
- struct pack_info *preferred = bsearch(preferred_pack_name,
- ctx.info, ctx.nr,
- sizeof(*ctx.info),
- idx_or_pack_name_cmp);
- if (preferred) {
- uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id];
- if (perm == PACK_EXPIRED)
- warning(_("preferred pack '%s' is expired"),
- preferred_pack_name);
- }
- }
-
- if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
- pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
- (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
-
- hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
- f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
-
- if (ctx.nr - dropped_packs == 0) {
- error(_("no pack files to index."));
- result = 1;
- goto cleanup;
- }
-
- if (!ctx.entries_nr) {
- if (flags & MIDX_WRITE_BITMAP)
- warning(_("refusing to write multi-pack .bitmap without any objects"));
- flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP);
- }
-
- cf = init_chunkfile(f);
-
- add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
- write_midx_pack_names);
- add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
- write_midx_oid_fanout);
- add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
- st_mult(ctx.entries_nr, the_hash_algo->rawsz),
- write_midx_oid_lookup);
- add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
- st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH),
- write_midx_object_offsets);
-
- if (ctx.large_offsets_needed)
- add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
- st_mult(ctx.num_large_offsets,
- MIDX_CHUNK_LARGE_OFFSET_WIDTH),
- write_midx_large_offsets);
-
- if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) {
- ctx.pack_order = midx_pack_order(&ctx);
- add_chunk(cf, MIDX_CHUNKID_REVINDEX,
- st_mult(ctx.entries_nr, sizeof(uint32_t)),
- write_midx_revindex);
- add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
- bitmapped_packs_concat_len,
- write_midx_bitmapped_packs);
- }
-
- write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
- write_chunkfile(cf, &ctx);
-
- finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA,
- CSUM_FSYNC | CSUM_HASH_IN_STREAM);
- free_chunkfile(cf);
-
- if (flags & MIDX_WRITE_REV_INDEX &&
- git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
- write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
-
- if (flags & MIDX_WRITE_BITMAP) {
- struct packing_data pdata;
- struct commit **commits;
- uint32_t commits_nr;
-
- if (!ctx.entries_nr)
- BUG("cannot write a bitmap without any objects");
-
- prepare_midx_packing_data(&pdata, &ctx);
-
- commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, &ctx);
-
- /*
- * The previous steps translated the information from
- * 'entries' into information suitable for constructing
- * bitmaps. We no longer need that array, so clear it to
- * reduce memory pressure.
- */
- FREE_AND_NULL(ctx.entries);
- ctx.entries_nr = 0;
-
- if (write_midx_bitmap(midx_name.buf, midx_hash, &pdata,
- commits, commits_nr, ctx.pack_order,
- flags) < 0) {
- error(_("could not write multi-pack bitmap"));
- result = 1;
- clear_packing_data(&pdata);
- free(commits);
- goto cleanup;
- }
-
- clear_packing_data(&pdata);
- free(commits);
- }
- /*
- * NOTE: Do not use ctx.entries beyond this point, since it might
- * have been freed in the previous if block.
- */
-
- if (ctx.m)
- close_object_store(the_repository->objects);
-
- if (commit_lock_file(&lk) < 0)
- die_errno(_("could not write multi-pack-index"));
-
- clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
- clear_midx_files_ext(object_dir, ".rev", midx_hash);
-
-cleanup:
- for (i = 0; i < ctx.nr; i++) {
- if (ctx.info[i].p) {
- close_pack(ctx.info[i].p);
- free(ctx.info[i].p);
- }
- free(ctx.info[i].pack_name);
- }
-
- free(ctx.info);
- free(ctx.entries);
- free(ctx.pack_perm);
- free(ctx.pack_order);
- strbuf_release(&midx_name);
-
- trace2_region_leave("midx", "write_midx_internal", the_repository);
-
- return result;
-}
-
-int write_midx_file(const char *object_dir,
- const char *preferred_pack_name,
- const char *refs_snapshot,
- unsigned flags)
-{
- return write_midx_internal(object_dir, NULL, NULL, preferred_pack_name,
- refs_snapshot, flags);
-}
-
-int write_midx_file_only(const char *object_dir,
- struct string_list *packs_to_include,
- const char *preferred_pack_name,
- const char *refs_snapshot,
- unsigned flags)
-{
- return write_midx_internal(object_dir, packs_to_include, NULL,
- preferred_pack_name, refs_snapshot, flags);
-}
-
struct clear_midx_data {
char *keep;
const char *ext;
@@ -1785,8 +508,8 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS
die_errno(_("failed to remove %s"), full_path);
}
-static void clear_midx_files_ext(const char *object_dir, const char *ext,
- unsigned char *keep_hash)
+void clear_midx_files_ext(const char *object_dir, const char *ext,
+ unsigned char *keep_hash)
{
struct clear_midx_data data;
memset(&data, 0, sizeof(struct clear_midx_data));
@@ -1989,256 +712,3 @@ cleanup:
return verify_midx_error;
}
-
-int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags)
-{
- uint32_t i, *count, result = 0;
- struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
- struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
- struct progress *progress = NULL;
-
- if (!m)
- return 0;
-
- CALLOC_ARRAY(count, m->num_packs);
-
- if (flags & MIDX_PROGRESS)
- progress = start_delayed_progress(_("Counting referenced objects"),
- m->num_objects);
- for (i = 0; i < m->num_objects; i++) {
- int pack_int_id = nth_midxed_pack_int_id(m, i);
- count[pack_int_id]++;
- display_progress(progress, i + 1);
- }
- stop_progress(&progress);
-
- if (flags & MIDX_PROGRESS)
- progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
- m->num_packs);
- for (i = 0; i < m->num_packs; i++) {
- char *pack_name;
- display_progress(progress, i + 1);
-
- if (count[i])
- continue;
-
- if (prepare_midx_pack(r, m, i))
- continue;
-
- if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
- continue;
-
- pack_name = xstrdup(m->packs[i]->pack_name);
- close_pack(m->packs[i]);
-
- string_list_insert(&packs_to_drop, m->pack_names[i]);
- unlink_pack_path(pack_name, 0);
- free(pack_name);
- }
- stop_progress(&progress);
-
- free(count);
-
- if (packs_to_drop.nr)
- result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
-
- string_list_clear(&packs_to_drop, 0);
-
- return result;
-}
-
-struct repack_info {
- timestamp_t mtime;
- uint32_t referenced_objects;
- uint32_t pack_int_id;
-};
-
-static int compare_by_mtime(const void *a_, const void *b_)
-{
- const struct repack_info *a, *b;
-
- a = (const struct repack_info *)a_;
- b = (const struct repack_info *)b_;
-
- if (a->mtime < b->mtime)
- return -1;
- if (a->mtime > b->mtime)
- return 1;
- return 0;
-}
-
-static int fill_included_packs_all(struct repository *r,
- struct multi_pack_index *m,
- unsigned char *include_pack)
-{
- uint32_t i, count = 0;
- int pack_kept_objects = 0;
-
- repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
- for (i = 0; i < m->num_packs; i++) {
- if (prepare_midx_pack(r, m, i))
- continue;
- if (!pack_kept_objects && m->packs[i]->pack_keep)
- continue;
- if (m->packs[i]->is_cruft)
- continue;
-
- include_pack[i] = 1;
- count++;
- }
-
- return count < 2;
-}
-
-static int fill_included_packs_batch(struct repository *r,
- struct multi_pack_index *m,
- unsigned char *include_pack,
- size_t batch_size)
-{
- uint32_t i, packs_to_repack;
- size_t total_size;
- struct repack_info *pack_info;
- int pack_kept_objects = 0;
-
- CALLOC_ARRAY(pack_info, m->num_packs);
-
- repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
-
- for (i = 0; i < m->num_packs; i++) {
- pack_info[i].pack_int_id = i;
-
- if (prepare_midx_pack(r, m, i))
- continue;
-
- pack_info[i].mtime = m->packs[i]->mtime;
- }
-
- for (i = 0; i < m->num_objects; i++) {
- uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
- pack_info[pack_int_id].referenced_objects++;
- }
-
- QSORT(pack_info, m->num_packs, compare_by_mtime);
-
- total_size = 0;
- packs_to_repack = 0;
- for (i = 0; total_size < batch_size && i < m->num_packs; i++) {
- int pack_int_id = pack_info[i].pack_int_id;
- struct packed_git *p = m->packs[pack_int_id];
- size_t expected_size;
-
- if (!p)
- continue;
- if (!pack_kept_objects && p->pack_keep)
- continue;
- if (p->is_cruft)
- continue;
- if (open_pack_index(p) || !p->num_objects)
- continue;
-
- expected_size = st_mult(p->pack_size,
- pack_info[i].referenced_objects);
- expected_size /= p->num_objects;
-
- if (expected_size >= batch_size)
- continue;
-
- packs_to_repack++;
- total_size += expected_size;
- include_pack[pack_int_id] = 1;
- }
-
- free(pack_info);
-
- if (packs_to_repack < 2)
- return 1;
-
- return 0;
-}
-
-int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags)
-{
- int result = 0;
- uint32_t i;
- unsigned char *include_pack;
- struct child_process cmd = CHILD_PROCESS_INIT;
- FILE *cmd_in;
- struct strbuf base_name = STRBUF_INIT;
- struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
-
- /*
- * When updating the default for these configuration
- * variables in builtin/repack.c, these must be adjusted
- * to match.
- */
- int delta_base_offset = 1;
- int use_delta_islands = 0;
-
- if (!m)
- return 0;
-
- CALLOC_ARRAY(include_pack, m->num_packs);
-
- if (batch_size) {
- if (fill_included_packs_batch(r, m, include_pack, batch_size))
- goto cleanup;
- } else if (fill_included_packs_all(r, m, include_pack))
- goto cleanup;
-
- repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
- repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
-
- strvec_push(&cmd.args, "pack-objects");
-
- strbuf_addstr(&base_name, object_dir);
- strbuf_addstr(&base_name, "/pack/pack");
- strvec_push(&cmd.args, base_name.buf);
-
- if (delta_base_offset)
- strvec_push(&cmd.args, "--delta-base-offset");
- if (use_delta_islands)
- strvec_push(&cmd.args, "--delta-islands");
-
- if (flags & MIDX_PROGRESS)
- strvec_push(&cmd.args, "--progress");
- else
- strvec_push(&cmd.args, "-q");
-
- strbuf_release(&base_name);
-
- cmd.git_cmd = 1;
- cmd.in = cmd.out = -1;
-
- if (start_command(&cmd)) {
- error(_("could not start pack-objects"));
- result = 1;
- goto cleanup;
- }
-
- cmd_in = xfdopen(cmd.in, "w");
-
- for (i = 0; i < m->num_objects; i++) {
- struct object_id oid;
- uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
-
- if (!include_pack[pack_int_id])
- continue;
-
- nth_midxed_object_oid(&oid, m, i);
- fprintf(cmd_in, "%s\n", oid_to_hex(&oid));
- }
- fclose(cmd_in);
-
- if (finish_command(&cmd)) {
- error(_("could not finish pack-objects"));
- result = 1;
- goto cleanup;
- }
-
- result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
-
-cleanup:
- free(include_pack);
- return result;
-}
diff --git a/midx.h b/midx.h
index f87a8fff26..dc477dff44 100644
--- a/midx.h
+++ b/midx.h
@@ -1,7 +1,6 @@
#ifndef MIDX_H
#define MIDX_H
-#include "repository.h"
#include "string-list.h"
struct object_id;
@@ -9,6 +8,25 @@ struct pack_entry;
struct repository;
struct bitmapped_pack;
+#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
+#define MIDX_VERSION 1
+#define MIDX_BYTE_FILE_VERSION 4
+#define MIDX_BYTE_HASH_VERSION 5
+#define MIDX_BYTE_NUM_CHUNKS 6
+#define MIDX_BYTE_NUM_PACKS 8
+#define MIDX_HEADER_SIZE 12
+
+#define MIDX_CHUNK_ALIGNMENT 4
+#define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
+#define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
+#define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
+#define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
+#define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
+#define MIDX_CHUNKID_REVINDEX 0x52494458 /* "RIDX" */
+#define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
+#define MIDX_LARGE_OFFSET_NEEDED 0x80000000
+
#define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
#define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
"GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP"
diff --git a/name-hash.c b/name-hash.c
index 251f036eef..3a58ce03d9 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -685,13 +685,20 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
return slow_same_name(name, namelen, ce->name, len);
}
-int index_dir_exists(struct index_state *istate, const char *name, int namelen)
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+ struct strbuf *canonical_path)
{
struct dir_entry *dir;
lazy_init_name_hash(istate);
expand_to_path(istate, name, namelen, 0);
dir = find_dir_entry(istate, name, namelen);
+
+ if (canonical_path && dir && dir->nr) {
+ strbuf_reset(canonical_path);
+ strbuf_add(canonical_path, dir->name, dir->namelen);
+ }
+
return dir && dir->nr;
}
diff --git a/name-hash.h b/name-hash.h
index b1b4b0fb33..0cbfc42863 100644
--- a/name-hash.h
+++ b/name-hash.h
@@ -4,7 +4,12 @@
struct cache_entry;
struct index_state;
-int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+
+int index_dir_find(struct index_state *istate, const char *name, int namelen,
+ struct strbuf *canonical_path);
+
+#define index_dir_exists(i, n, l) index_dir_find((i), (n), (l), NULL)
+
void adjust_dirname_case(struct index_state *istate, char *name);
struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
diff --git a/negotiator/noop.c b/negotiator/noop.c
index de39028ab7..65e3c20008 100644
--- a/negotiator/noop.c
+++ b/negotiator/noop.c
@@ -1,6 +1,5 @@
#include "git-compat-util.h"
#include "noop.h"
-#include "../commit.h"
#include "../fetch-negotiator.h"
static void known_common(struct fetch_negotiator *n UNUSED,
diff --git a/notes-merge.c b/notes-merge.c
index 8799b522a5..51282934ae 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -607,7 +607,8 @@ int notes_merge(struct notes_merge_options *o,
assert(local && remote);
/* Find merge bases */
- bases = repo_get_merge_bases(the_repository, local, remote);
+ if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
+ exit(128);
if (!bases) {
base_oid = null_oid();
base_tree_oid = the_hash_algo->empty_tree;
diff --git a/notes-utils.c b/notes-utils.c
index 97c031c26e..6197a5a455 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -5,7 +5,6 @@
#include "gettext.h"
#include "refs.h"
#include "notes-utils.h"
-#include "repository.h"
#include "strbuf.h"
void create_notes_commit(struct repository *r,
@@ -112,6 +111,8 @@ static int notes_rewrite_config(const char *k, const char *v,
}
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 1ef2a331ce..fed1eda80c 100644
--- a/notes.c
+++ b/notes.c
@@ -5,8 +5,6 @@
#include "notes.h"
#include "object-name.h"
#include "object-store-ll.h"
-#include "blob.h"
-#include "tree.h"
#include "utf8.h"
#include "strbuf.h"
#include "tree-walk.h"
diff --git a/object-file-convert.c b/object-file-convert.c
new file mode 100644
index 0000000000..4f6189095b
--- /dev/null
+++ b/object-file-convert.c
@@ -0,0 +1,277 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "hex.h"
+#include "repository.h"
+#include "hash-ll.h"
+#include "hash.h"
+#include "object.h"
+#include "loose.h"
+#include "commit.h"
+#include "gpg-interface.h"
+#include "object-file-convert.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+ const struct git_hash_algo *to, struct object_id *dest)
+{
+ /*
+ * If the source algorithm is not set, then we're using the
+ * default hash algorithm for that object.
+ */
+ const struct git_hash_algo *from =
+ src->algo ? &hash_algos[src->algo] : repo->hash_algo;
+
+ if (from == to) {
+ if (src != dest)
+ oidcpy(dest, src);
+ return 0;
+ }
+ if (repo_loose_object_map_oid(repo, src, to, dest)) {
+ /*
+ * We may have loaded the object map at repo initialization but
+ * another process (perhaps upstream of a pipe from us) may have
+ * written a new object into the map. If the object is missing,
+ * let's reload the map to see if the object has appeared.
+ */
+ repo_read_loose_object_map(repo);
+ if (repo_loose_object_map_oid(repo, src, to, dest))
+ return -1;
+ }
+ return 0;
+}
+
+static int decode_tree_entry_raw(struct object_id *oid, const char **path,
+ size_t *len, const struct git_hash_algo *algo,
+ const char *buf, unsigned long size)
+{
+ uint16_t mode;
+ const unsigned hashsz = algo->rawsz;
+
+ if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
+ return -1;
+ }
+
+ *path = parse_mode(buf, &mode);
+ if (!*path || !**path)
+ return -1;
+ *len = strlen(*path) + 1;
+
+ oidread_algop(oid, (const unsigned char *)*path + *len, algo);
+ return 0;
+}
+
+static int convert_tree_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ const char *p = buffer, *end = buffer + size;
+
+ while (p < end) {
+ struct object_id entry_oid, mapped_oid;
+ const char *path = NULL;
+ size_t pathlen;
+
+ if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
+ end - p))
+ return error(_("failed to decode tree entry"));
+ if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+ return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
+ strbuf_add(out, p, path - p);
+ strbuf_add(out, path, pathlen);
+ strbuf_add(out, mapped_oid.hash, to->rawsz);
+ p = path + pathlen + from->rawsz;
+ }
+ return 0;
+}
+
+static int convert_tag_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
+ const int entry_len = from->hexsz + 7;
+ size_t payload_size;
+ struct object_id oid, mapped_oid;
+ const char *p;
+
+ /* Consume the object line */
+ if ((entry_len >= size) ||
+ memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
+ return error("bogus tag object");
+ if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
+ return error("bad tag object ID");
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error("unable to map tree %s in tag object",
+ oid_to_hex(&oid));
+ size -= ((p + 1) - buffer);
+ buffer = p + 1;
+
+ /* Is there a signature for our algorithm? */
+ payload_size = parse_signed_buffer(buffer, size);
+ if (payload_size != size) {
+ /* Yes, there is. */
+ strbuf_add(&oursig, buffer + payload_size, size - payload_size);
+ }
+
+ /* Now, is there a signature for the other algorithm? */
+ parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
+ /*
+ * Our payload is now in payload and we may have up to two signatrures
+ * in oursig and othersig.
+ */
+
+ /* Add some slop for longer signature header in the new algorithm. */
+ strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
+ strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
+ strbuf_addbuf(out, &payload);
+ if (oursig.len)
+ add_header_signature(out, &oursig, from);
+ strbuf_addbuf(out, &othersig);
+
+ strbuf_release(&payload);
+ strbuf_release(&othersig);
+ strbuf_release(&oursig);
+ return 0;
+}
+
+static int convert_commit_object(struct strbuf *out,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const char *buffer, size_t size)
+{
+ const char *tail = buffer;
+ const char *bufptr = buffer;
+ const int tree_entry_len = from->hexsz + 5;
+ const int parent_entry_len = from->hexsz + 7;
+ struct object_id oid, mapped_oid;
+ const char *p, *eol;
+
+ tail += size;
+
+ while ((bufptr < tail) && (*bufptr != '\n')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ return error(_("bad %s in commit"), "line");
+
+ if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
+ {
+ if (((bufptr + tree_entry_len) != eol) ||
+ parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
+ (p != eol))
+ return error(_("bad %s in commit"), "tree");
+
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error(_("unable to map %s %s in commit object"),
+ "tree", oid_to_hex(&oid));
+ strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
+ }
+ else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
+ {
+ if (((bufptr + parent_entry_len) != eol) ||
+ parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
+ (p != eol))
+ return error(_("bad %s in commit"), "parent");
+
+ if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ return error(_("unable to map %s %s in commit object"),
+ "parent", oid_to_hex(&oid));
+
+ strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
+ }
+ else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
+ {
+ struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
+
+ /* Recover the tag object from the mergetag */
+ strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
+
+ bufptr = eol + 1;
+ while ((bufptr < tail) && (*bufptr == ' ')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol) {
+ strbuf_release(&tag);
+ return error(_("bad %s in commit"), "mergetag continuation");
+ }
+ strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
+ bufptr = eol + 1;
+ }
+
+ /* Compute the new tag object */
+ if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+ strbuf_release(&tag);
+ strbuf_release(&new_tag);
+ return -1;
+ }
+
+ /* Write the new mergetag */
+ strbuf_addstr(out, "mergetag");
+ strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
+ strbuf_release(&tag);
+ strbuf_release(&new_tag);
+ }
+ else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ else {
+ /* Unknown line fail it might embed an oid */
+ return -1;
+ }
+ /* Consume any trailing continuation lines */
+ bufptr = eol + 1;
+ while ((bufptr < tail) && (*bufptr == ' ')) {
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ return error(_("bad %s in commit"), "continuation");
+ strbuf_add(out, bufptr, (eol - bufptr) + 1);
+ bufptr = eol + 1;
+ }
+ }
+ if (bufptr < tail)
+ strbuf_add(out, bufptr, tail - bufptr);
+ return 0;
+}
+
+int convert_object_file(struct strbuf *outbuf,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const void *buf, size_t len,
+ enum object_type type,
+ int gentle)
+{
+ int ret;
+
+ /* Don't call this function when no conversion is necessary */
+ if ((from == to) || (type == OBJ_BLOB))
+ BUG("Refusing noop object file conversion");
+
+ switch (type) {
+ case OBJ_COMMIT:
+ ret = convert_commit_object(outbuf, from, to, buf, len);
+ break;
+ case OBJ_TREE:
+ ret = convert_tree_object(outbuf, from, to, buf, len);
+ break;
+ case OBJ_TAG:
+ ret = convert_tag_object(outbuf, from, to, buf, len);
+ break;
+ default:
+ /* Not implemented yet, so fail. */
+ ret = -1;
+ break;
+ }
+ if (!ret)
+ return 0;
+ if (gentle) {
+ strbuf_release(outbuf);
+ return ret;
+ }
+ die(_("Failed to convert object from %s to %s"),
+ from->name, to->name);
+}
diff --git a/object-file-convert.h b/object-file-convert.h
new file mode 100644
index 0000000000..a4f802aa8e
--- /dev/null
+++ b/object-file-convert.h
@@ -0,0 +1,24 @@
+#ifndef OBJECT_CONVERT_H
+#define OBJECT_CONVERT_H
+
+struct repository;
+struct object_id;
+struct git_hash_algo;
+struct strbuf;
+#include "object.h"
+
+int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
+ const struct git_hash_algo *to, struct object_id *dest);
+
+/*
+ * Convert an object file from one hash algorithm to another algorithm.
+ * Return -1 on failure, 0 on success.
+ */
+int convert_object_file(struct strbuf *outbuf,
+ const struct git_hash_algo *from,
+ const struct git_hash_algo *to,
+ const void *buf, size_t len,
+ enum object_type type,
+ int gentle);
+
+#endif /* OBJECT_CONVERT_H */
diff --git a/object-file.c b/object-file.c
index 7c7afe5793..610b1f465c 100644
--- a/object-file.c
+++ b/object-file.c
@@ -15,24 +15,16 @@
#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"
@@ -43,6 +35,8 @@
#include "setup.h"
#include "submodule.h"
#include "fsck.h"
+#include "loose.h"
+#include "object-file-convert.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
@@ -1092,9 +1086,11 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
void *buf, unsigned long size,
enum object_type type)
{
+ const struct git_hash_algo *algo =
+ oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
struct object_id real_oid;
- hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+ hash_object_file(algo, buf, size, type, &real_oid);
return !oideq(oid, &real_oid) ? -1 : 0;
}
@@ -1660,10 +1656,101 @@ static int do_oid_object_info_extended(struct repository *r,
return 0;
}
+static int oid_object_info_convert(struct repository *r,
+ const struct object_id *input_oid,
+ struct object_info *input_oi, unsigned flags)
+{
+ const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+ int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+ struct strbuf type_name = STRBUF_INIT;
+ struct object_id oid, delta_base_oid;
+ struct object_info new_oi, *oi;
+ unsigned long size;
+ void *content;
+ int ret;
+
+ if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(input_oid), the_hash_algo->name);
+ return -1;
+ }
+
+ /* Is new_oi needed? */
+ oi = input_oi;
+ if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+ input_oi->contentp)) {
+ new_oi = *input_oi;
+ /* Does delta_base_oid need to be converted? */
+ if (input_oi->delta_base_oid)
+ new_oi.delta_base_oid = &delta_base_oid;
+ /* Will the attributes differ when converted? */
+ if (input_oi->sizep || input_oi->contentp) {
+ new_oi.contentp = &content;
+ new_oi.sizep = &size;
+ new_oi.type_name = &type_name;
+ }
+ oi = &new_oi;
+ }
+
+ ret = oid_object_info_extended(r, &oid, oi, flags);
+ if (ret)
+ return -1;
+ if (oi == input_oi)
+ return ret;
+
+ if (new_oi.contentp) {
+ struct strbuf outbuf = STRBUF_INIT;
+ enum object_type type;
+
+ type = type_from_string_gently(type_name.buf, type_name.len,
+ !do_die);
+ if (type == -1)
+ return -1;
+ if (type != OBJ_BLOB) {
+ ret = convert_object_file(&outbuf,
+ the_hash_algo, input_algo,
+ content, size, type, !do_die);
+ if (ret == -1)
+ return -1;
+ free(content);
+ size = outbuf.len;
+ content = strbuf_detach(&outbuf, NULL);
+ }
+ if (input_oi->sizep)
+ *input_oi->sizep = size;
+ if (input_oi->contentp)
+ *input_oi->contentp = content;
+ else
+ free(content);
+ if (input_oi->type_name)
+ *input_oi->type_name = type_name;
+ else
+ strbuf_release(&type_name);
+ }
+ if (new_oi.delta_base_oid == &delta_base_oid) {
+ if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+ input_oi->delta_base_oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(&delta_base_oid),
+ input_algo->name);
+ return -1;
+ }
+ }
+ input_oi->whence = new_oi.whence;
+ input_oi->u = new_oi.u;
+ return ret;
+}
+
int oid_object_info_extended(struct repository *r, const struct object_id *oid,
struct object_info *oi, unsigned flags)
{
int ret;
+
+ if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+ return oid_object_info_convert(r, oid, oi, flags);
+
obj_read_lock();
ret = do_oid_object_info_extended(r, oid, oi, flags);
obj_read_unlock();
@@ -1952,9 +2039,12 @@ static int start_loose_object_common(struct strbuf *tmp_file,
const char *filename, unsigned flags,
git_zstream *stream,
unsigned char *buf, size_t buflen,
- git_hash_ctx *c,
+ git_hash_ctx *c, git_hash_ctx *compat_c,
char *hdr, int hdrlen)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int fd;
fd = create_tmpfile(tmp_file, filename);
@@ -1974,14 +2064,18 @@ static int start_loose_object_common(struct strbuf *tmp_file,
git_deflate_init(stream, zlib_compression_level);
stream->next_out = buf;
stream->avail_out = buflen;
- the_hash_algo->init_fn(c);
+ algo->init_fn(c);
+ if (compat && compat_c)
+ compat->init_fn(compat_c);
/* Start to feed header to zlib stream */
stream->next_in = (unsigned char *)hdr;
stream->avail_in = hdrlen;
while (git_deflate(stream, 0) == Z_OK)
; /* nothing */
- the_hash_algo->update_fn(c, hdr, hdrlen);
+ algo->update_fn(c, hdr, hdrlen);
+ if (compat && compat_c)
+ compat->update_fn(compat_c, hdr, hdrlen);
return fd;
}
@@ -1990,16 +2084,21 @@ static int start_loose_object_common(struct strbuf *tmp_file,
* Common steps for the inner git_deflate() loop for writing loose
* objects. Returns what git_deflate() returns.
*/
-static int write_loose_object_common(git_hash_ctx *c,
+static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
git_zstream *stream, const int flush,
unsigned char *in0, const int fd,
unsigned char *compressed,
const size_t compressed_len)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate(stream, flush ? Z_FINISH : 0);
- the_hash_algo->update_fn(c, in0, stream->next_in - in0);
+ algo->update_fn(c, in0, stream->next_in - in0);
+ if (compat && compat_c)
+ compat->update_fn(compat_c, in0, stream->next_in - in0);
if (write_in_full(fd, compressed, stream->next_out - compressed) < 0)
die_errno(_("unable to write loose object file"));
stream->next_out = compressed;
@@ -2014,15 +2113,21 @@ static int write_loose_object_common(git_hash_ctx *c,
* - End the compression of zlib stream.
* - Get the calculated oid to "oid".
*/
-static int end_loose_object_common(git_hash_ctx *c, git_zstream *stream,
- struct object_id *oid)
+static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,
+ git_zstream *stream, struct object_id *oid,
+ struct object_id *compat_oid)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
int ret;
ret = git_deflate_end_gently(stream);
if (ret != Z_OK)
return ret;
- the_hash_algo->final_oid_fn(oid, c);
+ algo->final_oid_fn(oid, c);
+ if (compat && compat_c)
+ compat->final_oid_fn(compat_oid, compat_c);
return Z_OK;
}
@@ -2046,7 +2151,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
fd = start_loose_object_common(&tmp_file, filename.buf, flags,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, NULL, hdr, hdrlen);
if (fd < 0)
return -1;
@@ -2056,14 +2161,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
do {
unsigned char *in0 = stream.next_in;
- ret = write_loose_object_common(&c, &stream, 1, in0, fd,
+ ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd,
compressed, sizeof(compressed));
} while (ret == Z_OK);
if (ret != Z_STREAM_END)
die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid),
ret);
- ret = end_loose_object_common(&c, &stream, &parano_oid);
+ ret = end_loose_object_common(&c, NULL, &stream, &parano_oid, NULL);
if (ret != Z_OK)
die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid),
ret);
@@ -2108,10 +2213,12 @@ static int freshen_packed_object(const struct object_id *oid)
int stream_loose_object(struct input_stream *in_stream, size_t len,
struct object_id *oid)
{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ struct object_id compat_oid;
int fd, ret, err = 0, flush = 0;
unsigned char compressed[4096];
git_zstream stream;
- git_hash_ctx c;
+ git_hash_ctx c, compat_c;
struct strbuf tmp_file = STRBUF_INIT;
struct strbuf filename = STRBUF_INIT;
int dirlen;
@@ -2135,7 +2242,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
*/
fd = start_loose_object_common(&tmp_file, filename.buf, 0,
&stream, compressed, sizeof(compressed),
- &c, hdr, hdrlen);
+ &c, &compat_c, hdr, hdrlen);
if (fd < 0) {
err = -1;
goto cleanup;
@@ -2153,7 +2260,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
if (in_stream->is_finished)
flush = 1;
}
- ret = write_loose_object_common(&c, &stream, flush, in0, fd,
+ ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd,
compressed, sizeof(compressed));
/*
* Unlike write_loose_object(), we do not have the entire
@@ -2176,7 +2283,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
*/
if (ret != Z_STREAM_END)
die(_("unable to stream deflate new object (%d)"), ret);
- ret = end_loose_object_common(&c, &stream, oid);
+ ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid);
if (ret != Z_OK)
die(_("deflateEnd on stream object failed (%d)"), ret);
close_loose_object(fd, tmp_file.buf);
@@ -2203,6 +2310,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
}
err = finalize_object_file(tmp_file.buf, filename.buf);
+ if (!err && compat)
+ err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
cleanup:
strbuf_release(&tmp_file);
strbuf_release(&filename);
@@ -2211,19 +2320,42 @@ cleanup:
int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
- unsigned flags)
+ struct object_id *compat_oid_in, unsigned flags)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);
+ /* Generate compat_oid */
+ if (compat) {
+ if (compat_oid_in)
+ oidcpy(&compat_oid, compat_oid_in);
+ else if (type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, type, &compat_oid);
+ else {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
+
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
*/
- write_object_file_prepare(the_hash_algo, buf, len, type, oid, hdr,
- &hdrlen);
+ write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
if (freshen_packed_object(oid) || freshen_loose_object(oid))
return 0;
- return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
+ if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags))
+ return -1;
+ if (compat)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
+ return 0;
}
int write_object_file_literally(const void *buf, unsigned long len,
@@ -2231,7 +2363,27 @@ int write_object_file_literally(const void *buf, unsigned long len,
unsigned flags)
{
char *header;
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *algo = repo->hash_algo;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
+ struct object_id compat_oid;
int hdrlen, status = 0;
+ int compat_type = -1;
+
+ if (compat) {
+ compat_type = type_from_string_gently(type, -1, 1);
+ if (compat_type == OBJ_BLOB)
+ hash_object_file(compat, buf, len, compat_type,
+ &compat_oid);
+ else if (compat_type != -1) {
+ struct strbuf converted = STRBUF_INIT;
+ convert_object_file(&converted, algo, compat,
+ buf, len, compat_type, 0);
+ hash_object_file(compat, converted.buf, converted.len,
+ compat_type, &compat_oid);
+ strbuf_release(&converted);
+ }
+ }
/* type string, SP, %lu of the length plus NUL must fit this */
hdrlen = strlen(type) + MAX_HEADER_LEN;
@@ -2244,6 +2396,8 @@ int write_object_file_literally(const void *buf, unsigned long len,
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
+ if (compat_type != -1)
+ return repo_add_loose_object_map(repo, oid, &compat_oid);
cleanup:
free(header);
@@ -2252,9 +2406,12 @@ cleanup:
int force_object_loose(const struct object_id *oid, time_t mtime)
{
+ struct repository *repo = the_repository;
+ const struct git_hash_algo *compat = repo->compat_hash_algo;
void *buf;
unsigned long len;
struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id compat_oid;
enum object_type type;
char hdr[MAX_HEADER_LEN];
int hdrlen;
@@ -2267,8 +2424,15 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
oi.contentp = &buf;
if (oid_object_info_extended(the_repository, oid, &oi, 0))
return error(_("cannot read object for %s"), oid_to_hex(oid));
+ if (compat) {
+ if (repo_oid_to_algop(repo, oid, compat, &compat_oid))
+ return error(_("cannot map object %s to %s"),
+ oid_to_hex(oid), compat->name);
+ }
hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
+ if (!ret && compat)
+ ret = repo_add_loose_object_map(the_repository, oid, &compat_oid);
free(buf);
return ret;
diff --git a/object-name.c b/object-name.c
index 0bfa29dbbf..523af6f64f 100644
--- a/object-name.c
+++ b/object-name.c
@@ -8,7 +8,6 @@
#include "tag.h"
#include "commit.h"
#include "tree.h"
-#include "blob.h"
#include "tree-walk.h"
#include "refs.h"
#include "remote.h"
@@ -21,10 +20,10 @@
#include "read-cache-ll.h"
#include "repository.h"
#include "setup.h"
-#include "submodule.h"
#include "midx.h"
#include "commit-reach.h"
#include "date.h"
+#include "object-file-convert.h"
static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
@@ -49,6 +48,7 @@ struct disambiguate_state {
static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
{
+ /* The hash algorithm of current has already been filtered */
if (ds->always_call_fn) {
ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
return;
@@ -134,6 +134,8 @@ static void unique_in_midx(struct multi_pack_index *m,
{
uint32_t num, i, first = 0;
const struct object_id *current = NULL;
+ int len = ds->len > ds->repo->hash_algo->hexsz ?
+ ds->repo->hash_algo->hexsz : ds->len;
num = m->num_objects;
if (!num)
@@ -149,7 +151,7 @@ static void unique_in_midx(struct multi_pack_index *m,
for (i = first; i < num && !ds->ambiguous; i++) {
struct object_id oid;
current = nth_midxed_object_oid(&oid, m, i);
- if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
+ if (!match_hash(len, ds->bin_pfx.hash, current->hash))
break;
update_candidates(ds, current);
}
@@ -159,6 +161,8 @@ static void unique_in_pack(struct packed_git *p,
struct disambiguate_state *ds)
{
uint32_t num, i, first = 0;
+ int len = ds->len > ds->repo->hash_algo->hexsz ?
+ ds->repo->hash_algo->hexsz : ds->len;
if (p->multi_pack_index)
return;
@@ -177,7 +181,7 @@ static void unique_in_pack(struct packed_git *p,
for (i = first; i < num && !ds->ambiguous; i++) {
struct object_id oid;
nth_packed_object_id(&oid, p, i);
- if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
+ if (!match_hash(len, ds->bin_pfx.hash, oid.hash))
break;
update_candidates(ds, &oid);
}
@@ -188,6 +192,10 @@ static void find_short_packed_object(struct disambiguate_state *ds)
struct multi_pack_index *m;
struct packed_git *p;
+ /* Skip, unless oids from the storage hash algorithm are wanted */
+ if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo))
+ return;
+
for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
m = m->next)
unique_in_midx(m, ds);
@@ -326,11 +334,12 @@ int set_disambiguate_hint_config(const char *var, const char *value)
static int init_object_disambiguation(struct repository *r,
const char *name, int len,
+ const struct git_hash_algo *algo,
struct disambiguate_state *ds)
{
int i;
- if (len < MINIMUM_ABBREV || len > the_hash_algo->hexsz)
+ if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ)
return -1;
memset(ds, 0, sizeof(*ds));
@@ -357,6 +366,7 @@ static int init_object_disambiguation(struct repository *r,
ds->len = len;
ds->hex_pfx[len] = '\0';
ds->repo = r;
+ ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN;
prepare_alt_odb(r);
return 0;
}
@@ -491,9 +501,10 @@ static int repo_collect_ambiguous(struct repository *r UNUSED,
return collect_ambiguous(oid, data);
}
-static int sort_ambiguous(const void *a, const void *b, void *ctx)
+static int sort_ambiguous(const void *va, const void *vb, void *ctx)
{
struct repository *sort_ambiguous_repo = ctx;
+ const struct object_id *a = va, *b = vb;
int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
int a_type_sort;
@@ -503,8 +514,12 @@ static int sort_ambiguous(const void *a, const void *b, void *ctx)
* Sorts by hash within the same object type, just as
* oid_array_for_each_unique() would do.
*/
- if (a_type == b_type)
- return oidcmp(a, b);
+ if (a_type == b_type) {
+ if (a->algo == b->algo)
+ return oidcmp(a, b);
+ else
+ return a->algo > b->algo ? 1 : -1;
+ }
/*
* Between object types show tags, then commits, and finally
@@ -533,8 +548,12 @@ static enum get_oid_result get_short_oid(struct repository *r,
int status;
struct disambiguate_state ds;
int quietly = !!(flags & GET_OID_QUIETLY);
+ const struct git_hash_algo *algo = r->hash_algo;
- if (init_object_disambiguation(r, name, len, &ds) < 0)
+ if (flags & GET_OID_HASH_ANY)
+ algo = NULL;
+
+ if (init_object_disambiguation(r, name, len, algo, &ds) < 0)
return -1;
if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -588,7 +607,7 @@ static enum get_oid_result get_short_oid(struct repository *r,
if (!ds.ambiguous)
ds.fn = NULL;
- repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+ repo_for_each_abbrev(r, ds.hex_pfx, algo, collect_ambiguous, &collect);
sort_ambiguous_oid_array(r, &collect);
if (oid_array_for_each(&collect, show_ambiguous_object, &out))
@@ -610,13 +629,14 @@ static enum get_oid_result get_short_oid(struct repository *r,
}
int repo_for_each_abbrev(struct repository *r, const char *prefix,
+ const struct git_hash_algo *algo,
each_abbrev_fn fn, void *cb_data)
{
struct oid_array collect = OID_ARRAY_INIT;
struct disambiguate_state ds;
int ret;
- if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
+ if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0)
return -1;
ds.always_call_fn = 1;
@@ -787,10 +807,12 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
int repo_find_unique_abbrev_r(struct repository *r, char *hex,
const struct object_id *oid, int len)
{
+ const struct git_hash_algo *algo =
+ oid->algo ? &hash_algos[oid->algo] : r->hash_algo;
struct disambiguate_state ds;
struct min_abbrev_data mad;
struct object_id oid_ret;
- const unsigned hexsz = r->hash_algo->hexsz;
+ const unsigned hexsz = algo->hexsz;
if (len < 0) {
unsigned long count = repo_approximate_object_count(r);
@@ -826,7 +848,7 @@ int repo_find_unique_abbrev_r(struct repository *r, char *hex,
find_abbrev_len_packed(&mad);
- if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
+ if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0)
return -1;
ds.fn = repo_extend_abbrev_len;
@@ -1036,6 +1058,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
len, str,
show_date(co_time, co_tz, DATE_MODE(RFC2822)));
}
+ } else if (nth == co_cnt && !is_null_oid(oid)) {
+ /*
+ * We were asked for the Nth reflog (counting
+ * from 0), but there were only N entries.
+ * read_ref_at() will have returned "1" to tell
+ * us it did not find an entry, but it did
+ * still fill in the oid with the "old" value,
+ * which we can use.
+ */
} else {
if (flags & GET_OID_QUIETLY) {
exit(128);
@@ -1481,7 +1512,7 @@ int repo_get_oid_mb(struct repository *r,
struct object_id *oid)
{
struct commit *one, *two;
- struct commit_list *mbs;
+ struct commit_list *mbs = NULL;
struct object_id oid_tmp;
const char *dots;
int st;
@@ -1509,7 +1540,10 @@ int repo_get_oid_mb(struct repository *r,
two = lookup_commit_reference_gently(r, &oid_tmp, 0);
if (!two)
return -1;
- mbs = repo_get_merge_bases(r, one, two);
+ if (repo_get_merge_bases(r, one, two, &mbs) < 0) {
+ free_commit_list(mbs);
+ return -1;
+ }
if (!mbs || mbs->next)
st = -1;
else {
diff --git a/object-name.h b/object-name.h
index 9ae5223071..064ddc97d1 100644
--- a/object-name.h
+++ b/object-name.h
@@ -67,7 +67,8 @@ enum get_oid_result get_oid_with_context(struct repository *repo, const char *st
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 repo_for_each_abbrev(struct repository *r, const char *prefix,
+ const struct git_hash_algo *algo, each_abbrev_fn, void *);
int set_disambiguate_hint_config(const char *var, const char *value);
diff --git a/object-store-ll.h b/object-store-ll.h
index 26a3895c82..c5f2bb2fc2 100644
--- a/object-store-ll.h
+++ b/object-store-ll.h
@@ -26,6 +26,9 @@ struct object_directory {
uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
struct oidtree *loose_objects_cache;
+ /* Map between object IDs for loose objects. */
+ struct loose_object_map *loose_map;
+
/*
* This is a temporary object store created by the tmp_objdir
* facility. Disable ref updates since the objects in the store
@@ -252,11 +255,11 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf,
int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
- unsigned flags);
+ struct object_id *comapt_oid_in, unsigned flags);
static inline int write_object_file(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid)
{
- return write_object_file_flags(buf, len, type, oid, 0);
+ return write_object_file_flags(buf, len, type, oid, NULL, 0);
}
int write_object_file_literally(const void *buf, unsigned long len,
diff --git a/object.c b/object.c
index 2c61e4c862..51e384828e 100644
--- a/object.c
+++ b/object.c
@@ -13,6 +13,7 @@
#include "alloc.h"
#include "packfile.h"
#include "commit-graph.h"
+#include "loose.h"
unsigned int get_max_object_index(void)
{
@@ -47,8 +48,7 @@ int type_from_string_gently(const char *str, ssize_t len, int gentle)
len = strlen(str);
for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
- if (!strncmp(str, object_type_strings[i], len) &&
- object_type_strings[i][len] == '\0')
+ if (!xstrncmpz(object_type_strings[i], str, len))
return i;
if (gentle)
@@ -272,6 +272,7 @@ struct object *parse_object_with_flags(struct repository *r,
enum parse_object_flags flags)
{
int skip_hash = !!(flags & PARSE_OBJECT_SKIP_HASH_CHECK);
+ int discard_tree = !!(flags & PARSE_OBJECT_DISCARD_TREE);
unsigned long size;
enum object_type type;
int eaten;
@@ -299,6 +300,17 @@ struct object *parse_object_with_flags(struct repository *r,
return lookup_object(r, oid);
}
+ /*
+ * If the caller does not care about the tree buffer and does not
+ * care about checking the hash, we can simply verify that we
+ * have the on-disk object with the correct type.
+ */
+ if (skip_hash && discard_tree &&
+ (!obj || obj->type == OBJ_TREE) &&
+ oid_object_info(r, oid, NULL) == OBJ_TREE) {
+ return &lookup_tree(r, oid)->object;
+ }
+
buffer = repo_read_object_file(r, oid, &type, &size);
if (buffer) {
if (!skip_hash &&
@@ -312,6 +324,8 @@ struct object *parse_object_with_flags(struct repository *r,
buffer, &eaten);
if (!eaten)
free(buffer);
+ if (discard_tree && type == OBJ_TREE)
+ free_tree_buffer((struct tree *)obj);
return obj;
}
return NULL;
@@ -540,6 +554,7 @@ void free_object_directory(struct object_directory *odb)
{
free(odb->path);
odb_clear_loose_cache(odb);
+ loose_object_map_clear(&odb->loose_map);
free(odb);
}
diff --git a/object.h b/object.h
index 114d45954d..9293e703cc 100644
--- a/object.h
+++ b/object.h
@@ -190,6 +190,24 @@ void *create_object(struct repository *r, const struct object_id *oid, void *obj
void *object_as_type(struct object *obj, enum object_type type, int quiet);
+
+static inline const char *parse_mode(const char *str, uint16_t *modep)
+{
+ unsigned char c;
+ unsigned int mode = 0;
+
+ if (*str == ' ')
+ return NULL;
+
+ while ((c = *str++) != ' ') {
+ if (c < '0' || c > '7')
+ return NULL;
+ mode = (mode << 3) + (c - '0');
+ }
+ *modep = mode;
+ return str;
+}
+
/*
* Returns the object, having parsed it to find out what it is.
*
@@ -197,6 +215,7 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet);
*/
enum parse_object_flags {
PARSE_OBJECT_SKIP_HASH_CHECK = 1 << 0,
+ PARSE_OBJECT_DISCARD_TREE = 1 << 1,
};
struct object *parse_object(struct repository *r, const struct object_id *oid);
struct object *parse_object_with_flags(struct repository *r,
diff --git a/oid-array.c b/oid-array.c
index 8e4717746c..1f36651754 100644
--- a/oid-array.c
+++ b/oid-array.c
@@ -6,12 +6,20 @@ void oid_array_append(struct oid_array *array, const struct object_id *oid)
{
ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
oidcpy(&array->oid[array->nr++], oid);
+ if (!oid->algo)
+ oid_set_algo(&array->oid[array->nr - 1], the_hash_algo);
array->sorted = 0;
}
-static int void_hashcmp(const void *a, const void *b)
+static int void_hashcmp(const void *va, const void *vb)
{
- return oidcmp(a, b);
+ const struct object_id *a = va, *b = vb;
+ int ret;
+ if (a->algo == b->algo)
+ ret = oidcmp(a, b);
+ else
+ ret = a->algo > b->algo ? 1 : -1;
+ return ret;
}
void oid_array_sort(struct oid_array *array)
diff --git a/oidset.c b/oidset.c
index d1e5376316..91d1385910 100644
--- a/oidset.c
+++ b/oidset.c
@@ -23,6 +23,16 @@ int oidset_insert(struct oidset *set, const struct object_id *oid)
return !added;
}
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src)
+{
+ struct oidset_iter iter;
+ struct object_id *src_oid;
+
+ oidset_iter_init(src, &iter);
+ while ((src_oid = oidset_iter_next(&iter)))
+ oidset_insert(dest, src_oid);
+}
+
int oidset_remove(struct oidset *set, const struct object_id *oid)
{
khiter_t pos = kh_get_oid_set(&set->set, *oid);
diff --git a/oidset.h b/oidset.h
index ba4a5a2cd3..262f4256d6 100644
--- a/oidset.h
+++ b/oidset.h
@@ -48,6 +48,12 @@ int oidset_contains(const struct oidset *set, const struct object_id *oid);
int oidset_insert(struct oidset *set, const struct object_id *oid);
/**
+ * Insert all the oids that are in set 'src' into set 'dest'; a copy
+ * is made of each oid inserted into set 'dest'.
+ */
+void oidset_insert_from_set(struct oidset *dest, struct oidset *src);
+
+/**
* Remove the oid from the set.
*
* Returns 1 if the oid was present in the set, 0 otherwise.
diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore
index 9acb74412e..a877c11f42 100644
--- a/oss-fuzz/.gitignore
+++ b/oss-fuzz/.gitignore
@@ -1,3 +1,5 @@
fuzz-commit-graph
+fuzz-config
+fuzz-date
fuzz-pack-headers
fuzz-pack-idx
diff --git a/oss-fuzz/dummy-cmd-main.c b/oss-fuzz/dummy-cmd-main.c
new file mode 100644
index 0000000000..071cb231ba
--- /dev/null
+++ b/oss-fuzz/dummy-cmd-main.c
@@ -0,0 +1,14 @@
+#include "git-compat-util.h"
+
+/*
+ * When linking the fuzzers, we link against common-main.o to pick up some
+ * symbols. However, even though we ignore common-main:main(), we still need to
+ * provide all the symbols it references. In the fuzzers' case, we need to
+ * provide a dummy cmd_main() for the linker to be happy. It will never be
+ * executed.
+ */
+
+int cmd_main(int argc, const char **argv) {
+ BUG("We should not execute cmd_main() from a fuzz target");
+ return 1;
+}
diff --git a/oss-fuzz/fuzz-config.c b/oss-fuzz/fuzz-config.c
new file mode 100644
index 0000000000..94027f5b97
--- /dev/null
+++ b/oss-fuzz/fuzz-config.c
@@ -0,0 +1,33 @@
+#include "git-compat-util.h"
+#include "config.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
+static int config_parser_callback(const char *, const char *,
+ const struct config_context *, void *);
+
+static int config_parser_callback(const char *key, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *data UNUSED)
+{
+ /*
+ * Visit every byte of memory we are given to make sure the parser
+ * gave it to us appropriately. We need to unconditionally return 0,
+ * but we also want to prevent the strlen from being optimized away.
+ */
+ size_t c = strlen(key);
+
+ if (value)
+ c += strlen(value);
+ return c == SIZE_MAX;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size)
+{
+ struct config_options config_opts = { 0 };
+
+ config_opts.error_action = CONFIG_ERROR_SILENT;
+ git_config_from_mem(config_parser_callback, CONFIG_ORIGIN_BLOB,
+ "fuzztest-config", (const char *)data, size, NULL,
+ CONFIG_SCOPE_UNKNOWN, &config_opts);
+ return 0;
+}
diff --git a/oss-fuzz/fuzz-date.c b/oss-fuzz/fuzz-date.c
new file mode 100644
index 0000000000..9619dae40e
--- /dev/null
+++ b/oss-fuzz/fuzz-date.c
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "date.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ int local;
+ int num;
+ char *str;
+ int16_t tz;
+ timestamp_t ts;
+ enum date_mode_type dmtype;
+ struct date_mode dm;
+
+ if (size <= 4)
+ /*
+ * we use the first byte to fuzz dmtype and the
+ * second byte to fuzz local, then the next two
+ * bytes to fuzz tz offset. The remainder
+ * (at least one byte) is fed as input to
+ * approxidate_careful().
+ */
+ return 0;
+
+ local = !!(*data++ & 0x10);
+ num = *data++ % DATE_UNIX;
+ if (num >= DATE_STRFTIME)
+ num++;
+ dmtype = (enum date_mode_type)num;
+ size -= 2;
+
+ tz = *data++;
+ tz = (tz << 8) | *data++;
+ size -= 2;
+
+ str = xmemdupz(data, size);
+
+ ts = approxidate_careful(str, &num);
+ free(str);
+
+ dm = date_mode_from_type(dmtype);
+ dm.local = local;
+ show_date(ts, (int)tz, dm);
+
+ date_mode_release(&dm);
+
+ return 0;
+}
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index ae37fb6976..c6c8f94cc5 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -4,12 +4,9 @@
#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"
@@ -373,7 +370,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,
if (parse_tree(tree) < 0)
die("unable to load tree object %s",
oid_to_hex(&tree->object.oid));
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
diff --git a/pack-bitmap.c b/pack-bitmap.c
index b314f912c9..35c5ef9d3c 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -51,13 +51,6 @@ struct bitmap_index {
struct packed_git *pack;
struct multi_pack_index *midx;
- /*
- * Mark the first `reuse_objects` in the packfile as reused:
- * they will be sent as-is without using them for repacking
- * calculations
- */
- uint32_t reuse_objects;
-
/* mmapped buffer of the whole bitmap index */
unsigned char *map;
size_t map_size; /* size of the mmaped buffer */
diff --git a/pack-check.c b/pack-check.c
index 977f619618..25104d5b14 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -3,7 +3,6 @@
#include "hex.h"
#include "repository.h"
#include "pack.h"
-#include "pack-revindex.h"
#include "progress.h"
#include "packfile.h"
#include "object-file.h"
diff --git a/pack-write.c b/pack-write.c
index b19ddf15b2..80ecfa544c 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -7,7 +7,6 @@
#include "remote.h"
#include "chunk-format.h"
#include "pack-mtimes.h"
-#include "oidmap.h"
#include "pack-objects.h"
#include "pack-revindex.h"
#include "path.h"
diff --git a/packfile.c b/packfile.c
index 9cc0a2e37a..d4df7fdeea 100644
--- a/packfile.c
+++ b/packfile.c
@@ -9,7 +9,6 @@
#include "mergesort.h"
#include "packfile.h"
#include "delta.h"
-#include "streaming.h"
#include "hash-lookup.h"
#include "commit.h"
#include "object.h"
@@ -2250,7 +2249,8 @@ static int add_promisor_object(const struct object_id *oid,
struct tree *tree = (struct tree *)obj;
struct tree_desc desc;
struct name_entry entry;
- if (init_tree_desc_gently(&desc, tree->buffer, tree->size, 0))
+ if (init_tree_desc_gently(&desc, &tree->object.oid,
+ tree->buffer, tree->size, 0))
/*
* Error messages are given when packs are
* verified, so do not print any here.
diff --git a/packfile.h b/packfile.h
index c3692308b8..28c8fd3e39 100644
--- a/packfile.h
+++ b/packfile.h
@@ -54,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);
diff --git a/parse-options.c b/parse-options.c
index e0c94b0546..30b9e68f8a 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -2,8 +2,6 @@
#include "parse-options.h"
#include "abspath.h"
#include "parse.h"
-#include "commit.h"
-#include "color.h"
#include "gettext.h"
#include "strbuf.h"
#include "string-list.h"
@@ -279,7 +277,8 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
opt_name = optnamearg(opt, arg, flags);
other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
- error(_("%s is incompatible with %s"), opt_name, other_opt_name);
+ error(_("options '%s' and '%s' cannot be used together"),
+ opt_name, other_opt_name);
free(opt_name);
free(other_opt_name);
return -1;
@@ -351,97 +350,107 @@ static int is_alias(struct parse_opt_ctx_t *ctx,
return 0;
}
+struct parsed_option {
+ const struct option *option;
+ enum opt_parsed flags;
+};
+
+static void register_abbrev(struct parse_opt_ctx_t *p,
+ const struct option *option, enum opt_parsed flags,
+ struct parsed_option *abbrev,
+ struct parsed_option *ambiguous)
+{
+ if (p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)
+ return;
+ if (abbrev->option &&
+ !(abbrev->flags == flags && is_alias(p, abbrev->option, option))) {
+ /*
+ * If this is abbreviated, it is
+ * ambiguous. So when there is no
+ * exact match later, we need to
+ * error out.
+ */
+ ambiguous->option = abbrev->option;
+ ambiguous->flags = abbrev->flags;
+ }
+ abbrev->option = option;
+ abbrev->flags = flags;
+}
+
static enum parse_opt_result parse_long_opt(
struct parse_opt_ctx_t *p, const char *arg,
const struct option *options)
{
const char *arg_end = strchrnul(arg, '=');
- const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
- enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
+ const char *arg_start = arg;
+ enum opt_parsed flags = OPT_LONG;
+ int arg_starts_with_no_no = 0;
+ struct parsed_option abbrev = { .option = NULL, .flags = OPT_LONG };
+ struct parsed_option ambiguous = { .option = NULL, .flags = OPT_LONG };
+
+ if (skip_prefix(arg_start, "no-", &arg_start)) {
+ if (skip_prefix(arg_start, "no-", &arg_start))
+ arg_starts_with_no_no = 1;
+ else
+ flags |= OPT_UNSET;
+ }
for (; options->type != OPTION_END; options++) {
const char *rest, *long_name = options->long_name;
- enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG;
+ enum opt_parsed opt_flags = OPT_LONG;
+ int allow_unset = !(options->flags & PARSE_OPT_NONEG);
if (options->type == OPTION_SUBCOMMAND)
continue;
if (!long_name)
continue;
-again:
- if (!skip_prefix(arg, long_name, &rest))
- rest = NULL;
- if (!rest) {
- /* abbreviated? */
- if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT) &&
- !strncmp(long_name, arg, arg_end - arg)) {
-is_abbreviated:
- if (abbrev_option &&
- !is_alias(p, abbrev_option, options)) {
- /*
- * If this is abbreviated, it is
- * ambiguous. So when there is no
- * exact match later, we need to
- * error out.
- */
- ambiguous_option = abbrev_option;
- ambiguous_flags = abbrev_flags;
- }
- if (!(flags & OPT_UNSET) && *arg_end)
- p->opt = arg_end + 1;
- abbrev_option = options;
- abbrev_flags = flags ^ opt_flags;
- continue;
- }
- /* negation allowed? */
- if (options->flags & PARSE_OPT_NONEG)
- continue;
- /* negated and abbreviated very much? */
- if (starts_with("no-", arg)) {
- flags |= OPT_UNSET;
- goto is_abbreviated;
- }
- /* negated? */
- if (!starts_with(arg, "no-")) {
- if (skip_prefix(long_name, "no-", &long_name)) {
- opt_flags |= OPT_UNSET;
- goto again;
- }
- continue;
- }
- flags |= OPT_UNSET;
- if (!skip_prefix(arg + 3, long_name, &rest)) {
- /* abbreviated and negated? */
- if (starts_with(long_name, arg + 3))
- goto is_abbreviated;
- else
- continue;
- }
- }
- if (*rest) {
- if (*rest != '=')
+ if (skip_prefix(long_name, "no-", &long_name))
+ opt_flags |= OPT_UNSET;
+ else if (arg_starts_with_no_no)
+ continue;
+
+ if (((flags ^ opt_flags) & OPT_UNSET) && !allow_unset)
+ continue;
+
+ if (skip_prefix(arg_start, long_name, &rest)) {
+ if (*rest == '=')
+ p->opt = rest + 1;
+ else if (*rest)
continue;
- p->opt = rest + 1;
+ return get_value(p, options, flags ^ opt_flags);
}
- return get_value(p, options, flags ^ opt_flags);
+
+ /* abbreviated? */
+ if (!strncmp(long_name, arg_start, arg_end - arg_start))
+ register_abbrev(p, options, flags ^ opt_flags,
+ &abbrev, &ambiguous);
+
+ /* negated and abbreviated very much? */
+ if (allow_unset && starts_with("no-", arg))
+ register_abbrev(p, options, OPT_UNSET ^ opt_flags,
+ &abbrev, &ambiguous);
}
- if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+ if (disallow_abbreviated_options && (ambiguous.option || abbrev.option))
die("disallowed abbreviated or ambiguous option '%.*s'",
(int)(arg_end - arg), arg);
- if (ambiguous_option) {
+ if (ambiguous.option) {
error(_("ambiguous option: %s "
"(could be --%s%s or --%s%s)"),
arg,
- (ambiguous_flags & OPT_UNSET) ? "no-" : "",
- ambiguous_option->long_name,
- (abbrev_flags & OPT_UNSET) ? "no-" : "",
- abbrev_option->long_name);
+ (ambiguous.flags & OPT_UNSET) ? "no-" : "",
+ ambiguous.option->long_name,
+ (abbrev.flags & OPT_UNSET) ? "no-" : "",
+ abbrev.option->long_name);
return PARSE_OPT_HELP;
}
- if (abbrev_option)
- return get_value(p, abbrev_option, abbrev_flags);
+ if (abbrev.option) {
+ if (*arg_end)
+ p->opt = arg_end + 1;
+ return get_value(p, abbrev.option, abbrev.flags);
+ }
return PARSE_OPT_UNKNOWN;
}
@@ -929,13 +938,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"))
diff --git a/patch-ids.c b/patch-ids.c
index c3e1a0dd21..a5683b462c 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -2,7 +2,6 @@
#include "diff.h"
#include "commit.h"
#include "hash.h"
-#include "hash-lookup.h"
#include "hex.h"
#include "patch-ids.h"
diff --git a/path.c b/path.c
index 67e2690efe..67229edb9c 100644
--- a/path.c
+++ b/path.c
@@ -28,8 +28,6 @@ static int get_st_mode_bits(const char *path, int *mode)
return 0;
}
-static char bad_path[] = "/bad-path/";
-
static struct strbuf *get_pathname(void)
{
static struct strbuf pathname_array[4] = {
@@ -59,21 +57,6 @@ static void strbuf_cleanup_path(struct strbuf *sb)
strbuf_remove(sb, 0, path - sb->buf);
}
-char *mksnpath(char *buf, size_t n, const char *fmt, ...)
-{
- va_list args;
- unsigned len;
-
- va_start(args, fmt);
- len = vsnprintf(buf, n, fmt, args);
- va_end(args);
- if (len >= n) {
- strlcpy(buf, bad_path, n);
- return buf;
- }
- return (char *)cleanup_path(buf);
-}
-
static int dir_prefix(const char *buf, const char *dir)
{
int len = strlen(dir);
@@ -871,7 +854,7 @@ const char *enter_repo(const char *path, int strict)
return NULL;
}
-static int calc_shared_perm(int mode)
+int calc_shared_perm(int mode)
{
int tweak;
@@ -1588,7 +1571,5 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
-REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
-REPO_GIT_PATH_FUNC(auto_merge, "AUTO_MERGE")
REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
REPO_GIT_PATH_FUNC(shallow, "shallow")
diff --git a/path.h b/path.h
index 639372edd9..ea96487b29 100644
--- a/path.h
+++ b/path.h
@@ -24,12 +24,6 @@ char *mkpathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
/*
- * Construct a path and place the result in the provided buffer `buf`.
- */
-char *mksnpath(char *buf, size_t n, const char *fmt, ...)
- __attribute__((format (printf, 3, 4)));
-
-/*
* The `git_common_path` family of functions will construct a path into a
* repository's common git directory, which is shared by all worktrees.
*/
@@ -175,14 +169,13 @@ const char *git_path_merge_msg(struct repository *r);
const char *git_path_merge_rr(struct repository *r);
const char *git_path_merge_mode(struct repository *r);
const char *git_path_merge_head(struct repository *r);
-const char *git_path_merge_autostash(struct repository *r);
-const char *git_path_auto_merge(struct repository *r);
const char *git_path_fetch_head(struct repository *r);
const char *git_path_shallow(struct repository *r);
int ends_with_path_components(const char *path, const char *components);
int validate_headref(const char *ref);
+int calc_shared_perm(int mode);
int adjust_shared_perm(const char *path);
char *interpolate_path(const char *path, int real_home);
diff --git a/perl/FromCPAN/Error.pm b/perl/FromCPAN/Error.pm
index d82b71325c..5b97e0315d 100644
--- a/perl/FromCPAN/Error.pm
+++ b/perl/FromCPAN/Error.pm
@@ -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
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 6ce2e283c8..7721708ce5 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -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();
}
diff --git a/pkt-line.c b/pkt-line.c
index af83a19f4d..24479eae4d 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -4,6 +4,7 @@
#include "gettext.h"
#include "hex.h"
#include "run-command.h"
+#include "sideband.h"
#include "trace.h"
#include "write-or-die.h"
@@ -462,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 &&
@@ -592,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
@@ -620,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;
}
diff --git a/pkt-line.h b/pkt-line.h
index 954eec8719..3b33cc64f3 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -2,7 +2,6 @@
#define PKTLINE_H
#include "strbuf.h"
-#include "sideband.h"
/*
* Write a packetized stream, where each line is preceded by
@@ -85,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);
/*
@@ -194,6 +194,9 @@ struct packet_reader {
/* hash algorithm in use */
const struct git_hash_algo *hash_algo;
+
+ /* hold temporary sideband message */
+ struct strbuf scratch;
};
/*
diff --git a/po/bg.po b/po/bg.po
index a3fc5b74d8..6b95addef4 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,7 +1,7 @@
# Bulgarian translation of git po-file.
-# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Alexander Shopov <ash@kambanaria.org>.
# This file is distributed under the same license as the git package.
-# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024.
# ========================
# DICTIONARY TO MERGE IN GIT GUI
# ------------------------
@@ -141,6 +141,8 @@
# midx, multi-pack index - файл Ñ Ð¸Ð½Ð´ÐµÐºÑа за множеÑтво пакети
# overlay mode - припокриващ режим (при изтеглÑнe)
# incremental file нараÑтващ файл
+# commit-graph граф Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ
+# commit-graph chain верига на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ
# split (commit-graphr) раздробен (граф Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ)
# clobber (a tag) презапиÑвам (етикет)
# blame извеждане на авторÑтво
@@ -207,6 +209,15 @@
# master/main branch оÑновен клон
# unborn/orphan branch неродѐн клон (а не неÑъздаден) - клон без никакви подаваниÑ, включително и началното
# parse анализ, анализирам
+# reinitialize repository занулÑване на хранилището и инициализиране
+# replay изпълнÑване/прилагане наново
+# BTMP chunk Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° побитова маÑка
+# OID fanout chunk Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° разпределÑнето
+# OID lookup chunk Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° Ñ‚ÑŠÑ€Ñенето
+# autostash автоматично Ñкатано
+# symref файл Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ» (regular file that stores a string that begins with ref: refs/)
+#
+#
#
# ------------------------
# „$var“ - може да не Ñработва за shell има gettext и eval_gettext - проверка - намират Ñе леÑно по „$
@@ -233,10 +244,10 @@
# for i in `sort -u FILES`; do cnt=`grep $i FILES | wc -l`; echo $cnt $i ;done | sort -n
msgid ""
msgstr ""
-"Project-Id-Version: git 2.43\n"
+"Project-Id-Version: git 2.44\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-17 15:49+0100\n"
-"PO-Revision-Date: 2023-11-18 13:23+0100\n"
+"POT-Creation-Date: 2024-02-16 09:33+0100\n"
+"PO-Revision-Date: 2024-02-16 09:38+0100\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
@@ -1691,6 +1702,10 @@ msgid "Unexpected option --output"
msgstr "Ðеочаквана Ð¾Ð¿Ñ†Ð¸Ñ â€ž--output“"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "излишна Ð¾Ð¿Ñ†Ð¸Ñ Ð¸Ð»Ð¸ ÑтойноÑÑ‚ на ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¸Ñ Ñ€ÐµÐ´: „%s“"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Ðепознат формат на архив: „%s“"
@@ -1738,6 +1753,14 @@ msgstr ""
"„GIT_ATTR_SOURCE“"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "„stat“ не може да Ñе изпълни върху „%s“"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "обектът „%s“ не може да бъде прочетен"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Ðеправилно цитирано Ñъдържание във файла „%s“: %s"
@@ -3074,12 +3097,13 @@ msgid "couldn't look up commit object for '%s'"
msgstr "обектът-подаване за „%s“ не може да Ñе открие"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "клонът „%s“ не е ÑлÑÑ‚ напълно"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
msgstr ""
-"клонът „%s“ не е ÑлÑÑ‚ напълно. Ðко Ñте Ñигурни, че иÑкате\n"
-"да го изтриете, изпълнете:\n"
+"Ðко Ñте Ñигурни, че иÑкате да го изтриете, изпълнете:\n"
"\n"
" git branch -D %s"
@@ -3126,7 +3150,7 @@ msgstr "подаването, Ñочено от ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ â€žHEAD“, Ð
#, c-format
msgid "HEAD (%s) points outside of refs/heads/"
-msgstr "„HEAD“ (%s) Ñочи извън директориÑта „refs/heads“"
+msgstr "„HEAD“ (%s) Ñочи извън директориÑта „refs/heads/“"
#, c-format
msgid "branch %s is being rebased at %s"
@@ -4138,8 +4162,8 @@ msgstr "принудително изтеглÑне (вашите промѐн
msgid "new-branch"
msgstr "ÐОВ_КЛОÐ"
-msgid "new unparented branch"
-msgstr "нов клон без родител"
+msgid "new unborn branch"
+msgstr "нов неродѐн клон"
msgid "update ignored files (default)"
msgstr "обновÑване на игнорираните файлове (Ñтандартно)"
@@ -4392,9 +4416,6 @@ msgstr ""
"което изиÑква нÑÐºÐ¾Ñ Ð¾Ñ‚ опциите „-i“, „-n“ или „-f“. ÐÑма да Ñе извърши "
"изчиÑтване"
-msgid "-x and -X cannot be used together"
-msgstr "опциите „-x“ и „-X“ Ñа неÑъвмеÑтими"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [ОПЦИЯ…] [--] ХРÐÐИЛИЩЕ [ДИРЕКТОРИЯ]"
@@ -4486,6 +4507,9 @@ msgstr "СЛУЖЕБÐÐ_ДИРЕКТОРИЯ"
msgid "separate git dir from working tree"
msgstr "отделна СЛУЖЕБÐÐ_ДИРЕКТОРИЯ за git извън работното дърво"
+msgid "specify the reference format to use"
+msgstr "указване на форма̀та за указател"
+
msgid "key=value"
msgstr "КЛЮЧ=СТОЙÐОСТ"
@@ -4613,12 +4637,9 @@ msgstr "Прекалено много аргументи."
msgid "You must specify a repository to clone."
msgstr "ТрÑбва да укажете кое хранилище иÑкате да клонирате."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"опциÑта „--bundle-uri“ е неÑъвмеÑтима Ñ â€ž--depth“, „--shallow-since“ и „--"
-"shallow-exclude“"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "непознат формат на Ñъхранение: „%s“"
#, c-format
msgid "repository '%s' does not exist"
@@ -4756,7 +4777,7 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir ДИРЕКТОРИЯ] [--append]\n"
" [--split[=СТРÐТЕГИЯ]] [--reachable|--stdin-packs|--"
@@ -7145,6 +7166,10 @@ msgid "unable to read tree (%s)"
msgstr "дървото не може да бъде прочетено (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "дървото не може да бъде прочетено: %s"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "не може да Ñе изпълни „grep“ от обект от вида %s"
@@ -7566,10 +7591,6 @@ msgstr ""
"СЪВПÐДЕÐИЕ ÐРСТОЙÐОСТИТЕ ЗРСУМИТЕ ЗРSHA1: „%s“ ÐРДВРРÐЗЛИЧÐИ ОБЕКТÐ!"
#, c-format
-msgid "unable to read %s"
-msgstr "обектът „%s“ не може да бъде прочетен"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "ÑъщеÑтвуващиÑÑ‚ обект в „%s“ не може да бъде прочетен"
@@ -7711,6 +7732,7 @@ msgstr "грешка при проверка Ñ â€žfsck“ на пакетнит
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
@@ -8449,6 +8471,14 @@ msgstr ""
"git merge-file [ОПЦИЯ…] [-L ИМЕ_1 [-L ОРИГИÐÐЛ [-L ИМЕ_2]]] ФÐЙЛ_1 ОРИГ_ФÐЙЛ "
"ФÐЙЛ_2"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"опциÑта приема Ñледните варианти за алгоритъм за разлики: „myers“ (по "
+"МайерÑ), „minimal“ (минимизиране на разликите), „patience“ (паÑианÑ) и "
+"„histogram“ (хиÑтограмен)"
+
msgid "send results to standard output"
msgstr "извеждане на резултатите на ÑÑ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð¸Ñ Ð¸Ð·Ñ…Ð¾Ð´"
@@ -8470,6 +8500,12 @@ msgstr "при конфликти да Ñе ползва чуждата верÑ
msgid "for conflicts, use a union version"
msgstr "при конфликти да Ñе ползва обединена верÑиÑ"
+msgid "<algorithm>"
+msgstr "ÐЛГОРИТЪМ"
+
+msgid "choose a diff algorithm"
+msgstr "избор на ÐЛГОРИТЪМа за разлики"
+
msgid "for conflicts, use this marker size"
msgstr "при конфликти да Ñе ползва маркер Ñ Ñ‚Ð°ÐºÑŠÐ² БРОЙ знаци"
@@ -8561,9 +8597,6 @@ msgstr "„--trivial-merge“ е неÑъвмеÑтима Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ‚Ðµ оп
msgid "unknown strategy option: -X%s"
msgstr "непозната Ð¾Ð¿Ñ†Ð¸Ñ Ð·Ð° ÑтратегиÑ: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "опциите „--merge-base“ и „--stdin“ Ñа неÑъвмеÑтими"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "входен ред Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÐµÐ½ формат: „%s“."
@@ -8606,7 +8639,7 @@ msgstr "(пÑевдоним на „--stat“)"
msgid "add (at most <n>) entries from shortlog to merge commit message"
msgstr ""
"добавÑне (на макÑимум такъв БРОЙ) запиÑи от ÑÑŠÐºÑ€Ð°Ñ‚ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð» в Ñъобщението "
-"за подаване"
+"за подаване ÑÑŠÑ Ñливане"
msgid "create a single commit instead of doing a merge"
msgstr "Ñъздаване на едно подаване вмеÑто извършване на Ñливане"
@@ -9523,6 +9556,11 @@ msgid "inconsistency with delta count"
msgstr "неправилен брой разлики"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr ""
+"неправилна ÑтойноÑÑ‚ за преизползването на пакети „pack.allowPackReuse“: „%s“"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9795,10 +9833,10 @@ msgstr "ИзброÑване на обектите"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Общо: %<PRIu32> (разлики: %<PRIu32>), преизползвани: %<PRIu32> (разлики: "
-"%<PRIu32>), преизползвани при пакетиране: %<PRIu32>"
+"%<PRIu32>), преизползвани при пакетиране: %<PRIu32> (от %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10792,13 +10830,6 @@ msgid "switch `C' expects a numerical value"
msgstr "опциÑта „C“ очаква чиÑло за аргумент"
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"опциите за прилагане Ñа неÑъвмеÑтими Ñ â€žrebase.autoSquash“. Пробвайте да "
-"добавите опциÑта „--no-autosquash“"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -11823,6 +11854,76 @@ msgstr "опциÑта „--convert-graft-file“ не приема аргуме
msgid "only one pattern can be given with -l"
msgstr "опциÑта „-l“ приема точно един шаблон"
+msgid "need some commits to replay"
+msgstr "необходимо е да има Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð·Ð° прилагане отново"
+
+msgid "--onto and --advance are incompatible"
+msgstr "опциите „--onto“ и „--advance“ Ñа неÑъвмеÑтими"
+
+msgid "all positive revisions given must be references"
+msgstr "вÑички зададени положителни верÑии Ñ‚Ñ€Ñбва да Ñа указатели"
+
+msgid "argument to --advance must be a reference"
+msgstr "аргументът към „--advance“ Ñ‚Ñ€Ñбва да е указател"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"цели Ñ Ð¼Ð½Ð¾Ð¶ÐµÑтво източници не може да Ñе придвижат напред, защото подредбата "
+"не е добре дефинирана"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"не може да Ñе определи дали това дейÑтвие е за „--advance“ или „--onto“"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"цели Ñ Ð¼Ð½Ð¾Ð¶ÐµÑтво клони-източници не може да Ñе придвижат напред, защото "
+"подредбата не е добре дефинирана"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "правилната база за „--onto“ не може да Ñе определи"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(ЕКСПЕРИМЕÐТÐЛÐО!) git replay ([--contained] --onto ÐОВÐ_БÐЗР| --advance "
+"КЛОÐ) ДИÐПÐЗОÐ_ПОДÐÐ’ÐÐИЯ…"
+
+msgid "make replay advance given branch"
+msgstr "прилагането наново придвижва Ð´Ð°Ð´ÐµÐ½Ð¸Ñ ÐšÐ›ÐžÐ Ð½Ð°Ð¿Ñ€ÐµÐ´"
+
+msgid "replay onto given commit"
+msgstr "прилагането наново върху даденото ПОДÐÐ’ÐÐЕ"
+
+msgid "advance all branches contained in revision-range"
+msgstr "придвижване на вÑички КЛОÐи в ДИÐПÐЗОÐа_ПОДÐÐ’ÐÐИЯ"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "изиÑква Ñе нÑÐºÐ¾Ñ Ð¾Ñ‚ опциите „--onto“ или „--advance“"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"нÑкои опции за проÑледÑване на указатели ще бъдат променени, защото битът "
+"„%s“ в Ñтруктурата „struct rev_info“ има превеÑ"
+
+msgid "error preparing revisions"
+msgstr "грешка при подготовката на верÑии"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "не Ñе поддържа прилагане наново и на началното подаване!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "не Ñе поддържа прилагане наново и на Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ ÑÑŠÑ Ñливане!"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr "git rerere [clear|forget ПЪТ…|diff|status|remaining|gc]"
@@ -12030,15 +12131,6 @@ msgstr "опциÑта „--prefix“ изиÑква аргумент"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "непознат режим за „--abbrev-ref“: „%s“"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "опциите „--exclude-hidden“ и „--branches“ Ñа неÑъвмеÑтими"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "опциите „--exclude-hidden“ и „--tags“ Ñа неÑъвмеÑтими"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "опциите „--exclude-hidden“ и „--remotes“ Ñа неÑъвмеÑтими"
-
msgid "this operation must be run in a work tree"
msgstr "тази команда Ñ‚Ñ€Ñбва да Ñе изпълни в работно дърво"
@@ -12242,7 +12334,7 @@ msgid "git shortlog [<options>] [<revision-range>] [[--] <path>...]"
msgstr "git shortlog [ОПЦИЯ…] [ДИÐПÐЗОÐ_ÐÐ_ВЕРСИИТЕ] [[--] [ПЪТ…]]"
msgid "git log --pretty=short | git shortlog [<options>]"
-msgstr "git log --pretty=short|git shortlog [ОПЦИЯ…]"
+msgstr "git log --pretty=short | git shortlog [ОПЦИЯ…]"
msgid "using multiple --group options with stdin is not supported"
msgstr "повече от една опции „--group“ Ñа неÑъвмеÑтими ÑÑŠÑ ÑÑ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð¸Ñ Ð²Ñ…Ð¾Ð´"
@@ -12455,10 +12547,6 @@ msgstr ""
"извеждане на указателите приети от ÑÑ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð¸Ñ Ð²Ñ…Ð¾Ð´, които липÑват в "
"локалното хранилище"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "опциите „%s“, „%s“ и „%s“ Ñа неÑъвмеÑтими"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13951,7 +14039,7 @@ msgstr "ЛипÑва клон-източник, затова Ñе приема â
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
@@ -13965,7 +14053,7 @@ msgstr ""
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
@@ -14035,6 +14123,10 @@ msgid "initializing"
msgstr "инициализациÑ"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "Ñъздаденото в „%s“ работно дърво липÑва"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "ПриготвÑне на работното дърво (нов клон „%s“)"
@@ -14076,10 +14168,6 @@ msgstr ""
"доÑтавете\n"
"обектите от отдалеченото хранилище"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "опциите „%s“ и „%s“ Ñа неÑъвмеÑтими"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "ИзтеглÑне КЛОÐа, дори и да е изтеглен в друго работно дърво"
@@ -14089,7 +14177,7 @@ msgstr "Ñъздаване на нов клон"
msgid "create or reset a branch"
msgstr "Ñъздаване или занулÑване на клони"
-msgid "create unborn/orphaned branch"
+msgid "create unborn branch"
msgstr "Ñъздаване на неродѐн клон"
msgid "populate the new working tree"
@@ -14112,11 +14200,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "опциите „%s“, „%s“ и „%s“ Ñа неÑъвмеÑтими"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "опциите „%s“ и „%s“ Ñа неÑъвмеÑтими"
-
-msgid "<commit-ish>"
-msgstr "ПОДÐÐ’ÐÐЕ"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опциите „%s“ и указателите към Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ñа неÑъвмеÑтими"
msgid "added with --lock"
msgstr "добавена Ñ â€ž--lock“"
@@ -14753,6 +14838,11 @@ msgstr "Пакетиране на непакетираните обекти в Ñ
msgid "Create, list, delete refs to replace objects"
msgstr "Създаване, извеждане, изтриване на указатели за замÑна на обекти"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"ЕКСПЕРИМЕÐТÐЛÐО: прилагане на Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð²ÑŠÑ€Ñ…Ñƒ нова база, работи и Ñ Ð³Ð¾Ð»Ð¸ "
+"хранилища"
+
msgid "Generates a summary of pending changes"
msgstr "Обобщение на предÑтоÑщите промѐни"
@@ -14991,6 +15081,36 @@ msgstr "ИнÑтрумент за управление на големи храÐ
msgid "commit-graph file is too small"
msgstr "файлът за гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е твърде малък"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "откъÑÑŠÑ‚ за разпределÑнето в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е прекалено малък"
+
+msgid "commit-graph fanout values out of order"
+msgstr ""
+"ÑтойноÑтите за Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° разпределÑне в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта не Ñа подредени"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "откъÑÑŠÑ‚ за Ñ‚ÑŠÑ€Ñенето в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е прекалено малък"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr ""
+"откъÑÑŠÑ‚ за данните за подаваниÑта в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÐµÐ½ размер"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "откъÑÑŠÑ‚ за поколениÑта в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÐµÐ½ размер"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"откъÑÑŠÑ‚ за индекÑа Ñ Ð¿Ñ€Ð¾Ð¼ÐµÌ€Ð½Ð¸ в пътищата в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е прекалено "
+"малък"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"преÑкачане на прекалено малък Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° индекÑа Ñ Ð¿Ñ€Ð¾Ð¼ÐµÌ€Ð½Ð¸ (%<PRIuMAX> < "
+"%<PRIuMAX>) в пътищата в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "отпечатъкът на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта %X не Ñъвпада Ñ %X"
@@ -15007,6 +15127,19 @@ msgstr "верÑиÑта на контролната Ñума на гра̀фа
msgid "commit-graph file is too small to hold %u chunks"
msgstr "файлът Ñ Ð³Ñ€Ð°Ì€Ñ„Ð° на подаваниÑта е твърде малък, за да Ñъдържа %u откъÑи"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"откъÑÑŠÑ‚ за разпределÑнето необходимо на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта липÑва или е "
+"повреден"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"откъÑÑŠÑ‚ за Ñ‚ÑŠÑ€Ñенето необходимо на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта липÑва или е повреден"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"откъÑÑŠÑ‚ за данните необходими на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта липÑва или е повреден"
+
msgid "commit-graph has no base graphs chunk"
msgstr "базовиÑÑ‚ Ð¾Ñ‚ÐºÑŠÑ Ð»Ð¸Ð¿Ñва в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта"
@@ -15020,6 +15153,9 @@ msgstr "веригата на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта не Ñъв
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "броÑÑ‚ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð² оÑÐ½Ð¾Ð²Ð½Ð¸Ñ Ð³Ñ€Ð°Ñ„ е прекалено голÑм: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "веригата на гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е твърде малка"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr ""
@@ -15038,12 +15174,16 @@ msgstr "подаването „%s“ не може да бъде открито
msgid "commit-graph requires overflow generation data but has none"
msgstr ""
-"графът Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта изиÑква генериране на данни за отмеÑтването, но такива "
-"липÑват"
+"графът Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта изиÑква данни за прелелите поколениÑ, но такива липÑват"
msgid "commit-graph overflow generation data is too small"
msgstr "прекалено малко данни за прелелите Ð¿Ð¾ÐºÐ¾Ð»ÐµÐ½Ð¸Ñ Ð² гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr ""
+"указателÑÑ‚ за допълнителните ребра в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта е извън Ð¿Ð¾Ð·Ð²Ð¾Ð»ÐµÐ½Ð¸Ñ "
+"диапазон"
+
msgid "Loading known commits in commit graph"
msgstr "Зареждане на познатите Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð² гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта"
@@ -16245,6 +16385,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "Ðепозната ÑтойноÑÑ‚ „%s“ за наÑтройката „diff.submodule“"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "непозната ÑтойноÑÑ‚ за наÑтройката „%s“: „%s“"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -16323,14 +16467,6 @@ msgstr "неправилен аргумент за „--color-moved“: „%s“
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "неправилен режим „%s“ за „ --color-moved-ws“"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"опциÑта приема Ñледните варианти за алгоритъм за разлики: „myers“ (по "
-"МайерÑ), „minimal“ (минимизиране на разликите), „patience“ (паÑианÑ) и "
-"„histogram“ (хиÑтограмен)"
-
#, c-format
msgid "invalid argument to %s"
msgstr "неправилен аргумент към „%s“"
@@ -16374,8 +16510,8 @@ msgstr "„--stat“ във формат за четене от програма
msgid "output only the last line of --stat"
msgstr "извеждане Ñамо на поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ñ€ÐµÐ´ на „--stat“"
-msgid "<param1,param2>..."
-msgstr "ПÐРÐМЕТЪР_1, ПÐРÐМЕТЪР_2, …"
+msgid "<param1>,<param2>..."
+msgstr "ПÐРÐМЕТЪР_1,ПÐРÐМЕТЪР_2,…"
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -16384,8 +16520,8 @@ msgstr "извеждане на разпределението на промѐ
msgid "synonym for --dirstat=cumulative"
msgstr "пÑевдоним на „--dirstat=cumulative“"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "пÑевдоним на „--dirstat=ФÐЙЛ…,ПÐРÐМЕТЪР_1,ПÐРÐМЕТЪР_2,…“"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "пÑевдоним на „--dirstat=files,ПÐРÐМЕТЪР_1,ПÐРÐМЕТЪР_2,…“"
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -16569,12 +16705,6 @@ msgstr "разлика чрез алгоритъм за подредба катÐ
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "разлика по хиÑÑ‚Ð¾Ð³Ñ€Ð°Ð¼Ð½Ð¸Ñ Ð°Ð»Ð³Ð¾Ñ€Ð¸Ñ‚ÑŠÐ¼"
-msgid "<algorithm>"
-msgstr "ÐЛГОРИТЪМ"
-
-msgid "choose a diff algorithm"
-msgstr "избор на ÐЛГОРИТЪМа за разлики"
-
msgid "<text>"
msgstr "ТЕКСТ"
@@ -18093,6 +18223,13 @@ msgid "multi-pack-index OID fanout is of the wrong size"
msgstr ""
"неправилен размер на откъÑа за разпределÑнето в индекÑа за множеÑтво пакети"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"неправилна подредба на откъÑи (OID fanout): fanout[%d] = %<PRIx32> > "
+"%<PRIx32> = fanout[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "неправилен размер на откъÑа за Ñ‚ÑŠÑ€Ñенето в индекÑа за множеÑтво пакети"
@@ -18149,6 +18286,14 @@ msgid "bad pack-int-id: %u (%u total packs)"
msgstr ""
"неправилен идентификатор на пакет (pack-int-id): %u (от общо %u пакети)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr ""
+"липÑва Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° побитова маÑка във файла за индекÑа за множеÑтво пакети"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "пакетът за битови маÑки %<PRIu32> не може да Ñе отвори"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"индекÑÑŠÑ‚ за множеÑтво пакети Ñъдържа 64-битови отмеÑтваниÑ, но размерът на "
@@ -18241,13 +18386,6 @@ msgstr "неправилна Ñума за проверка"
msgid "Looking for referenced packfiles"
msgstr "ТърÑене на указаните пакетни файлове"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"неправилна подредба на откъÑи (OID fanout): fanout[%d] = %<PRIx32> > "
-"%<PRIx32> = fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "във файла Ñ Ð¸Ð½Ð´ÐµÐºÑа за множеÑтво пакети нÑма идентификатори на обекти"
@@ -18792,6 +18930,11 @@ msgstr "задължителниÑÑ‚ обратен Ð¸Ð½Ð´ÐµÐºÑ Ð»Ð¸Ð¿Ñва в
msgid "could not open pack %s"
msgstr "пакетът „%s“ не може да Ñе отвори"
+msgid "could not determine MIDX preferred pack"
+msgstr ""
+"предпочитаниÑÑ‚ пакет за файла Ñ Ð¸Ð½Ð´ÐµÐºÑа за множеÑтво пакети не може да Ñе "
+"определи"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "предпочитаниÑÑ‚ пакет „%s“ е неправилен"
@@ -18819,6 +18962,11 @@ msgstr ""
"маÑка на подаване „%s“"
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"пакетът не може да Ñе зареди: „%s“, преизползването на пакети Ñе изключва"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "обектът „%s“ липÑва в битовата маÑка на видовете"
@@ -18917,6 +19065,9 @@ msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr ""
"неправилен размер на Ð¾Ñ‚ÐºÑŠÑ Ð·Ð° обратен Ð¸Ð½Ð´ÐµÐºÑ Ð² индекÑа за множеÑтво пакети"
+msgid "could not determine preferred pack"
+msgstr "предпочитаниÑÑ‚ пакет не може да Ñе определи"
+
msgid "cannot both write and verify reverse index"
msgstr "обратниÑÑ‚ Ð¸Ð½Ð´ÐµÐºÑ Ð½Ðµ може едновременно да Ñе запиÑва и да Ñе проверÑва"
@@ -18988,10 +19139,6 @@ 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“)"
@@ -19316,10 +19463,6 @@ msgid "unable to add '%s' to index"
msgstr "„%s“ не може да Ñе добави в индекÑа"
#, c-format
-msgid "unable to stat '%s'"
-msgstr "„stat“ не може да Ñе изпълни върху „%s“"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "„%s“ ÑъщеÑтвува и като файл, и като директориÑ"
@@ -19864,7 +20007,7 @@ msgstr "неправилно име на клон: „%s = %s“"
#, c-format
msgid "ignoring dangling symref %s"
-msgstr "игнориране на указател на обект извън клон „%s“"
+msgstr "игнориране на файл Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ» на обект извън клон „%s“"
#, c-format
msgid "log for ref %s has gap after %s"
@@ -19905,10 +20048,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "невъзможно е едновременно да Ñе обработват „%s“ и „%s“"
#, c-format
-msgid "could not remove reference %s"
-msgstr "УказателÑÑ‚ „%s“ не може да бъде изтрит"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "УказателÑÑ‚ „%s“ не може да бъде изтрит: %s"
@@ -21333,6 +21472,9 @@ msgstr "Конфликти при прилагането на автоматич
msgid "Autostash exists; creating a new stash entry."
msgstr "Вече има Ð·Ð°Ð¿Ð¸Ñ Ð·Ð° автоматично Ñкатано, затова Ñе Ñъздава нов запиÑ."
+msgid "autostash reference is a symref"
+msgstr "указателÑÑ‚ за автоматично Ñкатано e файл Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»"
+
msgid "could not detach HEAD"
msgstr "указателÑÑ‚ „HEAD“ не може да Ñе отдели"
@@ -21657,6 +21799,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "неправилно име на Ð¿ÑŠÑ€Ð²Ð¾Ð½Ð°Ñ‡Ð°Ð»Ð½Ð¸Ñ ÐºÐ»Ð¾Ð½: „%s“"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: „--initial-branch=%s“ Ñе пропуÑка"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "файлове от вид %d не Ñе поддържат"
@@ -21666,18 +21812,19 @@ msgstr "„%s“ не може да Ñе премеÑти в „%s“"
msgid "attempt to reinitialize repository with different hash"
msgstr ""
-"опит за повторно задаване на първото подаване в хранилището Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡Ð½Ð° "
-"контролна Ñума"
+"опит за занулÑване на хранилището и инициализиране Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡Ð½Ð° контролна Ñума"
+
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"опит за занулÑване на хранилището и инициализиране Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡ÐµÐ½ формат на "
+"ÑъхранÑване"
#, c-format
msgid "%s already exists"
msgstr "ДиректориÑта „%s“ вече ÑъщеÑтвува"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: „--initial-branch=%s“ Ñе пропуÑка"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr ""
"Инициализиране наново на ÑъщеÑтвуващо, Ñподелено хранилище на Git в „%s%s“\n"
@@ -21948,12 +22095,6 @@ msgstr ""
"какъв брой запиÑи в кеша на обектите-дървета да Ñе отбележат като невалидни "
"(Ñтандартно е 0)"
-msgid "unhandled options"
-msgstr "неподдържани опции"
-
-msgid "error preparing revisions"
-msgstr "грешка при подготовката на верÑии"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "подаването „%s“ не е отбелÑзано като доÑтижимо"
@@ -22110,9 +22251,6 @@ msgstr "протоколът не поддържа задаването на пÑ
msgid "invalid remote service path"
msgstr "неправилен път на отдалечената уÑлуга"
-msgid "operation not supported by protocol"
-msgstr "опциÑта не Ñе поддържа от протокола"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "неуÑпешно Ñвързване към подуÑлугата „%s“"
@@ -22248,10 +22386,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "протокол верÑÐ¸Ñ 2 вÑе още не Ñе поддържа"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "непозната ÑтойноÑÑ‚ за наÑтройката „%s“: „%s“"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "преноÑÑŠÑ‚ по „%s“ не е позволен"
@@ -22304,6 +22438,9 @@ msgstr ""
"ÑпъÑъкът Ñ Ð°Ð´Ñ€ÐµÑи на пратки обÑвени за налични от Ñървъра не може да Ñе "
"получи "
+msgid "operation not supported by protocol"
+msgstr "опциÑта не Ñе поддържа от протокола"
+
msgid "too-short tree object"
msgstr "прекалено кратък обект-дърво"
@@ -23147,6 +23284,10 @@ msgstr "оÑвен това в индекÑа има неподадени про
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "не може да извършите „%s“, защото в индекÑа има неподадени промѐни."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "непознат Ñтил „%s“ за „%s“"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
diff --git a/po/ca.po b/po/ca.po
index d1a4e56c3a..bcb4da80fb 100644
--- a/po/ca.po
+++ b/po/ca.po
@@ -1,7 +1,7 @@
# Catalan translations for Git.
# This file is distributed under the same license as the Git package.
# Alex Henrie <alexhenrie24@gmail.com>, 2014-2016.
-# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2023
+# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2016-2024
#
# Terminologia
#
@@ -56,6 +56,7 @@
# Anglès | Català
# -----------------+---------------------------------
# blame | «blame»
+# fanout | «fanout»
# HEAD | HEAD (f, la branca actual) - (no s'apostrofa)
# cherry pick | «cherry pick»
# promisor | «promisor»
@@ -70,14 +71,14 @@
#
# Criteris
# - Mantingueu en anglès les referències a seccions de la documentació, ja que no està traduïda.
-# - Usem la convenció valenciana per a «per / per a», que inclou l'ús de «per a» davant d'infintiu
+# - Usem la convenció valenciana per a «per / per a», que inclou l'ús de «per a» davant d'infinitiu
#
msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-13 18:55+0100\n"
-"PO-Revision-Date: 2023-11-13 19:00-0600\n"
+"POT-Creation-Date: 2024-02-16 07:14+0100\n"
+"PO-Revision-Date: 2024-02-16 07:16+0100\n"
"Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
"Language-Team: Catalan\n"
"Language: ca\n"
@@ -592,6 +593,7 @@ msgstr "«git apply --cached» ha fallat"
#. Consider translating (saying "no" discards!) as
#. (saying "n" for "no" discards!) if the translation
#. of the word "no" does not start with n.
+#.
msgid ""
"Your edited hunk does not apply. Edit again (saying \"no\" discards!) [y/n]? "
msgstr ""
@@ -1503,6 +1505,10 @@ msgid "Unexpected option --output"
msgstr "Opció inesperada --output"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "paràmetre extra de la línia d'ordres «%s»"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Format d'arxiu desconegut «%s»"
@@ -1548,6 +1554,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "--attr-source incorrecte o GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "no s'ha pogut fer «stat» a «%s»"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "no s'ha pogut llegir %s"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Comentari amb cometes errònies en el fitxer «%s»: %s"
@@ -1643,6 +1657,7 @@ msgstr[1] "(aproximadament %d passos)"
#. TRANSLATORS: the last %s will be replaced with "(roughly %d
#. steps)" translation.
+#.
#, c-format
msgid "Bisecting: %d revision left to test after this %s\n"
msgid_plural "Bisecting: %d revisions left to test after this %s\n"
@@ -1728,17 +1743,20 @@ msgstr "no s'està seguint: informació ambigua per a la referència «%s»"
#. TRANSLATORS: This is a line listing a remote with duplicate
#. refspecs in the advice message below. For RTL languages you'll
#. probably want to swap the "%s" and leading " " space around.
+#.
#. #-#-#-#-# object-name.c.po #-#-#-#-#
#. TRANSLATORS: This is line item of ambiguous object output
#. from describe_ambiguous_object() above. For RTL languages
#. you'll probably want to swap the "%s" and leading " " space
#. around.
+#.
#, c-format
msgid " %s\n"
msgstr " %s\n"
#. TRANSLATORS: The second argument is a \n-delimited list of
#. duplicate refspecs, composed above.
+#.
#, c-format
msgid ""
"There are multiple remotes whose fetch refspecs map to the remote\n"
@@ -2143,6 +2161,7 @@ msgstr "El cos de la comissió és:"
#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
#. in your translation. The program will only accept English
#. input at this point.
+#.
#, c-format
msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "
msgstr ""
@@ -2480,6 +2499,7 @@ msgstr "bisecant amb només una comissió %s"
#. TRANSLATORS: Make sure to include [Y] and [n] in your
#. translation. The program will only accept English input
#. at this point.
+#.
msgid "Are you sure [Y/n]? "
msgstr "N'esteu segur [Y/n]? "
@@ -2557,6 +2577,7 @@ msgstr "Cal començar per «git bisect start»\n"
#. TRANSLATORS: Make sure to include [Y] and [n] in your
#. translation. The program will only accept English input
#. at this point.
+#.
msgid "Do you want me to do it for you [Y/n]? "
msgstr "Voleu que ho faci per vostè [Y/n]? "
@@ -2777,6 +2798,7 @@ msgstr ""
#. among various forms of relative timestamps, but
#. your language may need more or fewer display
#. columns.
+#.
msgid "4 years, 11 months ago"
msgstr "fa 4 anys i 11 mesos"
@@ -2838,12 +2860,12 @@ msgid "couldn't look up commit object for '%s'"
msgstr "no s'ha pogut cercar l'objecte de comissió per a «%s»"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"la branca «%s» no està completament fusionada.\n"
-"Si esteu segur que voleu suprimir-la, executeu «git branch -D %s»"
+msgid "the branch '%s' is not fully merged"
+msgstr "la branca «%s» no està completament fusionada"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Si esteu segur que voleu suprimir-la, executeu «git branch -D %s»"
msgid "update of config-file failed"
msgstr "ha fallat l'actualització del fitxer de configuració"
@@ -3884,8 +3906,8 @@ msgstr "agafa a la força (descarta qualsevol modificació local)"
msgid "new-branch"
msgstr "branca-nova"
-msgid "new unparented branch"
-msgstr "branca òrfena nova"
+msgid "new unborn branch"
+msgstr "branca no nascuda nova"
msgid "update ignored files (default)"
msgstr "actualitza els fitxers ignorats (per defecte)"
@@ -4137,9 +4159,6 @@ msgstr ""
"clean.requireForce és per defecte cert i ni -i, -n ni -f s'han indicat; "
"refusant netejar"
-msgid "-x and -X cannot be used together"
-msgstr "-x i -X no es poden usar junts"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<opcions>] [--] <repositori> [<directori>]"
@@ -4228,6 +4247,9 @@ msgstr "directori de git"
msgid "separate git dir from working tree"
msgstr "separa el directori de git de l'arbre de treball"
+msgid "specify the reference format to use"
+msgstr "especifiqueu el format de referència a usar"
+
msgid "key=value"
msgstr "clau=valor"
@@ -4347,11 +4369,9 @@ msgstr "Hi ha massa arguments."
msgid "You must specify a repository to clone."
msgstr "Heu d'especificar un repositori per a clonar."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri és incompatible amb --depth, --shallow-since i --shallow-exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "el format d'emmagatzematge de referència «%s» és desconegut"
#, c-format
msgid "repository '%s' does not exist"
@@ -4481,14 +4501,14 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <dir>] [--append]\n"
" [--split[=<strategy>]] [--reachable | --stdin-packs | "
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgid "dir"
msgstr "directori"
@@ -5007,6 +5027,7 @@ msgstr "reusa el missatge de la comissió especificada"
#. TRANSLATORS: Leave "[(amend|reword):]" as-is,
#. and only translate <commit>.
+#.
msgid "[(amend|reword):]commit"
msgstr "[(amend|reword):]commit"
@@ -5544,7 +5565,7 @@ msgstr "No s'ha trobat cap nom, no es pot descriure res."
#, c-format
msgid "option '%s' and commit-ishes cannot be used together"
-msgstr "les opcions «%s» i de comissió no es poden usar juntes"
+msgstr "opció «%s» i les de comissió no es poden usar juntes"
msgid ""
"git diagnose [(-o | --output-directory) <path>] [(-s | --suffix) <format>]\n"
@@ -6820,6 +6841,7 @@ msgstr "s'ha especificat un nombre de fils no vàlid (%d) per a %s"
#. TRANSLATORS: %s is the configuration
#. variable for tweaking threads, currently
#. grep.threads
+#.
#, c-format
msgid "no threads support, ignoring %s"
msgstr "no s'admeten fils, s'ignorarà %s"
@@ -6829,6 +6851,10 @@ msgid "unable to read tree (%s)"
msgstr "no s'ha pogut llegir l'arbre (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "no s'ha pogut llegir l'arbre %s"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "no es pot fer grep des d'un objecte de tipus %s"
@@ -7247,10 +7273,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "S'HA TROBAT UNA COL·LISIÓ SHA1 AMB %s !"
#, c-format
-msgid "unable to read %s"
-msgstr "no s'ha pogut llegir %s"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "no es pot llegir la informació d'objecte existent %s"
@@ -7390,11 +7412,13 @@ msgstr "error fsck als objectes del paquet"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
@@ -8110,6 +8134,12 @@ msgstr ""
"git merge-file [<opcions>] [-L <nom1> [-L <original> [-L <nom2>]]] <fitxer1> "
"<fitxer-original> <fitxer2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
+
msgid "send results to standard output"
msgstr "envia els resultats a la sortida estàndard"
@@ -8131,6 +8161,12 @@ msgstr "en conflictes, usa la seva versió"
msgid "for conflicts, use a union version"
msgstr "en conflictes, usa una versió d'unió"
+msgid "<algorithm>"
+msgstr "<algorisme>"
+
+msgid "choose a diff algorithm"
+msgstr "trieu un algorisme per al diff"
+
msgid "for conflicts, use this marker size"
msgstr "en conflictes, usa aquesta mida de marcador"
@@ -8221,9 +8257,6 @@ msgstr "--trivial-merge és incompatible amb totes les altres opcions"
msgid "unknown strategy option: -X%s"
msgstr "opció d'estratègia desconeguda: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base és incompatible amb --stdin"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "línia d'entrada mal formada: «%s»."
@@ -8856,6 +8889,7 @@ msgstr "s'ha produït un error en copiar les notes de «%s» a «%s»"
#. TRANSLATORS: the first %s will be replaced by a git
#. notes command: 'add', 'merge', 'remove', etc.
+#.
#, c-format
msgid "refusing to %s notes in %s (outside of refs/notes/)"
msgstr "s'està refusant %s les notes en %s (fora de refs/notes/)"
@@ -9166,6 +9200,10 @@ msgid "inconsistency with delta count"
msgstr "inconsistència amb el comptador de diferències"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "valor pack.allowPackReuse value no vàlid: «%s»"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9430,10 +9468,10 @@ msgstr "S'estan enumerant els objectes"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Total %<PRIu32> (%<PRIu32> diferències), reusats %<PRIu32> (%<PRIu32> "
-"diferències), paquets reusats %<PRIu32>"
+"diferències), paquets reusats %<PRIu32> (de %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10398,13 +10436,6 @@ msgid "switch `C' expects a numerical value"
msgstr "«switch» «c» espera un valor numèric"
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"les opcions «apply» són incompatibles amb rebase.autoSquash. Considereu "
-"afegir-hi --no-autosquash"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -10922,6 +10953,7 @@ msgstr "(sense URL)"
#. TRANSLATORS: the colon ':' should align
#. with the one in " Fetch URL: %s"
#. translation.
+#.
#, c-format
msgid " Push URL: %s"
msgstr " URL de pujada: %s"
@@ -11414,6 +11446,77 @@ msgstr "--convert-graft-file arguments"
msgid "only one pattern can be given with -l"
msgstr "només es pot especificar un patró amb -l"
+msgid "need some commits to replay"
+msgstr "calen algunes comissions per tornar a reproduir"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto i --advance són incompatibles"
+
+msgid "all positive revisions given must be references"
+msgstr "totes les revisions positives que s'han donat han de ser referències"
+
+msgid "argument to --advance must be a reference"
+msgstr "l'argument per a --advance ha de ser una referència"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"no es pot avançar l'objectiu amb múltiples fonts perquè l'ordenació no "
+"estaria definida correctament"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"no es pot determinar implícitament si aquesta és una operació --advance o --"
+"onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"no es pot avançar l'objectiu amb múltiples branques d'origen perquè "
+"l'ordenació no estaria definida correctament"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "no es pot determinar implícitament la base correcta per a --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+
+msgid "make replay advance given branch"
+msgstr "fes avançar la repetició de la branca donada"
+
+msgid "replay onto given commit"
+msgstr "torna a reproduir a la comissió donada"
+
+msgid "advance all branches contained in revision-range"
+msgstr "avança totes les branques contingudes a l'interval de revisions"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "l'opció --onto o --advance és obligatòria"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"algunes opcions de referència se sobreescriuran de forma forçada com a «%s» "
+"bits a «struct rev_info»"
+
+msgid "error preparing revisions"
+msgstr "s'ha produït un error en preparar les revisions"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "encara no s'admet la reproducció cap avall en una comissió arrel"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "encara no s'admet la repetició de les comissió de fusió"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11622,15 +11725,6 @@ msgstr "--prefix requereix un argument"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "mode desconegut per a --abbrev-ref: %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden no es pot utilitzar juntament amb --remotes"
-
msgid "this operation must be run in a work tree"
msgstr "aquesta operació s'ha d'executar en un arbre de treball"
@@ -12040,10 +12134,6 @@ msgstr "no imprimeixis els resultats a stdout (útil amb --verify)"
msgid "show refs from stdin that aren't in local repository"
msgstr "mostra les referències de stdin que no siguin en el repositori local"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "només es poden donar les opcions «%s», «%s», o «%s»"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13519,28 +13609,28 @@ msgstr "No hi ha cap branca d'origen possible, inferint «--orphan»"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"Si voleu crear un arbre de treball que contingui una branca orfe nova\n"
-"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"Si voleu crear un arbre de treball que contingui una branca no nascuda\n"
+"nova (branca sense comissions) per a aquest repositori, podeu fer-ho\n"
"utilitzant l'argument --orphan:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"Si voleu crear un arbre de treball que contingui una branca orfe nova\n"
-"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"Si voleu crear un arbre de treball que contingui una branca no nascuda\n"
+"nova (branca sense comissions) per a aquest repositori, podeu fer-ho\n"
"utilitzant l'argument --orphan:\n"
"\n"
" git worktree add --orphan %s\n"
@@ -13603,6 +13693,10 @@ msgid "initializing"
msgstr "s'està inicialitzant"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "no s'ha pogut trobar l'arbre de treball creat «%s»"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "S'està preparant l'arbre de treball (branca nova «%s»)"
@@ -13641,10 +13735,6 @@ msgstr ""
"No hi ha referències locals o remotes malgrat hi existeix almenys un\n"
"remot, aturada; useu «add -f» per a anul·lar o obtenir primer un remot"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "les opcions «%s» i «%s» no es poden usar juntes"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "agafa <branca> encara que sigui agafada en altre arbre de treball"
@@ -13654,8 +13744,8 @@ msgstr "crea una branca nova"
msgid "create or reset a branch"
msgstr "crea o restableix una branca"
-msgid "create unborn/orphaned branch"
-msgstr "crea una branca no nascuda/òrfena"
+msgid "create unborn branch"
+msgstr "crea una branca no nascuda"
msgid "populate the new working tree"
msgstr "emplena l'arbre de treball nou"
@@ -13679,11 +13769,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "les opcions «%s», «%s», i «%s» no es poden usar juntes"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "les opcions «%s» i «%s» no es poden usar juntes"
-
-msgid "<commit-ish>"
-msgstr "<commit-ish>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "opció «%s» i les de comissió no es poden usar juntes"
msgid "added with --lock"
msgstr "afegit amb --lock"
@@ -14313,6 +14400,11 @@ msgstr "Empaqueta els objectes desempaquetats en un repositori"
msgid "Create, list, delete refs to replace objects"
msgstr "Crea, llista i esborra referències per a substituir objectes"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTAL: torna a reproduir comissions sobre una nova base, també "
+"funciona amb repositoris nus"
+
msgid "Generates a summary of pending changes"
msgstr "Genera un resum dels canvis pendents"
@@ -14554,6 +14646,35 @@ msgstr "Una eina per a gestionar dipòsits Git grans"
msgid "commit-graph file is too small"
msgstr "el fitxer del graf de comissions és massa petit"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr ""
+"el fragment de «fanout» de l'oid del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph fanout values out of order"
+msgstr "valors de graf de comissions de «fanout» estan fora d'ordre"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "el fragment de cerca OID és de mida incorrecta"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "el fragment de dades del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr ""
+"el fragment de les generacions del graf de comissions és de mida incorrecta"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"el fragment d'índex del canvi del camí del graf de comissions és massa petit"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"s'ignorarà un fragment massa petit de camí canviat (%<PRIuMAX> < %<PRIuMAX>) "
+"al fitxer del graf de comissions"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr ""
@@ -14573,6 +14694,20 @@ msgid "commit-graph file is too small to hold %u chunks"
msgstr ""
"el fitxer del graf de comissions és massa petit per a guardar %u fragments"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment del «fanout» OID requerit al graf de "
+"comissions"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"manca o està malmès el fragment de cerca d'OID requerit al graf de comissions"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"manca o està corromput el fragment de dades de publicació requerit al graf "
+"de comissions"
+
msgid "commit-graph has no base graphs chunk"
msgstr "el fragment del graf de comissions no té grafs de base"
@@ -14586,6 +14721,9 @@ msgstr "la cadena del graf de comissions no coincideix"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "el nombre de comissions en el graf base és massa alt: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "el fitxer de cadena del graf de comissions és massa petit"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr ""
@@ -14613,6 +14751,9 @@ msgstr ""
"les dades de generació de desbordament del graf de comissions són massa "
"petites"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "punter de vores extra del graf de comissió està fora dels límits"
+
msgid "Loading known commits in commit graph"
msgstr "S'estan carregant comissions conegudes al graf de comissions"
@@ -15786,6 +15927,10 @@ msgstr ""
"Valor desconegut de la variable de configuració de «diff.submodule»: «%s»"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "valor desconegut per al config «%s»': %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -15862,12 +16007,6 @@ msgstr "argument --color-moved incorrecte: %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "mode «%s» no vàlid en --color-moved-ws"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"l'opció diff-algorithm accepta «myers», «minimal», «patience» i «histogram»"
-
#, c-format
msgid "invalid argument to %s"
msgstr "argument no vàlid a %s"
@@ -15912,8 +16051,8 @@ msgstr "llegible per una màquina --stat"
msgid "output only the last line of --stat"
msgstr "mostra només l'última línia de --stat"
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -15924,8 +16063,8 @@ msgstr ""
msgid "synonym for --dirstat=cumulative"
msgstr "sinònim de --dirstat=cumulative"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "sinònim de --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "sinònim de --dirstat=files,<param1>,<param2>..."
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -16108,12 +16247,6 @@ msgstr "genera diff usant l'algorisme «patience diff»"
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "genera diff usant l'algorisme «histogram diff»"
-msgid "<algorithm>"
-msgstr "<algorisme>"
-
-msgid "choose a diff algorithm"
-msgstr "trieu un algorisme per al diff"
-
msgid "<text>"
msgstr "<text>"
@@ -16499,12 +16632,14 @@ msgstr "s'ha produït un error en processar els acks: %d"
#. TRANSLATORS: The parameter will be 'ready', a protocol
#. keyword.
+#.
#, c-format
msgid "expected packfile to be sent after '%s'"
msgstr "s'esperava que el fitxer de paquet s'enviés després de «%s»"
#. TRANSLATORS: The parameter will be 'ready', a protocol
#. keyword.
+#.
#, c-format
msgid "expected no other sections to be sent after no '%s'"
msgstr "no s'esperava que cap altra secció s'enviés després de «%s»"
@@ -17322,6 +17457,7 @@ msgstr ""
#. name, and the second argument is the abbreviated id of the
#. commit that needs to be merged. For example:
#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#.
#, c-format
msgid ""
" - go to submodule (%s), and either merge commit %s\n"
@@ -17356,6 +17492,7 @@ msgstr ""
#. TRANSLATORS: The %s arguments are: 1) tree hash of a merge
#. base, and 2-3) the trees for the two trees we're merging.
+#.
#, c-format
msgid "collecting merge info failed for trees %s, %s, %s"
msgstr ""
@@ -17616,6 +17753,12 @@ msgstr "s'ha produït un error en llegir la memòria cau"
msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "l'OID «fanout» de l'índex multipaquet és d'una mida incorrecta"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid «fanout» desordenat: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "El fragment de cerca OID índex multipaquet és de mida incorrecta"
@@ -17676,6 +17819,13 @@ msgstr ""
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "pack-int-id: %u incorrecte (%u paquets en total)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX no conté el fragment BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "no s'ha pogut carregar el paquet amb bits %<PRIu32>"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"l'índex multipaquet emmagatzema un desplaçament de 64 bits, però off_t és "
@@ -17764,11 +17914,6 @@ msgstr "suma de verificació incorrecta"
msgid "Looking for referenced packfiles"
msgstr "S'estan cercant fitxers empaquetats referenciats"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "oid fanout desordenat: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "el midx no conté cap oid"
@@ -17851,6 +17996,7 @@ msgstr "S'està refusant reescriure les notes en %s (fora de refs/notes/)"
#. TRANSLATORS: The first %s is the name of
#. the environment variable, the second %s is
#. its value.
+#.
#, c-format
msgid "Bad %s value: '%s'"
msgstr "Valor erroni de %s: «%s»"
@@ -18069,6 +18215,7 @@ msgstr "no s'han pogut desempaquetar els continguts de %s"
#. TRANSLATORS: This is a line of ambiguous object
#. output shown when we cannot look up or parse the
#. object in question. E.g. "deadbeef [bad object]".
+#.
#, c-format
msgid "%s [bad object]"
msgstr "%s [objecte incorrecte]"
@@ -18077,6 +18224,7 @@ msgstr "%s [objecte incorrecte]"
#. object output. E.g.:
#. *
#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#.
#, c-format
msgid "%s commit %s - %s"
msgstr "%s comissió %s - %s"
@@ -18091,6 +18239,7 @@ msgstr "%s comissió %s - %s"
#. *
#. The third argument is the "tag" string
#. from object.c.
+#.
#, c-format
msgid "%s tag %s - %s"
msgstr "%s etiqueta %s - %s"
@@ -18100,18 +18249,21 @@ msgstr "%s etiqueta %s - %s"
#. the tag itself. E.g.:
#. *
#. "deadbeef [bad tag, could not parse it]"
+#.
#, c-format
msgid "%s [bad tag, could not parse it]"
msgstr "%s [etiqueta malmesa, no s'ha pogut analitzar]"
#. TRANSLATORS: This is a line of ambiguous <type>
#. object output. E.g. "deadbeef tree".
+#.
#, c-format
msgid "%s tree"
msgstr "arbre %s"
#. TRANSLATORS: This is a line of ambiguous <type>
#. object output. E.g. "deadbeef blob".
+#.
#, c-format
msgid "%s blob"
msgstr "blob %s"
@@ -18123,6 +18275,7 @@ msgstr "l'id d'objecte curt %s és ambigu"
#. TRANSLATORS: The argument is the list of ambiguous
#. objects composed in show_ambiguous_object(). See
#. its "TRANSLATORS" comments for details.
+#.
#, c-format
msgid ""
"The candidates are:\n"
@@ -18292,6 +18445,9 @@ msgstr "falta l'índex invers necessari al mapa de bits multipaquet"
msgid "could not open pack %s"
msgstr "no s'ha pogut obrir el paquet %s"
+msgid "could not determine MIDX preferred pack"
+msgstr "no s'ha pogut determinar el paquet preferit MIDX"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "el paquet preferit (%s) no és vàlid"
@@ -18318,6 +18474,12 @@ msgstr ""
"comissió «%s»"
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"no s'ha pogut carregar el paquet: «%s», s'està inhabilitant lareutilització "
+"de paquets"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "no s'ha trobat l'objecte «%s» als tipus de mapes de bits"
@@ -18410,6 +18572,9 @@ msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr ""
"el fragment de l'index invers de l'índex multipaquet és de mida incorrecta"
+msgid "could not determine preferred pack"
+msgstr "no s'ha pogut determinar el paquet preferit"
+
msgid "cannot both write and verify reverse index"
msgstr "no es pot escriure i verificar l'índex invers"
@@ -18475,10 +18640,6 @@ msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
msgstr "%s espera un valor enter no negatiu amb un sufix opcional k/m/g"
#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s és incompatible amb %s"
-
-#, c-format
msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
msgstr "opció ambigua: %s (pot ser --%s%s o --%s%s)"
@@ -18514,6 +18675,7 @@ msgstr "ús: %s"
#. TRANSLATORS: the colon here should align with the
#. one in "usage: %s" translation.
+#.
#, c-format
msgid " or: %s"
msgstr " o: %s"
@@ -18536,6 +18698,7 @@ msgstr " o: %s"
#. function. The "%s" is a line in the (hopefully already
#. translated) N_() usage string, which contained embedded
#. newlines before we split it up.
+#.
#, c-format
msgid "%*s%s"
msgstr "%*s%s"
@@ -18799,10 +18962,6 @@ msgid "unable to add '%s' to index"
msgstr "no s'ha pogut afegir «%s» a l'índex"
#, c-format
-msgid "unable to stat '%s'"
-msgstr "no s'ha pogut fer «stat» a «%s»"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "«%s» apareix com a fitxer i com a directori"
@@ -19384,10 +19543,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "no es poden processar «%s» i «%s» a la vegada"
#, c-format
-msgid "could not remove reference %s"
-msgstr "no s'ha pogut eliminar la referència %s"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "no s'ha pogut suprimir la referència %s: %s"
@@ -19570,6 +19725,7 @@ msgstr ""
#. TRANSLATORS: "matches '%s'%" is the <dst> part of "git push
#. <remote> <src>:<dst>" push, and "being pushed ('%s')" is
#. the <src>.
+#.
#, c-format
msgid ""
"The destination you provided is not a full refname (i.e.,\n"
@@ -19610,7 +19766,7 @@ msgid ""
"'%s:refs/tags/%s'?"
msgstr ""
"La part <src> de l'especificació de la referència és un objecte d'etiqueta.\n"
-"Voleu crear una etiqueta pujant-la a «%srefs/tags/%s»?"
+"Voleu crear una etiqueta pujant-la a «%s:refs/tags/%s»?"
#, c-format
msgid ""
@@ -19619,7 +19775,7 @@ msgid ""
"'%s:refs/tags/%s'?"
msgstr ""
"La part <src> de l'especificació de la referència és un objecte d'arbre.\n"
-"Voleu crear una etiqueta pujant-la a «%srefs/tags/%s»?"
+"Voleu crear una etiqueta pujant-la a «%s:refs/tags/%s»?"
#, c-format
msgid ""
@@ -20203,6 +20359,7 @@ msgstr "cometeu els vostres canvis o feu un «stash» per a procedir."
#. TRANSLATORS: %s will be "revert", "cherry-pick" or
#. "rebase".
+#.
#, c-format
msgid "%s: Unable to write new index file"
msgstr "%s: No s'ha pogut escriure un fitxer d'índex nou"
@@ -20762,6 +20919,9 @@ msgid "Autostash exists; creating a new stash entry."
msgstr ""
"El «stash» automàtic ja existeix; s'està creant una entrada «stash» nova."
+msgid "autostash reference is a symref"
+msgstr "la referència d'autostash és un symref"
+
msgid "could not detach HEAD"
msgstr "no s'ha pogut separar HEAD"
@@ -21005,7 +21165,8 @@ msgid ""
"not a git repository (or any parent up to mount point %s)\n"
"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."
msgstr ""
-"no és un repositori de git (ni cap pare fins al punt de muntatge %s)\n"
+"no és un repositori de git (ni existeix cap pare fins al punt de muntatge "
+"%s)\n"
"S'atura a la frontera de sistema de fitxers (GIT_DISCOVERY_ACROSS_FILESYSTEM "
"no està establert)."
@@ -21078,6 +21239,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "nom de branca inicial no vàlid: «%s»"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "no s'ha pogut gestionar el tipus de fitxer %d"
@@ -21088,15 +21253,17 @@ msgstr "no s'ha pogut moure %s a %s"
msgid "attempt to reinitialize repository with different hash"
msgstr "s'ha intentat reinicialitzar el repositori amb un resum diferent"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"s'ha intentat reactivar el repositori amb un format d'emmagatzematge de "
+"referència diferent"
+
#, c-format
msgid "%s already exists"
msgstr "%s ja existeix"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "reinicialització: s'ha ignorat --initial-branch=%s"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "S'ha reinicialitzat el repositori compartit existent del Git en %s%s\n"
@@ -21365,12 +21532,6 @@ msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr ""
"nombre d'entrades a l'arbre de la memòria cau a invalidar (per defecte 0)"
-msgid "unhandled options"
-msgstr "opcions no gestionades"
-
-msgid "error preparing revisions"
-msgstr "s'ha produït un error en preparar les revisions"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "la comissió %s no està marcada com abastable"
@@ -21523,9 +21684,6 @@ msgstr "el protocol no permet establir el camí del servei remot"
msgid "invalid remote service path"
msgstr "el camí del servei remot no és vàlid"
-msgid "operation not supported by protocol"
-msgstr "opció no admesa pel protocol"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "no es pot connectar al subservei %s"
@@ -21658,10 +21816,6 @@ msgstr ""
"encara no s'ha implementat la compatibilitat amb la versió v2 del protocol"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "valor desconegut per al config «%s»': %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "no es permet el transport «%s»"
@@ -21714,6 +21868,9 @@ msgid "could not retrieve server-advertised bundle-uri list"
msgstr ""
"no s'ha pogut recuperar la llista de paquets d'URI anunciats pel servidor"
+msgid "operation not supported by protocol"
+msgstr "opció no admesa pel protocol"
+
msgid "too-short tree object"
msgstr "objecte d'arbre massa curt"
@@ -22555,6 +22712,10 @@ msgstr "addicionalment, el vostre índex conté canvis sense cometre."
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "no es pot %s: El vostre índex conté canvis sense cometre."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "estil desconegut «%s» donat per a «%s»"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
@@ -22950,64 +23111,3 @@ msgstr "S'està ometent %s amb el sufix de còpia de seguretat «%s».\n"
#, perl-format
msgid "Do you really want to send %s? [y|N]: "
msgstr "Esteu segur que voleu enviar %s? [y|N]: "
-
-#, c-format
-#~ msgid "options '%s=%s' and '%s=%s' cannot be used together"
-#~ msgstr "les opcions «%s=%s» i «%s=%s» no es poden usar juntes"
-
-#, c-format
-#~ msgid "%s : incompatible with something else"
-#~ msgstr "%s: és incompatible amb alguna altra cosa"
-
-#~ msgid "Could not write patch"
-#~ msgstr "No s'ha pogut escriure el pedaç"
-
-#, c-format
-#~ msgid "Could not stat '%s'"
-#~ msgstr "No s'ha pogut fer stat a «%s»"
-
-#, c-format
-#~ msgid "Cannot delete branch '%s' checked out at '%s'"
-#~ msgstr "No es pot suprimir la branca «%s» agafada a «%s»"
-
-#~ msgid "unable to write new_index file"
-#~ msgstr "no s'ha pogut escriure el fitxer new_index"
-
-#~ msgid "do not apply config rules"
-#~ msgstr "no apliquis les regles de configuració"
-
-#~ msgid "join whitespace-continued values"
-#~ msgstr "uneix els valors continus amb espais en blanc"
-
-#~ msgid "set parsing options"
-#~ msgstr "estableix les opcions d'anàlisi"
-
-#~ msgid "cannot move directory over file"
-#~ msgstr "no es pot moure un directori sobre un fitxer"
-
-#~ msgid "cannot use --filter without --stdout"
-#~ msgstr "no es pot utilitzar --filter sense --stdout"
-
-#~ msgid "cannot use --max-pack-size with --cruft"
-#~ msgstr "no es pot usar --max-pack-size amb --cruft"
-
-#~ msgid "--strategy requires --merge or --interactive"
-#~ msgstr "--strategy requereix --merge o --interactive"
-
-#, c-format
-#~ msgid ""
-#~ "commit-graph has generation number zero for commit %s, but non-zero "
-#~ "elsewhere"
-#~ msgstr ""
-#~ "el graf de comissions té nombre de generació zero per a la comissió %s, "
-#~ "però té no zero en altres llocs"
-
-#~ msgid "--merge-base only works with commits"
-#~ msgstr "--merge-base només funciona amb comissions"
-
-#~ msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-#~ msgstr "scalar clone [<opcions>] [--] <repositori> [<dir>]"
-
-#, c-format
-#~ msgid "could not rename '%s' to '%s'"
-#~ msgstr "no s'ha pogut canviar el nom «%s» a «%s»"
diff --git a/po/de.po b/po/de.po
index 95a185afd2..37d6c80998 100644
--- a/po/de.po
+++ b/po/de.po
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-09 11:29+0100\n"
-"PO-Revision-Date: 2023-11-10 14:28+0100\n"
+"POT-Creation-Date: 2024-02-17 18:07+0100\n"
+"PO-Revision-Date: 2024-02-17 18:14+0100\n"
"Last-Translator: Ralf Thielow <ralf.thielow@gmail.com>\n"
"Language-Team: German\n"
"Language: de\n"
@@ -1474,6 +1474,10 @@ msgid "Unexpected option --output"
msgstr "Unerwartete Option --output"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "zusätzlicher Befehlszeilenparameter '%s'"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Unbekanntes Archivformat '%s'"
@@ -1519,6 +1523,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "ungültiges --attr-source oder GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "konnte '%s' nicht lesen"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "kann %s nicht lesen"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Ungültiger Inhalt bzgl. Anführungszeichen in Datei '%s': %s"
@@ -1698,12 +1710,10 @@ msgstr ""
msgid "not tracking: ambiguous information for ref '%s'"
msgstr "kein Tracking: mehrdeutige Informationen für Referenz '%s'"
-#. #-#-#-#-# branch.c.po #-#-#-#-#
#. TRANSLATORS: This is a line listing a remote with duplicate
#. refspecs in the advice message below. For RTL languages you'll
#. probably want to swap the "%s" and leading " " space around.
#.
-#. #-#-#-#-# object-name.c.po #-#-#-#-#
#. TRANSLATORS: This is line item of ambiguous object output
#. from describe_ambiguous_object() above. For RTL languages
#. you'll probably want to swap the "%s" and leading " " space
@@ -2830,13 +2840,14 @@ msgid "couldn't look up commit object for '%s'"
msgstr "konnte Commit-Objekt für '%s' nicht nachschlagen"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "der Branch '%s' ist nicht vollständig zusammengeführt"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
msgstr ""
-"Der Branch '%s' ist nicht vollständig zusammengeführt.\n"
-"Wenn Sie sicher sind diesen Branch zu entfernen, führen Sie 'git branch -D "
-"%s' aus."
+"Wenn Sie sicher sind, dass Sie den Branch löschen wollen, führen Sie 'git "
+"branch -D %s' aus."
msgid "update of config-file failed"
msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
@@ -3905,8 +3916,8 @@ msgstr "Auschecken erzwingen (verwirft lokale Änderungen)"
msgid "new-branch"
msgstr "neuer Branch"
-msgid "new unparented branch"
-msgstr "neuer Branch ohne Eltern-Commit"
+msgid "new unborn branch"
+msgstr "neuer ungeborener Branch"
msgid "update ignored files (default)"
msgstr "ignorierte Dateien aktualisieren (Standard)"
@@ -4160,9 +4171,6 @@ msgstr ""
"clean.requireForce standardmäßig auf \"true\" gesetzt und weder -i, -n noch -"
"f gegeben; \"clean\" verweigert"
-msgid "-x and -X cannot be used together"
-msgstr "-x und -X können nicht gemeinsam verwendet werden"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
@@ -4256,6 +4264,9 @@ msgstr ".git-Verzeichnis"
msgid "separate git dir from working tree"
msgstr "Git-Verzeichnis vom Arbeitsverzeichnis separieren"
+msgid "specify the reference format to use"
+msgstr "das zu verwendende Referenzformat angeben"
+
msgid "key=value"
msgstr "Schlüssel=Wert"
@@ -4379,12 +4390,9 @@ msgstr "Zu viele Argumente."
msgid "You must specify a repository to clone."
msgstr "Sie müssen ein Repository zum Klonen angeben."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri ist inkompatibel mit --depth, --shallow-since und --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "unbekanntes Speicherformat für Referenzen '%s'"
#, c-format
msgid "repository '%s' does not exist"
@@ -4526,7 +4534,7 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <Verzeichnis>] [--append]\n"
" [--split[=<Strategie>]] [--reachable | --stdin-packs "
@@ -6892,7 +6900,6 @@ msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
msgid "invalid number of threads specified (%d) for %s"
msgstr "ungültige Anzahl von Threads (%d) für %s angegeben"
-#. #-#-#-#-# grep.c.po #-#-#-#-#
#. TRANSLATORS: %s is the configuration
#. variable for tweaking threads, currently
#. grep.threads
@@ -6906,6 +6913,10 @@ msgid "unable to read tree (%s)"
msgstr "konnte \"Tree\"-Objekt (%s) nicht lesen"
#, c-format
+msgid "unable to read tree %s"
+msgstr "konnte \"Tree\"-Objekt (%s) nicht lesen"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "kann \"grep\" nicht mit Objekten des Typs %s durchführen"
@@ -7327,10 +7338,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "SHA1 KOLLISION MIT %s GEFUNDEN !"
#, c-format
-msgid "unable to read %s"
-msgstr "kann %s nicht lesen"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "Kann existierende Informationen zu Objekt %s nicht lesen."
@@ -7470,11 +7477,13 @@ msgstr "fsck Fehler beim Packen von Objekten"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<Vorlagenverzeichnis>]\n"
" [--separate-git-dir <Git-Verzeichnis>] [--object-format=<Format>]\n"
+" [--ref-format=<Format>]\n"
" [-b <Branchname> | --initial-branch=<Branchname>]\n"
" [--shared[=<Berechtigungen>]] [<Verzeichnis>]"
@@ -8195,6 +8204,13 @@ msgstr ""
"git merge-file [<Optionen>] [-L <Name1> [-L <orig> [-L <Name2>]]] <Datei1> "
"<orig-Datei> <Datei2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"Option diff-algorithm akzeptiert: \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+
msgid "send results to standard output"
msgstr "Ergebnisse zur Standard-Ausgabe senden"
@@ -8216,6 +8232,12 @@ msgstr "bei Konflikten ihre Variante verwenden"
msgid "for conflicts, use a union version"
msgstr "bei Konflikten eine gemeinsame Variante verwenden"
+msgid "<algorithm>"
+msgstr "<Algorithmus>"
+
+msgid "choose a diff algorithm"
+msgstr "einen Algorithmus für Änderungen wählen"
+
msgid "for conflicts, use this marker size"
msgstr "bei Konflikten diese Kennzeichnungslänge verwenden"
@@ -8307,9 +8329,6 @@ msgstr "--trivial-merge ist mit allen anderen Optionen inkompatibel"
msgid "unknown strategy option: -X%s"
msgstr "unbekannte Strategie-Option: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base ist inkompatibel mit --stdin"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "Fehlerhafte Eingabezeile: '%s'."
@@ -9265,6 +9284,10 @@ msgid "inconsistency with delta count"
msgstr "Inkonsistenz mit der Anzahl von Deltas"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "ungültiger Wert für pack.allowPackReuse: '%s'"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9533,10 +9556,10 @@ msgstr "Objekte aufzählen"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Gesamt %<PRIu32> (Delta %<PRIu32>), Wiederverwendet %<PRIu32> (Delta "
-"%<PRIu32>), Pack wiederverwendet %<PRIu32>"
+"%<PRIu32>), Paket wiederverwendet %<PRIu32> (von %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10548,13 +10571,6 @@ msgid "switch `C' expects a numerical value"
msgstr "Schalter `C' erwartet einen numerischen Wert."
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"apply-Optionen sind mit rebase.autoSquash nicht kompatibel. Erwägen Sie das "
-"Hinzufügen von --no-autosquash"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -11567,6 +11583,77 @@ msgstr "--convert-graft-file erwartet keine Argumente"
msgid "only one pattern can be given with -l"
msgstr "Mit -l kann nur ein Muster angegeben werden"
+msgid "need some commits to replay"
+msgstr "zum erneuten Abspielen werden Commits benötigt"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto und --advance sind inkompatibel"
+
+msgid "all positive revisions given must be references"
+msgstr "alle angegebenen positiven Commits müssen Referenzen sein"
+
+msgid "argument to --advance must be a reference"
+msgstr "Argument für --advance muss eine Referenz sein"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"kann Ziel nicht mit mehreren Quellen erweitern, da die Reihenfolge unklar "
+"wäre"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"kann nicht implizit bestimmen, ob es sich um eine --advance oder --onto "
+"Operation handelt"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"kann Ziel nicht mit mehreren Quell-Branches erweitern, da die Reihenfolge "
+"unklar wäre"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "kann nicht implizit die richtige Basis für --onto bestimmen"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTELL!) git replay ([--contained] --onto <neue-Basis> | --advance "
+"<Branch>) <Commitbereich>..."
+
+msgid "make replay advance given branch"
+msgstr "angegebenen Branch durch neues Abspielen erweitern"
+
+msgid "replay onto given commit"
+msgstr "auf angegebenen Commit neu abspielen"
+
+msgid "advance all branches contained in revision-range"
+msgstr "alle Branches erweitern, die in Commitbereich liegen"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "Option --onto oder --advance erforderlich"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"einige Optionen für das Abgehen von Commits werden außer Kraft gesetzt, da "
+"das '%s' Bit in 'struct rev_info' erzwungen wird"
+
+msgid "error preparing revisions"
+msgstr "Fehler beim Vorbereiten der Commits"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "erneutes Abspielen bis zum Root-Commit wird noch nicht unterstützt!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "erneutes Abspielen von Merge-Commits wird noch nicht unterstützt!"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11778,15 +11865,6 @@ msgstr "--prefix benötigt ein Argument"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "unbekannter Modus für --abbrev-ref: %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden kann nicht zusammen mit --branches verwendet werden"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden kann nicht zusammen mit --tags verwendet werden"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden kann nicht zusammen mit --remotes verwendet werden"
-
msgid "this operation must be run in a work tree"
msgstr "Diese Operation muss in einem Arbeitsverzeichnis ausgeführt werden."
@@ -12202,10 +12280,6 @@ msgstr ""
"Referenzen von der Standard-Eingabe anzeigen, die sich nicht im lokalen "
"Repository befinden"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "es kann nur eines von '%s', '%s' oder '%s' angegeben werden"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13717,33 +13791,29 @@ msgstr "Kein möglicher Quell-Branch, der auf '--orphan' schließen lässt"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, um einen neuen verwaisten "
-"Branch\n"
-"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
-"mit\n"
-"der Option --orphan tun:\n"
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, welches einen neuen\n"
+"ungeborenen Branch (Branch ohne Commits) für dieses Repository erzeugt,\n"
+"können Sie dies mit der Option --orphan tun:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, um einen neuen verwaisten "
-"Branch\n"
-"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
-"mit\n"
-"der Option --orphan tun:\n"
+"Wenn Sie ein Arbeitsverzeichnis erstellen möchten, welches einen neuen\n"
+"ungeborenen Branch (Branch ohne Commits) für dieses Repository erzeugt,\n"
+"können Sie dies mit der Option --orphan tun:\n"
"\n"
" git worktree add --orphan %s\n"
@@ -13806,6 +13876,10 @@ msgid "initializing"
msgstr "initialisiere"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "konnte erstelltes Arbeitsverzeichnis '%s' nicht finden"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Bereite Arbeitsverzeichnis vor (neuer Branch '%s')"
@@ -13845,10 +13919,6 @@ msgstr ""
"Referenz zu überschreiben\n"
"oder rufen Sie diese zuerst ab"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' und '%s' können nicht zusammen verwendet werden"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr ""
"<Branch> auschecken, auch wenn dieser bereits in einem anderen "
@@ -13860,8 +13930,8 @@ msgstr "neuen Branch erstellen"
msgid "create or reset a branch"
msgstr "Branch erstellen oder umsetzen"
-msgid "create unborn/orphaned branch"
-msgstr "ungeborenen/verwaisten Branch erstellen"
+msgid "create unborn branch"
+msgstr "ungeborenen Branch erzeugen"
msgid "populate the new working tree"
msgstr "das neue Arbeitsverzeichnis auschecken"
@@ -13886,11 +13956,8 @@ msgstr ""
"die Optionen '%s', '%s' und '%s' können nicht gemeinsam verwendet werden"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden"
-
-msgid "<commit-ish>"
-msgstr "<Commit-Angabe>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "Option '%s' und commit-ish können nicht gemeinsam verwendet werden"
msgid "added with --lock"
msgstr "mit --lock hinzugefügt"
@@ -14541,6 +14608,11 @@ msgstr "ungepackte Objekte in einem Repository packen"
msgid "Create, list, delete refs to replace objects"
msgstr "Referenzen für ersetzende Objekte erstellen, auflisten, löschen"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTELL: Commits auf neuer Basis abspielen, funktioniert auch mit Bare-"
+"Repositories"
+
msgid "Generates a summary of pending changes"
msgstr "eine Übersicht über ausstehende Änderungen generieren"
@@ -14785,6 +14857,32 @@ msgstr "Ein Werkzeug zur Verwaltung großer Git-Repositories"
msgid "commit-graph file is too small"
msgstr "Commit-Graph-Datei ist zu klein"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "Commit-Graph OID fanout Chunk hat die falsche Größe"
+
+msgid "commit-graph fanout values out of order"
+msgstr "Commit-Graph fanout-Werte sind nicht in Ordnung"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "Commit-Graph OID Lookup Chunk hat die falsche Größe"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "Commit-Graph Commit Daten Chunk hat die falsche Größe"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "Commit-Graph Generations Chunk hat die falsche Größe"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "Commit-Graph changed-path Index Chunk ist zu klein"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"ignoriere zu kleinen Chunk für geänderte Pfade (%<PRIuMAX> < %<PRIuMAX>) in "
+"Commit-Graph-Datei"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "Commit-Graph-Signatur %X stimmt nicht mit Signatur %X überein"
@@ -14801,6 +14899,16 @@ msgstr "Hash-Version des Commit-Graph %X stimmt nicht mit Version %X überein"
msgid "commit-graph file is too small to hold %u chunks"
msgstr "Commit-Graph-Datei ist zu klein, um %u Chunks zu enthalten"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "Commit-Graph benötigter OID fanout Chunk fehlt oder ist beschädigt"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "Commit-Graph benötigter OID lookup Chunk fehlt oder ist beschädigt"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"Commit-Graph erforderlicher Commit-Daten Chunk fehlt oder ist beschädigt"
+
msgid "commit-graph has no base graphs chunk"
msgstr "Commit-Graph hat keinen Basis-Graph-Chunk"
@@ -14814,6 +14922,9 @@ msgstr "Commit-Graph Verkettung stimmt nicht überein"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "Anzahl der Commits im Basisgraph zu hoch: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "Commit-Graph Chain-Datei zu klein"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr "Ungültige Commit-Graph Verkettung: Zeile '%s' ist kein Hash"
@@ -14834,6 +14945,9 @@ msgstr "Commit-Graph erfordert Ãœberlaufgenerierungsdaten, aber hat keine"
msgid "commit-graph overflow generation data is too small"
msgstr "Commit-Graph Ãœberlaufgenerierungsdaten sind zu klein"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "commit-graph extra-edges Zeiger außerhalb der Grenzen"
+
msgid "Loading known commits in commit graph"
msgstr "Lade bekannte Commits in Commit-Graph"
@@ -16001,6 +16115,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "Unbekannter Wert in Konfigurationsvariable 'diff.submodule': '%s'"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "Unbekannter Wert für Konfiguration '%s': %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -16082,13 +16200,6 @@ msgstr "ungültiges --color-moved Argument: %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "ungültiger Modus '%s' in --color-moved-ws"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"Option diff-algorithm akzeptiert: \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-
#, c-format
msgid "invalid argument to %s"
msgstr "ungültiges Argument für %s"
@@ -16132,8 +16243,8 @@ msgstr "maschinenlesbare Ausgabe von --stat"
msgid "output only the last line of --stat"
msgstr "nur die letzte Zeile von --stat ausgeben"
-msgid "<param1,param2>..."
-msgstr "<Parameter1,Parameter2>..."
+msgid "<param1>,<param2>..."
+msgstr "<Parameter1>,<Parameter2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -16144,8 +16255,8 @@ msgstr ""
msgid "synonym for --dirstat=cumulative"
msgstr "Synonym für --dirstat=cumulative"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "Synonym für --dirstat=files,Parameter1,Parameter2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "Synonym für --dirstat=files,<Parameter1>,<Parameter2>..."
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -16328,12 +16439,6 @@ msgstr "Änderungen durch Nutzung des Algorithmus \"Patience Diff\" erzeugen"
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "Änderungen durch Nutzung des Algorithmus \"Histogram Diff\" erzeugen"
-msgid "<algorithm>"
-msgstr "<Algorithmus>"
-
-msgid "choose a diff algorithm"
-msgstr "einen Algorithmus für Änderungen wählen"
-
msgid "<text>"
msgstr "<Text>"
@@ -17547,7 +17652,7 @@ msgstr ""
#. conflict in a submodule. The first argument is the submodule
#. name, and the second argument is the abbreviated id of the
#. commit that needs to be merged. For example:
-#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
#.
#, c-format
msgid ""
@@ -17848,6 +17953,13 @@ msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "Multi-Pack-Index OID fanout hat die falsche Größe"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"Ungültige oid fanout Reihenfolge: fanout[%d] = %<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "multi-pack-index OID-Lookup-Chunk hat die falsche Größe"
@@ -17898,6 +18010,13 @@ msgstr "Falsche Reihenfolge bei Multi-Pack-Index Pack-Namen: '%s' vor '%s'"
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "Ungültige pack-int-id: %u (%u Pakete insgesamt)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX enthält keinen BTMP-Chunk"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "konnte Bitmap-Paket nicht laden %<PRIu32>"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"Multi-Pack-Index speichert einen 64-Bit Offset, aber off_t ist zu klein"
@@ -17983,13 +18102,6 @@ msgstr "Prüfsumme nicht korrekt"
msgid "Looking for referenced packfiles"
msgstr "Suche nach referenzierten Pack-Dateien"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"Ungültige oid fanout Reihenfolge: fanout[%d] = %<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "das midx enthält keine oid"
@@ -18304,7 +18416,7 @@ msgstr "%s [ungültiges Objekt]"
#. TRANSLATORS: This is a line of ambiguous commit
#. object output. E.g.:
#. *
-#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
#.
#, c-format
msgid "%s commit %s - %s"
@@ -18313,7 +18425,7 @@ msgstr "%s Commit %s - %s"
#. TRANSLATORS: This is a line of ambiguous
#. tag object output. E.g.:
#. *
-#. "deadbeef tag 2022-01-01 - Some Tag Message"
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
#. *
#. The second argument is the YYYY-MM-DD found
#. in the tag.
@@ -18329,7 +18441,7 @@ msgstr "%s Tag %s - %s"
#. tag object output where we couldn't parse
#. the tag itself. E.g.:
#. *
-#. "deadbeef [bad tag, could not parse it]"
+#. "deadbeef [bad tag, could not parse it]"
#.
#, c-format
msgid "%s [bad tag, could not parse it]"
@@ -18525,6 +18637,9 @@ msgstr "Multi-Pack-Bitmap fehlt erforderlicher Reverse-Index"
msgid "could not open pack %s"
msgstr "konnte Paket '%s' nicht öffnen"
+msgid "could not determine MIDX preferred pack"
+msgstr "konnte das von MIDX bevorzugte Paket nicht ermitteln"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "bevorzugtes Paket (%s) ist ungültig"
@@ -18548,6 +18663,12 @@ msgstr ""
"fehlerhafte ewah-Bitmap: abgeschnittener Header für Bitmap des Commits \"%s\""
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"Paket kann nicht geladen werden: '%s', Deaktivierung der Paket-"
+"Wiederverwendung"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "Objekt '%s' nicht im Typ Bitmaps gefunden"
@@ -18638,6 +18759,9 @@ msgstr "ungültige rev-index Position bei %<PRIu64>: %<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "multi-pack-index Reverse-Index Chunk hat die falsche Größe"
+msgid "could not determine preferred pack"
+msgstr "konnte das bevorzugte Paket nicht bestimmen"
+
msgid "cannot both write and verify reverse index"
msgstr ""
"Reverse-Index kann nicht gleichzeitig geschrieben und verifiziert werden"
@@ -18704,10 +18828,6 @@ msgstr ""
"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)"
@@ -19031,10 +19151,6 @@ msgid "unable to add '%s' to index"
msgstr "Konnte '%s' nicht dem Index hinzufügen."
#, c-format
-msgid "unable to stat '%s'"
-msgstr "konnte '%s' nicht lesen"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' scheint eine Datei und ein Verzeichnis zu sein"
@@ -19613,10 +19729,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "kann '%s' und '%s' nicht zur selben Zeit verarbeiten"
#, c-format
-msgid "could not remove reference %s"
-msgstr "konnte Referenz %s nicht löschen"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "konnte Referenz %s nicht entfernen: %s"
@@ -19810,16 +19922,13 @@ msgid ""
"\n"
"Neither worked, so we gave up. You must fully qualify the ref."
msgstr ""
-"Das angegebene Ziel ist kein vollständiger Referenzname (startet mit \"refs/"
-"\").\n"
-"Wir versuchten zu erraten, was Sie meinten, mit:\n"
+"Das angegebene Ziel ist kein vollständiger Referenzname (startet mit\n"
+"\"refs/\"). Wir versuchten zu erraten, was Sie meinten, mit:\n"
"\n"
"- Suche einer Referenz, die mit '%s' übereinstimmt, auf der Remote-Seite\n"
-"- Prüfung, ob die versendete <Quelle> ('%s') eine Referenz in \"refs/{heads,"
-"tags}\"\n"
-" ist, in dessen Falle wir einen entsprechenden refs/{heads,tags} Präfix "
-"auf\n"
-" der Remote-Seite hinzufügen würden.\n"
+"- Prüfung, ob die versendete <Quelle> ('%s') eine Referenz in\n"
+" \"refs/{heads,tags}/\" ist, in dessen Falle wir einen entsprechenden\n"
+" refs/{heads,tags}/ Präfix auf der Remote-Seite hinzufügen würden.\n"
"\n"
"Keines hat funktioniert, sodass wir aufgegeben haben. Sie müssen die\n"
"Referenz mit vollqualifizierten Namen angeben."
@@ -21004,6 +21113,9 @@ msgstr "Beim Anwenden des automatischen Stash traten Konflikte auf."
msgid "Autostash exists; creating a new stash entry."
msgstr "Automatischer Stash existiert; ein neuer Stash-Eintrag wird erstellt."
+msgid "autostash reference is a symref"
+msgstr "Referenz für autostash ist eine symbolische Referenz"
+
msgid "could not detach HEAD"
msgstr "konnte HEAD nicht loslösen"
@@ -21324,6 +21436,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "ungültiger initialer Branchname: '%s'"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "kann nicht mit Dateityp %d umgehen"
@@ -21334,15 +21450,17 @@ msgstr "konnte %s nicht nach %s verschieben"
msgid "attempt to reinitialize repository with different hash"
msgstr "Versuch, das Repository mit einem anderen Hash zu reinitialisieren"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"Versuch, das Repository mit einem anderen Referenzspeicherformat neu zu "
+"initialisieren"
+
#, c-format
msgid "%s already exists"
msgstr "%s existiert bereits"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "Neu-Initialisierung: --initial-branch=%s ignoriert"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "Bestehendes verteiltes Git-Repository in %s%s neuinitialisiert\n"
@@ -21611,12 +21729,6 @@ msgstr ""
"Anzahl der Einträge im Cache-Verzeichnis, die ungültig gemacht werden sollen "
"(Standardwert 0)"
-msgid "unhandled options"
-msgstr "unbehandelte Optionen"
-
-msgid "error preparing revisions"
-msgstr "Fehler beim Vorbereiten der Commits"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "Commit %s ist nicht als erreichbar gekennzeichnet."
@@ -21772,9 +21884,6 @@ msgstr ""
msgid "invalid remote service path"
msgstr "ungültiger Remote-Service Pfad."
-msgid "operation not supported by protocol"
-msgstr "die Operation wird von dem Protokoll nicht unterstützt"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "kann keine Verbindung zu Subservice %s herstellen"
@@ -21905,10 +22014,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "Unterstützung für Protokoll v2 noch nicht implementiert."
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "Unbekannter Wert für Konfiguration '%s': %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "Ãœbertragungsart '%s' nicht erlaubt."
@@ -21961,6 +22066,9 @@ msgstr "bundle-uri Operation wird vom Protokoll nicht unterstützt"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "konnte die vom Server angekündigte bundle-uri-Liste nicht abrufen"
+msgid "operation not supported by protocol"
+msgstr "die Operation wird von dem Protokoll nicht unterstützt"
+
msgid "too-short tree object"
msgstr "zu kurzes Tree-Objekt"
@@ -22849,6 +22957,10 @@ msgid "cannot %s: Your index contains uncommitted changes."
msgstr ""
"%s nicht möglich: Die Staging-Area enthält nicht committete Änderungen."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "unbekannter Stil '%s' für '%s' angegeben"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
diff --git a/po/fr.po b/po/fr.po
index ee2e610ef1..736a90f6bb 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -80,8 +80,8 @@ msgid ""
msgstr ""
"Project-Id-Version: git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-08 04:57+0000\n"
-"PO-Revision-Date: 2023-11-11 10:00+0100\n"
+"POT-Creation-Date: 2024-02-16 19:18+0100\n"
+"PO-Revision-Date: 2024-02-16 19:19+0100\n"
"Last-Translator: Cédric Malard <c.malard-git@valdun.net>\n"
"Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n"
"Language: fr\n"
@@ -1526,6 +1526,10 @@ msgid "Unexpected option --output"
msgstr "Option --output inattendue"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "paramètre de commande supplémentaire '%s'"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Format d'archive inconnu '%s'"
@@ -1571,6 +1575,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "mauvais --attr-source ou GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "fstat de '%s' impossible"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "impossible de lire %s"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Contenu mal cité dans le fichier '%s' : %s"
@@ -2874,12 +2886,12 @@ msgid "couldn't look up commit object for '%s'"
msgstr "impossible de rechercher l'objet commit pour '%s'"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"la branche '%s' n'est pas totalement fusionnée.\n"
-"Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "la branche '%s' n'est pas complètement fusionnée"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Si vous souhaitez réellement la supprimer, lancez 'git branch -D %s'"
msgid "update of config-file failed"
msgstr "échec de la mise à jour du fichier de configuration"
@@ -3937,8 +3949,8 @@ msgstr "forcer l'extraction (laisser tomber les modifications locales)"
msgid "new-branch"
msgstr "nouvelle branche"
-msgid "new unparented branch"
-msgstr "nouvelle branche sans parent"
+msgid "new unborn branch"
+msgstr "nouvelle branche non née"
msgid "update ignored files (default)"
msgstr "mettre à jour les fichiers ignorés (par défaut)"
@@ -4192,9 +4204,6 @@ msgstr ""
"clean.requireForce à true par défaut et ni -i, -n ou -f fourni ; refus de "
"nettoyer"
-msgid "-x and -X cannot be used together"
-msgstr "-x et -X ne peuvent pas être utilisés ensemble"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<options>] [--] <dépôt> [<répertoire>]"
@@ -4285,6 +4294,9 @@ msgstr "gitdir"
msgid "separate git dir from working tree"
msgstr "séparer le répertoire git de la copie de travail"
+msgid "specify the reference format to use"
+msgstr "spécifier le format de réference à utiliser"
+
msgid "key=value"
msgstr "clé=valeur"
@@ -4407,12 +4419,9 @@ msgstr "Trop d'arguments."
msgid "You must specify a repository to clone."
msgstr "Vous devez spécifier un dépôt à cloner."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri est incompatible avec --depth, --shallow-since, et --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "Format de stockage de réf inconnu '%s'"
#, c-format
msgid "repository '%s' does not exist"
@@ -4546,14 +4555,14 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <rép>] [--append]\n"
" [--split[=<stratégie>]] [--reachable | --stdin-packs "
"| --stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <options de division>"
+" <options-de-division>"
msgid "dir"
msgstr "répertoire"
@@ -6907,6 +6916,10 @@ msgid "unable to read tree (%s)"
msgstr "impossible de lire l'arbre (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "impossible de lire l'arbre %s"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "impossible de faire un grep sur un objet de type %s"
@@ -7325,10 +7338,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "COLLISION SHA1 TROUVÉE AVEC %s !"
#, c-format
-msgid "unable to read %s"
-msgstr "impossible de lire %s"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "impossible de lire l'information existante de l'objet %s"
@@ -7469,12 +7478,14 @@ msgstr "erreur de fsck dans les objets paquets"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
-"git init [-q | --quiet] [--bare] [--template=<répertoire-modèle>]\n"
-" [--separate-git-dir <rép-git>] [--object-format=<format>]\\n\"\n"
-" [-b <nom-de-branche> | --initial-branch=<nom-de-branche>]\\n\"\n"
+"git init [-q | --quiet] [--bare] [--template=<répertoire-de-modèles>]\n"
+" [--separate-git-dir <rép-git>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
+" [-b <nom-de-branch> | --initial-branch=<nom-de-branche>]\n"
" [--shared[=<permissions>]] [<répertoire>]"
msgid "permissions"
@@ -8204,6 +8215,13 @@ msgstr ""
"git merge-file [<options>] [-L <nom1> [-L <orig> [-L <nom2>]]] <fichier1> "
"<fichier-orig> <fichier2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"l'option diff-algorithm accept \"myers\", \"minimal\", \"patience\" et "
+"\"histogram\""
+
msgid "send results to standard output"
msgstr "envoyer les résultats sur la sortie standard"
@@ -8225,6 +8243,12 @@ msgstr "pour les conflits, utiliser leur version (their)"
msgid "for conflicts, use a union version"
msgstr "pour les conflits, utiliser l'ensemble des versions"
+msgid "<algorithm>"
+msgstr "<algorithme>"
+
+msgid "choose a diff algorithm"
+msgstr "choisir un algorithme de différence"
+
msgid "for conflicts, use this marker size"
msgstr "pour les conflits, utiliser cette taille de marqueur"
@@ -8315,9 +8339,6 @@ msgstr "--trivial-merge est incompatible avec d'autres options"
msgid "unknown strategy option: -X%s"
msgstr "option de stratégie inconnue : -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base est incompatible avec --stdin"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "ligne en entrée malformée : '%s'."
@@ -9265,6 +9286,10 @@ msgid "inconsistency with delta count"
msgstr "inconsistance dans le compte de delta"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "valeur invalide de pack.allowPackReuse : '%s'"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9527,10 +9552,10 @@ msgstr "Énumération des objets"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Total %<PRIu32> (delta %<PRIu32>), réutilisés %<PRIu32> (delta %<PRIu32>), "
-"réutilisés du pack %<PRIu32>"
+"réutilisés du paquet %<PRIu32> (depuis %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10520,13 +10545,6 @@ msgid "switch `C' expects a numerical value"
msgstr "l'option `C' attend un valeur numérique"
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"les options d'application sont incompatibles avec rebase.autoSquash. "
-"Considérez l'ajout de --no-autosquash"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -11535,6 +11553,77 @@ msgstr "--convert-graft-file ne supporte aucun argument"
msgid "only one pattern can be given with -l"
msgstr "-l n'accepte qu'un motifs"
+msgid "need some commits to replay"
+msgstr "commits requis pour pouvoir rejouer"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto et --advance sont incompatibles"
+
+msgid "all positive revisions given must be references"
+msgstr "toutes les révisions positives fournies doivent être des références"
+
+msgid "argument to --advance must be a reference"
+msgstr "l'argument de --advance doit être une référence"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"impossible d'avancer la cible avec des sources multiples parce l'ordre ne "
+"serait pas total"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"impossible de déterminer implicitement s'il y a une opération --advance ou --"
+"onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"impossible d'avancer la cible sur des branches sources multiples parce que "
+"l'ordre ne serait pas total"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "impossible de déterminer implicitement une base correcte pour --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <nouvelle-base> | --advance "
+"<branche>) <plage-de-révision>..."
+
+msgid "make replay advance given branch"
+msgstr "faire rejouer en avançant la branche indiquée"
+
+msgid "replay onto given commit"
+msgstr "rejouer par-dessus le commit indiqué"
+
+msgid "advance all branches contained in revision-range"
+msgstr "avancer toutes les branches contenues dans la plage-de-révisions"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "une option --onto ou --advance est obligatoire"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"certaines options de parcours de révs seront surchargées car le bit '%s' "
+"dans 'struct rev_info' sera forcé"
+
+msgid "error preparing revisions"
+msgstr "erreur lors de la préparation des révisions"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "rejouer jusqu'au commit racine n'est pas encore géré !"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "rejouer des commits de fusion n'est pas encore géré !"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11746,15 +11835,6 @@ msgstr "--prefix exige un argument"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "mode inconnu pour --abbrev-ref : %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden ne peut être utilisé avec --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden ne peut pas être utilisé avec --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden ne peut pas être utilisé avec --remotes"
-
msgid "this operation must be run in a work tree"
msgstr "cette opération doit être effectuée dans un arbre de travail"
@@ -12171,10 +12251,6 @@ msgstr ""
"afficher les références de l'entrée standard qui ne sont pas dans le dépôt "
"local"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "les options '%s', '%s' et '%s' sont mutuellement exclusives"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13673,28 +13749,28 @@ msgstr "Aucune branche source possible, activation de '--orphan'"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
-"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"non-née (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
"en utilisant le drapeau --orphan :\n"
"\n"
" git worktree add --orphan -b %s %s\n"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
"Si vous vouliez créer un arbre-de-travail contenant une nouvelle branche\n"
-"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
+"non-née (une branche sans commit) pour ce dépôt, vous pouvez le faire\n"
"en utilisant le drapeau --orphan :\n"
"\n"
" git worktree add --orphan %s\n"
@@ -13757,6 +13833,10 @@ msgid "initializing"
msgstr "initialisation"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "impossible de trouver l'arbre-de-travail créé '%s'"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Préparation de l'arbre de travail (nouvelle branche '%s')"
@@ -13797,10 +13877,6 @@ msgstr ""
"on arrête ; utilisez 'add -f' pour passe outre ou récupérer le distant en "
"premier"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' et '%s' ne peuvent pas être utilisées ensemble"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr ""
"extraire la <branche> même si elle est déjà extraite dans une autre copie de "
@@ -13812,8 +13888,8 @@ msgstr "créer une nouvelle branche"
msgid "create or reset a branch"
msgstr "créer ou réinitialiser une branche"
-msgid "create unborn/orphaned branch"
-msgstr "créer une branche non née/orpheline"
+msgid "create unborn branch"
+msgstr "créer une branche non née"
msgid "populate the new working tree"
msgstr "remplissage de la nouvelle copie de travail"
@@ -13835,11 +13911,9 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "les options '%s', '%s' et '%s' ne peuvent pas être utilisées ensemble"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "les options '%s' et '%s' ne peuvent pas être utilisées ensemble"
-
-msgid "<commit-ish>"
-msgstr "<commit-esque>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr ""
+"l'option '%s' et des commit-esques ne peuvent pas être utilisés ensemble"
msgid "added with --lock"
msgstr "ajouté avec --lock"
@@ -14480,6 +14554,11 @@ msgstr "Empaqueter les objets non-empaquetés d'un dépôt"
msgid "Create, list, delete refs to replace objects"
msgstr "Créer, lister, supprimer des référence pour remplacer des objets"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPÉRIMENTAL ; rejoue des commits sur une nouvelle base, fonctionne aussi "
+"avec les dépôts nus"
+
msgid "Generates a summary of pending changes"
msgstr "Générer une résumé des modifications en attentes"
@@ -14722,6 +14801,35 @@ msgstr "Un outil pour gérer les grands dépôts Git"
msgid "commit-graph file is too small"
msgstr "le graphe de commit est trop petit"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr ""
+"le tronçon de distribution d'oid du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph fanout values out of order"
+msgstr "les valeurs de distribution du graphe de commit sont désordonnées"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr ""
+"le tronçon de recherche de l'OID du graphe de commits n'a pas la bonne taille"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "le tronçon de données du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "le tronçon des générations du graphe de commit n'a pas la bonne taille"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr ""
+"le tronçon d'index des chemins modifiés du graphe de commit est trop petit"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"tronçon de chemin modifié dans le fichier de graphe de commits trop petit "
+"((%<PRIuMAX> < %<PRIuMAX>)) ignoré"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr ""
@@ -14741,6 +14849,21 @@ msgstr ""
msgid "commit-graph file is too small to hold %u chunks"
msgstr "le graphe de commit est trop petit pour contenir %u tronçons"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"le tronçon de distribution des OID requis du graphe de commits est manquant "
+"ou corrompu"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"le tronçon de recherche OID requis par le graphe de commits est manquant ou "
+"corrompu"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"le tronçon d'étalement OID requis par le graphe de commits est manquant ou "
+"corrompu"
+
msgid "commit-graph has no base graphs chunk"
msgstr "le graphe de commit n'a pas de tronçon de graphes de base"
@@ -14754,6 +14877,9 @@ msgstr "la chaîne de graphe de commit ne correspond pas"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "nombre de commits dans le graphe de base trop haut : %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "la chaine du graphe de commit est trop petite"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr ""
@@ -14781,6 +14907,9 @@ msgstr ""
"les données de génération de débordement du graphe de commits sont trop "
"petites"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "pointeur hors-gamme d'arêtes supplémentaires du graphe de commits"
+
msgid "Loading known commits in commit graph"
msgstr "Lecture des commits connus dans un graphe de commit"
@@ -15961,6 +16090,10 @@ msgstr ""
"Valeur inconnue pour la variable de configuration 'diff.submodule' : '%s'"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "valeur inconnue pour la config '%s' : %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -16042,13 +16175,6 @@ msgstr "mauvais argument --color-moved : %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "mode invalide '%s' dans --color-moved-ws"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"l'option diff-algorithm accept \"myers\", \"minimal\", \"patience\" et "
-"\"histogram\""
-
#, c-format
msgid "invalid argument to %s"
msgstr "argument invalide pour %s"
@@ -16092,8 +16218,8 @@ msgstr "--stat pour traitement automatique"
msgid "output only the last line of --stat"
msgstr "afficher seulement la dernière ligne de --stat"
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -16104,8 +16230,8 @@ msgstr ""
msgid "synonym for --dirstat=cumulative"
msgstr "synonyme pour --dirstat=cumulative"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "synonyme pour --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "synonyme pour --dirstat=files,<param1>,<param2>..."
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -16293,12 +16419,6 @@ msgid "generate diff using the \"histogram diff\" algorithm"
msgstr ""
"générer un diff en utilisant l'algorithme de différence \"histogramme\""
-msgid "<algorithm>"
-msgstr "<algorithme>"
-
-msgid "choose a diff algorithm"
-msgstr "choisir un algorithme de différence"
-
msgid "<text>"
msgstr "<texte>"
@@ -17808,6 +17928,13 @@ msgstr "impossible de lire le cache"
msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "l'étalement de l'OID d'index multi-paquet n'a pas la bonne taille"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"étalement oid en désordre : étalement[%d] = %<PRIx32> > %<PRIx32> = "
+"étalement[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr ""
"le tronçon de recherche de l'OID d'index multi-paquet n'a pas la bonne taille"
@@ -17868,6 +17995,13 @@ msgstr ""
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "mauvais pack-int-id : %u (%u paquets au total)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "le MIDX ne contient pas de tronçon BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "impossible d'ouvrir le paquet bitmappé %<PRIu32>"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"l'index multi-paquet stocke un décalage en 64-bit, mais off_t est trop petit"
@@ -17953,13 +18087,6 @@ msgstr "somme de contrôle incorrecte"
msgid "Looking for referenced packfiles"
msgstr "Recherche de fichiers paquets référencés"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"étalement oid en désordre : étalement[%d] = %<PRIx32> > %<PRIx32> = "
-"étalement[%d]"
-
msgid "the midx contains no oid"
msgstr "le midx ne contient aucun oid"
@@ -18486,6 +18613,9 @@ msgstr "l'index inverse requis manque dans l'index multi-paquet"
msgid "could not open pack %s"
msgstr "impossible d'ouvrir le paquet '%s'"
+msgid "could not determine MIDX preferred pack"
+msgstr "impossible de déterminer le paquet préféré de MIDX"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "le paquet préféré (%s) est invalide"
@@ -18508,6 +18638,10 @@ msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
msgstr "bitmap ewah corrompue : entête tronqué pour la bitmap du commit '%s'"
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "impossible de charger le paquet : '%s', pack-reuse désactivé"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "objet '%s' non trouvé dans les bitmaps de type"
@@ -18599,6 +18733,9 @@ msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr ""
"le tronçon d'index inversé de l'index multi-paquet n'a pas la bonne taille"
+msgid "could not determine preferred pack"
+msgstr "impossible de déterminer le paquet préféré"
+
msgid "cannot both write and verify reverse index"
msgstr "impossible de lire et vérifier à la fois l'index inverse"
@@ -18663,10 +18800,6 @@ 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)"
@@ -18995,10 +19128,6 @@ msgid "unable to add '%s' to index"
msgstr "impossible d'ajouter '%s' à l'index"
#, c-format
-msgid "unable to stat '%s'"
-msgstr "fstat de '%s' impossible"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' existe à la fois comme un fichier et un répertoire"
@@ -19575,10 +19704,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "impossible de traiter '%s' et '%s' en même temps"
#, c-format
-msgid "could not remove reference %s"
-msgstr "impossible de supprimer la référence %s"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "impossible de supprimer la référence %s : %s"
@@ -19773,7 +19898,7 @@ msgid ""
"Neither worked, so we gave up. You must fully qualify the ref."
msgstr ""
"La destination que vous avez fournie n'est pas un nom de référence complète\n"
-"(c'est-à-dire commençant par \"ref/\"). Essai d'approximation par :\n"
+"(c'est-à-dire commençant par \"refs/\"). Essai d'approximation par :\n"
"\n"
"- Recherche d'une référence qui correspond à '%s' sur le serveur distant.\n"
"- Vérification si la <source> en cours de poussée ('%s')\n"
@@ -20961,6 +21086,9 @@ msgid "Autostash exists; creating a new stash entry."
msgstr ""
"Un remisage automatique existe ; création d'une nouvelle entrée de remisage."
+msgid "autostash reference is a symref"
+msgstr "la référence d'auto-remisage est une symref"
+
msgid "could not detach HEAD"
msgstr "impossible de détacher HEAD"
@@ -21280,6 +21408,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "nom de branche initiale invalide : '%s'"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-initialisation : --initial-branch=%s ignoré"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "impossible de traiter le fichier de type %d"
@@ -21290,15 +21422,17 @@ msgstr "impossible de déplacer %s vers %s"
msgid "attempt to reinitialize repository with different hash"
msgstr "essai de réinitialisation du dépôt avec une empreinte différente"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"essai de réinitialisation du dépôt avec un format de stockage de références "
+"différent"
+
#, c-format
msgid "%s already exists"
msgstr "%s existe déjà"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-initialisation : --initial-branch=%s ignoré"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "Dépôt Git existant partagé réinitialisé dans %s%s\n"
@@ -21567,12 +21701,6 @@ msgstr "effacer l'arbre de cache avant chaque itération"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "nombre d'entrées dans l'arbre de cache à invalider (par défaut, 0)"
-msgid "unhandled options"
-msgstr "options non gérées"
-
-msgid "error preparing revisions"
-msgstr "erreur lors de la préparation des révisions"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "le commit %s n'est pas marqué joignable"
@@ -21732,9 +21860,6 @@ msgstr ""
msgid "invalid remote service path"
msgstr "chemin de service distant invalide"
-msgid "operation not supported by protocol"
-msgstr "option non supportée par le protocole"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "impossible de se connecter au sous-service %s"
@@ -21867,10 +21992,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "le support du protocole v2 n'est pas encore implanté"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "valeur inconnue pour la config '%s' : %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "transport '%s' non permis"
@@ -21924,6 +22045,9 @@ msgid "could not retrieve server-advertised bundle-uri list"
msgstr ""
"impossible de récupérer la liste de bundle-uris annoncée par le serveur"
+msgid "operation not supported by protocol"
+msgstr "option non supportée par le protocole"
+
msgid "too-short tree object"
msgstr "objet arbre trop court"
@@ -22773,6 +22897,10 @@ msgstr "de plus, votre index contient des modifications non validées."
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "%s impossible : votre index contient des modifications non validées."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "style inconnu '%s' pour '%s'"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
@@ -23174,6 +23302,61 @@ msgstr "%s sauté avec un suffix de sauvegarde '%s'.\n"
msgid "Do you really want to send %s? [y|N]: "
msgstr "Souhaitez-vous réellement envoyer %s ?[y|N] : "
+#~ msgid "-x and -X cannot be used together"
+#~ msgstr "-x et -X ne peuvent pas être utilisés ensemble"
+
+#~ msgid ""
+#~ "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+#~ "exclude"
+#~ msgstr ""
+#~ "--bundle-uri est incompatible avec --depth, --shallow-since, et --shallow-"
+#~ "exclude"
+
+#~ msgid "--merge-base is incompatible with --stdin"
+#~ msgstr "--merge-base est incompatible avec --stdin"
+
+#~ msgid ""
+#~ "apply options are incompatible with rebase.autoSquash. Consider adding --"
+#~ "no-autosquash"
+#~ msgstr ""
+#~ "les options d'application sont incompatibles avec rebase.autoSquash. "
+#~ "Considérez l'ajout de --no-autosquash"
+
+#~ msgid "--exclude-hidden cannot be used together with --branches"
+#~ msgstr "--exclude-hidden ne peut être utilisé avec --branches"
+
+#~ msgid "--exclude-hidden cannot be used together with --tags"
+#~ msgstr "--exclude-hidden ne peut pas être utilisé avec --tags"
+
+#~ msgid "--exclude-hidden cannot be used together with --remotes"
+#~ msgstr "--exclude-hidden ne peut pas être utilisé avec --remotes"
+
+#, c-format
+#~ msgid "only one of '%s', '%s' or '%s' can be given"
+#~ msgstr "les options '%s', '%s' et '%s' sont mutuellement exclusives"
+
+#, c-format
+#~ msgid "'%s' and '%s' cannot be used together"
+#~ msgstr "'%s' et '%s' ne peuvent pas être utilisées ensemble"
+
+#, c-format
+#~ msgid "options '%s', and '%s' cannot be used together"
+#~ msgstr "les options '%s' et '%s' ne peuvent pas être utilisées ensemble"
+
+#~ msgid "<commit-ish>"
+#~ msgstr "<commit-esque>"
+
+#, c-format
+#~ msgid "%s is incompatible with %s"
+#~ msgstr "%s est incompatible avec %s"
+
+#, c-format
+#~ msgid "could not remove reference %s"
+#~ msgstr "impossible de supprimer la référence %s"
+
+#~ msgid "unhandled options"
+#~ msgstr "options non gérées"
+
#, c-format
#~ msgid "options '%s=%s' and '%s=%s' cannot be used together"
#~ msgstr ""
diff --git a/po/id.po b/po/id.po
index 9698367606..3e8b212b61 100644
--- a/po/id.po
+++ b/po/id.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-12 20:04+0700\n"
-"PO-Revision-Date: 2023-11-12 20:30+0700\n"
+"POT-Creation-Date: 2024-02-16 09:36+0700\n"
+"PO-Revision-Date: 2024-02-16 10:45+0700\n"
"Last-Translator: Bagas Sanjaya <bagasdotme@gmail.com>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
@@ -909,7 +909,7 @@ msgid "unclosed quote"
msgstr "tanda kutip tak ditutup"
#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
msgid "too many arguments"
msgstr "terlalu banyak argumen"
@@ -924,12 +924,13 @@ msgid "unrecognized whitespace ignore option '%s'"
msgstr "opsi abai spasi putih tidak dikenal '%s'"
#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
-#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
-#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
-#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
-#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
-#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/checkout.c builtin/clean.c builtin/clone.c builtin/commit.c
+#: builtin/describe.c builtin/diff-tree.c builtin/difftool.c
+#: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c
+#: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c
+#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.c
+#: builtin/rev-parse.c builtin/show-branch.c builtin/stash.c
#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
#: range-diff.c revision.c
#, c-format
@@ -1754,6 +1755,11 @@ msgstr "Opsi --output tak diharapkan"
#: archive.c
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "parameter konfigurasi tambahan: '%s'"
+
+#: archive.c
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Format arsip tidak dikenal '%s'"
@@ -1808,6 +1814,17 @@ msgstr "mengabaikan blob gitattributes '%s' yang terlalu besar"
msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "--attr-source atau GIT_ATTR_SOURCE jelek"
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "tidak dapat men-stat '%s'"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "tidak dapat membaca %s"
+
#: bisect.c
#, c-format
msgid "Badly quoted content in file '%s': %s"
@@ -2359,8 +2376,8 @@ msgid "bad action '%s' for '%s'"
msgstr "tindakan jelek '%s' untuk '%s'"
#: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
#, c-format
msgid "invalid value for '%s': '%s'"
msgstr "nilai tidak valid untuk '%s': '%s'"
@@ -2523,8 +2540,7 @@ msgstr "git write-tree gagal menulis sebuah pohon"
msgid "applying to an empty history"
msgstr "menerapkan ke sebuah riwayat kosong"
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
msgid "failed to write commit object"
msgstr "gagal menulis objek komit"
@@ -2710,8 +2726,9 @@ msgid "n"
msgstr "n"
#: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.c
+#: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c builtin/init-db.c
+#: builtin/ls-files.c builtin/ls-tree.c builtin/replace.c builtin/tag.c
+#: builtin/verify-tag.c
msgid "format"
msgstr "format"
@@ -3406,12 +3423,13 @@ msgstr "tidak dapat mencari objek komit untuk '%s'"
#: builtin/branch.c
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"cabang '%s' belum sepenuhnya tergabung.\n"
-"kalau Anda yakin ingin menghapusnya, jalankan 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "cabang '%s' belum sepenuhnya digabungkan"
+
+#: builtin/branch.c
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Jika anda yakin untuk menghapusnya, lakukan 'git branch -D %s'"
#: builtin/branch.c
msgid "update of config-file failed"
@@ -4443,7 +4461,7 @@ msgstr "Tidak dapat melakukan reflog untuk '%s': %s\n"
msgid "HEAD is now at"
msgstr "HEAD sekarang berada di"
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
msgid "unable to update HEAD"
msgstr "tidak dapat memperbarui HEAD"
@@ -4714,8 +4732,8 @@ msgid "new-branch"
msgstr "cabang baru"
#: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "cabang baru tanpa induk"
+msgid "new unborn branch"
+msgstr "cabang yatim baru"
#: builtin/checkout.c builtin/merge.c
msgid "update ignored files (default)"
@@ -4783,7 +4801,7 @@ msgstr ""
msgid "you must specify path(s) to restore"
msgstr "Anda harus sebutkan jalur untuk dipulihkan"
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
#: builtin/submodule--helper.c builtin/worktree.c
msgid "branch"
msgstr "cabang"
@@ -5027,10 +5045,6 @@ msgstr ""
"clean.requireForce asal ke true dan baik -i, -n, atau -f tidak diberikan; "
"menolak membersihkan"
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x dan -X tidak dapat digunakan bersamaan"
-
#: builtin/clone.c
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<opsi>] [--] <repo> [<direktori>]"
@@ -5122,6 +5136,7 @@ msgid "create a shallow clone since a specific time"
msgstr "buat klon dangkal sejak waktu yang disebutkan"
#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
msgid "revision"
msgstr "revisi"
@@ -5149,6 +5164,10 @@ msgstr "direktori git"
msgid "separate git dir from working tree"
msgstr "pisahkan direktori git dari pohon kerja"
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "sebutkan format referensi untuk digunakan"
+
#: builtin/clone.c
msgid "key=value"
msgstr "kunci=nilai"
@@ -5299,13 +5318,10 @@ msgstr "Terlalu banyak argumen."
msgid "You must specify a repository to clone."
msgstr "Anda harus sebutkan repositori untuk diklon."
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri tidak kompatibel dengan --depth, --shallow-since, dan --shallow-"
-"exclude"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "format penyimpanan referensi tidak dikenal '%s'"
#: builtin/clone.c
#, c-format
@@ -5471,13 +5487,13 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <direktori>] [--append]\n"
" [--split[=<strategi>]] [--reachable | --stdin-packs | "
"--stdin-commits]\n"
-" [--changed-paths] [--[no-]max-new-filters <n>] [--[-"
-"no-]progress]\n"
+" [--changed-paths] [--[no-]max-new-filters <n>] [--"
+"[no-]progress]\n"
" <opsi pemisahan>"
#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
@@ -8402,6 +8418,11 @@ msgstr "tidak dapat membaca pohon (%s)"
#: builtin/grep.c
#, c-format
+msgid "unable to read tree %s"
+msgstr "tidak dapat membaca pohon %s"
+
+#: builtin/grep.c
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "tidak dapan men-grep dari objek dengan tipe %s"
@@ -8929,11 +8950,6 @@ msgstr "inkonsistensi inflate serius"
msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "TUMBUKAN SHA1 DITEMUKAN DENGAN %s !"
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "tidak dapat membaca %s"
-
#: builtin/index-pack.c
#, c-format
msgid "cannot read existing object info %s"
@@ -9111,11 +9127,13 @@ msgstr "kesalahan fsck dalam objek paket"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<direktori templat>]\n"
" [--separate-git-dir <direktori git>] [--object-format=<format>]\n"
+"\t [--ref-format=<format>]\n"
" [-b <nama cabang> | --initial-branch=<nama cabang>]\n"
" [--shared[=<perizinan>]] [<direktori>]"
@@ -9280,7 +9298,7 @@ msgstr ""
"lacak evolusi rentang baris <awal>,<akhir> atau fungsi :<nama fungsi> dalam "
"<berkas>"
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
#, c-format
msgid "unrecognized argument: %s"
msgstr "argumen tidak dikenal: %s"
@@ -10036,6 +10054,14 @@ msgstr ""
"git merge-file [<opsi>] [-L <nama 1> [-L <asli> [-L <nama 2>]]] <berkas 1> "
"<berkas asli> <berkas 2>"
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"opsi diff-algorithm terima \"myers\", \"minimal\", \"patience\" dan "
+"\"histogram\""
+
#: builtin/merge-file.c
msgid "send results to standard output"
msgstr "kirim hasil ke keluaran standar"
@@ -10064,6 +10090,14 @@ msgstr "untuk konflik, gunakan versi mereka"
msgid "for conflicts, use a union version"
msgstr "untuk konflik, gunakan versi bersatu"
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<algoritma>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "pilih algoritma diff"
+
#: builtin/merge-file.c
msgid "for conflicts, use this marker size"
msgstr "untuk konflik, gunakan ukuran penanda ini"
@@ -10181,10 +10215,6 @@ msgstr "--trivial-merge tidak kompatibel dengan semua opsi lainnya"
msgid "unknown strategy option: -X%s"
msgstr "opsi strategi tidak dikenal: -X%s"
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base tidak kompatibel dengan --stdin"
-
#: builtin/merge-tree.c builtin/notes.c
#, c-format
msgid "malformed input line: '%s'."
@@ -10342,7 +10372,7 @@ msgstr "'%s' tidak menunjuk pada sebuah komit"
msgid "Bad branch.%s.mergeoptions string: %s"
msgstr "Untai branch.%s.mergeoptions jelek: %s"
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
msgid "Unable to write index."
msgstr "Tidak dapat menulis indeks."
@@ -10350,7 +10380,7 @@ msgstr "Tidak dapat menulis indeks."
msgid "Not handling anything other than two heads merge."
msgstr "Tak tangani apapun selain penggabungan dua kepala."
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
#, c-format
msgid "unable to write %s"
msgstr "tidak dapat menulis %s"
@@ -11365,6 +11395,11 @@ msgstr "ketidakkonsistenan dengan hitungan delta"
#: builtin/pack-objects.c
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "nilai pack.allowPackReuse tidak valid: '%s'"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -11694,10 +11729,10 @@ msgstr "Menghitung objek"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Total %<PRIu32> (delta %<PRIu32>), digunakan ulang %<PRIu32> (delta "
-"%<PRIu32>), pak yang digunakan ulang %<PRIu32>"
+"%<PRIu32>), pak yang digunakan ulang %<PRIu32> (dari %<PRIuMAX>)"
#: builtin/pack-redundant.c
msgid ""
@@ -12816,7 +12851,7 @@ msgid "The --edit-todo action can only be used during interactive rebase."
msgstr ""
"Aksi --edit-todo hanya dapat digunakan selama pendasaran ulang interaktif."
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
msgid "Cannot read HEAD"
msgstr "Tidak dapat membaca HEAD"
@@ -12864,14 +12899,6 @@ msgstr "tombol `C' harap nilai numerik"
#: builtin/rebase.c
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"opsi penerapan tidak kompatibel dengan rebase.autoSquash. "
-"Pertimbangkanmenambahkan --no-autosquash"
-
-#: builtin/rebase.c
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -13368,10 +13395,10 @@ msgid_plural ""
"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
"to delete them, use:"
msgstr[0] ""
-"Catatan: Sebuah cabang diluar hierarki refs/remotes tidak dihapus;\n"
+"Catatan: Sebuah cabang diluar hierarki refs/remotes/ tidak dihapus;\n"
"untuk menghapusnya, gunakan:"
msgstr[1] ""
-"Catatan: Beberapa cabang diluar hierarki refs/remotes tidak dihapus;\n"
+"Catatan: Beberapa cabang diluar hierarki refs/remotes/ tidak dihapus;\n"
"untuk menghapusnya, gunakan:"
#: builtin/remote.c
@@ -14108,6 +14135,94 @@ msgstr "--convert-graft-file tidak mengambil argumen"
msgid "only one pattern can be given with -l"
msgstr "hanya satu pola yang dapat diberikan dengan -l"
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "butuh beberapa komit untuk dimainkan ulang"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto dan --advance tidak kompatibel"
+
+#: builtin/replay.c
+msgid "all positive revisions given must be references"
+msgstr "semua revisi positif yang diberikan haruslah referensi"
+
+#: builtin/replay.c
+msgid "argument to --advance must be a reference"
+msgstr "argumen pada --advance harus sebuah referensi"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"tidak dapat memajukan target dengan banyak sumber karena pengurutannya akan "
+"menjadi tidak jelas"
+
+#: builtin/replay.c
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"tidak dapat menentukan secara tidak langsung apakah ini operasi --advance "
+"atau --onto"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"tidak dapat memajukan target dengan banyak cabang sumber karena "
+"pengurutannya akan menjadi tidak jelas"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "tidak dapat menentukan secara tidak langsung dasar untuk --onto"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EKSPERIMENTAL!) git replay ([--contained] --onto <dasar baru> | --advance "
+"<cabang>) <rentang revisi>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "buat pemainan ulang memajukan caban yang diberikan"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "mainkan ulang pada komit yang diberikan"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "majukan semua cabang yang berada pada rentang komit"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "opsi --onto atau --advance diwajibkan"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"beberapa opsi jalan revisi akan ditimpa oleh karena bit '%s' di 'struct "
+"rev_info' akan dipaksakan"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "kesalahan menyiapkan revisi"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "memainkan ulang ke komit akar belum didukung!"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "memainkan ulang komit penggabungan belum didukung!"
+
#: builtin/rerere.c
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -14370,18 +14485,6 @@ msgstr "--prefix butuh sebuah argumen"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "mode untuk --abbrev-ref tidak dikenal: %s"
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --branches"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden tidak dapat digunakan bersamaan dengan --tags"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden tidak dapat digunakan dengan --remotes"
-
#: builtin/rev-parse.c setup.c
msgid "this operation must be run in a work tree"
msgstr "operasi ini harus dijalankan di dalam pohon kerja"
@@ -14894,11 +14997,6 @@ msgstr ""
"perlihatkan referensi dari masukan standar yang tidak ada dalam repositori "
"lokal"
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "hanya salah satu dari '%s', '%s', dan '%s' dapat diberikan"
-
#: builtin/sparse-checkout.c
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16720,7 +16818,7 @@ msgstr "Tidak ada cabang sumber yang mungkin, menyimpulkan '--orphan'"
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
@@ -16734,7 +16832,7 @@ msgstr ""
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
@@ -16814,6 +16912,11 @@ msgstr "menginisialisasi"
#: builtin/worktree.c
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "tidak dapat menemukan pohon kerja yang dibuat '%s'"
+
+#: builtin/worktree.c
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Menyiapkan pohon kerja (cabang baru '%s')"
@@ -16859,11 +16962,6 @@ msgstr ""
"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 ""
"checkout <cabang> bahkan jika sudah di-checkout pada pohon kerja lainnya"
@@ -16877,7 +16975,7 @@ msgid "create or reset a branch"
msgstr "buat atau setel ulang sebuah cabang"
#: builtin/worktree.c
-msgid "create unborn/orphaned branch"
+msgid "create unborn branch"
msgstr "buat cabang belum lahir/yatim"
#: builtin/worktree.c
@@ -16907,12 +17005,8 @@ msgstr "Opsi '%s', '%s', dan '%s' tidak dapat digunakan bersamaan"
#: builtin/worktree.c
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "Opsi '%s', dan '%s' tidak dapat digunakan bersamaan"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<mirip-komit>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "opsi '%s' dan mirip-komit tidak dapat digunakan bersamaan"
#: builtin/worktree.c
msgid "added with --lock"
@@ -17715,6 +17809,12 @@ msgid "Create, list, delete refs to replace objects"
msgstr "Buat, daftar, hapus referensi untuk mengganti objek"
#: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EKSPERIMENTAL: Mainkan ulang komit pada dasar baru, dan juga bekerja pada "
+"repositori bare"
+
+#: command-list.h
msgid "Generates a summary of pending changes"
msgstr "Buat ringkasan perubahan tertunda"
@@ -18032,6 +18132,39 @@ msgid "commit-graph file is too small"
msgstr "berkas grafik komit terlalu kecil"
#: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "bingkah oid grafik komit kipas keluar salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "nilai kipas keluar grafik komit tidak berurutan"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "bingkah OID pencarian grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "bingkah data grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "bingkah generasi grafik komit salah ukuran"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "bingkah indeks grafik komit jalur berubah terlalu kecil"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"mengabaikan bingkah jalur berubah yang terlalu kecil (%<PRIuMAX> < "
+"%<PRIuMAX>) di dalam berkas grafik komit"
+
+#: commit-graph.c
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "tanda tangan grafik komit %X tidak cocok dengan tanda tangan %X"
@@ -18052,6 +18185,19 @@ msgid "commit-graph file is too small to hold %u chunks"
msgstr "berkas grafik komit terlalu kecil untuk menyimpan %u bingkah"
#: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"bingkah grafik komit OID kipas keluar yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "bingkah grafik komit OID pencarian yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "bingkah grafik komit data komit yang diperlukan hilang atau rusak"
+
+#: commit-graph.c
msgid "commit-graph has no base graphs chunk"
msgstr "grafik komit tidak punya bingkah grafik dasar"
@@ -18069,6 +18215,10 @@ msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "jumlah komit pada grafik dasar terlalu tinggi: %<PRIuMAX>"
#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "berkas rantai grafik komit terlalu kecil"
+
+#: commit-graph.c
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr "rantai grafik komit tidak cocok: baris '%s' bukan sebuah hash"
@@ -18095,6 +18245,10 @@ msgid "commit-graph overflow generation data is too small"
msgstr "data generasi luapan grafik komit terlalu kecil"
#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "penunjuk tepi tambahan grafik komit di luar batas"
+
+#: commit-graph.c
msgid "Loading known commits in commit graph"
msgstr "Memuat komit yang dikenal di grafik komit"
@@ -19505,6 +19659,11 @@ msgstr ""
msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "Nilai tidak dikenal untuk variabel konfigurasi 'diff.submodule': '%s'"
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "nilai tidak dikenal untuk konfigurasi '%s': %s"
+
#: diff.c
#, c-format
msgid ""
@@ -19602,14 +19761,6 @@ msgid "invalid mode '%s' in --color-moved-ws"
msgstr "mode tidak valid '%s' dalam --color-moved-ws"
#: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"opsi diff-algorithm terima \"myers\", \"minimal\", \"patience\" dan "
-"\"histogram\""
-
-#: diff.c
#, c-format
msgid "invalid argument to %s"
msgstr "argumen tidak valid ke %s"
@@ -19666,8 +19817,8 @@ msgid "output only the last line of --stat"
msgstr "keluarkan hanya baris terakhir --stat"
#: diff.c
-msgid "<param1,param2>..."
-msgstr "<parameter 1,parameter 2>..."
+msgid "<param1>,<param2>..."
+msgstr "<parameter 1>,<parameter 2>..."
#: diff.c
msgid ""
@@ -19680,8 +19831,8 @@ msgid "synonym for --dirstat=cumulative"
msgstr "sinonim untuk --dirstat=cumulative"
#: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "sinonim untuk --dirstat=files,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "sinonim untuk --dirstat=files,<parameter 1>,<parameter 2>..."
#: diff.c
msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19914,14 +20065,6 @@ msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "buat diff menggunakan algoritma \"diff histogram\""
#: diff.c
-msgid "<algorithm>"
-msgstr "<algoritma>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "pilih algoritma diff"
-
-#: diff.c
msgid "<text>"
msgstr "<teks>"
@@ -21730,6 +21873,14 @@ msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "OID kipas-keluar indeks multipak salah ukuran"
#: midx.c
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"kipas-keluar oid tidak berurutan: fanout[%d] =%<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
+#: midx.c
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "bingkah pencarian OID kipas-keluar indeks multipak salah ukuran"
@@ -21791,6 +21942,15 @@ msgid "bad pack-int-id: %u (%u total packs)"
msgstr "pack-int-id jelek: %u (total pak %u)"
#: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX tidak berisi bingkah BTMP"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "tidak dapat membuka pak terbitmap %<PRIu32>"
+
+#: midx.c
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr "indeks multipak simpan offset 64-bit, tapi off_t terlalu kecil"
@@ -21898,14 +22058,6 @@ msgid "Looking for referenced packfiles"
msgstr "Mencari berkas pak yang direferensikan"
#: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"kipas-keluar oid tidak berurutan: fanout[%d] =%<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
-#: midx.c
msgid "the midx contains no oid"
msgstr "midx tidak berisi oid"
@@ -22542,6 +22694,10 @@ msgstr "bitmap multipak kehilangan indeks balik yang diperlukan"
msgid "could not open pack %s"
msgstr "tidak dapat membuka '%s'"
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "tidak dapat menentukan pak MIDX terpilih"
+
#: pack-bitmap.c
#, c-format
msgid "preferred pack (%s) is invalid"
@@ -22567,6 +22723,11 @@ msgstr "bitmap ewah rusak: kepala terpotong untuk bitmap komit \"%s\""
#: pack-bitmap.c
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "tidak dapat memuat pak: '%s', mematikan penggunaan ulang pak"
+
+#: pack-bitmap.c
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "objek '%s' tidak ditemukan di bitmap tipe"
@@ -22680,6 +22841,10 @@ msgstr "posisi indeks balik tidak valid pada %<PRIu64>: %<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "bingkah indeks balik multipak salah ukuran"
+#: pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "tidak dapat menentukan pak terpilih"
+
#: pack-write.c
msgid "cannot both write and verify reverse index"
msgstr "tidak dapat kedua-duanya menulis dan memverifikasi indeks balik"
@@ -22762,11 +22927,6 @@ msgstr ""
#: 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)"
msgstr "opsi ambigu: %s (bisa jadi --%s%s atau --%s%s)"
@@ -23166,11 +23326,6 @@ msgstr "tidak dapat menambahkan '%s' ke indeks"
#: read-cache.c
#, c-format
-msgid "unable to stat '%s'"
-msgstr "tidak dapat men-stat '%s'"
-
-#: read-cache.c
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' muncul baik sebagai sebuah berkas dan sebagai sebuah direktori"
@@ -23855,17 +24010,12 @@ msgstr "'%s' ada; tidak dapat membuat '%s'"
msgid "cannot process '%s' and '%s' at the same time"
msgstr "tidak dapat memproses '%s' dan '%s' pada waktu yang bersamaan"
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "tidak dapat menghapus referensi %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete reference %s: %s"
msgstr "tidak dapat menghapus referensi %s: %s"
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete references: %s"
msgstr "tidak dapat menghapus referensi: %s"
@@ -24100,9 +24250,9 @@ msgid ""
msgstr ""
"Tujuan yang Anda berikan bukan nama referensi penuh (seperti \n"
"dimulai dengan \"refs/\"). Kami mencoba menebak maksud Anda dengan:\n"
-"- Cari referensi yang cocok dengan '%s' pada sisi remote.\n"
-"- Perika apakah <src> yang sedang didorong ('%s') adalah \n"
-" referensi pada \"refs/{heads,tags}\". Bila demikian kami \n"
+"- mencari referensi yang cocok dengan '%s' pada sisi remote.\n"
+"- memeriksa apakah <src> yang sedang didorong ('%s') adalah \n"
+" referensi pada \"refs/{heads,tags}/\". Bila demikian kami \n"
" menambahkan awalan refs/{heads,tags}/ yang bersesuaian pada sisi \n"
" remote.\n"
"\n"
@@ -25042,7 +25192,7 @@ msgstr "identitas pengarang tidak valid '%s'"
msgid "corrupt author: missing date information"
msgstr "pengarang rusak: informasi tanggal hilang"
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
#, c-format
msgid "could not update %s"
msgstr "tidak dapat memperbarui %s"
@@ -25537,6 +25687,10 @@ msgid "Autostash exists; creating a new stash entry."
msgstr "Stase otomatis sudah ada; membuat entri stase baru."
#: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "referensi autostase itu referensi simbolik"
+
+#: sequencer.c
msgid "could not detach HEAD"
msgstr "tidak dapat melepas HEAD"
@@ -25918,6 +26072,11 @@ msgstr "nama cabang asal salah: '%s'"
#: setup.c
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s diabaikan"
+
+#: setup.c
+#, c-format
msgid "unable to handle file type %d"
msgstr "tidak dapat menangani tipe berkas %d"
@@ -25931,14 +26090,16 @@ msgid "attempt to reinitialize repository with different hash"
msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
#: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s sudah ada"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"mencoba menginisialisasi ulang repositori dengan format penyimpanan "
+"referensi yang berbeda"
#: setup.c
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s diabaikan"
+msgid "%s already exists"
+msgstr "%s sudah ada"
#: setup.c
#, c-format
@@ -26264,14 +26425,6 @@ msgstr "bersihkan pohon tembolok sebelum setiap iterasi"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "jumlah entri di dalam pohon tembolok untuk dinirvalidasi (asali 0)"
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "opsi tak tertangani"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "kesalahan menyiapkan revisi"
-
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -26469,10 +26622,6 @@ msgstr "menyetel jalur layanan remote tidak didukung oleh protokol"
msgid "invalid remote service path"
msgstr "jalur layanan remote tidak valid"
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "operasi tidak didukung oleh protokol"
-
#: transport-helper.c
#, c-format
msgid "can't connect to subservice %s"
@@ -26638,11 +26787,6 @@ msgstr "dukungan untuk protokol v2 belum diterapkan"
#: transport.c
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "nilai tidak dikenal untuk konfigurasi '%s': %s"
-
-#: transport.c
-#, c-format
msgid "transport '%s' not allowed"
msgstr "transportasi '%s' tidak diperbolehkan"
@@ -26701,6 +26845,10 @@ msgstr "operasi bundle-uri tidak didukung oleh protokol"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "tidak dapat menerima daftar bundle-uri teriklankan server"
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "operasi tidak didukung oleh protokol"
+
#: tree-walk.c
msgid "too-short tree object"
msgstr "objek pohon terlalu pendek"
@@ -27722,6 +27870,11 @@ msgstr "juga indeks Anda berisi perubahan yang belum dikomit."
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "tidak dapat %s: indeks Anda berisi perubahan yang belum dikomit."
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "nilai gaya tidak dikenal '%s' diberikan untuk '%s'"
+
#: git-merge-octopus.sh git-merge-resolve.sh
msgid ""
"Error: Your local changes to the following files would be overwritten by "
diff --git a/po/sv.po b/po/sv.po
index cd2a86a4a5..786c2f749e 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,14 +1,14 @@
# Swedish translations for Git.
-# Copyright (C) 2010-2023 Peter Krefting <peter@softwolves.pp.se>
+# Copyright (C) 2010-2024 Peter Krefting <peter@softwolves.pp.se>
# This file is distributed under the same license as the Git package.
-# Peter Krefting <peter@softwolves.pp.se>, 2010-2023.
+# Peter Krefting <peter@softwolves.pp.se>, 2010-2024.
#
msgid ""
msgstr ""
-"Project-Id-Version: git 2.43.0\n"
+"Project-Id-Version: git 2.44.0\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-09 14:12+0100\n"
-"PO-Revision-Date: 2023-11-09 14:28+0100\n"
+"POT-Creation-Date: 2024-02-16 07:58+0100\n"
+"PO-Revision-Date: 2024-02-16 07:59+0100\n"
"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
"Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
@@ -1418,6 +1418,10 @@ msgid "Unexpected option --output"
msgstr "Oväntad flagga --output"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "falsk konfigureringsparameter: â€%sâ€"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Okänt arkivformat â€%sâ€"
@@ -1463,6 +1467,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "felaktig --attr-source eller GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "kan inte ta status pÃ¥ â€%sâ€"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "kunde inte läsa %s"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Felaktigt citerat innehÃ¥ll i filen â€%sâ€: %s"
@@ -2582,7 +2594,7 @@ msgid "do not show object names of boundary commits (Default: off)"
msgstr "visa inte objektnamn för gränsincheckningar (Standard: av)"
msgid "do not treat root commits as boundaries (Default: off)"
-msgstr "vehandla inte rotincheckningar som gränser (Standard: av)"
+msgstr "behandla inte rotincheckningar som gränser (Standard: av)"
msgid "show work cost statistics"
msgstr "visa statistik över arbetskostnad"
@@ -2734,12 +2746,12 @@ msgid "couldn't look up commit object for '%s'"
msgstr "kunde inte slÃ¥ upp incheckningsobjekt för â€%sâ€"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"grenen â€%s†har inte slagits samman i sin helhet.\n"
-"Om du är säker pÃ¥ att du vill ta bort den, kör â€git branch -D %sâ€"
+msgid "the branch '%s' is not fully merged"
+msgstr "grenen â€%s†har inte slagits samman i sin helhet"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Om du är säker pÃ¥ att du vill ta bort den, kör â€git branch -D %sâ€"
msgid "update of config-file failed"
msgstr "misslyckades uppdatera konfigurationsfil"
@@ -3772,8 +3784,8 @@ msgstr "tvinga utcheckning (kasta bort lokala ändringar)"
msgid "new-branch"
msgstr "ny-gren"
-msgid "new unparented branch"
-msgstr "ny gren utan förälder"
+msgid "new unborn branch"
+msgstr "ny ofödd gren"
msgid "update ignored files (default)"
msgstr "uppdatera ignorerade filer (standard)"
@@ -4023,9 +4035,6 @@ msgstr ""
"clean.requireForce har standardvärdet true och varken -i, -n eller -f "
"angavs; vägrar städa"
-msgid "-x and -X cannot be used together"
-msgstr "-x och -X kan inte användas samtidigt"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<flaggor>] [--] <arkiv> [<kat>]"
@@ -4113,6 +4122,9 @@ msgstr "gitkat"
msgid "separate git dir from working tree"
msgstr "separera gitkatalogen från arbetskatalogen"
+msgid "specify the reference format to use"
+msgstr "använd referensformatet som ska användas"
+
msgid "key=value"
msgstr "nyckel=värde"
@@ -4228,12 +4240,9 @@ msgstr "För många argument."
msgid "You must specify a repository to clone."
msgstr "Du måste ange ett arkiv att klona."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri är inkompatibelt med --depth, --shallow-since och --shallow-"
-"exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "okänt format för lagring av referenser â€%sâ€"
#, c-format
msgid "repository '%s' does not exist"
@@ -4361,7 +4370,7 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <kat>] [--append]\n"
" [--split[=<strategi>]] [--reachable | --stdin-packs | "
@@ -6675,6 +6684,10 @@ msgid "unable to read tree (%s)"
msgstr "kunde inte läsa träd (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "kunde inte läsa trädet %s"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "kunde inte â€grep†frÃ¥n objekt av typen %s"
@@ -7081,10 +7094,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "SHA1-KOLLISION UPPTÄCKT VID %s !"
#, c-format
-msgid "unable to read %s"
-msgstr "kunde inte läsa %s"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "kan inte läsa information om befintligt objekt %s"
@@ -7224,11 +7233,13 @@ msgstr "fsck-fel i packat objekt"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<mallkatalog>]\n"
" [--separate-git-dir <git-kat>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <grennamn> | --initial-branch=<grennamn>]\n"
" [--shared[=<behörigheter>]] [<katalog>]"
@@ -7927,6 +7938,12 @@ msgstr ""
"git merge-file [<alternativ>] [-L <namn1> [-L <orig> [-L <namn2>]]] <fil1> "
"<origfil> <fil2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"flaggan diff-algorithm godtar â€myersâ€, â€minimalâ€, â€patience†och â€histogramâ€"
+
msgid "send results to standard output"
msgstr "sänd resultat till standard ut"
@@ -7948,6 +7965,12 @@ msgstr "för konflikter, använd deras version"
msgid "for conflicts, use a union version"
msgstr "för konflikter, använd en förenad version"
+msgid "<algorithm>"
+msgstr "<algoritm>"
+
+msgid "choose a diff algorithm"
+msgstr "välj en diff-algoritm"
+
msgid "for conflicts, use this marker size"
msgstr "för konflikter, använd denna markörstorlek"
@@ -8038,9 +8061,6 @@ msgstr "--trivial-merge är inkompatibelt med andra flaggor"
msgid "unknown strategy option: -X%s"
msgstr "okänd strategiflagga: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base är inkompatibel med --stdin"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "felaktig indatarad: â€%sâ€."
@@ -8967,6 +8987,10 @@ msgid "inconsistency with delta count"
msgstr "deltaräknaren är inkonsekvent"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "felaktigt värde för pack.allowPackReuse: â€%sâ€"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9219,10 +9243,10 @@ msgstr "Räknar upp objekt"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Totalt %<PRIu32> (delta %<PRIu32>), återanvände %<PRIu32> (delta %<PRIu32>), "
-"paket-återanvända %<PRIu32>"
+"paket-återanvända %<PRIu32> (från %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10176,13 +10200,6 @@ msgid "switch `C' expects a numerical value"
msgstr "flaggan â€C†förväntar ett numeriskt värde"
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"argument för â€apply†är inkompatibla med rebase.autoSquash. Överväg att "
-"lägga till --no-autosquash"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -11167,6 +11184,76 @@ msgstr "--convert-graft-file tar inga argument"
msgid "only one pattern can be given with -l"
msgstr "endast ett mönster kan anges med -l"
+msgid "need some commits to replay"
+msgstr "behöver några incheckningar för omspelning"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto och --advance kan inte kombineras"
+
+msgid "all positive revisions given must be references"
+msgstr "alla positiva revisioner som anges måste vara referenser"
+
+msgid "argument to --advance must be a reference"
+msgstr "argumentet till --advance måste vara en referens"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"kan inte flytta målet framåt när det finns flera källor eftersom ordningen "
+"inte kan fastställas"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"kan inte avgöra om den underförstådda processen är --advance eller --onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"kan inte flytta målet framåt när det finns flera källgrenar eftersom "
+"ordningen inte kan fastställas"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "kan inte avgöra den underförstådda basen för --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(EXPERIMENTELLT!) git replay ([--contained] --onto <nybas> | --advance "
+"<gren>) <revisions-intervall>..."
+
+msgid "make replay advance given branch"
+msgstr "låt omspelningen flytta den givna grenen framåt"
+
+msgid "replay onto given commit"
+msgstr "spela om ovanpå en given incheckning"
+
+msgid "advance all branches contained in revision-range"
+msgstr "flytta alla grenar som finns i revisionsintervallet framåt"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "flaggan --onto eller --advance måste anges"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"nÃ¥gra flaggor för revisionstraversering kommer överstyras eftersom â€%sâ€-"
+"biten i â€struct rev_info†kommer att tvingas"
+
+msgid "error preparing revisions"
+msgstr "fel när revisioner skulle förberedas"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "kan ännu inte spela om hela vägen ned till rotincheckningen!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "kan ännu inte spela om sammanslagningsincheckningar!"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11373,15 +11460,6 @@ msgstr "--prefix kräver ett argument"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "okänt läge för --abbrev-ref: %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden kan endast användas tillsammans med --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden kan kan inte användas tillsammans med --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden kan kan inte användas tillsammans med --remotes"
-
msgid "this operation must be run in a work tree"
msgstr "funktionen måste köras i en arbetskatalog"
@@ -11784,10 +11862,6 @@ msgstr "visa inte resultat på standard ut (användbart med --verify)"
msgid "show refs from stdin that aren't in local repository"
msgstr "visa referenser från standard in som inte finns i lokalt arkiv"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "endast en av â€%sâ€, â€%s†och â€%s†kan anges"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13232,13 +13306,13 @@ msgstr "Ingen möjlig källgren, använder â€--orphanâ€"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"Om meningen var att skapa en arbetskatalog från en ny föräldrals\n"
+"Om meningen var att skapa en arbetskatalog från en ny ofödd\n"
"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
"det med flaggan --orphan:\n"
"\n"
@@ -13246,13 +13320,13 @@ msgstr ""
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"Om meningen var att skapa en arbetskatalog från en ny föräldrals\n"
+"Om meningen var att skapa en arbetskatalog från en ny ofödd\n"
"gren (gren utan incheckningar) för det här arkivet kan du göra\n"
"det med flaggan --orphan:\n"
"\n"
@@ -13315,6 +13389,10 @@ msgid "initializing"
msgstr "initierar"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "kunde inte hitta den skapade arbetskatalogen â€%sâ€"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Förbereder arbetskatalog (ny gren â€%sâ€)"
@@ -13352,10 +13430,6 @@ msgstr ""
"finns, avslutar; använd â€add -f†för att överstyra eller hämta frÃ¥n en fjärr "
"först"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "â€%s†och â€%s†kan inte användas samtidigt"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr ""
"checka ut <gren> även om den redan är utcheckad i en annan arbetskatalog"
@@ -13366,8 +13440,8 @@ msgstr "skapa en ny gren"
msgid "create or reset a branch"
msgstr "skapa eller återställ en gren"
-msgid "create unborn/orphaned branch"
-msgstr "skapa en ofödd/övergiven gren"
+msgid "create unborn branch"
+msgstr "skapa en ofödd gren"
msgid "populate the new working tree"
msgstr "befolka den nya arbetskatalogen"
@@ -13389,11 +13463,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "flaggorna â€%sâ€, â€%s†och â€%s†kan inte användas samtidigt"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "flaggorna â€%s†och â€%s†kan inte användas samtidigt"
-
-msgid "<commit-ish>"
-msgstr "<incheckning-igt>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "flaggorna â€%s†och incheckning-igt kan inte användas samtidigt"
msgid "added with --lock"
msgstr "lagt till med --lock"
@@ -14006,6 +14077,11 @@ msgstr "Packa opackade objekt i ett arkiv"
msgid "Create, list, delete refs to replace objects"
msgstr "Skapa, visa, ta bort referenser för att ersätta objekt"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"EXPERIMENTELLT: Spela om incheckningar ovanpå en ny bas, fungerar även med "
+"nakna arkiv"
+
msgid "Generates a summary of pending changes"
msgstr "Skapar en sammanfattning av väntande ändringar"
@@ -14243,6 +14319,32 @@ msgstr "Verktyg för att hantera stora Git-arkiv"
msgid "commit-graph file is too small"
msgstr "incheckningsgraffilen %s är för liten"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "incheckningsgrafens oid-utbredningsstycke har fel storlek"
+
+msgid "commit-graph fanout values out of order"
+msgstr "incheckningsgrafens utbredningsvärden är i fel ordning"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "incheckningsgrafens OID-uppslagningsstycket har fel storlek"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "incheckningsgrafens incheckningsdatastycke har fel storlek"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "incheckningsgrafens generationsstycke har fel storlek"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "incheckningsgrafens ändrade-sökvägar-indexstycke är förö litet"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"ignorerar för litet ändrade-sökvägar-stycke (%<PRIuMAX> < %<PRIuMAX>) i "
+"incheckningsgraffilen"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "incheckningsgrafens signatur %X stämmer inte med signaturen %X"
@@ -14259,6 +14361,18 @@ msgstr "incheckningsgrafens hashversion %X stämmer inte med versionen %X"
msgid "commit-graph file is too small to hold %u chunks"
msgstr "incheckningsgraffilen är för liten för att innehålla %u stycken"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga OID-utbredningsstycke saknas eller är trasigt"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga OID-uppslagningsstycke saknas eller är trasigt"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"incheckningsgrafens nödvändiga incheckningsdatastycke saknas eller är trasigt"
+
msgid "commit-graph has no base graphs chunk"
msgstr "incheckningsgrafen har inga bas-graf-stycken"
@@ -14272,6 +14386,9 @@ msgstr "incheckningsgrafens kedja stämmer inte"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "antalet incheckningar i basgrafen för högt: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "incheckningsgrafens kedjefil är för liten"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr "ogiltig incheckingsgrafkedja: rad â€%s†är inte ett hash-värde"
@@ -14292,6 +14409,9 @@ msgstr "incheckningsgraf kräver spillgenerationsdata, men har ingen"
msgid "commit-graph overflow generation data is too small"
msgstr "incheckningsgrafens spillgenerationsdata är för liten"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "incheckningsgrafens extra-kant-pekare är utanför intervallet"
+
msgid "Loading known commits in commit graph"
msgstr "Läser in kända incheckningar i incheckningsgraf"
@@ -15431,6 +15551,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "Okänt värde för konfigurationsvariabeln â€diff.submoduleâ€: â€%sâ€"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "okänt värde för inställningen â€%sâ€: %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -15509,12 +15633,6 @@ msgstr "felaktigt argument till --color-moved: %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "ogiltigt läge %s†i --color-moved-ws"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"flaggan diff-algorithm godtar â€myersâ€, â€minimalâ€, â€patience†och â€histogramâ€"
-
#, c-format
msgid "invalid argument to %s"
msgstr "ogiltigt argument för %s"
@@ -15558,8 +15676,8 @@ msgstr "maskinläsbar --stat"
msgid "output only the last line of --stat"
msgstr "skriv bara ut den sista raden för --stat"
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -15569,8 +15687,8 @@ msgstr ""
msgid "synonym for --dirstat=cumulative"
msgstr "synonym för --dirstat=cumulative"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "synonym för --dirstat=filer,param1,param2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "synonym för --dirstat=filer,<param1>,<param2>..."
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr "varna om ändringar introducerar konfliktmarkörer eller blankstegsfel"
@@ -15744,12 +15862,6 @@ msgstr "skapa diffar med algoritmen â€patience diffâ€"
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "skapa diffar med algoritmen â€histogram diffâ€"
-msgid "<algorithm>"
-msgstr "<algoritm>"
-
-msgid "choose a diff algorithm"
-msgstr "välj en diff-algoritm"
-
msgid "<text>"
msgstr "<text>"
@@ -17220,6 +17332,12 @@ msgstr "misslyckades läsa cachen"
msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "OID-utbredning för multi-pack-index har fel storlek"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid-utbredning i fel ordning: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "OID-uppslagningsstycket för multi-pack-index har fel storlek"
@@ -17270,6 +17388,13 @@ msgstr "paketnamn för multi-pack-index i fel ordning: â€%s†före â€%sâ€"
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "bad pack-int-id: %u (%u paket totalt)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX innehåller inte BTMP-stycket"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "kunde inte läsa det bitmappade paketet %<PRIu32>"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr "multi-pack-index innehåller 64-bitars offset, men off_t är för liten"
@@ -17353,12 +17478,6 @@ msgstr "felaktig kontrollsumma"
msgid "Looking for referenced packfiles"
msgstr "Ser efter refererade packfiler"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"oid-utbredning i fel ordning: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "midx saknar oid"
@@ -17875,6 +17994,9 @@ msgstr "flerpaketsbitkarta saknar nödvändigt omvänt index"
msgid "could not open pack %s"
msgstr "kunde inte öppna paketfilen %s"
+msgid "could not determine MIDX preferred pack"
+msgstr "kunde inte bestämma det föredragna MIDX-paketet"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "föredragen paketfil (%s) är ogiltig"
@@ -17895,6 +18017,10 @@ msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
msgstr "trasig ewah-bitkarta: avhugget huvud för bitkarta för incheckning â€%sâ€"
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "kunde inte läsa paketet: â€%sâ€, inaktiverar Ã¥teranvändning av paket"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "objektet â€%s†hittades inte i typbitkartor"
@@ -17985,6 +18111,9 @@ msgstr "ogiltig rev-indexposition vid %<PRIu64>: %<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "baklängesindex-stycke för multi-pack-index har fel storlek"
+msgid "could not determine preferred pack"
+msgstr "kunde inte bestämma föredraget paket"
+
msgid "cannot both write and verify reverse index"
msgstr "kan inte både skriva och bekräfta reverse-index"
@@ -18048,10 +18177,6 @@ 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)"
@@ -18365,10 +18490,6 @@ msgid "unable to add '%s' to index"
msgstr "kan inte lägga till â€%s†till indexet"
#, c-format
-msgid "unable to stat '%s'"
-msgstr "kan inte ta status pÃ¥ â€%sâ€"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "â€%s†finns bÃ¥de som en fil och en katalog"
@@ -18941,10 +19062,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "kan inte hantera â€%s†och â€%s†samtidigt"
#, c-format
-msgid "could not remove reference %s"
-msgstr "kunde inte ta bort referensen %s"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "kunde inte ta bort referensen %s: %s"
@@ -19138,7 +19255,7 @@ msgstr ""
"- Se efter en referens som motsvarar â€%s†pÃ¥ fjärrsidan.\n"
"- Se om <källan> som sänds (â€%sâ€)\n"
" är en referens i â€refs/{heads,tags}/â€. Om sÃ¥ lägger vi till\n"
-" motsvarande refs/{heads,tags}/-prefix på fjärrsidan.\n"
+" motsvarande â€refs/{heads,tags}/â€-prefix pÃ¥ fjärrsidan.\n"
"\n"
"Inget av dem fungerade, så vi gav upp. Ange fullständig referens."
@@ -20299,6 +20416,9 @@ msgstr "Tillämpning av autostash gav konflikter."
msgid "Autostash exists; creating a new stash entry."
msgstr "Autostash finns; skapar ny stash-post."
+msgid "autostash reference is a symref"
+msgstr "autostash-referensen är en symbolisk referens"
+
msgid "could not detach HEAD"
msgstr "kunde inte koppla från HEAD"
@@ -20611,6 +20731,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "ogiltigt namn pÃ¥ första gren: â€%sâ€"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ignorerade --initial-branch=%s"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "kan inte hantera filtyp %d"
@@ -20621,15 +20745,15 @@ msgstr "kan inte flytta %s till %s"
msgid "attempt to reinitialize repository with different hash"
msgstr "försöker initiera arkivet på nytt med annan hash"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "försöker initiera arkivet på nytt med annat referenslagringsformat"
+
#, c-format
msgid "%s already exists"
msgstr "%s finns redan"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: ignorerade --initial-branch=%s"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "Ominitierade befintligt delat Git-arkiv i %s%s\n"
@@ -20893,12 +21017,6 @@ msgstr "töm cacheträdet före varje iteration"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "antal poster i cacheträdet att ogiltigförklara (förval är 0)"
-msgid "unhandled options"
-msgstr "flaggor som inte hanterats"
-
-msgid "error preparing revisions"
-msgstr "fel när revisioner skulle förberedas"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "incheckning %s är inte märkt nåbar"
@@ -21054,9 +21172,6 @@ msgstr "protkollet stöder inte att sätta sökväg till fjärrtjänst"
msgid "invalid remote service path"
msgstr "felaktig sökväg till fjärrtjänst"
-msgid "operation not supported by protocol"
-msgstr "funktionen stöds inte av protokollet"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "kan inte ansluta till undertjänsten %s"
@@ -21187,10 +21302,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "stöd för protokoll v2 ännu ej implementerat"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "okänt värde för inställningen â€%sâ€: %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "transporten â€%s†tillÃ¥ts inte"
@@ -21243,6 +21354,9 @@ msgstr "bundle-uri-funktionen stöds inte av protokollet"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "kunde inte hämta bundle-uri-listan som servern annonserade"
+msgid "operation not supported by protocol"
+msgstr "funktionen stöds inte av protokollet"
+
msgid "too-short tree object"
msgstr "trädobjekt för kort"
@@ -22066,6 +22180,10 @@ msgstr "dessutom innehåller dit index ändringar som inte har checkats in."
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "kan inte %s: Ditt index innehåller ändringar som inte checkats in."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "okänd stil â€%s†angavs för â€%sâ€"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
diff --git a/po/tr.po b/po/tr.po
index f01962d1db..5837752d0b 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -1,8 +1,8 @@
# Turkish translations for Git
# Git Türkçe çevirileri
-# Copyright (C) 2020-2023 Emir SARI <emir_sari@icloud.com>
+# Copyright (C) 2020-2024 Emir SARI <emir_sari@icloud.com>
# This file is distributed under the same license as the Git package.
-# Emir SARI <emir_sari@icloud.com>, 2020-2023
+# Emir SARI <emir_sari@icloud.com>, 2020-2024
#
# ######################################################### #
# Git Türkçe kavramlar dizini / Git Turkish Glossary #
@@ -20,13 +20,14 @@
# clone | klon(lamak) #
# commit (ad) | iÅŸleme #
# commit (eyl.) | iÅŸlemek #
-# commitish | iÅŸlememsi #
+# commit-ish | iÅŸlememsi #
# conflict | çakışma #
# cruft | süprüntü #
# dangling object | sallanan nesne #
# detached HEAD | ayrık HEAD #
# dirty | kirli #
# evil merge | uÄŸursuz birleÅŸtirme #
+# fanout | çıkış sayısı #
# fast-forward | ileri sarım/sarmak #
# fetch | getirme(k) #
# fixup | düzeltmek #
@@ -48,10 +49,10 @@
# pathspec | yol belirteci #
# pattern | dizgi #
# porcelain | okunabilir #
-# prune | budamak #
+# prune | buda(mak) #
# pseudoref | yalancıktan başvuru #
-# pull | çekme(k) #
-# push | itme(k) #
+# pull | çek(mek) #
+# push | it(mek) #
# rebase | yeniden temellendirme(k) #
# record | kayıt yaz(mak) #
# ref | baÅŸvuru #
@@ -85,6 +86,7 @@
# trailer | artbilgi #
# tree | ağaç #
# treeish | ağacımsı #
+# unborn | henüz doğmamış (dal) #
# unstage | hazırlıktan çıkar(mak) #
# upstream | üstkaynak #
# worktree/working tree | çalışma ağacı #
@@ -94,8 +96,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git Turkish Localization Project\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-09 11:15+0300\n"
-"PO-Revision-Date: 2023-11 13:00+0300\n"
+"POT-Creation-Date: 2024-02-16 22:04+0300\n"
+"PO-Revision-Date: 2024-02-16 22:00+0300\n"
"Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
"Language-Team: Turkish (https://github.com/bitigchi/git-po/)\n"
"Language: tr\n"
@@ -1500,6 +1502,10 @@ msgid "Unexpected option --output"
msgstr "Beklenmedik seçenek --output"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "fazladan komut satırı parametresi '%s'"
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Bilinmeyen arşiv biçimi '%s'"
@@ -1545,6 +1551,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "hatalı --attr-source veya GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "'%s' dosyasının bilgileri alınamıyor"
+
+#, c-format
+msgid "unable to read %s"
+msgstr "%s okunamıyor"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "'%s' dosyasında hatalı tırnağa alınmış içerik: %s"
@@ -2823,12 +2837,12 @@ msgid "couldn't look up commit object for '%s'"
msgstr "'%s' için işleme nesnesi aranamadı"
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"'%s' dalı tümüyle birleştirilmemiş.\n"
-"Eğer silmek istediğinizden eminseniz 'git branch -D %s' çalıştırın."
+msgid "the branch '%s' is not fully merged"
+msgstr "'%s' dalı tümüyle birleştirilmedi"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "Onu silmek istediğinizden eminseniz 'git branch -D %s' çalıştırın."
msgid "update of config-file failed"
msgstr "config-file güncellenemedi"
@@ -3863,8 +3877,8 @@ msgstr "zorla çıkış yap (yerel değişiklikleri çöpe at)"
msgid "new-branch"
msgstr "yeni dal"
-msgid "new unparented branch"
-msgstr "yeni üst ögesi olmayan dal"
+msgid "new unborn branch"
+msgstr "yeni henüz doğmamış dal"
msgid "update ignored files (default)"
msgstr "yok sayılan dosyaları güncelle (öntanımlı)"
@@ -4115,9 +4129,6 @@ msgstr ""
"clean.requireForce öntanımlı olarak 'true' ve ne -i ne -n ne de -f verilmiş; "
"temizleme reddediliyor"
-msgid "-x and -X cannot be used together"
-msgstr "-x ve -X birlikte kullanılamaz"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<seçenekler>] [--] <depo> [<dizin>]"
@@ -4205,6 +4216,9 @@ msgstr "git dizini"
msgid "separate git dir from working tree"
msgstr "git dizinini çalışma ağacından ayır"
+msgid "specify the reference format to use"
+msgstr "kullanılacak başvuru biçimini belirt"
+
msgid "key=value"
msgstr "anahtar=deÄŸer"
@@ -4322,11 +4336,9 @@ msgstr "Çok fazla argüman."
msgid "You must specify a repository to clone."
msgstr "Klonlamak için bir depo belirtmelisiniz."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri; --depth, --shallow-since ve --shallow-exclude ile uyumsuz"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "bilinmeyen başvuru depolama biçimi '%s'"
#, c-format
msgid "repository '%s' does not exist"
@@ -4454,11 +4466,11 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <dizin>] [--append]\n"
-" [--split[=<<strateji>]] [--reachable | --stdin-packs "
-"| --stdin-commits]\n"
+" [--split[=<strateji>]] [--reachable | --stdin-packs | "
+"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
" <bölme-seçenekleri>"
@@ -6778,6 +6790,10 @@ msgid "unable to read tree (%s)"
msgstr "ağaç okunamıyor (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "%s ağacı okunamıyor"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "%s türündeki bir nesneden grep yapılamıyor"
@@ -7190,10 +7206,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "%s İLE SHA1 ÇARPIŞMASI BULUNDU!"
#, c-format
-msgid "unable to read %s"
-msgstr "%s okunamıyor"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "var olan nesne bilgisi %s okunamıyor"
@@ -7333,13 +7345,15 @@ msgstr "paket nesnelerinde fsck hatası"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<ÅŸablon-dizini>]\n"
" [--separate-git-dir <git-dizini>] [--object-format=<biçim>]\n"
+" [--ref-format=<biçim>]\n"
" [-b <dal-adı> | --initial-branch=<dal-adı>]\n"
-" [--shared[=<izinler>]] [<dizin>]"
+" [--shared[=<izin>]] [<dizin>]"
msgid "permissions"
msgstr "izinler"
@@ -8039,6 +8053,13 @@ msgstr ""
"git merge-file [<seçenekler>] [-L <ad1> [-L <orij> [-L <ad2>]]] <dosya1> "
"<orij-dosya> <dosya2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"diff-algorithm seçeneği şunları kabul eder: \"myers\", \"minimal\", "
+"\"patience\" ve \"histogram\""
+
msgid "send results to standard output"
msgstr "sonuçları standart çıktıya gönder"
@@ -8060,6 +8081,12 @@ msgstr "çakışmalarda onların sürümünü kullan"
msgid "for conflicts, use a union version"
msgstr "çakışmalarda birlik olmuş bir sürüm kullan"
+msgid "<algorithm>"
+msgstr "<algoritma>"
+
+msgid "choose a diff algorithm"
+msgstr "bir diff algoritması seç"
+
msgid "for conflicts, use this marker size"
msgstr "çakışmalarda bu imleyici boyutunu kullan"
@@ -8150,9 +8177,6 @@ msgstr "--trivial-merge, tüm diğer seçeneklerle uyumsuz"
msgid "unknown strategy option: -X%s"
msgstr "bilinmeyen strateji seçeneği: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base, --stdin ile uyumsuz"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "hatalı oluşturulmuş girdi satırı: '%s'."
@@ -9078,6 +9102,10 @@ msgid "inconsistency with delta count"
msgstr "delta sayımında tutarsızlık"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "geçersiz pack.allowPackReuse değeri: '%s'"
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9332,10 +9360,10 @@ msgstr "Nesneler ortaya dökülüyor"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Toplam %<PRIu32> (delta %<PRIu32>), yeniden kullanılan %<PRIu32> (delta "
-"%<PRIu32>), yeniden kullanılan paket %<PRIu32>"
+"%<PRIu32>), yeniden kullanılan paket %<PRIu32> (%<PRIuMAX> konumundan)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10299,13 +10327,6 @@ msgid "switch `C' expects a numerical value"
msgstr "'C' anahtarı sayısal bir değer bekliyor"
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr ""
-"seçenekleri uygula, rebase.autoSquash ile uyumlu değil. --no-autosquash "
-"eklemeyi düşünün"
-
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr ""
@@ -10717,10 +10738,10 @@ msgid_plural ""
"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
"to delete them, use:"
msgstr[0] ""
-"Not: refs/remotes hiyerarşisi dışındaki bir dal kaldırılmadı;\n"
+"Not: refs/remotes/ hiyerarşisi dışındaki bir dal kaldırılmadı;\n"
"onu silmek için şunu kullanın:"
msgstr[1] ""
-"Not: refs/remotes hiyerarşisi dışındaki bazı dallar kaldırılmadı;\n"
+"Not: refs/remotes/ hiyerarşisi dışındaki bazı dallar kaldırılmadı;\n"
"onları silmek için şunu kullanın:"
#, c-format
@@ -11298,6 +11319,76 @@ msgstr "--convert-graft-file argüman almaz"
msgid "only one pattern can be given with -l"
msgstr "-l ile yalnızca bir dizgi verilebilir"
+msgid "need some commits to replay"
+msgstr "yeniden oynatmak için birkaç işleme gerekli"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto ve --advance birbiriyle uyumsuz"
+
+msgid "all positive revisions given must be references"
+msgstr "verilen tüm pozitif revizyonlar, başvuru olmalı"
+
+msgid "argument to --advance must be a reference"
+msgstr "--advance'a olan argüman bir başvuru olmalı"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"birden çok kaynaklı hedef ilerletilemiyor; çünkü sıralama hatalı tanımlanmış "
+"olurdu"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr ""
+"bunun --advance veya --onto işlemi olup olmadığı örtük olarak algılanamıyor"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"birden çok kaynak dallı hedef ilerletilemiyor; çünkü sıralama hatalı "
+"tanımlanmış olurdu"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "--onto için olan doğru temel örtük olarak algılanamıyor"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(DENEYSEL!) git replay ([--contained] --onto <yeni-temel> | --advance <dal>) "
+"<revizyon-erimi>..."
+
+msgid "make replay advance given branch"
+msgstr "verilen dalı önceden yeniden oynat"
+
+msgid "replay onto given commit"
+msgstr "verilen iÅŸlemeye yeniden oynat"
+
+msgid "advance all branches contained in revision-range"
+msgstr "revizyon eriminde içerilen tüm dalları ilerlet"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "--onto veya --advance seçeneğinin kullanımı zorunlu"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"'struct rev_info' içindeki '%s' biti zorlanacağından kimi revizyon yürütme "
+"seçenekleri geçersiz kılınacak"
+
+msgid "error preparing revisions"
+msgstr "revizyonlar hazırlanırken hata"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "kök işlemeye kadar yeniden oynatma henüz desteklenmiyor!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "birleştirme işlemelerini yeniden oynatma henüz desteklenmiyor!"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11506,15 +11597,6 @@ msgstr "--prefix bir argüman gerektiriyor"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "--abbrev-ref için bilinmeyen kip: %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden, --branches ile birlikte kullanılamıyor"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden, --tags ile birlikte kullanılamıyor"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden, --remotes ile birlikte kullanılamıyor"
-
msgid "this operation must be run in a work tree"
msgstr "bu işlem bir çalışma ağacı içinde çalıştırılmalı"
@@ -11920,9 +12002,6 @@ msgstr "sonuçları stdout'a yazdırma (--verify ile birlikte kullanışlı)"
msgid "show refs from stdin that aren't in local repository"
msgstr "stdin'den yerel bir depoda olmayan başvuruları göster"
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "yalnızca '%s', '%s' veya '%s' arasından biri verilebilir"
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -13366,29 +13445,29 @@ msgstr "Olası kaynak dal yok, '--orphan' anlamı çıkarılıyor"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"Bu depo için yeni bir yetim dal içeren (işlemesiz dal) bir\n"
-"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
-"ile yapabilirsiniz:\n"
+"Bu depo için henüz doğmamış bir dal (işleme içermeyen dal) içeren\n"
+"bir çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağını\n"
+"kullanarak yapabilirsiniz:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"Bu depo için yeni bir yetim dal içeren (işlemesiz dal) bir\n"
-"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
-"ile yapabilirsiniz:\n"
+"Bu depo için henüz doğmamış bir dal (işleme içermeyen dal) içeren\n"
+"bir çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağını\n"
+"kullanarak yapabilirsiniz:\n"
"\n"
" git worktree add --orphan %s\n"
@@ -13447,6 +13526,10 @@ msgid "initializing"
msgstr "ilklendiriliyor"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "oluşturulan '%s' çalışma ağacı bulunamadı"
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Çalışma ağacı hazırlanıyor (yeni dal '%s')"
@@ -13483,10 +13566,6 @@ msgstr ""
"Bir uzak konum olmasına rağmen hiçbir yerel/uzak başvuru yok, durduruluyor;\n"
"geçersiz kılmak veya önce bir uzak konum getirmek için 'add -f' kullanın"
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' ve '%s' birlikte kullanılamaz"
-
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "diğer çalışma ağacında çıkış yapılmış olsa bile <dal> çıkışını yap"
@@ -13496,8 +13575,8 @@ msgstr "yeni bir dal oluÅŸtur"
msgid "create or reset a branch"
msgstr "yeni bir dal oluştur veya sıfırla"
-msgid "create unborn/orphaned branch"
-msgstr "doğmamış/yetim bırakılmış dal oluştur"
+msgid "create unborn branch"
+msgstr "henüz doğmamış dal oluştur"
msgid "populate the new working tree"
msgstr "yeni çalışma ağacını doldur"
@@ -13519,11 +13598,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "'%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "'%s' ve '%s' seçenekleri birlikte kullanılamaz"
-
-msgid "<commit-ish>"
-msgstr "<iÅŸlememsi>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "'%s' seçeneği ve işlememsiler birlikte kullanılamaz"
msgid "added with --lock"
msgstr "--lock ile eklendi"
@@ -14135,6 +14211,10 @@ msgstr "Bir depodaki paketlenmemiÅŸ nesneleri paketle"
msgid "Create, list, delete refs to replace objects"
msgstr "Nesne değiştirmek için başvurular oluştur, sil, listele"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"DENEYSEL: İşlemeleri yeni temelde yeniden oynat, çıplak depolarla da çalışır"
+
msgid "Generates a summary of pending changes"
msgstr "Bekleyen değişikliklerin bir özetini çıkart"
@@ -14373,6 +14453,32 @@ msgstr "Büyük Git depolarını yönetmek için bir araç"
msgid "commit-graph file is too small"
msgstr "commit-graph dosyası pek küçük"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "commit-graph OID çıkış sayısı iri parçası boyutu yanlış"
+
+msgid "commit-graph fanout values out of order"
+msgstr "commit-graph çıkış sayısı değerleri sırasız: fanout[%d] = %u != %u"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "commit-graph OID arama iri parçası boyutu yanlış"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "commit-graph işleme verisi iri parçası boyutu yanlış"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "commit-graph kuşaklar iri parçası boyutu yanlış"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "commit-graph changed-path indeksi iri parçası boyutu pek küçük"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"commit-graph dosyasındaki pek küçük changed-path iri parçası (%<PRIuMAX> < "
+"%<PRIuMAX>) yok sayılıyor"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "commit-graph imzası %X, %X ile eşleşmiyor"
@@ -14389,6 +14495,17 @@ msgstr "commit-graph sağlama sürümü %X, %X ile eşleşmiyor"
msgid "commit-graph file is too small to hold %u chunks"
msgstr "commit-graph dosyası %u iri parça tutmak için pek küçük"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr ""
+"commit-graph'ten gerekli OID çıkış sayısı iri parçası eksik veya hasarlı"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "commit-graph'ten gerekli OID arama iri parçası eksik veya hasarlı"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr ""
+"commit-graph'ten gerekli OID çıkış sayısı iri parçası eksik veya hasarlı"
+
msgid "commit-graph has no base graphs chunk"
msgstr "commit-graph temel grafiği iri parçasına iye değil"
@@ -14402,6 +14519,9 @@ msgstr "commit-graph zinciri eÅŸleÅŸmiyor"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "temel grafikteki işleme sayısı pek yüksek: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "commit-graph zincir dosyası pek küçük"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr "geçersiz commit-graph zinciri: '%s'. satır bir sağlama değil"
@@ -14422,6 +14542,9 @@ msgstr "commit-graph, taşım oluşturma verisi gerektiriyor; ancak hiç yok"
msgid "commit-graph overflow generation data is too small"
msgstr "commit-graph, taşım üretim verisi pek küçük"
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "commit-graph extra-edges işaretçisi sınırlar dışında"
+
msgid "Loading known commits in commit graph"
msgstr "İşleme grafiğindeki bilinen işlemeler yükleniyor"
@@ -15563,6 +15686,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "'diff.submodule' yapılandırma değişkeni için bilinmeyen değer: '%s'"
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "'%s' yapılandırması için bilinmeyen değer: %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -15642,13 +15769,6 @@ msgstr "hatalı --color-moved argümanı: %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "--color-moved-ws içinde geçersiz kip '%s'"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm seçeneği şunları kabul eder: \"myers\", \"minimal\", "
-"\"patience\" ve \"histogram\""
-
#, c-format
msgid "invalid argument to %s"
msgstr "%s için geçersiz argüman"
@@ -15692,8 +15812,8 @@ msgstr "makinede okunabilen --stat"
msgid "output only the last line of --stat"
msgstr "--stat'ın yalnızca son satırını çıktı ver"
-msgid "<param1,param2>..."
-msgstr "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -15703,8 +15823,8 @@ msgstr ""
msgid "synonym for --dirstat=cumulative"
msgstr "--dirstat=cumulative eşanlamlısı"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "--dirstat=files,param1,param2... eşanlamlısı"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "--dirstat=files,<param1>,<param2>... eşanlamlısı"
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -15879,12 +15999,6 @@ msgstr "diff'i \"patience diff\" algoritmasını kullanarak oluştur"
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "diff'i \"histogram diff\" algoritmasını kullanarak oluştur"
-msgid "<algorithm>"
-msgstr "<algoritma>"
-
-msgid "choose a diff algorithm"
-msgstr "bir diff algoritması seç"
-
msgid "<text>"
msgstr "<metin>"
@@ -17367,7 +17481,13 @@ msgid "failed to read the cache"
msgstr "önbellek okunamadı"
msgid "multi-pack-index OID fanout is of the wrong size"
-msgstr "multi-pack-index OID ikiye bölümünün boyutu hatalı"
+msgstr "multi-pack-index OID çıkış sayısı boyutu yanlış"
+
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"oid çıkış sayısı sırasız: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "multi-pack-index OID arama iri parçası yanlış boyutlu"
@@ -17415,6 +17535,13 @@ msgstr "multi-pack-index paket adlarının sırasız: '%s' şundan önce: '%s'"
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "hatalı pack-int-id: %u (%u toplam paket)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX, BTMP iri parçasını içermiyor"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "biteşlemli %<PRIu32> paketi yüklenemedi"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr "multi-pack-index bir 64 bit ofset depoluyor; ancak off_t pek küçük"
@@ -17498,11 +17625,6 @@ msgstr "yanlış sağlama toplamı"
msgid "Looking for referenced packfiles"
msgstr "Başvurulmuş paket dosyaları aranıyor"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "oid fanout sırasız: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "midx bir oid içermiyor"
@@ -18022,9 +18144,12 @@ msgstr "çoklu paket biteşlemi gereken ters indeksi içermiyor"
msgid "could not open pack %s"
msgstr "%s paketi açılamadı"
+msgid "could not determine MIDX preferred pack"
+msgstr "MIDX yeğlenen paketi algılanamadı"
+
#, c-format
msgid "preferred pack (%s) is invalid"
-msgstr "tercih edilen (%s) paket geçersiz"
+msgstr "yeğlenen paket (%s) geçersiz"
msgid "corrupt bitmap lookup table: triplet position out of index"
msgstr "hasarlı biteşlem arama tablosu: üçlü konum indeks dışında"
@@ -18042,6 +18167,10 @@ msgstr ""
"hasarlı ewah biteşlemi: \"%s\" işlemesinin biteşleminde kısaltılmış üstbilgi"
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "paket yüklenemiyor: '%s', pack-reuse devre dışı bırakılıyor"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "'%s' nesnesi, tür biteşlemlerinde bulunamadı"
@@ -18132,6 +18261,9 @@ msgstr "%<PRIu64> konumunda geçersiz rev-index konumu: %<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "multi-pack-index reverse-index iri parçası yanlış boyutlu"
+msgid "could not determine preferred pack"
+msgstr "yeğlenen paket algılanamadı"
+
msgid "cannot both write and verify reverse index"
msgstr "ters indeks dosyası hem yazılıp hem doğrulanamıyor"
@@ -18195,10 +18327,6 @@ 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)"
@@ -18512,10 +18640,6 @@ msgid "unable to add '%s' to index"
msgstr "'%s' indekse eklenemiyor"
#, c-format
-msgid "unable to stat '%s'"
-msgstr "'%s' dosyasının bilgileri alınamıyor"
-
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' hem bir dosya hem de bir dizin olarak görünüyor"
@@ -19087,10 +19211,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "'%s' ve '%s' aynı anda işlenemiyor"
#, c-format
-msgid "could not remove reference %s"
-msgstr "%s başvurusu kaldırılamadı"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "%s baÅŸvurusu silinemedi: %s"
@@ -20446,6 +20566,9 @@ msgstr "Kendiliğinden zulalama çakışmalara neden oldu."
msgid "Autostash exists; creating a new stash entry."
msgstr "KendiliÄŸinden zulalama mevcut; yeni bir zula girdisi oluÅŸturuluyor."
+msgid "autostash reference is a symref"
+msgstr "kendiliÄŸinden zulalama baÅŸvurusu bir sembol baÅŸvurusu"
+
msgid "could not detach HEAD"
msgstr "HEAD ayrılamadı"
@@ -20757,6 +20880,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "geçersiz başlangıç dalı adı: '%s'"
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: --initial-branch=%s yok sayıldı"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "%d dosya türü ele alınamıyor"
@@ -20767,15 +20894,16 @@ msgstr "%s şuraya taşınamıyor: %s"
msgid "attempt to reinitialize repository with different hash"
msgstr "depoyu baÅŸka bir saÄŸlama ile yeniden ilklendirme deneniyor"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr ""
+"depo başka bir başvuru depolama biçimiyle yeniden ilklendirilmeye çalışılıyor"
+
#, c-format
msgid "%s already exists"
msgstr "%s halihazırda var"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s yok sayıldı"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "%s%s içindeki var olan paylaşılan Git deposu yeniden ilklendirildi\n"
@@ -21039,12 +21167,6 @@ msgstr "her bir yinelemeden önce önbellek ağacını temizle"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "önbellek ağacındaki geçersizleştirilecek girdi sayısı (öntanımlı 0)"
-msgid "unhandled options"
-msgstr "beklenmeyen seçenekler"
-
-msgid "error preparing revisions"
-msgstr "revizyonlar hazırlanırken hata"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "%s işlemesi ulaşılabilir olarak imlenmedi"
@@ -21198,9 +21320,6 @@ msgstr "uzak servis yolu ayarlama protokol tarafından desteklenmiyor"
msgid "invalid remote service path"
msgstr "geçersiz uzak konum servis yolu"
-msgid "operation not supported by protocol"
-msgstr "işlem protokol tarafından desteklenmiyor"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "%s altservisine bağlanılamıyor"
@@ -21331,10 +21450,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "protokol v2 desteği henüz yerine getirilmedi"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "'%s' yapılandırması için bilinmeyen değer: %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "'%s' taşıyıcısına izin verilmiyor"
@@ -21387,6 +21502,9 @@ msgstr "bundle-uri işlemi protokol tarafından desteklenmiyor"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "sunucu tarafından tanıtılan bundle-uri listesi alınamadı"
+msgid "operation not supported by protocol"
+msgstr "işlem protokol tarafından desteklenmiyor"
+
msgid "too-short tree object"
msgstr "ağaç nesnesi çok kısa"
@@ -22210,6 +22328,10 @@ msgstr "Ek olarak, indeksiniz işlenmemiş değişiklikler içeriyor."
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "%s yapılamıyor: İndeksiniz işlenmemiş değişiklikler içeriyor."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "'%s' bilinmeyen biçemi şunun için verildi: '%s'"
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
diff --git a/po/uk.po b/po/uk.po
index 086890552a..0507e387ba 100644
--- a/po/uk.po
+++ b/po/uk.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: Git v2.43\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
"POT-Creation-Date: 2023-11-09 14:26-0800\n"
-"PO-Revision-Date: 2023-11-09 14:34-0800\n"
+"PO-Revision-Date: 2024-02-11 09:26-0800\n"
"Last-Translator: Arkadii Yakovets <ark@cho.red>\n"
"Language-Team: Ukrainian <https://github.com/arkid15r/git-uk-l10n/>\n"
"Language: uk\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
-"X-Generator: Poedit 3.4.1\n"
+"X-Generator: Poedit 3.4.2\n"
#, c-format
msgid "Huh (%s)?"
@@ -1444,6 +1444,10 @@ msgid "Unexpected option --output"
msgstr "Ðеочікувана Ð¾Ð¿Ñ†Ñ–Ñ --output"
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "зайвий параметр командного Ñ€Ñдка: \"%s\""
+
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "Ðевідомий формат архіву \"%s\""
@@ -1489,6 +1493,14 @@ msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "невірний --attr-source або GIT_ATTR_SOURCE"
#, c-format
+msgid "unable to stat '%s'"
+msgstr "не вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ stat Ð´Ð»Ñ \"%s\""
+
+#, c-format
+msgid "unable to read %s"
+msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ %s"
+
+#, c-format
msgid "Badly quoted content in file '%s': %s"
msgstr "Ðевірно процитований вміÑÑ‚ у файлі \"%s\": %s"
@@ -1666,10 +1678,12 @@ msgstr ""
msgid "not tracking: ambiguous information for ref '%s'"
msgstr "не відÑтежуєтьÑÑ: неоднозначна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð´Ð»Ñ Ð¿Ð¾ÑÐ¸Ð»Ð°Ð½Ð½Ñ \"%s\""
+#. #-#-#-#-# branch.c.po #-#-#-#-#
#. TRANSLATORS: This is a line listing a remote with duplicate
#. refspecs in the advice message below. For RTL languages you'll
#. probably want to swap the "%s" and leading " " space around.
#.
+#. #-#-#-#-# object-name.c.po #-#-#-#-#
#. TRANSLATORS: This is line item of ambiguous object output
#. from describe_ambiguous_object() above. For RTL languages
#. you'll probably want to swap the "%s" and leading " " space
@@ -2325,7 +2339,7 @@ msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
msgstr ""
-"git bisect start [--term-{new,bad}=<термін> --term-{old,good}=<термін>] "
+"git bisect start [--term-(new,bad)=<термін> --term-(old,good)=<термін>] "
"[--no-checkout] [--first-parent] [<поганий> [<добрий>...]] [--] "
"[<визначник шлÑху>...]"
@@ -2785,11 +2799,12 @@ msgid "couldn't look up commit object for '%s'"
msgstr "не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ об’єкт коміту Ð´Ð»Ñ \"%s\""
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "гілка \"%s\" злита не повніÑÑ‚ÑŽ"
+
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
msgstr ""
-"гілка \"%s\" злита не повніÑÑ‚ÑŽ.\n"
"Якщо ви впевнені, що хочете її видалити, виконайте \"git branch -D %s\""
msgid "update of config-file failed"
@@ -2856,11 +2871,11 @@ msgstr "неприпуÑтима назва гілки: \"%s\""
#, c-format
msgid "no commit on branch '%s' yet"
-msgstr "поки що немає комітів в гілці \"%s\""
+msgstr "поки що немає комітів у гілці \"%s\""
#, c-format
msgid "no branch named '%s'"
-msgstr "немає гілки з ім’Ñм \"%s\""
+msgstr "немає гілки з назвою \"%s\""
msgid "branch rename failed"
msgstr "не вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ñ‚Ð¸ гілку"
@@ -3078,7 +3093,7 @@ msgid ""
"'--set-upstream-to' instead"
msgstr ""
"Ð¾Ð¿Ñ†Ñ–Ñ \"--set-upstream\" більше не підтримуєтьÑÑ. Будь лаÑка, викориÑтовуйте "
-"\"--track\" або \"--set-upstream-to\""
+"\"--track\" або \"--set-upstream-to\" заміÑÑ‚ÑŒ неї"
msgid "git version:\n"
msgstr "верÑÑ–Ñ git:\n"
@@ -3848,8 +3863,8 @@ msgstr "переключити примуÑово (викинути локаль
msgid "new-branch"
msgstr "нова-гілка"
-msgid "new unparented branch"
-msgstr "нова гілка без джерела"
+msgid "new unborn branch"
+msgstr "нова ненароджена гілка"
msgid "update ignored files (default)"
msgstr "оновити ігноровані файли (за замовчуваннÑм)"
@@ -4100,9 +4115,6 @@ msgstr ""
"clean.requireForce вÑтановлено у true за замовчуваннÑм Ñ– не задано ні -i, ні "
"-n, ні -f; відмовлено в прибиранні"
-msgid "-x and -X cannot be used together"
-msgstr "-x та -X не можна викориÑтовувати разом"
-
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<опції>] [--] <Ñховище> [<директоріÑ>]"
@@ -4193,6 +4205,9 @@ msgstr "git директоріÑ"
msgid "separate git dir from working tree"
msgstr "відокремити git-директорію від робочого дерева"
+msgid "specify the reference format to use"
+msgstr "вкажіть формат поÑиланнÑ, Ñкий потрібно викориÑтовувати"
+
msgid "key=value"
msgstr "ключ=значеннÑ"
@@ -4311,11 +4326,9 @@ msgstr "Забагато аргументів."
msgid "You must specify a repository to clone."
msgstr "Треба вказати Ñховище Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ."
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr ""
-"--bundle-uri неÑуміÑний з --depth, --shallow-since та --shallow-exclude"
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "невідомий формат Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ð¾Ñилань \"%s\""
#, c-format
msgid "repository '%s' does not exist"
@@ -4450,7 +4463,7 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <директоріÑ>] [--append] [--object-dir "
"<директоріÑ>] [--append] [--object-dir <директоріÑ>] [--append\n"
@@ -4458,7 +4471,7 @@ msgstr ""
"| --stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <чиÑло>] [--"
"[no-]progress]\n"
-" <опції розділеннÑ>"
+" <опції-розділеннÑ>"
msgid "dir"
msgstr "директоріÑ"
@@ -6757,7 +6770,7 @@ msgid "scheduler to trigger git maintenance run"
msgstr "планувальник Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ git"
msgid "failed to set up maintenance schedule"
-msgstr "не вдалоÑÑ Ð²Ñтановити графік обÑлуговуваннÑ"
+msgstr "не вдалоÑÑ Ð²Ñтановити розклад обÑлуговуваннÑ"
msgid "failed to add repo to global config"
msgstr "не вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ Ñховище до глобальної конфігурації"
@@ -6776,6 +6789,7 @@ msgstr "grep: не вдалоÑÑ Ñтворити потік: %s"
msgid "invalid number of threads specified (%d) for %s"
msgstr "невірно вказана кількіÑÑ‚ÑŒ потоків (%d) Ð´Ð»Ñ %s"
+#. #-#-#-#-# grep.c.po #-#-#-#-#
#. TRANSLATORS: %s is the configuration
#. variable for tweaking threads, currently
#. grep.threads
@@ -6789,6 +6803,10 @@ msgid "unable to read tree (%s)"
msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ дерево (%s)"
#, c-format
+msgid "unable to read tree %s"
+msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ дерево %s"
+
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "не вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ grep Ð´Ð»Ñ Ð¾Ð±â€™Ñ”ÐºÑ‚Ð° типу %s"
@@ -7201,10 +7219,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "ВИЯВЛЕÐО SHA1 КОЛІЗІЮ З %s!"
#, c-format
-msgid "unable to read %s"
-msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ %s"
-
-#, c-format
msgid "cannot read existing object info %s"
msgstr "неможливо прочитати інформацію про Ñ–Ñнуючий об’єкт %s"
@@ -7348,11 +7362,13 @@ msgstr "помилка fsck в об’єктах пакунка"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<шаблон-директоріÑ>]\n"
" [--separate-git-dir <git-директоріÑ>] [--object-format=<формат>]\n"
+" [--ref-format=<формат>]\n"
" [-b <назва-гілки> | --initial-branch=<назва-гілки>]\n"
" [--shared[=<дозволи>]] [<директоріÑ>]"
@@ -7400,8 +7416,8 @@ msgid ""
" [--parse] [<file>...]"
msgstr ""
"git interpret-trailers [--in-place] [--trim-empty]\n"
-" [(--trailer (<ключ>|<аліаÑКлюча>)"
-"[(=|:)<значеннÑ>])...]\n"
+" [(--trailer <ключ>|"
+"<аліаÑКлюча>[(=|:)<значеннÑ>])...]\n"
" [--parse] [<файл>...]"
msgid "edit files in place"
@@ -7411,7 +7427,7 @@ msgid "trim empty trailers"
msgstr "обрізати порожні причепи"
msgid "placement"
-msgstr "розташуваннÑ"
+msgstr "розміщеннÑ"
msgid "where to place the new trailer"
msgstr "де розміÑтити новий причіп"
@@ -8065,11 +8081,18 @@ msgstr ""
"git merge-file [<опції>] [-L <назва1> [-L <оріг> [-L <назва2>]]] <файл1> "
"<оріг-файл> <файл2>"
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"Ð¾Ð¿Ñ†Ñ–Ñ diff-algorithm приймає Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ \"myers\", \"minimal\", \"patience\" "
+"та \"histogram\""
+
msgid "send results to standard output"
msgstr "надÑилати результати до Ñтандартного виводу"
msgid "use object IDs instead of filenames"
-msgstr "викориÑтовувати ID обʼєктів заміÑÑ‚ÑŒ назв файлів"
+msgstr "викориÑтовувати ідентифікатори обʼєктів заміÑÑ‚ÑŒ назв файлів"
msgid "use a diff3 based merge"
msgstr "викориÑтовувати Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ð° оÑнові diff3"
@@ -8086,6 +8109,12 @@ msgstr "у разі конфліктів викориÑтовувати їхню
msgid "for conflicts, use a union version"
msgstr "у разі конфліктів викориÑтовувати об’єднану верÑÑ–ÑŽ"
+msgid "<algorithm>"
+msgstr "<алгоритм>"
+
+msgid "choose a diff algorithm"
+msgstr "вибрати алгоритм різниці"
+
msgid "for conflicts, use this marker size"
msgstr "у разі конфліктів викориÑтовувати цей розмір маркера"
@@ -8097,7 +8126,7 @@ msgstr "вÑтановити мітки Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»1/оріг-файл/фаÐ
#, c-format
msgid "object '%s' does not exist"
-msgstr "обʼєкт \"%s\" не Ñ–Ñнує"
+msgstr "об’єкт \"%s\" не Ñ–Ñнує"
msgid "Could not write object file"
msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл обʼєкта"
@@ -8177,9 +8206,6 @@ msgstr "--trivial-merge неÑуміÑна з уÑіма іншими опціÑ
msgid "unknown strategy option: -X%s"
msgstr "невідомий варіант Ñтратегії: -X%s"
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base неÑуміÑна з --stdin"
-
#, c-format
msgid "malformed input line: '%s'."
msgstr "невірно Ñформований Ñ€Ñдок вводу: \"%s\"."
@@ -9113,6 +9139,10 @@ msgid "inconsistency with delta count"
msgstr "неÑÐ¿Ñ–Ð²Ð¿Ð°Ð´Ñ–Ð½Ð½Ñ Ð· підрахунком дельти"
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "неприпуÑтиме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ pack.allowPackReuse: \"%s\""
+
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -9368,10 +9398,10 @@ msgstr "ÐŸÐµÑ€ÐµÑ€Ð°Ñ…ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð²"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"Ð’Ñього %<PRIu32> (дельта %<PRIu32>), повторно викориÑтано %<PRIu32> (дельта "
-"%<PRIu32>), повторно викориÑтано пакунків %<PRIu32>"
+"%<PRIu32>), повторно викориÑтано пакунків %<PRIu32> (з %<PRIuMAX>)"
msgid ""
"'git pack-redundant' is nominated for removal.\n"
@@ -10361,13 +10391,6 @@ 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 ""
@@ -11156,7 +11179,7 @@ msgid "write a multi-pack index of the resulting packs"
msgstr "запиÑати multi-pack-index результуючих пакунків"
msgid "pack prefix to store a pack containing pruned objects"
-msgstr "Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° з обрізаними обʼєктами"
+msgstr "Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° з обрізаними обʼєктами"
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° з відфільтрованими обʼєктами"
@@ -11166,7 +11189,7 @@ msgstr "неможливо видалити пакунки в precious-objects Ñ
#, c-format
msgid "option '%s' can only be used along with '%s'"
-msgstr "опцію \"%s\" можна викориÑтовувати тільки разом з \"%s\""
+msgstr "Ð¾Ð¿Ñ†Ñ–Ñ \"%s\" може бути викориÑтана тільки разом з \"%s\""
msgid "Nothing new to pack."
msgstr "Ðемає нічого нового Ð´Ð»Ñ Ð¿Ð°ÐºÑƒÐ²Ð°Ð½Ð½Ñ."
@@ -11371,6 +11394,75 @@ msgstr "--convert-graft-file не потребує аргументів"
msgid "only one pattern can be given with -l"
msgstr "тільки один шаблон може бути заданий з -l"
+msgid "need some commits to replay"
+msgstr "потрібні деÑкі комміти Ð´Ð»Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ"
+
+msgid "--onto and --advance are incompatible"
+msgstr "--onto та --advance неÑуміÑні"
+
+msgid "all positive revisions given must be references"
+msgstr "вÑÑ– надані позитивні ревізії мають бути поÑиланнÑми"
+
+msgid "argument to --advance must be a reference"
+msgstr "аргумент до --advance має бути поÑиланнÑм"
+
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr ""
+"неможливо проÑунути поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð· декількома джерелами, тому що впорÑÐ´ÐºÑƒÐ²Ð°Ð½Ð½Ñ "
+"буде нечітко визначеним"
+
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "неможливо неÑвно визначити, чи це Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ --advance або --onto"
+
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr ""
+"неможливо проÑунути поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð· декількома джерельними гілками, тому що "
+"впорÑÐ´ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ нечітко визначеним"
+
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "неможливо неÑвно визначити вірну базу Ð´Ð»Ñ --onto"
+
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(ЕКСПЕРИМЕÐТÐЛЬÐО!) git replay ([--contained] --onto <нова-база> | --advance "
+"<гілка>) <діапазон-ревізій>..."
+
+msgid "make replay advance given branch"
+msgstr "зробити Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð· проÑуваннÑм даної гілки"
+
+msgid "replay onto given commit"
+msgstr "відтворити на заданий коміт"
+
+msgid "advance all branches contained in revision-range"
+msgstr "проÑунути вÑÑ– гілки, що міÑÑ‚ÑÑ‚ÑŒÑÑ Ð² діапазоні ревізій"
+
+msgid "option --onto or --advance is mandatory"
+msgstr "Ð¾Ð¿Ñ†Ñ–Ñ --onto або --advance Ñ” обовʼÑзковою"
+
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr ""
+"деÑкі опції Ð¿Ñ€Ð¾Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾ ревізіÑм будуть перевизначені, оÑкільки біт "
+"\"%s\" у \"struct rev_info\" буде примуÑово викориÑтано"
+
+msgid "error preparing revisions"
+msgstr "помилка при підготовці ревізій"
+
+msgid "replaying down to root commit is not supported yet!"
+msgstr "Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð¾ кореневого коміту поки що не підтримуєтьÑÑ!"
+
+msgid "replaying merge commits is not supported yet!"
+msgstr "Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð¼Ñ–Ñ‚Ñ–Ð² Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ð¾ÐºÐ¸ що не підтримуєтьÑÑ!"
+
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
msgstr ""
@@ -11583,15 +11675,6 @@ msgstr "--prefix потребує аргументу"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "невідомий режим Ð´Ð»Ñ --abbrev-ref: %s"
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden не можна викориÑтовувати разом з --branches"
-
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden неможливо викориÑтовувати разом з --tags"
-
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden неможливо викориÑтовувати разом з --remotes"
-
msgid "this operation must be run in a work tree"
msgstr "цю операцію треба виконувати в робочому дереві"
@@ -12005,10 +12088,6 @@ msgstr "не виводити результати у stdout (кориÑно з
msgid "show refs from stdin that aren't in local repository"
msgstr "показати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð· stdin, Ñких немає в локальному Ñховищі"
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "можна вказати тільки один з \"%s\", \"%s\" або \"%s\""
-
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
"rules) [<options>]"
@@ -12820,7 +12899,7 @@ msgstr ""
"branch] [--] [<шлÑÑ…>...]"
msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð¿Ñ–Ð·Ð½Ð°Ñ‚Ð¸ HEAD Ñк дійÑне поÑиланнÑ."
+msgstr "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð²Ê¼Ñзати HEAD в дійÑне поÑиланнÑ."
msgid "git submodule absorbgitdirs [<options>] [<path>...]"
msgstr "git submodule absorbgitdirs [<опції>] [<шлÑÑ…>...]"
@@ -13325,7 +13404,7 @@ msgstr "%d\n"
#, c-format
msgid "index-version: was %d, set to %d"
-msgstr "index-version: було %d, Ñтало %d"
+msgstr "верÑÑ–Ñ Ñ–Ð½Ð´ÐµÐºÑу: була %d, Ñтала %d"
msgid ""
"core.splitIndex is set to false; remove or change it, if you really want to "
@@ -13482,13 +13561,13 @@ msgstr "Ðемає можливої джерельної гілки, що озн
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"Якщо ви хочете Ñтворити робоче дерево, що міÑтить нову ÑирітÑьку гілку\n"
+"Якщо ви хочете Ñтворити робоче дерево, що міÑтить нову ненароджену гілку\n"
"(гілку без комітів) Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища, ви можете зробити це\n"
"за допомогою Ð¿Ñ€Ð°Ð¿Ð¾Ñ€Ñ†Ñ --orphan:\n"
"\n"
@@ -13496,13 +13575,13 @@ msgstr ""
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"Якщо ви хочете Ñтворити робоче дерево, що міÑтить нову ÑирітÑьку гілку\n"
+"Якщо ви хочете Ñтворити робоче дерево, що міÑтить нову ненароджену гілку\n"
"(гілку без комітів) Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища, ви можете зробити це\n"
"за допомогою Ð¿Ñ€Ð°Ð¿Ð¾Ñ€Ñ†Ñ --orphan:\n"
"\n"
@@ -13566,6 +13645,10 @@ msgid "initializing"
msgstr "ініціалізаціÑ"
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ñтворене робоче дерево \"%s\""
+
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "Підготовка робочого дерева (нова гілка \"%s\")"
@@ -13601,12 +13684,8 @@ msgid ""
msgstr ""
"Ðе Ñ–Ñнує локальних або віддалених поÑилань, незважаючи на наÑвніÑÑ‚ÑŒ "
"принаймні одного віддаленого\n"
-"призначеннÑ, зупинка; ÑкориÑтайтеÑÑŒ \"add -f\" Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ "
-"Ñпочатку виконайте Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾Ð³Ð¾ поÑиланнÑ"
-
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "\"%s\" Ñ– \"%s\" не можна викориÑтовувати разом"
+"призначеннÑ, зупинка; ÑкориÑтайтеÑÑŒ \"add -f\", щоб перевизначити, або "
+"Ñпочатку виконайте Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð· віддаленного Ñховища"
msgid "checkout <branch> even if already checked out in other worktree"
msgstr ""
@@ -13618,8 +13697,8 @@ msgstr "Ñтворити нову гілку"
msgid "create or reset a branch"
msgstr "Ñтворити або Ñкинути гілку"
-msgid "create unborn/orphaned branch"
-msgstr "Ñтворити ненароджену/ÑирітÑьку гілку"
+msgid "create unborn branch"
+msgstr "Ñтворити ненароджену гілку"
msgid "populate the new working tree"
msgstr "заповнити нове робоче дерево"
@@ -13642,11 +13721,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
msgstr "опції \"%s\", \"%s\" та \"%s\" не можна викориÑтовувати разом"
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "опції \"%s\" Ñ– \"%s\" не можна викориÑтовувати разом"
-
-msgid "<commit-ish>"
-msgstr "<комітоподібне>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опцію \"%s\" не можна викориÑтовувати разом з комітоподібними"
msgid "added with --lock"
msgstr "додано з --lock"
@@ -13926,7 +14002,7 @@ msgstr "ідентифікатор Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ„Ñ€Ð°Ð³Ð¼ÐµÐ½Ñ‚Ð° зʼÑ
#, c-format
msgid "chunk id %<PRIx32> not %d-byte aligned"
-msgstr "шматок в id %<PRIx32> не %d-byte впорÑдкований"
+msgstr "шматок id %<PRIx32> не Ñ” %d-byte впорÑдкованим"
#, c-format
msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
@@ -13981,7 +14057,7 @@ msgid "Move objects and refs by archive"
msgstr "ПеренеÑти архів обʼєктів та поÑилань"
msgid "Provide contents or details of repository objects"
-msgstr "Показати вміÑÑ‚ або інформацію про обʼєкти Ñховища"
+msgstr "Ðадавати вміÑÑ‚ або деталі обʼєктів Ñховища"
msgid "Display gitattributes information"
msgstr "Відобразити інформацію про gitattributes"
@@ -14269,6 +14345,11 @@ msgstr "Запакувати розпаковані обʼєкти у Ñхови
msgid "Create, list, delete refs to replace objects"
msgstr "Створити, показати, видалити поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±â€™Ñ”ÐºÑ‚Ñ–Ð² заміни"
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr ""
+"ЕКСПЕРИМЕÐТÐЛЬÐО: Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð¼Ñ–Ñ‚Ñ–Ð² на новій базі також працює з "
+"порожніми Ñховищами"
+
msgid "Generates a summary of pending changes"
msgstr "Створює підÑумок змін Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð»Ñду"
@@ -14391,7 +14472,7 @@ msgid "Display version information about Git"
msgstr "Показати інформацію про верÑÑ–ÑŽ Git"
msgid "Show logs with differences each commit introduces"
-msgstr "Показати журнал з різницею, Ñку вноÑить кожен з комітів"
+msgstr "Показати журнал з різницÑми, Ñкі вноÑить кожен коміт"
msgid "Manage multiple working trees"
msgstr "Керувати кількома робочими деревами"
@@ -14507,6 +14588,32 @@ msgstr "ІнÑтрумент Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²ÐµÐ»Ð¸ÐºÐ¸Ð¼Ð¸ Ñхов
msgid "commit-graph file is too small"
msgstr "файл коміт-графа занадто малий"
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "шматок oid fanout коміт-графа має невірний розмір"
+
+msgid "commit-graph fanout values out of order"
+msgstr "Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ fanout коміт-графа впорÑдковані невірно"
+
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "шматок OID lookup коміт-графа має невірний розмір"
+
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "шматок commit data коміт-графа має невірний розмір"
+
+msgid "commit-graph generations chunk is wrong size"
+msgstr "шматок generations коміт-графа має невірний розмір"
+
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "шматок changed-path index коміт-графа має невірний розмір"
+
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr ""
+"Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð½Ð°Ð´Ñ‚Ð¾ малого шматка changed-path (%<PRIuMAX> < %<PRIuMAX>) "
+"файла коміт-графа"
+
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "Ð¿Ñ–Ð´Ð¿Ð¸Ñ ÐºÐ¾Ð¼Ñ–Ñ‚-графа %X не збігаєтьÑÑ Ð· підпиÑом %X"
@@ -14523,11 +14630,20 @@ msgstr "хеш верÑÑ–Ñ ÐºÐ¾Ð¼Ñ–Ñ‚-графа %X не збігаєтьÑÑ Ð
msgid "commit-graph file is too small to hold %u chunks"
msgstr "файл коміт-графа занадто малий, щоб вміÑтити %u шматків"
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "необхідний шматок OID fanout коміт-графа відÑутній або пошкоджений"
+
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "необхідний шматок OID lookup коміт-графа відÑутній або пошкоджений"
+
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "необхідний шматок commit data коміт-графа відÑутній або пошкоджений"
+
msgid "commit-graph has no base graphs chunk"
msgstr "коміт-граф не має шматка базових графів"
msgid "commit-graph base graphs chunk is too small"
-msgstr "занадто малий шматок базових графів коміт-графа"
+msgstr "шматок base graphs коміт-графа занадто малий"
msgid "commit-graph chain does not match"
msgstr "ланцюжок коміт-графа не Ñпівпадає"
@@ -14536,6 +14652,9 @@ msgstr "ланцюжок коміт-графа не Ñпівпадає"
msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "кількіÑÑ‚ÑŒ комітів у базовому графі занадто велика: %<PRIuMAX>"
+msgid "commit-graph chain file too small"
+msgstr "файл ланцюжка коміт-графа занадто малий"
+
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr "неприпуÑтимий ланцюжок коміт-графа: Ñ€Ñдок \"%s\" не Ñ” хешем"
@@ -14551,10 +14670,13 @@ msgid "could not find commit %s"
msgstr "не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ коміт %s"
msgid "commit-graph requires overflow generation data but has none"
-msgstr "коміт-граф потребує даних генерації переповненнÑ, але Ñ—Ñ… немаєданих"
+msgstr "коміт-граф потребує даних генерації переповненнÑ, але не має Ñ—Ñ…"
msgid "commit-graph overflow generation data is too small"
-msgstr "занадто мало даних про Ð¿ÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚-граф генерації"
+msgstr "занадто мало даних генерації Ð¿ÐµÑ€ÐµÐ¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚-графа"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "extra-edges pointer коміт-графа виходить за межі"
msgid "Loading known commits in commit graph"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ð¾Ð¼Ð¸Ñ… комітів у коміт-графі"
@@ -14692,7 +14814,8 @@ msgid ""
"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
"'%s')"
msgstr ""
-"коміт-граф має Ñк нульові, так Ñ– ненульові генерації (коміти \"%s\" Ñ– \"%s\")"
+"коміт-граф має Ñк нульові, так Ñ– ненульові генерації (наприклад, коміти "
+"\"%s\" Ñ– \"%s\")"
msgid "Verifying commits in commit graph"
msgstr "Перевірка комітів у коміт-графі"
@@ -15703,6 +15826,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "Ðевідоме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ð¹Ð½Ð¾Ñ— змінної \"diff.submodule\": \"%s\""
#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "невідоме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— \"%s\": %s"
+
+#, c-format
msgid ""
"Found errors in 'diff.dirstat' config variable:\n"
"%s"
@@ -15782,13 +15909,6 @@ msgstr "невірний --color-moved аргумент: %s"
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "неприпуÑтимий режим \"%s\" у --color-moved-ws"
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"Ð¾Ð¿Ñ†Ñ–Ñ diff-algorithm приймає Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ \"myers\", \"minimal\", \"patience\" "
-"та \"histogram\""
-
#, c-format
msgid "invalid argument to %s"
msgstr "неприпуÑтимий аргумент до %s"
@@ -15832,8 +15952,8 @@ msgstr "машинний вивід --stat"
msgid "output only the last line of --stat"
msgstr "вивеÑти лише оÑтанній Ñ€Ñдок --stat"
-msgid "<param1,param2>..."
-msgstr "<параметр1,параметр2>..."
+msgid "<param1>,<param2>..."
+msgstr "<параметр1>,<параметр2>..."
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
@@ -15842,8 +15962,8 @@ msgstr "вивеÑти розподіл відноÑної кількоÑÑ‚Ñ– з
msgid "synonym for --dirstat=cumulative"
msgstr "Ñинонім Ð´Ð»Ñ --dirstat=cumulative"
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "Ñинонім Ð´Ð»Ñ --dirstat=files,параметр1,параметр2..."
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "Ñинонім Ð´Ð»Ñ --dirstat=files,<параметр1>,<параметр2>..."
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
@@ -16019,12 +16139,6 @@ msgstr "згенерувати різницю за алгоритмом \"patien
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "згенерувати різницю за алгоритмом \"histogram diff\""
-msgid "<algorithm>"
-msgstr "<алгоритм>"
-
-msgid "choose a diff algorithm"
-msgstr "вибрати алгоритм різниці"
-
msgid "<text>"
msgstr "<текÑÑ‚>"
@@ -17224,7 +17338,7 @@ msgstr ""
#. conflict in a submodule. The first argument is the submodule
#. name, and the second argument is the abbreviated id of the
#. commit that needs to be merged. For example:
-#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
#.
#, c-format
msgid ""
@@ -17513,12 +17627,18 @@ msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ кеш"
msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "multi-pack-index OID розÑÑ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð¼Ð°Ñ” невірний розмір"
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr ""
+"невірна поÑлідовніÑÑ‚ÑŒ oid fanout: fanout[%d] = %<PRIx32> > %<PRIx32> = "
+"fanout[%d]"
+
msgid "multi-pack-index OID lookup chunk is the wrong size"
-msgstr "multi-pack-index шматок пошуку OID має невірний розмір"
+msgstr "multi-pack-index OID lookup шматок має невірний розмір"
msgid "multi-pack-index object offset chunk is the wrong size"
-msgstr ""
-"multi-pack-index необхідній шматок Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ð° має невірний розмір"
+msgstr "multi-pack-index object offset шматок має невірний розмір"
#, c-format
msgid "multi-pack-index file %s is too small"
@@ -17537,24 +17657,22 @@ msgid "multi-pack-index hash version %u does not match version %u"
msgstr "верÑÑ–Ñ Ñ…ÐµÑˆÑƒ multi-pack-index %u не збігаєтьÑÑ Ð· верÑією %u"
msgid "multi-pack-index required pack-name chunk missing or corrupted"
-msgstr ""
-"multi-pack-index необхідний шматок імені пакунка відÑутній або пошкоджений"
+msgstr "необхідний шматок pack-name multi-pack-index відÑутній або пошкоджений"
msgid "multi-pack-index required OID fanout chunk missing or corrupted"
msgstr ""
-"multi-pack-index необхідний шматок розÑÑ–ÑŽÐ²Ð°Ð½Ð½Ñ OID відÑутній або пошкоджений"
+"необхідний шматок OID fanout multi-pack-index відÑутній або пошкоджений"
msgid "multi-pack-index required OID lookup chunk missing or corrupted"
msgstr ""
-"multi-pack-index необхідний шматок пошуку OID відÑутній або пошкоджений"
+"необхідний шматок OID lookup multi-pack-index відÑутній або пошкоджений"
msgid "multi-pack-index required object offsets chunk missing or corrupted"
msgstr ""
-"multi-pack-index необхідний шматок Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð² відÑутній або "
-"пошкоджений"
+"необхідний шматок object offsets multi-pack-index відÑутній або пошкоджений"
msgid "multi-pack-index pack-name chunk is too short"
-msgstr "multi-pack-index pack-name шматок занадто малий"
+msgstr "шматок pack-name multi-pack-index занадто малий"
#, c-format
msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17566,12 +17684,19 @@ msgstr ""
msgid "bad pack-int-id: %u (%u total packs)"
msgstr "невірний pack-int-id: %u (%u вÑього пакунків)"
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX не міÑтить шматок BTMP"
+
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ бітмаповий пакунок %<PRIu32>"
+
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"multi-pack-index зберігає 64-бітне зміщеннÑ, але Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ off_t занадто мале"
msgid "multi-pack-index large offset out of bounds"
-msgstr "multi-pack-index large зÑув виходить за межі"
+msgstr "large offset multi-pack-index виходить за межі"
#, c-format
msgid "failed to add packfile '%s'"
@@ -17651,13 +17776,6 @@ msgstr "невірна контрольна Ñума"
msgid "Looking for referenced packfiles"
msgstr "Пошук файлів пакунків, на Ñкі Ñ” поÑиланнÑ"
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"невірна поÑлідовніÑÑ‚ÑŒ oid розÑіюваннÑ: fanout[%d] = %<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
msgid "the midx contains no oid"
msgstr "midx не міÑтить oid"
@@ -17961,7 +18079,7 @@ msgstr "%s [невірний обʼект]"
#. TRANSLATORS: This is a line of ambiguous commit
#. object output. E.g.:
#. *
-#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
#.
#, c-format
msgid "%s commit %s - %s"
@@ -17970,7 +18088,7 @@ msgstr "%s коміт %s - %s"
#. TRANSLATORS: This is a line of ambiguous
#. tag object output. E.g.:
#. *
-#. "deadbeef tag 2022-01-01 - Some Tag Message"
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
#. *
#. The second argument is the YYYY-MM-DD found
#. in the tag.
@@ -17986,7 +18104,7 @@ msgstr "%s тег %s - %s"
#. tag object output where we couldn't parse
#. the tag itself. E.g.:
#. *
-#. "deadbeef [bad tag, could not parse it]"
+#. "deadbeef [bad tag, could not parse it]"
#.
#, c-format
msgid "%s [bad tag, could not parse it]"
@@ -18182,6 +18300,9 @@ msgstr "у мультіпакунковому bitmap відÑутній необ
msgid "could not open pack %s"
msgstr "не вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ пакунок %s"
+msgid "could not determine MIDX preferred pack"
+msgstr "не вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ бажаний пакунок MIDX"
+
#, c-format
msgid "preferred pack (%s) is invalid"
msgstr "бажаний пакунок (%s) Ñ” неприпуÑтимим"
@@ -18203,6 +18324,12 @@ msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
msgstr "пошкоджений ewah bitmap: урізаний заголовок Ð´Ð»Ñ bitmap коміту \"%s\""
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr ""
+"не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ пакунок: \"%s\", Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ð³Ð¾ викориÑÑ‚Ð°Ð½Ð½Ñ "
+"пакунків"
+
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "обʼєкт \"%s\" не знайдено в типах bitmap"
@@ -18296,6 +18423,9 @@ msgstr ""
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "multi-pack-index reverse-index шматок має невірний розмір"
+msgid "could not determine preferred pack"
+msgstr "не вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ бажаний пакунок"
+
msgid "cannot both write and verify reverse index"
msgstr "неможливо одночаÑно запиÑувати та звірÑти зворотний індекÑ"
@@ -18359,10 +18489,6 @@ msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
msgstr "%s очікує невід'ємне ціле Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· опціональним ÑуфікÑом k/m/g"
#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s неÑуміÑний з %s"
-
-#, c-format
msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
msgstr "неоднозначна опціÑ: %s (може бути --%s%s або --%s%s)"
@@ -18469,7 +18595,7 @@ msgstr ""
#, c-format
msgid "bad boolean environment value '%s' for '%s'"
-msgstr "невірне булеве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— \"%s\" Ð´Ð»Ñ \"%s\""
+msgstr "невірне булеве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¾Ñ‚Ð¾Ñ‡ÐµÐ½Ð½Ñ \"%s\" Ð´Ð»Ñ \"%s\""
#, c-format
msgid "failed to parse %s"
@@ -18684,10 +18810,6 @@ 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\" відображаєтьÑÑ Ñк файл Ñ– Ñк каталог"
@@ -19261,10 +19383,6 @@ msgid "cannot process '%s' and '%s' at the same time"
msgstr "неможливо обробити \"%s\" Ñ– \"%s\" одночаÑно"
#, c-format
-msgid "could not remove reference %s"
-msgstr "не вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ поÑÐ¸Ð»Ð°Ð½Ð½Ñ %s"
-
-#, c-format
msgid "could not delete reference %s: %s"
msgstr "не вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ поÑÐ¸Ð»Ð°Ð½Ð½Ñ %s: %s"
@@ -20651,6 +20769,9 @@ msgstr "ЗаÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ñхову призвело до кон
msgid "Autostash exists; creating a new stash entry."
msgstr "ÐвтоÑхов Ñ–Ñнує; ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ запиÑу Ñхову."
+msgid "autostash reference is a symref"
+msgstr "поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ñхову Ñ” Ñимвольним поÑиланнÑм"
+
msgid "could not detach HEAD"
msgstr "не вдалоÑÑ Ð²Ñ–Ð´Ê¼Ñ”Ð´Ð½Ð°Ñ‚Ð¸ HEAD"
@@ -20972,6 +21093,10 @@ msgid "invalid initial branch name: '%s'"
msgstr "неприпуÑтиме початкове Ñ–Ð¼â€™Ñ Ð³Ñ–Ð»ÐºÐ¸: \"%s\""
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: ігноровано --initial-branch=%s"
+
+#, c-format
msgid "unable to handle file type %d"
msgstr "не вдалоÑÑ Ð¾Ð±Ñ€Ð¾Ð±Ð¸Ñ‚Ð¸ тип файлу %d"
@@ -20982,15 +21107,15 @@ msgstr "не вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñтити %s на %s"
msgid "attempt to reinitialize repository with different hash"
msgstr "Ñпроба переініціалізувати репозиторій з іншим хеш-алгоритмом"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "Ñпроба переініціалізувати Ñховище з іншим форматом зберіганнÑ"
+
#, c-format
msgid "%s already exists"
msgstr "%s вже Ñ–Ñнує"
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: ігноровано --initial-branch=%s"
-
-#, c-format
msgid "Reinitialized existing shared Git repository in %s%s\n"
msgstr "Переініціалізовано Ñ–Ñнуюче Ñпільне Git Ñховище в %s%s\n"
@@ -21261,12 +21386,6 @@ msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr ""
"кількіÑÑ‚ÑŒ запиÑів у дереві кешу, Ñкі потрібно анулювати (за замовчуваннÑм 0)"
-msgid "unhandled options"
-msgstr "необроблені опції"
-
-msgid "error preparing revisions"
-msgstr "помилка при підготовці ревізій"
-
#, c-format
msgid "commit %s is not marked reachable"
msgstr "коміт %s не позначений Ñк доÑÑжний"
@@ -21423,9 +21542,6 @@ msgstr "вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑˆÐ»Ñху до віддаленого Ñерв
msgid "invalid remote service path"
msgstr "неприпуÑтимий шлÑÑ… до віддаленої Ñлужби"
-msgid "operation not supported by protocol"
-msgstr "Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¾Ð¼"
-
#, c-format
msgid "can't connect to subservice %s"
msgstr "неможливо підключитиÑÑ Ð´Ð¾ підÑервіÑу %s"
@@ -21558,10 +21674,6 @@ msgid "support for protocol v2 not implemented yet"
msgstr "підтримка протоколу v2 ще не запроваджена"
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "невідоме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— \"%s\": %s"
-
-#, c-format
msgid "transport '%s' not allowed"
msgstr "заÑіб передачі \"%s\" не дозволений"
@@ -21613,6 +21725,9 @@ msgstr "bundle-uri Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾Ðº
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ ÑпиÑок Ð°Ð´Ñ€ÐµÑ Ð¿Ð°ÐºÐµÑ‚Ñ–Ð², оголошений Ñервером"
+msgid "operation not supported by protocol"
+msgstr "Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ Ð¿Ñ€Ð¾Ñ‚Ð¾ÐºÐ¾Ð»Ð¾Ð¼"
+
msgid "too-short tree object"
msgstr "занадто короткий обʼєкт дерева"
@@ -22014,7 +22129,7 @@ msgid "unable to get random bytes"
msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ випадкові байти"
msgid "Unmerged paths:"
-msgstr "не злиті шлÑхи:"
+msgstr "Ðе злиті шлÑхи:"
msgid " (use \"git restore --staged <file>...\" to unstage)"
msgstr ""
@@ -22466,6 +22581,10 @@ msgstr "крім того, ваш Ñ–Ð½Ð´ÐµÐºÑ Ð¼Ñ–Ñтить незакоміч
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "неможливо виконати %s: Ваш Ñ–Ð½Ð´ÐµÐºÑ Ð¼Ñ–Ñтить незакомічені зміни."
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "невідомий Ñтиль \"%s\" наданий Ð´Ð»Ñ \"%s\""
+
msgid ""
"Error: Your local changes to the following files would be overwritten by "
"merge"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 86402725b2..39efaf1012 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -46,6 +46,7 @@
# commit | æ交
# commit message | æ交说明
# commit object | æ交对象
+# commit-graph | æ交图
# commit-ish (also committish) | æ交å·
# cone | 锥形(稀ç–检出模型);锥(稀ç–检出)
# conflict | 冲çª
@@ -99,6 +100,7 @@
# plumbing | 管件(Git 底层核心命令的别称)
# porcelain | 瓷件(Git 上层å°è£…命令的别称)
# precious-objects repo | çå“仓库
+# preferred pack | 首选包(多包索引中引入的首选包概念)
# promisor | 承诺者
# prune | 清除
# pull | 拉,拉å–
@@ -151,8 +153,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-10 10:49+0800\n"
-"PO-Revision-Date: 2023-11-10 17:13+0800\n"
+"POT-Creation-Date: 2024-02-16 14:27+0800\n"
+"PO-Revision-Date: 2024-02-18 11:47+0800\n"
"Last-Translator: Teng Long <dyroneteng@gmail.com>\n"
"Language-Team: GitHub <https://github.com/dyrone/git/>\n"
"Language: zh_CN\n"
@@ -1031,7 +1033,7 @@ msgid "unclosed quote"
msgstr "未关闭的引å·"
#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
msgid "too many arguments"
msgstr "太多å‚æ•°"
@@ -1046,12 +1048,13 @@ msgid "unrecognized whitespace ignore option '%s'"
msgstr "未能识别的空白字符忽略选项 '%s'"
#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
-#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
-#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
-#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
-#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
-#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/checkout.c builtin/clean.c builtin/clone.c builtin/commit.c
+#: builtin/describe.c builtin/diff-tree.c builtin/difftool.c
+#: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c
+#: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c
+#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.c
+#: builtin/rev-parse.c builtin/show-branch.c builtin/stash.c
#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
#: range-diff.c revision.c
#, c-format
@@ -1861,6 +1864,11 @@ msgstr "未知å‚æ•° --output"
#: archive.c
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "é¢å¤–的命令行å‚数:'%s'"
+
+#: archive.c
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "æœªçŸ¥å½’æ¡£æ ¼å¼ '%s'"
@@ -1915,6 +1923,17 @@ msgstr "忽略过大的 gitattributes æ•°æ®å¯¹è±¡ '%s'"
msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "错误的 --attr-source 或 GIT_ATTR_SOURCE"
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "无法对 %s 执行 stat"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "ä¸èƒ½è¯» %s"
+
#: bisect.c
#, c-format
msgid "Badly quoted content in file '%s': %s"
@@ -2448,8 +2467,8 @@ msgid "bad action '%s' for '%s'"
msgstr "'%2$s' 的错误动作 '%1$s'"
#: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
#, c-format
msgid "invalid value for '%s': '%s'"
msgstr "'%s' 的值无效:'%s'"
@@ -2602,8 +2621,7 @@ msgstr "git write-tree 无法写入树对象"
msgid "applying to an empty history"
msgstr "正应用到一个空历å²ä¸Š"
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
msgid "failed to write commit object"
msgstr "无法写æ交对象"
@@ -2783,8 +2801,9 @@ msgid "n"
msgstr "n"
#: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.c
+#: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c builtin/init-db.c
+#: builtin/ls-files.c builtin/ls-tree.c builtin/replace.c builtin/tag.c
+#: builtin/verify-tag.c
msgid "format"
msgstr "æ ¼å¼"
@@ -2916,7 +2935,9 @@ msgstr "git archive:应有一个 flush 包"
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
-msgstr "git bisect start [--term-{new|bad}=<术语> --term-{old|good}=<术语>] [--no-checkout] [--first-parent] [<å> [<好>...]] [--] [<路径规格>...]"
+msgstr ""
+"git bisect start [--term-{new|bad}=<术语> --term-{old|good}=<术语>] [--no-"
+"checkout] [--first-parent] [<å> [<好>...]] [--] [<路径规格>...]"
#: builtin/bisect.c
msgid "git bisect (good|bad) [<rev>...]"
@@ -3473,12 +3494,13 @@ msgstr "无法查询 '%s' 指å‘çš„æ交对象"
#: builtin/branch.c
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"分支 '%s' 没有完全åˆå¹¶ã€‚\n"
-"如果您确认è¦åˆ é™¤å®ƒï¼Œæ‰§è¡Œ 'git branch -D %s'"
+msgid "the branch '%s' is not fully merged"
+msgstr "分支 '%s' 没有完全åˆå¹¶"
+
+#: builtin/branch.c
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "如果您确认è¦åˆ é™¤å®ƒï¼Œæ‰§è¡Œ 'git branch -D %s'"
#: builtin/branch.c
msgid "update of config-file failed"
@@ -4488,7 +4510,7 @@ msgstr "ä¸èƒ½å¯¹ '%s' 执行 reflog æ“作:%s\n"
msgid "HEAD is now at"
msgstr "HEAD ç›®å‰ä½äºŽ"
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
msgid "unable to update HEAD"
msgstr "ä¸èƒ½æ›´æ–° HEAD"
@@ -4757,8 +4779,8 @@ msgid "new-branch"
msgstr "新分支"
#: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "新的没有父æ交的分支"
+msgid "new unborn branch"
+msgstr "新的未诞生的分支"
#: builtin/checkout.c builtin/merge.c
msgid "update ignored files (default)"
@@ -4824,7 +4846,7 @@ msgstr ""
msgid "you must specify path(s) to restore"
msgstr "您必须指定è¦æ¢å¤çš„路径"
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
#: builtin/submodule--helper.c builtin/worktree.c
msgid "branch"
msgstr "分支"
@@ -5065,10 +5087,6 @@ msgid ""
msgstr ""
"clean.requireForce 默认为 true 且未æä¾› -iã€-n 或 -f 选项,拒ç»æ‰§è¡Œæ¸…ç†åŠ¨ä½œ"
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x å’Œ -X ä¸èƒ½åŒæ—¶ä½¿ç”¨"
-
#: builtin/clone.c
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<选项>] [--] <仓库> [<路径>]"
@@ -5160,6 +5178,7 @@ msgid "create a shallow clone since a specific time"
msgstr "从一个特定时间创建一个浅克隆"
#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
msgid "revision"
msgstr "版本"
@@ -5187,6 +5206,10 @@ msgstr "git目录"
msgid "separate git dir from working tree"
msgstr "git目录和工作区分离"
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "指定è¦ä½¿ç”¨çš„引用格å¼"
+
#: builtin/clone.c
msgid "key=value"
msgstr "key=value"
@@ -5336,11 +5359,10 @@ msgstr "太多å‚数。"
msgid "You must specify a repository to clone."
msgstr "您必须指定一个仓库æ¥å…‹éš†ã€‚"
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr "--bundle-uri 与 --depthã€--shallow-since å’Œ --shallow-exclude ä¸å…¼å®¹"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "æœªçŸ¥çš„å¼•ç”¨å­˜å‚¨æ ¼å¼ '%s'"
#: builtin/clone.c
#, c-format
@@ -5501,7 +5523,7 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <目录>] [--append]\n"
" [--split[=<ç­–ç•¥>]] [--reachable | --stdin-packs | --"
@@ -5520,17 +5542,17 @@ msgstr "ä¿å­˜å›¾å½¢çš„对象目录"
#: builtin/commit-graph.c
msgid "if the commit-graph is split, only verify the tip file"
-msgstr "如果æ交图形被拆分,åªéªŒè¯å¤´ä¸€ä¸ªæ–‡ä»¶"
+msgstr "如果æ交图被拆分,åªéªŒè¯å¤´ä¸€ä¸ªæ–‡ä»¶"
#: builtin/commit-graph.c
#, c-format
msgid "Could not open commit-graph '%s'"
-msgstr "无法打开æ交图形 '%s'"
+msgstr "无法打开æ交图 '%s'"
#: builtin/commit-graph.c
#, c-format
msgid "could not open commit-graph chain '%s'"
-msgstr "无法打开æ交图形链 '%s'"
+msgstr "无法打开æ交图链 '%s'"
#: builtin/commit-graph.c
#, c-format
@@ -5574,15 +5596,15 @@ msgstr "å¯ç”¨å˜æ›´è·¯å¾„的计算"
#: builtin/commit-graph.c
msgid "allow writing an incremental commit-graph file"
-msgstr "å…许写一个增é‡æ交图形文件"
+msgstr "å…许写一个增é‡æ交图文件"
#: builtin/commit-graph.c
msgid "maximum number of commits in a non-base split commit-graph"
-msgstr "在éžåŸºæœ¬æ‹†åˆ†æ交图形中的最大æ交数"
+msgstr "在éžåŸºæœ¬æ‹†åˆ†æ交图中的最大æ交数"
#: builtin/commit-graph.c
msgid "maximum ratio between two levels of a split commit-graph"
-msgstr "一个拆分æ交图形的两个级别之间的最大比率"
+msgstr "一个拆分æ交图的两个级别之间的最大比率"
#: builtin/commit-graph.c
msgid "only expire files older than a given date-time"
@@ -8380,6 +8402,11 @@ msgstr "无法读å–树(%s)"
#: builtin/grep.c
#, c-format
+msgid "unable to read tree %s"
+msgstr "无法读å–æ ‘ %s"
+
+#: builtin/grep.c
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "无法抓å–æ¥è‡ªäºŽ %s 类型的对象"
@@ -8901,11 +8928,6 @@ msgstr "解压缩严é‡çš„ä¸ä¸€è‡´"
msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "å‘现 %s 出现 SHA1 冲çªï¼"
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "ä¸èƒ½è¯» %s"
-
#: builtin/index-pack.c
#, c-format
msgid "cannot read existing object info %s"
@@ -9083,11 +9105,13 @@ msgstr "在打包对象中 fsck 检查出错"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<模æ¿ç›®å½•>]\n"
" [--separate-git-dir <git 目录>] [--object-format=<æ ¼å¼>]\n"
+" [--ref-format=<æ ¼å¼>]\n"
" [-b <分支å> | --initial-branch=<分支å>]\n"
" [--shared[=<æƒé™>]] [<目录>]"
@@ -9246,7 +9270,7 @@ msgid ""
"<file>"
msgstr "跟踪 <文件> 中 <开始>,<结æŸ> 范围内的行或函数 :<函数å> çš„æ¼”å˜"
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
#, c-format
msgid "unrecognized argument: %s"
msgstr "未能识别的å‚数:%s"
@@ -9990,6 +10014,12 @@ msgstr ""
"git merge-file [<选项>] [-L <åå­—1> [-L <åˆå§‹åå­—> [-L <åå­—2>]]] <文件1> <åˆ"
"始文件> <文件2>"
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr "选项 diff-algorithm 接å—å‚æ•° \"myers\"ã€\"minimal\"ã€\"patience\" å’Œ \"histogram\""
+
#: builtin/merge-file.c
msgid "send results to standard output"
msgstr "将结果å‘é€åˆ°æ ‡å‡†è¾“出"
@@ -10018,6 +10048,14 @@ msgstr "如果冲çªï¼Œä½¿ç”¨ä»–们的版本"
msgid "for conflicts, use a union version"
msgstr "如果冲çªï¼Œä½¿ç”¨è”åˆç‰ˆæœ¬"
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<算法>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "选择一个差异算法"
+
#: builtin/merge-file.c
msgid "for conflicts, use this marker size"
msgstr "如果冲çªï¼Œä½¿ç”¨æŒ‡å®šé•¿åº¦çš„标记"
@@ -10135,10 +10173,6 @@ msgstr "--trivial-merge 与其他所有选项ä¸å…¼å®¹"
msgid "unknown strategy option: -X%s"
msgstr "未知的策略选项:-X%s"
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base 与 --stdin ä¸å…¼å®¹"
-
#: builtin/merge-tree.c builtin/notes.c
#, c-format
msgid "malformed input line: '%s'."
@@ -10296,7 +10330,7 @@ msgstr "'%s' 没有指å‘一个æ交"
msgid "Bad branch.%s.mergeoptions string: %s"
msgstr "åçš„ branch.%s.mergeoptions 字符串:%s"
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
msgid "Unable to write index."
msgstr "ä¸èƒ½å†™å…¥ç´¢å¼•ã€‚"
@@ -10304,7 +10338,7 @@ msgstr "ä¸èƒ½å†™å…¥ç´¢å¼•ã€‚"
msgid "Not handling anything other than two heads merge."
msgstr "未处ç†ä¸¤ä¸ªå¤´åˆå¹¶ä¹‹å¤–的任何æ“作。"
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
#, c-format
msgid "unable to write %s"
msgstr "ä¸èƒ½å†™ %s"
@@ -11292,6 +11326,11 @@ msgstr "ä¸ä¸€è‡´çš„差异计数"
#: builtin/pack-objects.c
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "无效的 pack.allowPackReuse 值:'%s'"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -11614,10 +11653,10 @@ msgstr "枚举对象中"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"总共 %<PRIu32>(差异 %<PRIu32>),å¤ç”¨ %<PRIu32>(差异 %<PRIu32>),包å¤ç”¨ "
-"%<PRIu32>"
+"%<PRIu32>(æ¥è‡ª %<PRIuMAX> 个包)"
#: builtin/pack-redundant.c
msgid ""
@@ -12691,7 +12730,7 @@ msgstr "没有正在进行的å˜åŸºï¼Ÿ"
msgid "The --edit-todo action can only be used during interactive rebase."
msgstr "动作 --edit-todo åªèƒ½ç”¨åœ¨äº¤äº’å¼å˜åŸºè¿‡ç¨‹ä¸­ã€‚"
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
msgid "Cannot read HEAD"
msgstr "ä¸èƒ½è¯»å– HEAD"
@@ -12737,12 +12776,6 @@ msgstr "开关 `C' 期望一个数字值"
#: builtin/rebase.c
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr "应用的选项与 rebase.autoSquash ä¸å…¼å®¹ã€‚考虑加上 --no-autosquash"
-
-#: builtin/rebase.c
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr "应用的选项与 rebase.rebaseMerges ä¸å…¼å®¹ã€‚考虑加上 --no-rebase-merges"
@@ -13224,8 +13257,8 @@ msgid ""
msgid_plural ""
"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
"to delete them, use:"
-msgstr[0] "注æ„:ref/remotes 层级之外的一个分支未被移除。è¦åˆ é™¤å®ƒï¼Œä½¿ç”¨ï¼š"
-msgstr[1] "注æ„:ref/remotes 层级之外的一些分支未被移除。è¦åˆ é™¤å®ƒä»¬ï¼Œä½¿ç”¨ï¼š"
+msgstr[0] "注æ„:refs/remotes/ 层级之外的一个分支未被移除。è¦åˆ é™¤å®ƒï¼Œä½¿ç”¨ï¼š"
+msgstr[1] "注æ„:refs/remotes/ 层级之外的一些分支未被移除。è¦åˆ é™¤å®ƒä»¬ï¼Œä½¿ç”¨ï¼š"
#: builtin/remote.c
#, c-format
@@ -13956,6 +13989,84 @@ msgstr "--convert-graft-file ä¸å¸¦å‚æ•°"
msgid "only one pattern can be given with -l"
msgstr "åªèƒ½ä¸º -l æ供一个模å¼"
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "需è¦ä¸€äº›æ交æ¥é‡æ”¾"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto å’Œ --advance ä¸å…¼å®¹"
+
+#: builtin/replay.c
+msgid "all positive revisions given must be references"
+msgstr "æ供的所有正å‘版本必须为引用"
+
+#: builtin/replay.c
+msgid "argument to --advance must be a reference"
+msgstr "--advance çš„å‚数必须是引用"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr "ä¸èƒ½ä½¿ç”¨å¤šä¸ªæºæŽ¨è¿›ç›®æ ‡ï¼Œå› ä¸ºæ— æ³•æ˜Žç¡®å¦‚何排åº"
+
+#: builtin/replay.c
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "ä¸èƒ½éšå¼åœ°ç¡®å®šè¿™æ˜¯ --advance 还是 --onto çš„æ“作"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr "ä¸èƒ½ä½¿ç”¨å¤šä¸ªæºåˆ†æ”¯æŽ¨è¿›ç›®æ ‡ï¼Œå› ä¸ºæ— æ³•æ˜Žç¡®å¦‚何排åº"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "ä¸èƒ½éšå¼åœ°ç¡®å®š --onto 正确的基线"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr "(试验中ï¼ï¼‰git replay ([--contained] --onto <新基线> | --advance <分支>) <版本范围>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "é‡æ”¾æ—¶æ¼”进给定的分支"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "é‡æ”¾åˆ°ç»™å®šæ交"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "演进版本范围中包å«çš„所有分支"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "选项 --onto 或 --advance 必须指定其一"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr "一些版本é历选项将被覆盖,如 'struct rev_info' 中的 '%s' ä½å°†è¢«å¼ºåˆ¶è®¾å®š"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "准备版本时错误"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "ç›®å‰è¿˜ä¸æ”¯æŒé‡æ”¾åˆ°æ ¹æ交ï¼"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "ç›®å‰è¿˜ä¸æ”¯æŒé‡æ”¾åˆ°åˆå¹¶æ交ï¼"
+
#: builtin/rerere.c
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -14215,18 +14326,6 @@ msgstr "--prefix 需è¦ä¸€ä¸ªå‚æ•°"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "未知的 --abbrev-ref 模å¼ï¼š%s"
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden ä¸èƒ½ä¸Ž --branches 一起使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden ä¸èƒ½ä¸Ž --tags 一起使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden ä¸èƒ½ä¸Ž --remotes 一起使用"
-
#: builtin/rev-parse.c setup.c
msgid "this operation must be run in a work tree"
msgstr "该æ“作必须在一个工作区中è¿è¡Œ"
@@ -14730,11 +14829,6 @@ msgstr "ä¸æ‰“å°ç»“果到标准输出(例如与 --verify å‚数共用)"
msgid "show refs from stdin that aren't in local repository"
msgstr "显示从标准输入中读入的ä¸åœ¨æœ¬åœ°ä»“库中的引用"
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "选项 '%s'ã€'%s' 或 '%s' åªèƒ½ä½¿ç”¨å…¶ä¸€"
-
#: builtin/sparse-checkout.c
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16509,30 +16603,30 @@ msgstr "没有å¯ç”¨çš„æºåˆ†æ”¯ï¼Œå°†åŸºäºŽ '--orphan' 选项进行推断"
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
-"如果你打算为此仓库创建一个包å«æ–°çš„孤立分支\n"
-"(没有æ交的分支)的工作区,你å¯ä»¥ä½¿ç”¨é€‰é¡¹\n"
-"--orphan æ¥æ‰§è¡Œæ­¤æ“作:\n"
+"如果你打算为此仓库创建一个包å«æ–°çš„未诞生的\n"
+"分支(没有æ交的分支)的工作区,你å¯ä»¥ä½¿ç”¨\n"
+"选项 --orphan æ¥æ‰§è¡Œæ­¤æ“作:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
-"如果你打算为此仓库创建一个包å«æ–°çš„孤立分支\n"
-"(没有æ交的分支)的工作区,你å¯ä»¥ä½¿ç”¨é€‰é¡¹\n"
-"--orphan æ¥æ‰§è¡Œæ­¤æ“作:\n"
+"如果你打算为此仓库创建一个包å«æ–°çš„未诞生的\n"
+"分支(没有æ交的分支)的工作区,你å¯ä»¥ä½¿ç”¨\n"
+"选项 --orphan æ¥æ‰§è¡Œæ­¤æ“作:\n"
"\n"
" git worktree add --orphan %s\n"
@@ -16603,6 +16697,11 @@ msgstr "åˆå§‹åŒ–"
#: builtin/worktree.c
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "无法找到已创建的工作树 '%s'"
+
+#: builtin/worktree.c
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "准备工作区(新分支 '%s')"
@@ -16646,11 +16745,6 @@ msgstr ""
"请先使用 'add -f' æ¥è¦†ç›–或拉å–一个远程仓库"
#: builtin/worktree.c
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' å’Œ '%s' ä¸èƒ½åŒæ—¶ä½¿ç”¨"
-
-#: builtin/worktree.c
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "检出 <分支>,å³ä½¿å·²ç»è¢«æ£€å‡ºåˆ°å…¶å®ƒå·¥ä½œåŒº"
@@ -16663,8 +16757,8 @@ msgid "create or reset a branch"
msgstr "创建或é‡ç½®ä¸€ä¸ªåˆ†æ”¯"
#: builtin/worktree.c
-msgid "create unborn/orphaned branch"
-msgstr "创建一个尚未诞生的/孤立的分支"
+msgid "create unborn branch"
+msgstr "创建一个尚未诞生的分支"
#: builtin/worktree.c
msgid "populate the new working tree"
@@ -16693,12 +16787,8 @@ msgstr "选项 '%s'ã€'%s' å’Œ '%s' ä¸èƒ½åŒæ—¶ä½¿ç”¨"
#: builtin/worktree.c
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "选项 '%s' 与 '%s' ä¸èƒ½åŒæ—¶ä½¿ç”¨"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<æ交å·>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "选项 '%s' å’Œæ交å·ä¸èƒ½åŒæ—¶ä½¿ç”¨"
#: builtin/worktree.c
msgid "added with --lock"
@@ -17489,6 +17579,10 @@ msgid "Create, list, delete refs to replace objects"
msgstr "创建ã€åˆ—出ã€åˆ é™¤å¯¹è±¡æ›¿æ¢å¼•ç”¨"
#: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr "试验中:基于一个新基线é‡æ”¾æ交,åŒæ ·é€‚用于纯仓库"
+
+#: command-list.h
msgid "Generates a summary of pending changes"
msgstr "生æˆå¾…定更改的摘è¦"
@@ -17803,39 +17897,82 @@ msgstr "一个管ç†å¤§åž‹ Git 仓库的工具"
#: commit-graph.c
msgid "commit-graph file is too small"
-msgstr "æ交图形文件太å°"
+msgstr "æ交图文件太å°"
+
+#: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "æ交图中对象 ID 的扇出å—大å°é”™è¯¯"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "æ交图的扇出值失åº"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "æ交图的对象 ID 查询å—大å°é”™è¯¯"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "æ交图的æ交数æ®å—大å°é”™è¯¯"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "æ交图的世代å—大å°é”™è¯¯"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "æ交图的å˜æ›´è·¯å¾„的索引å—太å°"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr "忽略æ交图文件中过å°çš„更改路径å—(%<PRIuMAX> < %<PRIuMAX>)"
#: commit-graph.c
#, c-format
msgid "commit-graph signature %X does not match signature %X"
-msgstr "æ交图形签å %X 和签å %X ä¸åŒ¹é…"
+msgstr "æ交图签å %X 和签å %X ä¸åŒ¹é…"
#: commit-graph.c
#, c-format
msgid "commit-graph version %X does not match version %X"
-msgstr "æ交图形版本 %X 和版本 %X ä¸åŒ¹é…"
+msgstr "æ交图版本 %X 和版本 %X ä¸åŒ¹é…"
#: commit-graph.c
#, c-format
msgid "commit-graph hash version %X does not match version %X"
-msgstr "æ交图形哈希版本 %X 和版本 %X ä¸åŒ¹é…"
+msgstr "æ交图哈希版本 %X 和版本 %X ä¸åŒ¹é…"
#: commit-graph.c
#, c-format
msgid "commit-graph file is too small to hold %u chunks"
-msgstr "æ交图形文件太å°ï¼Œå®¹ä¸ä¸‹ %u 个å—"
+msgstr "æ交图文件太å°ï¼Œå®¹ä¸ä¸‹ %u 个å—"
+
+#: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "æ交图所需的对象 ID 扇出å—缺失或æŸå"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "æ交图所需的对象 ID 查询å—缺失或æŸå"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "æ交图所需的æ交数æ®å—缺失或æŸå"
#: commit-graph.c
msgid "commit-graph has no base graphs chunk"
-msgstr "æ交图形没有基础图形å—"
+msgstr "æ交图没有基础图形å—"
#: commit-graph.c
msgid "commit-graph base graphs chunk is too small"
-msgstr "æ交图形的基础图形å—过å°"
+msgstr "æ交图的基础图形å—过å°"
#: commit-graph.c
msgid "commit-graph chain does not match"
-msgstr "æ交图形链ä¸åŒ¹é…"
+msgstr "æ交图链ä¸åŒ¹é…"
#: commit-graph.c
#, c-format
@@ -17843,17 +17980,21 @@ msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "基础图形中的æ交数é‡è¿‡é«˜ï¼š%<PRIuMAX>"
#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "æ交图链文件太å°"
+
+#: commit-graph.c
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "无效的æ交图形链:行 '%s' ä¸æ˜¯ä¸€ä¸ªå“ˆå¸Œå€¼"
+msgstr "无效的æ交图链:行 '%s' ä¸æ˜¯ä¸€ä¸ªå“ˆå¸Œå€¼"
#: commit-graph.c
msgid "unable to find all commit-graph files"
-msgstr "无法找到所有æ交图形文件"
+msgstr "无法找到所有æ交图文件"
#: commit-graph.c
msgid "invalid commit position. commit-graph is likely corrupt"
-msgstr "无效的æ交ä½ç½®ã€‚æ交图形å¯èƒ½å·²æŸå"
+msgstr "无效的æ交ä½ç½®ã€‚æ交图å¯èƒ½å·²æŸå"
#: commit-graph.c
#, c-format
@@ -17869,6 +18010,10 @@ msgid "commit-graph overflow generation data is too small"
msgstr "æ交图溢出世代数æ®è¿‡å°"
#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "æ交图é¢å¤–边的指针越界"
+
+#: commit-graph.c
msgid "Loading known commits in commit graph"
msgstr "正在加载æ交图中的已知æ交"
@@ -17943,26 +18088,25 @@ msgstr[1] "正在用 %d 步写出æ交图"
#: commit-graph.c
msgid "unable to open commit-graph chain file"
-msgstr "无法打开æ交图形链文件"
+msgstr "无法打开æ交图链文件"
#: commit-graph.c
msgid "failed to rename base commit-graph file"
-msgstr "无法é‡å‘½å基础æ交图形文件"
+msgstr "无法é‡å‘½å基础æ交图文件"
#: commit-graph.c
msgid "failed to rename temporary commit-graph file"
-msgstr "无法é‡å‘½å临时æ交图形文件"
+msgstr "无法é‡å‘½å临时æ交图文件"
#: commit-graph.c
#, c-format
msgid "cannot merge graphs with %<PRIuMAX>, %<PRIuMAX> commits"
-msgstr ""
-"无法åˆå¹¶æ交图形,总共已累加æ交数:%<PRIuMAX>,当å‰å¾…累加æ交数:%<PRIuMAX>"
+msgstr "无法åˆå¹¶æ交图,总共已累加æ交数:%<PRIuMAX>,当å‰å¾…累加æ交数:%<PRIuMAX>"
#: commit-graph.c
#, c-format
msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
-msgstr "无法åˆå¹¶æ交图形 %s, æ交过多:%<PRIuMAX>"
+msgstr "无法åˆå¹¶æ交图 %s, æ交过多:%<PRIuMAX>"
#: commit-graph.c
msgid "Scanning merged commits"
@@ -17970,7 +18114,7 @@ msgstr "正在扫æåˆå¹¶æ交"
#: commit-graph.c
msgid "Merging commit-graph"
-msgstr "正在åˆå¹¶æ交图形"
+msgstr "正在åˆå¹¶æ交图"
#: commit-graph.c
msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
@@ -17987,59 +18131,59 @@ msgstr "æ交图文件的校验ç é”™è¯¯ï¼Œå¯èƒ½å·²ç»æŸå"
#: commit-graph.c
#, c-format
msgid "commit-graph has incorrect OID order: %s then %s"
-msgstr "æ交图形的对象 ID 顺åºä¸æ­£ç¡®ï¼š%s ç„¶åŽ %s"
+msgstr "æ交图的对象 ID 顺åºä¸æ­£ç¡®ï¼š%s ç„¶åŽ %s"
#: commit-graph.c
#, c-format
msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
-msgstr "æ交图形有ä¸æ­£ç¡®çš„扇出值:fanout[%d] = %u != %u"
+msgstr "æ交图有ä¸æ­£ç¡®çš„扇出值:fanout[%d] = %u != %u"
#: commit-graph.c
#, c-format
msgid "failed to parse commit %s from commit-graph"
-msgstr "无法从æ交图形中解æžæ交 %s"
+msgstr "无法从æ交图中解æžæ交 %s"
#: commit-graph.c
#, c-format
msgid "failed to parse commit %s from object database for commit-graph"
-msgstr "无法从æ交图形的对象库中解æžæ交 %s"
+msgstr "无法从æ交图的对象库中解æžæ交 %s"
#: commit-graph.c
#, c-format
msgid "root tree OID for commit %s in commit-graph is %s != %s"
-msgstr "æ交图形中的æ交 %s 的根树对象 ID 是 %s != %s"
+msgstr "æ交图中的æ交 %s 的根树对象 ID 是 %s != %s"
#: commit-graph.c
#, c-format
msgid "commit-graph parent list for commit %s is too long"
-msgstr "æ交 %s çš„æ交图形父æ交列表太长了"
+msgstr "æ交 %s çš„æ交图父æ交列表太长了"
#: commit-graph.c
#, c-format
msgid "commit-graph parent for %s is %s != %s"
-msgstr "%s çš„æ交图形父æ交是 %s != %s"
+msgstr "%s çš„æ交图父æ交是 %s != %s"
#: commit-graph.c
#, c-format
msgid "commit-graph parent list for commit %s terminates early"
-msgstr "æ交 %s çš„æ交图形父æ交列表过早终止"
+msgstr "æ交 %s çš„æ交图父æ交列表过早终止"
#: commit-graph.c
#, c-format
msgid "commit-graph generation for commit %s is %<PRIuMAX> < %<PRIuMAX>"
-msgstr "æ交图形中的æ交 %s 的世代å·æ˜¯ %<PRIuMAX> < %<PRIuMAX>"
+msgstr "æ交图中的æ交 %s 的世代å·æ˜¯ %<PRIuMAX> < %<PRIuMAX>"
#: commit-graph.c
#, c-format
msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
-msgstr "æ交图形中æ交 %s çš„æ交日期是 %<PRIuMAX> != %<PRIuMAX>"
+msgstr "æ交图中æ交 %s çš„æ交日期是 %<PRIuMAX> != %<PRIuMAX>"
#: commit-graph.c
#, c-format
msgid ""
"commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
"'%s')"
-msgstr "æ交图形具有零和éžé›¶çš„世代(例如:æ交 '%s' å’Œ '%s')"
+msgstr "æ交图具有零和éžé›¶çš„世代(例如:æ交 '%s' å’Œ '%s')"
#: commit-graph.c
msgid "Verifying commits in commit graph"
@@ -18073,7 +18217,7 @@ msgstr ""
#: commit.c
#, c-format
msgid "commit %s exists in commit-graph but not in the object database"
-msgstr "æ交 %s 存在于æ交图形中,但ä¸å­˜åœ¨äºŽå¯¹è±¡æ•°æ®åº“中"
+msgstr "æ交 %s 存在于æ交图中,但ä¸å­˜åœ¨äºŽå¯¹è±¡æ•°æ®åº“中"
#: commit.c
#, c-format
@@ -19255,6 +19399,11 @@ msgstr "color-moved-ws:allow-indentation-change ä¸èƒ½ä¸Žå…¶å®ƒç©ºç™½å­—符模
msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "é…ç½®å˜é‡ 'diff.submodule' 未知的å–值:'%s'"
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "é…ç½® '%s' 未知的å–值:%s"
+
#: diff.c
#, c-format
msgid ""
@@ -19349,13 +19498,6 @@ msgid "invalid mode '%s' in --color-moved-ws"
msgstr "--color-moved-ws ä¸­çš„æ— æ•ˆæ¨¡å¼ '%s' "
#: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm 选项有 \"myers\"ã€\"minimal\"ã€\"patience\" å’Œ \"histogram\""
-
-#: diff.c
#, c-format
msgid "invalid argument to %s"
msgstr "%s çš„å‚数无效"
@@ -19412,8 +19554,8 @@ msgid "output only the last line of --stat"
msgstr "åªè¾“出 --stat 的最åŽä¸€è¡Œ"
#: diff.c
-msgid "<param1,param2>..."
-msgstr "<å‚æ•°1,å‚æ•°2>..."
+msgid "<param1>,<param2>..."
+msgstr "<å‚æ•°1>,<å‚æ•°2>..."
#: diff.c
msgid ""
@@ -19425,8 +19567,8 @@ msgid "synonym for --dirstat=cumulative"
msgstr "å’Œ --dirstat=cumulative åŒä¹‰"
#: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "是 --dirstat=files,param1,param2... çš„åŒä¹‰è¯"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "是 --dirstat=files,<å‚æ•°1>,<å‚æ•°2>... çš„åŒä¹‰è¯"
#: diff.c
msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19649,14 +19791,6 @@ msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "使用 \"histogram diff\" 算法生æˆå·®å¼‚"
#: diff.c
-msgid "<algorithm>"
-msgstr "<算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "选择一个差异算法"
-
-#: diff.c
msgid "<text>"
msgstr "<文本>"
@@ -21412,6 +21546,12 @@ msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "多包索引的对象 ID 扇出表大å°é”™è¯¯"
#: midx.c
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr "对象 ID 扇出失åºï¼šfanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+#: midx.c
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "多包索引的对象 ID 查询å—大å°é”™è¯¯"
@@ -21470,6 +21610,15 @@ msgid "bad pack-int-id: %u (%u total packs)"
msgstr "错的 pack-int-id:%u(共有 %u 个包)"
#: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "å¤šåŒ…ç´¢å¼•ä¸­æœªåŒ…å« BTMP å—"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "ä¸èƒ½æ‰“开已被ä½å›¾ç´¢å¼•çš„包 %<PRIu32>"
+
+#: midx.c
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr "多包索引存储一个64ä½å移,但是 off_t 太å°"
@@ -21577,12 +21726,6 @@ msgid "Looking for referenced packfiles"
msgstr "正在查找引用的包文件"
#: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "对象 ID 扇出无åºï¼šfanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
-#: midx.c
msgid "the midx contains no oid"
msgstr "midx ä¸åŒ…å« oid"
@@ -22211,6 +22354,10 @@ msgstr "多包ä½å›¾ç¼ºå°‘必需的åå‘索引"
msgid "could not open pack %s"
msgstr "ä¸èƒ½æ‰“开包 %s"
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "ä¸èƒ½ç¡®å®šå¤šåŒ…索引的首选包"
+
#: pack-bitmap.c
#, c-format
msgid "preferred pack (%s) is invalid"
@@ -22236,6 +22383,11 @@ msgstr "æŸåçš„ EWAH ä½å›¾ï¼šæ交 \"%s\" ä½å›¾çš„文件头被截断"
#: pack-bitmap.c
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "无法打开包:'%s',ç¦ç”¨åŒ…é‡ç”¨"
+
+#: pack-bitmap.c
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "对象 %s 为在类型ä½å›¾ä¸­æ‰¾åˆ°"
@@ -22349,6 +22501,10 @@ msgstr "ä½äºŽ %<PRIu64> 的无效的åå‘索引:%<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "多包索引的åå‘索引å—大å°é”™è¯¯"
+#: pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "无法确定首选包"
+
#: pack-write.c
msgid "cannot both write and verify reverse index"
msgstr "无法åŒæ—¶å†™å…¥å’Œæ ¡éªŒåå‘索引"
@@ -22429,11 +22585,6 @@ 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)"
msgstr "有歧义的选项:%s(å¯ä»¥æ˜¯ --%s%s 或 --%s%s)"
@@ -22823,11 +22974,6 @@ msgstr "无法在索引中添加 '%s'"
#: read-cache.c
#, c-format
-msgid "unable to stat '%s'"
-msgstr "无法对 %s 执行 stat"
-
-#: read-cache.c
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' 看起æ¥æ—¢æ˜¯æ–‡ä»¶åˆæ˜¯ç›®å½•"
@@ -23500,17 +23646,12 @@ msgstr "'%s' 已存在,无法创建 '%s'"
msgid "cannot process '%s' and '%s' at the same time"
msgstr "无法åŒæ—¶å¤„ç† '%s' å’Œ '%s'"
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "无法删除引用 %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete reference %s: %s"
msgstr "无法删除引用 %s:%s"
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete references: %s"
msgstr "无法删除引用:%s"
@@ -24665,7 +24806,7 @@ msgstr "无效的作者身份 '%s'"
msgid "corrupt author: missing date information"
msgstr "æŸå的作者:缺失日期信æ¯"
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
#, c-format
msgid "could not update %s"
msgstr "ä¸èƒ½æ›´æ–° %s"
@@ -25156,6 +25297,10 @@ msgid "Autostash exists; creating a new stash entry."
msgstr "自动贮è—å·²ç»å­˜åœ¨ï¼›æ­£åœ¨åˆ›å»ºä¸€ä¸ªæ–°çš„è´®è—æ¡ç›®ã€‚"
#: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "自动贮è—的引用是一个符å·å¼•ç”¨"
+
+#: sequencer.c
msgid "could not detach HEAD"
msgstr "ä¸èƒ½åˆ†ç¦»å¤´æŒ‡é’ˆ"
@@ -25531,6 +25676,11 @@ msgstr "无效的åˆå§‹åˆ†æ”¯å:'%s'"
#: setup.c
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init:已忽略 --initial-branch=%s"
+
+#: setup.c
+#, c-format
msgid "unable to handle file type %d"
msgstr "ä¸èƒ½å¤„ç† %d 类型的文件"
@@ -25544,14 +25694,14 @@ msgid "attempt to reinitialize repository with different hash"
msgstr "å°è¯•ç”¨ä¸åŒçš„哈希算法é‡æ–°åˆå§‹åŒ–仓库"
#: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s å·²ç»å­˜åœ¨"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "å°è¯•ä½¿ç”¨ä¸åŒçš„引用存储格å¼é‡æ–°åˆå§‹åŒ–仓库"
#: setup.c
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init:已忽略 --initial-branch=%s"
+msgid "%s already exists"
+msgstr "%s å·²ç»å­˜åœ¨"
#: setup.c
#, c-format
@@ -25869,14 +26019,6 @@ msgstr "在æ¯æ¬¡è¿­ä»£å‰æ¸…除缓存树"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "缓存树中无效化的æ¡ç›®æ•°é‡ï¼ˆé»˜è®¤ 0)"
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "未处ç†çš„选项"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "准备版本时错误"
-
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -26070,10 +26212,6 @@ msgstr "åè®®ä¸æ”¯æŒè®¾ç½®è¿œç¨‹æœåŠ¡è·¯å¾„"
msgid "invalid remote service path"
msgstr "无效的远程æœåŠ¡è·¯å¾„"
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "åè®®ä¸æ”¯æŒè¯¥æ“作"
-
#: transport-helper.c
#, c-format
msgid "can't connect to subservice %s"
@@ -26239,11 +26377,6 @@ msgstr "åè®® v2 的支æŒå°šæœªå®žçŽ°"
#: transport.c
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "é…ç½® '%s' çš„å–值未知:%s"
-
-#: transport.c
-#, c-format
msgid "transport '%s' not allowed"
msgstr "传输 '%s' ä¸å…许"
@@ -26301,6 +26434,10 @@ msgstr "åè®®ä¸æ”¯æŒ bundle-uri æ“作"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "无法获å–æœåŠ¡å™¨å…¬å¸ƒçš„ bundle-uri 列表"
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "åè®®ä¸æ”¯æŒè¯¥æ“作"
+
#: tree-walk.c
msgid "too-short tree object"
msgstr "太短的树对象"
@@ -27329,6 +27466,11 @@ msgstr "å¦å¤–,您的索引中包å«æœªæ交的å˜æ›´ã€‚"
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "ä¸èƒ½%s:您的索引中包å«æœªæ交的å˜æ›´ã€‚"
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "'%2$s' 的未知风格å–值 '%1$s'"
+
#: git-merge-octopus.sh git-merge-resolve.sh
msgid ""
"Error: Your local changes to the following files would be overwritten by "
diff --git a/po/zh_TW.po b/po/zh_TW.po
index f777a0596f..312dd128a4 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -19,15 +19,15 @@
# - Yichao Yu <yyc1992 AT gmail.com>
# - Zhuang Ya <zhuangya AT me.com>
#
-# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023, 2024.
# Kaiyang Wu <self@origincode.me>, 2022.
-# lumynou5 <lumynou5.tw@gmail.com>, 2023.
+# lumynou5 <lumynou5.tw@gmail.com>, 2023, 2024.
msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-19 22:29+0800\n"
-"PO-Revision-Date: 2023-11-19 23:34+0800\n"
+"POT-Creation-Date: 2024-02-18 20:48+0800\n"
+"PO-Revision-Date: 2024-02-18 20:50+0800\n"
"Last-Translator: Yi-Jyun Pan <pan93412@gmail.com>\n"
"Language-Team: Chinese (Traditional) <http://weblate.slat.org/projects/git-"
"po/git-cli/zh_Hant/>\n"
@@ -36,7 +36,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 3.4.1\n"
+"X-Generator: Poedit 3.4.2\n"
"X-ZhConverter: ç¹åŒ–姬 dict-f4bc617e-r910 @ 2019/11/16 20:23:12 | https://"
"zhconvert.org\n"
@@ -910,7 +910,7 @@ msgid "unclosed quote"
msgstr "未閉åˆçš„引號"
#: alias.c builtin/cat-file.c builtin/notes.c builtin/prune-packed.c
-#: builtin/receive-pack.c builtin/tag.c
+#: builtin/receive-pack.c builtin/tag.c t/helper/test-pkt-line.c
msgid "too many arguments"
msgstr "引數éŽå¤š"
@@ -925,12 +925,13 @@ msgid "unrecognized whitespace ignore option '%s'"
msgstr "空白字元忽略é¸é … “%s†無法識別"
#: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/clone.c builtin/commit.c builtin/describe.c
-#: builtin/diff-tree.c builtin/difftool.c builtin/fast-export.c builtin/fetch.c
-#: builtin/help.c builtin/index-pack.c builtin/init-db.c builtin/log.c
-#: builtin/ls-files.c builtin/merge-base.c builtin/merge.c
-#: builtin/pack-objects.c builtin/push.c builtin/rebase.c builtin/repack.c
-#: builtin/reset.c builtin/rev-list.c builtin/show-branch.c builtin/stash.c
+#: builtin/checkout.c builtin/clean.c builtin/clone.c builtin/commit.c
+#: builtin/describe.c builtin/diff-tree.c builtin/difftool.c
+#: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c
+#: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c
+#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c
+#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.c
+#: builtin/rev-parse.c builtin/show-branch.c builtin/stash.c
#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c
#: range-diff.c revision.c
#, c-format
@@ -1240,12 +1241,12 @@ msgstr "%s:已存在於工作å€ä¸­"
#: apply.c
#, c-format
msgid "new mode (%o) of %s does not match old mode (%o)"
-msgstr "%2$s çš„æ–°æ¨¡å¼ (%1$o) å’ŒèˆŠæ¨¡å¼ (%3$o) ä¸ç¬¦"
+msgstr "%2$s 的新模å¼ï¼ˆ%1$o)和舊模å¼ï¼ˆ%3$o)ä¸ç¬¦"
#: apply.c
#, c-format
msgid "new mode (%o) of %s does not match old mode (%o) of %s"
-msgstr "%2$s çš„æ–°æ¨¡å¼ (%1$o) å’Œ %4$s çš„èˆŠæ¨¡å¼ (%3$o) ä¸ç¬¦"
+msgstr "%2$s 的新模å¼ï¼ˆ%1$o)和 %4$s 的舊模å¼ï¼ˆ%3$o)ä¸ç¬¦"
#: apply.c
#, c-format
@@ -1734,6 +1735,11 @@ msgstr "éžé æœŸé¸é … --output"
#: archive.c
#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "多出命令列åƒæ•¸ “%sâ€"
+
+#: archive.c
+#, c-format
msgid "Unknown archive format '%s'"
msgstr "å°å­˜æ ¼å¼ “%s†未知"
@@ -1788,6 +1794,17 @@ msgstr "忽略éŽå¤§çš„ gitattributes 資料物件 “%sâ€"
msgid "bad --attr-source or GIT_ATTR_SOURCE"
msgstr "無效的 --attr-source 或 GIT_ATTR_SOURCE"
+#: attr.c read-cache.c
+#, c-format
+msgid "unable to stat '%s'"
+msgstr "ç„¡æ³•å° %s 執行 stat"
+
+#: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c
+#: builtin/pack-objects.c combine-diff.c rerere.c
+#, c-format
+msgid "unable to read %s"
+msgstr "ä¸èƒ½è®€ %s"
+
#: bisect.c
#, c-format
msgid "Badly quoted content in file '%s': %s"
@@ -2323,8 +2340,8 @@ msgid "bad action '%s' for '%s'"
msgstr "“%sâ€ å‹•ä½œå° â€œ%s†無效"
#: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c
-#: builtin/pull.c diff-merges.c gpg-interface.c ls-refs.c parallel-checkout.c
-#: sequencer.c setup.c
+#: builtin/pull.c config.c diff-merges.c gpg-interface.c ls-refs.c
+#: parallel-checkout.c sequencer.c setup.c
#, c-format
msgid "invalid value for '%s': '%s'"
msgstr "“%s†的值無效:“%sâ€"
@@ -2477,8 +2494,7 @@ msgstr "git write-tree 無法寫入樹狀物件"
msgid "applying to an empty history"
msgstr "正在套用至空白歷å²è¨˜éŒ„上"
-#: builtin/am.c builtin/commit.c builtin/merge.c sequencer.c
-#: t/helper/test-fast-rebase.c
+#: builtin/am.c builtin/commit.c builtin/merge.c builtin/replay.c sequencer.c
msgid "failed to write commit object"
msgstr "無法寫入æ交物件"
@@ -2661,8 +2677,9 @@ msgid "n"
msgstr "n"
#: builtin/am.c builtin/branch.c builtin/bugreport.c builtin/cat-file.c
-#: builtin/diagnose.c builtin/for-each-ref.c builtin/ls-files.c
-#: builtin/ls-tree.c builtin/replace.c builtin/tag.c builtin/verify-tag.c
+#: builtin/clone.c builtin/diagnose.c builtin/for-each-ref.c builtin/init-db.c
+#: builtin/ls-files.c builtin/ls-tree.c builtin/replace.c builtin/tag.c
+#: builtin/verify-tag.c
msgid "format"
msgstr "format"
@@ -3357,12 +3374,13 @@ msgstr "無法查詢 “%s†指å‘çš„æ交物件"
#: builtin/branch.c
#, c-format
-msgid ""
-"the branch '%s' is not fully merged.\n"
-"If you are sure you want to delete it, run 'git branch -D %s'"
-msgstr ""
-"分支 “%s†沒有完全åˆä½µã€‚\n"
-"如果確定è¦åˆªé™¤å®ƒï¼Œè«‹åŸ·è¡Œ “git branch -D %sâ€"
+msgid "the branch '%s' is not fully merged"
+msgstr "分支 “%s†沒有完全åˆä½µ"
+
+#: builtin/branch.c
+#, c-format
+msgid "If you are sure you want to delete it, run 'git branch -D %s'"
+msgstr "如果確定è¦åˆªé™¤å®ƒï¼Œè«‹åŸ·è¡Œ “git branch -D %sâ€"
#: builtin/branch.c
msgid "update of config-file failed"
@@ -4370,7 +4388,7 @@ msgstr "ç„¡æ³•å° â€œ%s†執行 reflog 動作:%s\n"
msgid "HEAD is now at"
msgstr "HEAD ç›®å‰ä½æ–¼"
-#: builtin/checkout.c builtin/clone.c t/helper/test-fast-rebase.c
+#: builtin/checkout.c builtin/clone.c
msgid "unable to update HEAD"
msgstr "無法更新 HEAD"
@@ -4631,8 +4649,8 @@ msgid "new-branch"
msgstr "new-branch"
#: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "新的,沒有父æ交的分支"
+msgid "new unborn branch"
+msgstr "新的未誕生分支"
#: builtin/checkout.c builtin/merge.c
msgid "update ignored files (default)"
@@ -4699,7 +4717,7 @@ msgstr ""
msgid "you must specify path(s) to restore"
msgstr "您必須指定è¦é‚„原的路徑"
-#: builtin/checkout.c builtin/clone.c builtin/remote.c
+#: builtin/checkout.c builtin/clone.c builtin/remote.c builtin/replay.c
#: builtin/submodule--helper.c builtin/worktree.c
msgid "branch"
msgstr "branch"
@@ -4940,10 +4958,6 @@ msgid ""
msgstr ""
"clean.requireForce é è¨­ç‚º true 且未æä¾› -iã€-n 或 -f é¸é …,拒絕執行清ç†å‹•ä½œ"
-#: builtin/clean.c
-msgid "-x and -X cannot be used together"
-msgstr "-x å’Œ -X ä¸èƒ½åŒæ™‚使用"
-
#: builtin/clone.c
msgid "git clone [<options>] [--] <repo> [<dir>]"
msgstr "git clone [<options>] [--] <repo> [<dir>]"
@@ -5035,6 +5049,7 @@ msgid "create a shallow clone since a specific time"
msgstr "建立從指定時間到ç¾åœ¨çš„淺層複製"
#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
msgid "revision"
msgstr "revision"
@@ -5062,6 +5077,10 @@ msgstr "gitdir"
msgid "separate git dir from working tree"
msgstr "git 目錄和工作å€åˆ†é›¢"
+#: builtin/clone.c builtin/init-db.c
+msgid "specify the reference format to use"
+msgstr "指定è¦ä½¿ç”¨çš„引用格å¼"
+
#: builtin/clone.c
msgid "key=value"
msgstr "key=value"
@@ -5211,11 +5230,10 @@ msgstr "太多åƒæ•¸ã€‚"
msgid "You must specify a repository to clone."
msgstr "您必須指定è¦è¤‡è£½çš„版本庫。"
-#: builtin/clone.c
-msgid ""
-"--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
-"exclude"
-msgstr "--bundle-uri 與 --depthã€--shallow-since å’Œ --shallow-exclude ä¸ç›¸å®¹"
+#: builtin/clone.c builtin/init-db.c setup.c
+#, c-format
+msgid "unknown ref storage format '%s'"
+msgstr "æœªçŸ¥çš„å¼•ç”¨å„²å­˜æ ¼å¼ â€œ%sâ€"
#: builtin/clone.c
#, c-format
@@ -5376,14 +5394,14 @@ msgid ""
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
msgstr ""
"git commit-graph write [--object-dir <dir>] [--append]\n"
" [--split[=<strategy>]] [--reachable | --stdin-packs | "
"--stdin-commits]\n"
" [--changed-paths] [--[no-]max-new-filters <n>] [--"
"[no-]progress]\n"
-" <split options>"
+" <split-options>"
#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
msgid "dir"
@@ -8264,6 +8282,11 @@ msgstr "無法讀å–樹(%s)"
#: builtin/grep.c
#, c-format
+msgid "unable to read tree %s"
+msgstr "ç„¡æ³•è®€å– %s 樹狀物件"
+
+#: builtin/grep.c
+#, c-format
msgid "unable to grep from object of type %s"
msgstr "無法抓å–來自於 %s 類型的物件"
@@ -8784,11 +8807,6 @@ msgstr "解壓縮嚴é‡çš„ä¸ä¸€è‡´"
msgid "SHA1 COLLISION FOUND WITH %s !"
msgstr "ç™¼ç¾ %s å‡ºç¾ SHA1 è¡çªï¼"
-#: builtin/index-pack.c builtin/pack-objects.c
-#, c-format
-msgid "unable to read %s"
-msgstr "ä¸èƒ½è®€ %s"
-
#: builtin/index-pack.c
#, c-format
msgid "cannot read existing object info %s"
@@ -8962,11 +8980,13 @@ msgstr "在打包物件中 fsck 檢查發生錯誤"
msgid ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
msgstr ""
"git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
" [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+" [--ref-format=<format>]\n"
" [-b <branch-name> | --initial-branch=<branch-name>]\n"
" [--shared[=<permissions>]] [<directory>]"
@@ -9125,7 +9145,7 @@ msgid ""
"<file>"
msgstr "追蹤 <開始>,<çµæŸ> 範åœä¸­æ©«åˆ—或 <檔案> 中> :<函數å稱> 的變化å²"
-#: builtin/log.c builtin/shortlog.c bundle.c
+#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c
#, c-format
msgid "unrecognized argument: %s"
msgstr "無法識別的åƒæ•¸ï¼š%s"
@@ -9868,6 +9888,13 @@ msgstr ""
"git merge-file [<é¸é …>] [-L <檔案1> [-L <åˆå§‹> [-L <åå­—2>]]] <檔案1> <åˆå§‹æ–‡"
"件> <檔案2>"
+#: builtin/merge-file.c diff.c
+msgid ""
+"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
+"\"histogram\""
+msgstr ""
+"diff-algorithm é¸é …有 \"myers\"ã€\"minimal\"ã€\"patience\" å’Œ \"histogram\""
+
#: builtin/merge-file.c
msgid "send results to standard output"
msgstr "å°‡çµæžœå‚³é€åˆ°æ¨™æº–輸出"
@@ -9896,6 +9923,14 @@ msgstr "如果è¡çªï¼Œä½¿ç”¨ä»–們的版本"
msgid "for conflicts, use a union version"
msgstr "如果è¡çªï¼Œä½¿ç”¨è¯åˆç‰ˆæœ¬"
+#: builtin/merge-file.c diff.c
+msgid "<algorithm>"
+msgstr "<演算法>"
+
+#: builtin/merge-file.c diff.c
+msgid "choose a diff algorithm"
+msgstr "é¸æ“‡ä¸€å€‹å·®ç•°æ¼”算法"
+
#: builtin/merge-file.c
msgid "for conflicts, use this marker size"
msgstr "如果è¡çªï¼Œä½¿ç”¨æŒ‡å®šé•·åº¦çš„標記"
@@ -10012,10 +10047,6 @@ msgstr "--trivial-merge 和其他所有é¸é …都ä¸ç›¸å®¹"
msgid "unknown strategy option: -X%s"
msgstr "未知的策略é¸é …:-X%s"
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base 與 --stdin ä¸ç›¸å®¹"
-
#: builtin/merge-tree.c builtin/notes.c
#, c-format
msgid "malformed input line: '%s'."
@@ -10174,7 +10205,7 @@ msgstr "'%s' 沒有指å‘一個æ交"
msgid "Bad branch.%s.mergeoptions string: %s"
msgstr "壞的 branch.%s.mergeoptions 字串:%s"
-#: builtin/merge.c builtin/stash.c merge-recursive.c
+#: builtin/merge.c merge-recursive.c
msgid "Unable to write index."
msgstr "ä¸èƒ½å¯«å…¥ç´¢å¼•ã€‚"
@@ -10182,7 +10213,7 @@ msgstr "ä¸èƒ½å¯«å…¥ç´¢å¼•ã€‚"
msgid "Not handling anything other than two heads merge."
msgstr "未處ç†å…©å€‹é ­åˆä½µä¹‹å¤–的任何動作。"
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
#, c-format
msgid "unable to write %s"
msgstr "ä¸èƒ½å¯« %s"
@@ -11170,6 +11201,11 @@ msgstr "ä¸ä¸€è‡´çš„差異計數"
#: builtin/pack-objects.c
#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "無效的 pack.allowPackReuse 值:“%sâ€"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
@@ -11492,10 +11528,10 @@ msgstr "枚舉物件"
#, c-format
msgid ""
"Total %<PRIu32> (delta %<PRIu32>), reused %<PRIu32> (delta %<PRIu32>), pack-"
-"reused %<PRIu32>"
+"reused %<PRIu32> (from %<PRIuMAX>)"
msgstr ""
"總共 %<PRIu32> (差異 %<PRIu32>),復用 %<PRIu32> (差異 %<PRIu32>),é‡ç”¨åŒ… "
-"%<PRIu32>"
+"%<PRIu32> (總共 %<PRIuMAX>)"
#: builtin/pack-redundant.c
msgid ""
@@ -12574,7 +12610,7 @@ msgstr "沒有正在進行的é‡å®šåŸºåº•ï¼Ÿ"
msgid "The --edit-todo action can only be used during interactive rebase."
msgstr "動作 --edit-todo åªèƒ½ç”¨åœ¨äº’å‹•å¼é‡å®šåŸºåº•éŽç¨‹ä¸­ã€‚"
-#: builtin/rebase.c t/helper/test-fast-rebase.c
+#: builtin/rebase.c
msgid "Cannot read HEAD"
msgstr "ä¸èƒ½è®€å– HEAD"
@@ -12620,12 +12656,6 @@ msgstr "開關 `C' 期望一個數字值"
#: builtin/rebase.c
msgid ""
-"apply options are incompatible with rebase.autoSquash. Consider adding --no-"
-"autosquash"
-msgstr "apply é¸é …與 rebase.autoSquash ä¸ç›¸å®¹ã€‚請考慮加上 --no-autosquash"
-
-#: builtin/rebase.c
-msgid ""
"apply options are incompatible with rebase.rebaseMerges. Consider adding --"
"no-rebase-merges"
msgstr "apply é¸é …與 rebase.rebaseMerges ä¸ç›¸å®¹ã€‚請考慮加上 --no-rebase-merges"
@@ -13109,7 +13139,7 @@ msgid ""
msgid_plural ""
"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
"to delete them, use:"
-msgstr[0] "注æ„:ref/remotes 層級之外的一個分支未被移除。è¦åˆªé™¤å®ƒï¼Œä½¿ç”¨ï¼š"
+msgstr[0] "注æ„:refs/remotes/ 層級之外的一個分支未被移除。è¦åˆªé™¤å®ƒï¼Œä½¿ç”¨ï¼š"
#: builtin/remote.c
#, c-format
@@ -13838,6 +13868,86 @@ msgstr "--convert-graft-file ä¸å¸¶åƒæ•¸"
msgid "only one pattern can be given with -l"
msgstr "åªèƒ½ç‚º -l æ供一個模å¼"
+#: builtin/replay.c
+msgid "need some commits to replay"
+msgstr "需è¦ä¸€äº›æ交æ‰èƒ½é‡æ”¾"
+
+#: builtin/replay.c
+msgid "--onto and --advance are incompatible"
+msgstr "--onto å’Œ --advance ä¸ç›¸å®¹"
+
+#: builtin/replay.c
+msgid "all positive revisions given must be references"
+msgstr "æ供的所有正å‘修訂集必須為引用"
+
+#: builtin/replay.c
+msgid "argument to --advance must be a reference"
+msgstr "傳入 --advance 的引數必須為引用"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple sources because ordering would be ill-"
+"defined"
+msgstr "無法用多個來æºæ¼”進目的地,以å…無法確定排åº"
+
+#: builtin/replay.c
+msgid ""
+"cannot implicitly determine whether this is an --advance or --onto operation"
+msgstr "無法å‡è¨­é€™æ˜¯ --advance 還是 --onto 動作"
+
+#: builtin/replay.c
+msgid ""
+"cannot advance target with multiple source branches because ordering would "
+"be ill-defined"
+msgstr "無法由多個來æºåˆ†æ”¯æ¼”進目的地,以å…無法確定排åº"
+
+#: builtin/replay.c
+msgid "cannot implicitly determine correct base for --onto"
+msgstr "無法å‡è¨­ --onto 的正確基底"
+
+#: builtin/replay.c
+msgid ""
+"(EXPERIMENTAL!) git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+msgstr ""
+"(實驗性功能ï¼ï¼‰git replay ([--contained] --onto <newbase> | --advance "
+"<branch>) <revision-range>..."
+
+#: builtin/replay.c
+msgid "make replay advance given branch"
+msgstr "在指定分支上進行é‡æ”¾æ¼”進"
+
+#: builtin/replay.c
+msgid "replay onto given commit"
+msgstr "é‡æ”¾åˆ°æŒ‡å®šæ交"
+
+#: builtin/replay.c
+msgid "advance all branches contained in revision-range"
+msgstr "演進所有包å«åœ¨ revision-range 中的分支"
+
+#: builtin/replay.c
+msgid "option --onto or --advance is mandatory"
+msgstr "必須傳入 --onto 或 --advance é¸é …"
+
+#: builtin/replay.c
+#, c-format
+msgid ""
+"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
+"will be forced"
+msgstr "將覆寫部分修訂版éæ­·é¸é …,強制使用 “struct rev_info†的 “%s†ä½å…ƒ"
+
+#: builtin/replay.c
+msgid "error preparing revisions"
+msgstr "無法準備修訂集"
+
+#: builtin/replay.c
+msgid "replaying down to root commit is not supported yet!"
+msgstr "å°šä¸æ”¯æ´é‡æ”¾åˆ°æ ¹æ交ï¼"
+
+#: builtin/replay.c
+msgid "replaying merge commits is not supported yet!"
+msgstr "å°šä¸æ”¯æ´é‡æ”¾åˆä½µæ交ï¼"
+
#: builtin/rerere.c
msgid ""
"git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]"
@@ -14100,18 +14210,6 @@ msgstr "--prefix éœ€è¦ 1 個引數"
msgid "unknown mode for --abbrev-ref: %s"
msgstr "--abbrev-ref 的模å¼æœªçŸ¥ï¼š%s"
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --branches"
-msgstr "--exclude-hidden 無法與 --branches åŒæ™‚使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --tags"
-msgstr "--exclude-hidden 無法與 --tags åŒæ™‚使用"
-
-#: builtin/rev-parse.c revision.c
-msgid "--exclude-hidden cannot be used together with --remotes"
-msgstr "--exclude-hidden 無法與 --remotes åŒæ™‚使用"
-
#: builtin/rev-parse.c setup.c
msgid "this operation must be run in a work tree"
msgstr "該動作必須在一個工作å€ä¸­åŸ·è¡Œ"
@@ -14610,11 +14708,6 @@ msgstr "ä¸åˆ—å°çµæžœåˆ°æ¨™æº–輸出(例如與 --verify åƒæ•¸å…±ç”¨ï¼‰"
msgid "show refs from stdin that aren't in local repository"
msgstr "顯示從標準輸入中讀入的ä¸åœ¨æœ¬æ©Ÿç‰ˆæœ¬åº«ä¸­çš„引用"
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "åªèƒ½å‚³å…¥ “%sâ€ã€â€œ%s†或 “%sâ€"
-
#: builtin/sparse-checkout.c
msgid ""
"git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16389,14 +16482,14 @@ msgstr "沒有å¯èƒ½çš„來æºåˆ†æ”¯ï¼ŒæŽ¨æ¸¬ç‚º “--orphanâ€"
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
msgstr ""
"如果您是想è¦åœ¨é€™å€‹ç‰ˆæœ¬åº«å»ºç«‹ä¸€å€‹å·¥ä½œå€ï¼Œè£¡é¢åŒ…å«ä¸€å€‹\n"
-"孤立分支(å³æ²’有æ交的分支),å¯ä»¥ä½¿ç”¨ --orphan é”到\n"
+"未誕生分支(å³æ²’有æ交的分支),å¯ä»¥ä½¿ç”¨ --orphan é”到\n"
"這個效果:\n"
"\n"
" git worktree add --orphan -b %s %s\n"
@@ -16404,14 +16497,14 @@ msgstr ""
#: builtin/worktree.c
#, c-format
msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"If you meant to create a worktree containing a new unborn branch\n"
"(branch with no commits) for this repository, you can do so\n"
"using the --orphan flag:\n"
"\n"
" git worktree add --orphan %s\n"
msgstr ""
"如果您是想è¦åœ¨é€™å€‹ç‰ˆæœ¬åº«å»ºç«‹ä¸€å€‹å·¥ä½œå€ï¼Œè£¡é¢åŒ…å«ä¸€å€‹\n"
-"孤立分支(å³æ²’有æ交的分支),å¯ä»¥ä½¿ç”¨ --orphan é”到\n"
+"未誕生分支(å³æ²’有æ交的分支),å¯ä»¥ä½¿ç”¨ --orphan é”到\n"
"這個效果:\n"
"\n"
" git worktree add --orphan %s\n"
@@ -16483,6 +16576,11 @@ msgstr "正在åˆå§‹åŒ–"
#: builtin/worktree.c
#, c-format
+msgid "could not find created worktree '%s'"
+msgstr "找ä¸åˆ°å»ºç«‹çš„工作å€ã€Œ%sã€"
+
+#: builtin/worktree.c
+#, c-format
msgid "Preparing worktree (new branch '%s')"
msgstr "準備工作å€ï¼ˆæ–°åˆ†æ”¯ '%s')"
@@ -16526,11 +16624,6 @@ msgstr ""
"æ•…åœæ­¢ã€‚使用 “add -f†先覆蓋或抓å–é ç«¯"
#: builtin/worktree.c
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "無法åŒæ™‚使用 “%s†和 “%sâ€"
-
-#: builtin/worktree.c
msgid "checkout <branch> even if already checked out in other worktree"
msgstr "簽出 <分支>,å³ä½¿å·²ç¶“被簽出到其它工作å€"
@@ -16543,8 +16636,8 @@ msgid "create or reset a branch"
msgstr "建立或é‡è¨­ä¸€å€‹åˆ†æ”¯"
#: builtin/worktree.c
-msgid "create unborn/orphaned branch"
-msgstr "建立尚無內容(孤立)的分支"
+msgid "create unborn branch"
+msgstr "建立未誕生分支"
#: builtin/worktree.c
msgid "populate the new working tree"
@@ -16573,12 +16666,8 @@ msgstr "「%sã€ã€ã€Œ%sã€å’Œã€Œ%sã€é¸é …ä¸å¾—åŒæ™‚使用"
#: builtin/worktree.c
#, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "無法åŒæ™‚使用 “%s†和 “%s†é¸é …"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<æ交指示元>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "“%s†é¸é …å’Œæ交號ä¸å¾—åŒæ™‚使用"
#: builtin/worktree.c
msgid "added with --lock"
@@ -17367,6 +17456,10 @@ msgid "Create, list, delete refs to replace objects"
msgstr "建立ã€åˆ—出ã€åˆªé™¤ç‰©ä»¶å–代引用"
#: command-list.h
+msgid "EXPERIMENTAL: Replay commits on a new base, works with bare repos too"
+msgstr "實驗性功能:在新的基底é‡æ”¾æ交,亦支æ´è£¸ç‰ˆæœ¬åº«"
+
+#: command-list.h
msgid "Generates a summary of pending changes"
msgstr "生æˆå¾…定更改的摘è¦"
@@ -17684,6 +17777,37 @@ msgid "commit-graph file is too small"
msgstr "æ交圖形檔案太å°"
#: commit-graph.c
+msgid "commit-graph oid fanout chunk is wrong size"
+msgstr "æ交圖形 OID 扇出å€å¡Šå¤§å°æœ‰èª¤"
+
+#: commit-graph.c
+msgid "commit-graph fanout values out of order"
+msgstr "æ交圖形扇出的數值失åº"
+
+#: commit-graph.c
+msgid "commit-graph OID lookup chunk is the wrong size"
+msgstr "æ交圖形 OID 查詢å€å¡Šçš„大å°æœ‰èª¤"
+
+#: commit-graph.c
+msgid "commit-graph commit data chunk is wrong size"
+msgstr "æ交圖形的æ交資料å€å¡Šå¤§å°æœ‰èª¤"
+
+#: commit-graph.c
+msgid "commit-graph generations chunk is wrong size"
+msgstr "æ交圖形的世代å€å¡Šå¤§å°æœ‰èª¤"
+
+#: commit-graph.c
+msgid "commit-graph changed-path index chunk is too small"
+msgstr "æ交圖形的更動路徑索引å€å¡ŠéŽå°"
+
+#: commit-graph.c
+#, c-format
+msgid ""
+"ignoring too-small changed-path chunk (%<PRIuMAX> < %<PRIuMAX>) in commit-"
+"graph file"
+msgstr "忽略æ交圖形檔案中éŽå°çš„更動路徑å€å¡Š (%<PRIuMAX> < %<PRIuMAX>)"
+
+#: commit-graph.c
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "æ交圖形簽å %X 和簽å %X ä¸ç¬¦åˆ"
@@ -17701,7 +17825,19 @@ msgstr "æ交圖形雜湊版本 %X 和版本 %X ä¸ç¬¦åˆ"
#: commit-graph.c
#, c-format
msgid "commit-graph file is too small to hold %u chunks"
-msgstr "commit-graph 檔案ä¸å¤ æ”¾ç½® %u 個å€å¡Š"
+msgstr "æ交圖形檔案ä¸å¤ æ”¾ç½® %u 個å€å¡Š"
+
+#: commit-graph.c
+msgid "commit-graph required OID fanout chunk missing or corrupted"
+msgstr "æ交圖形需è¦çš„ OID 扇出å€å¡Šéºå¤±æˆ–æ壞"
+
+#: commit-graph.c
+msgid "commit-graph required OID lookup chunk missing or corrupted"
+msgstr "æ交圖形需è¦çš„ OID 查詢å€å¡Šéºå¤±æˆ–æ壞"
+
+#: commit-graph.c
+msgid "commit-graph required commit data chunk missing or corrupted"
+msgstr "æ交圖形需è¦çš„æ交資料å€å¡Šéºå¤±æˆ–æ壞"
#: commit-graph.c
msgid "commit-graph has no base graphs chunk"
@@ -17721,9 +17857,13 @@ msgid "commit count in base graph too high: %<PRIuMAX>"
msgstr "基礎圖 (base graph) 中的æ交數éŽå¤šï¼š%<PRIuMAX>"
#: commit-graph.c
+msgid "commit-graph chain file too small"
+msgstr "æ交圖形éˆæª”案éŽå°"
+
+#: commit-graph.c
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
-msgstr "無效的æ交圖形éˆï¼šè¡Œ '%s' ä¸æ˜¯ä¸€å€‹é›œæ¹Šå€¼"
+msgstr "無效的æ交圖形éˆï¼šã€Œ%sã€åˆ—ä¸æ˜¯é›œæ¹Šå€¼"
#: commit-graph.c
msgid "unable to find all commit-graph files"
@@ -17747,6 +17887,10 @@ msgid "commit-graph overflow generation data is too small"
msgstr "æ交圖形的溢出世代資料éŽå°"
#: commit-graph.c
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "æ交圖形的延伸邊界指é‡è¶…出範åœ"
+
+#: commit-graph.c
msgid "Loading known commits in commit graph"
msgstr "正在載入æ交圖中的已知æ交"
@@ -19120,6 +19264,11 @@ msgstr "color-moved-ws:allow-indentation-change ä¸èƒ½èˆ‡å…¶å®ƒç©ºç™½å­—元模
msgid "Unknown value for 'diff.submodule' config variable: '%s'"
msgstr "設定變數 'diff.submodule' 未知的å–值:'%s'"
+#: diff.c transport.c
+#, c-format
+msgid "unknown value for config '%s': %s"
+msgstr "設定 '%s' çš„å–值未知:%s"
+
#: diff.c
#, c-format
msgid ""
@@ -19214,13 +19363,6 @@ msgid "invalid mode '%s' in --color-moved-ws"
msgstr "--color-moved-ws ä¸­çš„ç„¡æ•ˆæ¨¡å¼ '%s'"
#: diff.c
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"diff-algorithm é¸é …有 \"myers\"ã€\"minimal\"ã€\"patience\" å’Œ \"histogram\""
-
-#: diff.c
#, c-format
msgid "invalid argument to %s"
msgstr "%s çš„åƒæ•¸ç„¡æ•ˆ"
@@ -19277,8 +19419,9 @@ msgid "output only the last line of --stat"
msgstr "åªè¼¸å‡º --stat 的最後一行"
#: diff.c
-msgid "<param1,param2>..."
-msgstr "<åƒæ•¸1,åƒæ•¸2>..."
+#| msgid "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
#: diff.c
msgid ""
@@ -19290,8 +19433,8 @@ msgid "synonym for --dirstat=cumulative"
msgstr "å’Œ --dirstat=cumulative åŒç¾©"
#: diff.c
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "是 --dirstat=files,param1,param2... çš„åŒç¾©è©ž"
+msgid "synonym for --dirstat=files,<param1>,<param2>..."
+msgstr "是 --dirstat=files,<param1>,<param2>... çš„åŒç¾©è©ž"
#: diff.c
msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19514,14 +19657,6 @@ msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "使用 \"histogram diff\" 演算法生æˆå·®ç•°"
#: diff.c
-msgid "<algorithm>"
-msgstr "<演算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "é¸æ“‡ä¸€å€‹å·®ç•°æ¼”算法"
-
-#: diff.c
msgid "<text>"
msgstr "<文字>"
@@ -20940,7 +21075,7 @@ msgstr ""
#. conflict in a submodule. The first argument is the submodule
#. name, and the second argument is the abbreviated id of the
#. commit that needs to be merged. For example:
-#. - go to submodule (mysubmodule), and either merge commit abc1234"
+#. - go to submodule (mysubmodule), and either merge commit abc1234"
#.
#: merge-ort.c
#, c-format
@@ -21275,6 +21410,12 @@ msgid "multi-pack-index OID fanout is of the wrong size"
msgstr "多包索引的物件 ID fanout 大å°éŒ¯èª¤"
#: midx.c
+#, c-format
+msgid ""
+"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+msgstr "物件 ID 扇出無åºï¼šfanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
+
+#: midx.c
msgid "multi-pack-index OID lookup chunk is the wrong size"
msgstr "多包索引 OID 查詢å€å¡Šçš„大å°æœ‰èª¤"
@@ -21333,6 +21474,15 @@ msgid "bad pack-int-id: %u (%u total packs)"
msgstr "錯的 pack-int-id:%u(共有 %u 個包)"
#: midx.c
+msgid "MIDX does not contain the BTMP chunk"
+msgstr "MIDX æœªåŒ…å« BTMP å€å¡Š"
+
+#: midx.c
+#, c-format
+msgid "could not load bitmapped pack %<PRIu32>"
+msgstr "無法載入ä½åœ–化 (bitmapped) çš„å°è£ %<PRIu32>"
+
+#: midx.c
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr "多包索引儲存一個64ä½ä½ç§»ï¼Œä½†æ˜¯ off_t 太å°"
@@ -21440,12 +21590,6 @@ msgid "Looking for referenced packfiles"
msgstr "正在尋找引用的 packfile"
#: midx.c
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr "物件 ID 扇出無åºï¼šfanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-
-#: midx.c
msgid "the midx contains no oid"
msgstr "midx 沒有 oid"
@@ -21820,7 +21964,7 @@ msgstr "%s [無效物件]"
#. TRANSLATORS: This is a line of ambiguous commit
#. object output. E.g.:
#. *
-#. "deadbeef commit 2021-01-01 - Some Commit Message"
+#. "deadbeef commit 2021-01-01 - Some Commit Message"
#.
#: object-name.c
#, c-format
@@ -21830,7 +21974,7 @@ msgstr "%s æ交 %s - %s"
#. TRANSLATORS: This is a line of ambiguous
#. tag object output. E.g.:
#. *
-#. "deadbeef tag 2022-01-01 - Some Tag Message"
+#. "deadbeef tag 2022-01-01 - Some Tag Message"
#. *
#. The second argument is the YYYY-MM-DD found
#. in the tag.
@@ -21847,7 +21991,7 @@ msgstr "%s 標籤 %s - %s"
#. tag object output where we couldn't parse
#. the tag itself. E.g.:
#. *
-#. "deadbeef [bad tag, could not parse it]"
+#. "deadbeef [bad tag, could not parse it]"
#.
#: object-name.c
#, c-format
@@ -22075,6 +22219,10 @@ msgstr "多包ä½åœ–缺少需è¦çš„åå‘索引"
msgid "could not open pack %s"
msgstr "無法開啟å°åŒ… %s"
+#: pack-bitmap.c t/helper/test-read-midx.c
+msgid "could not determine MIDX preferred pack"
+msgstr "無法確定 MIDX å好的å°è£"
+
#: pack-bitmap.c
#, c-format
msgid "preferred pack (%s) is invalid"
@@ -22100,6 +22248,11 @@ msgstr "ewah ä½åœ–æ壞:æ交 “%s†之ä½åœ–的標頭é­æˆªæ–·"
#: pack-bitmap.c
#, c-format
+msgid "unable to load pack: '%s', disabling pack-reuse"
+msgstr "無法載入「%sã€å°è£ï¼Œåœç”¨ pack-reuse"
+
+#: pack-bitmap.c
+#, c-format
msgid "object '%s' not found in type bitmaps"
msgstr "在類型ä½åœ–中,找ä¸åˆ°ã€Œ%sã€ç‰©ä»¶"
@@ -22213,6 +22366,10 @@ msgstr "%<PRIu64> ä½ç½®çš„修訂版索引 (rev-index) 無效:%<PRIu32> != %<P
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "多包索引的åå‘索引å€å¡Šå¤§å°æœ‰èª¤"
+#: pack-revindex.c
+msgid "could not determine preferred pack"
+msgstr "無法確定å好å°è£"
+
#: pack-write.c
msgid "cannot both write and verify reverse index"
msgstr "無法åŒæ™‚寫入和驗證倒排索引"
@@ -22293,11 +22450,6 @@ 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)"
msgstr "有歧義的é¸é …:%s(å¯ä»¥æ˜¯ --%s%s 或 --%s%s)"
@@ -22687,11 +22839,6 @@ msgstr "無法在索引中新增 '%s'"
#: read-cache.c
#, c-format
-msgid "unable to stat '%s'"
-msgstr "ç„¡æ³•å° %s 執行 stat"
-
-#: read-cache.c
-#, c-format
msgid "'%s' appears as both a file and as a directory"
msgstr "'%s' 看起來既是檔案åˆæ˜¯ç›®éŒ„"
@@ -23364,17 +23511,12 @@ msgstr "'%s' 已存在,無法建立 '%s'"
msgid "cannot process '%s' and '%s' at the same time"
msgstr "無法åŒæ™‚è™•ç† '%s' å’Œ '%s'"
-#: refs/files-backend.c
-#, c-format
-msgid "could not remove reference %s"
-msgstr "無法刪除引用 %s"
-
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete reference %s: %s"
msgstr "無法刪除引用 %s:%s"
-#: refs/files-backend.c refs/packed-backend.c
+#: refs.c
#, c-format
msgid "could not delete references: %s"
msgstr "無法刪除引用:%s"
@@ -24532,7 +24674,7 @@ msgstr "無效的作者身分 '%s'"
msgid "corrupt author: missing date information"
msgstr "作者資訊æ壞:缺少日期資訊"
-#: sequencer.c t/helper/test-fast-rebase.c
+#: sequencer.c
#, c-format
msgid "could not update %s"
msgstr "ä¸èƒ½æ›´æ–° %s"
@@ -25024,6 +25166,10 @@ msgid "Autostash exists; creating a new stash entry."
msgstr "已有自動貯存;建立新貯存項目。"
#: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "autostash 引用是符號引用"
+
+#: sequencer.c
msgid "could not detach HEAD"
msgstr "ä¸èƒ½åˆ†é›¢é–‹é ­æŒ‡æ¨™"
@@ -25397,6 +25543,11 @@ msgstr "無效的åˆå§‹åˆ†æ”¯å稱:'%s'"
#: setup.c
#, c-format
+msgid "re-init: ignored --initial-branch=%s"
+msgstr "re-init: 忽略 --initial-branch=%s"
+
+#: setup.c
+#, c-format
msgid "unable to handle file type %d"
msgstr "ä¸èƒ½è™•ç† %d 類型的檔案"
@@ -25410,14 +25561,14 @@ msgid "attempt to reinitialize repository with different hash"
msgstr "嘗試以ä¸åŒçš„雜湊值é‡æ–°åˆå§‹åŒ–版本庫"
#: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已經存在"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "嘗試以ä¸åŒçš„引用儲存格å¼é‡æ–°åˆå§‹åŒ–版本庫"
#: setup.c
#, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: 忽略 --initial-branch=%s"
+msgid "%s already exists"
+msgstr "%s 已經存在"
#: setup.c
#, c-format
@@ -25733,14 +25884,6 @@ msgstr "æ¯æ¬¡è¿­ä»£å‰æ¸…除快å–樹狀物件"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "在快å–樹狀物件中,è¦ä½¿å¤±æ•ˆçš„項目數é‡ï¼ˆé è¨­å€¼ç‚º 0)"
-#: t/helper/test-fast-rebase.c
-msgid "unhandled options"
-msgstr "未處ç†é¸é …"
-
-#: t/helper/test-fast-rebase.c
-msgid "error preparing revisions"
-msgstr "準備修訂版本時發生錯誤"
-
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -25934,10 +26077,6 @@ msgstr "å”定ä¸æ”¯æ´è¨­å®šé ç«¯æœå‹™è·¯å¾‘"
msgid "invalid remote service path"
msgstr "無效的é ç«¯æœå‹™è·¯å¾‘"
-#: transport-helper.c transport.c
-msgid "operation not supported by protocol"
-msgstr "å”定ä¸æ”¯æ´è©²å‹•ä½œ"
-
#: transport-helper.c
#, c-format
msgid "can't connect to subservice %s"
@@ -26103,11 +26242,6 @@ msgstr "å”定 v2 的支æ´å°šæœªå¯¦ç¾"
#: transport.c
#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "設定 '%s' çš„å–值未知:%s"
-
-#: transport.c
-#, c-format
msgid "transport '%s' not allowed"
msgstr "傳輸 '%s' ä¸å…許"
@@ -26165,6 +26299,10 @@ msgstr "通訊å”定ä¸æ”¯æ´ bundle-uri 動作"
msgid "could not retrieve server-advertised bundle-uri list"
msgstr "無法å–得伺æœå™¨å…¬ä½ˆçš„ bundle-uri 清單"
+#: transport.c
+msgid "operation not supported by protocol"
+msgstr "å”定ä¸æ”¯æ´è©²å‹•ä½œ"
+
#: tree-walk.c
msgid "too-short tree object"
msgstr "太短的樹狀物件"
@@ -27193,6 +27331,11 @@ msgstr "å¦å¤–,您的索引中包å«æœªæ交的變更。"
msgid "cannot %s: Your index contains uncommitted changes."
msgstr "ä¸èƒ½%s:您的索引中包å«æœªæ交的變更。"
+#: xdiff-interface.c
+#, c-format
+msgid "unknown style '%s' given for '%s'"
+msgstr "給予「%2$sã€çš„「%1$sã€æ¨£å¼æœªçŸ¥"
+
#: git-merge-octopus.sh git-merge-resolve.sh
msgid ""
"Error: Your local changes to the following files would be overwritten by "
@@ -27649,3 +27792,55 @@ msgstr "ç•¥éŽ %s å«å‚™ä»½å¾Œç¶´ '%s'。\n"
#, perl-format
msgid "Do you really want to send %s? [y|N]: "
msgstr "您真的è¦å‚³é€ %s?[y|N]: "
+
+#~ msgid "-x and -X cannot be used together"
+#~ msgstr "-x å’Œ -X ä¸èƒ½åŒæ™‚使用"
+
+#~ msgid ""
+#~ "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+#~ "exclude"
+#~ msgstr ""
+#~ "--bundle-uri 與 --depthã€--shallow-since å’Œ --shallow-exclude ä¸ç›¸å®¹"
+
+#~ msgid "--merge-base is incompatible with --stdin"
+#~ msgstr "--merge-base 與 --stdin ä¸ç›¸å®¹"
+
+#~ msgid ""
+#~ "apply options are incompatible with rebase.autoSquash. Consider adding --"
+#~ "no-autosquash"
+#~ msgstr "apply é¸é …與 rebase.autoSquash ä¸ç›¸å®¹ã€‚請考慮加上 --no-autosquash"
+
+#~ msgid "--exclude-hidden cannot be used together with --branches"
+#~ msgstr "--exclude-hidden 無法與 --branches åŒæ™‚使用"
+
+#~ msgid "--exclude-hidden cannot be used together with --tags"
+#~ msgstr "--exclude-hidden 無法與 --tags åŒæ™‚使用"
+
+#~ msgid "--exclude-hidden cannot be used together with --remotes"
+#~ msgstr "--exclude-hidden 無法與 --remotes åŒæ™‚使用"
+
+#, c-format
+#~ msgid "only one of '%s', '%s' or '%s' can be given"
+#~ msgstr "åªèƒ½å‚³å…¥ “%sâ€ã€â€œ%s†或 “%sâ€"
+
+#, c-format
+#~ msgid "'%s' and '%s' cannot be used together"
+#~ msgstr "無法åŒæ™‚使用 “%s†和 “%sâ€"
+
+#, c-format
+#~ msgid "options '%s', and '%s' cannot be used together"
+#~ msgstr "無法åŒæ™‚使用 “%s†和 “%s†é¸é …"
+
+#~ msgid "<commit-ish>"
+#~ msgstr "<æ交指示元>"
+
+#, c-format
+#~ msgid "%s is incompatible with %s"
+#~ msgstr "%s 與 %s ä¸ç›¸å®¹"
+
+#, c-format
+#~ msgid "could not remove reference %s"
+#~ msgstr "無法刪除引用 %s"
+
+#~ msgid "unhandled options"
+#~ msgstr "未處ç†é¸é …"
diff --git a/pretty.c b/pretty.c
index cf964b060c..7ead078998 100644
--- a/pretty.c
+++ b/pretty.c
@@ -147,7 +147,7 @@ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought,
for (i = 0; i < commit_formats_len; i++) {
size_t match_len;
- if (!starts_with(commit_formats[i].name, sought))
+ if (!istarts_with(commit_formats[i].name, sought))
continue;
match_len = strlen(commit_formats[i].name);
@@ -428,7 +428,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
}
const char *show_ident_date(const struct ident_split *ident,
- const struct date_mode *mode)
+ struct date_mode mode)
{
timestamp_t date = 0;
long tz = 0;
@@ -592,7 +592,7 @@ void pp_user_info(struct pretty_print_context *pp,
switch (pp->fmt) {
case CMIT_FMT_MEDIUM:
strbuf_addf(sb, "Date: %s\n",
- show_ident_date(&ident, &pp->date_mode));
+ show_ident_date(&ident, pp->date_mode));
break;
case CMIT_FMT_EMAIL:
case CMIT_FMT_MBOXRD:
@@ -601,7 +601,7 @@ void pp_user_info(struct pretty_print_context *pp,
break;
case CMIT_FMT_FULLER:
strbuf_addf(sb, "%sDate: %s\n", what,
- show_ident_date(&ident, &pp->date_mode));
+ show_ident_date(&ident, pp->date_mode));
break;
default:
/* notin' */
@@ -775,7 +775,7 @@ static int mailmap_name(const char **email, size_t *email_len,
static size_t format_person_part(struct strbuf *sb, char part,
const char *msg, int len,
- const struct date_mode *dmode)
+ struct date_mode dmode)
{
/* currently all placeholders have same length */
const int placeholder_len = 2;
@@ -1034,7 +1034,7 @@ static void rewrap_message_tail(struct strbuf *sb,
static int format_reflog_person(struct strbuf *sb,
char part,
struct reflog_walk_info *log,
- const struct date_mode *dmode)
+ struct date_mode dmode)
{
const char *ident;
@@ -1602,7 +1602,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (c->pretty_ctx->reflog_info)
get_reflog_selector(sb,
c->pretty_ctx->reflog_info,
- &c->pretty_ctx->date_mode,
+ c->pretty_ctx->date_mode,
c->pretty_ctx->date_mode_explicit,
(placeholder[1] == 'd'));
return 2;
@@ -1617,7 +1617,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
return format_reflog_person(sb,
placeholder[1],
c->pretty_ctx->reflog_info,
- &c->pretty_ctx->date_mode);
+ c->pretty_ctx->date_mode);
}
return 0; /* unknown %g placeholder */
case 'N':
@@ -1712,11 +1712,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
case 'a': /* author ... */
return format_person_part(sb, placeholder[1],
msg + c->author.off, c->author.len,
- &c->pretty_ctx->date_mode);
+ c->pretty_ctx->date_mode);
case 'c': /* committer ... */
return format_person_part(sb, placeholder[1],
msg + c->committer.off, c->committer.len,
- &c->pretty_ctx->date_mode);
+ c->pretty_ctx->date_mode);
case 'e': /* encoding */
if (c->commit_encoding)
strbuf_addstr(sb, c->commit_encoding);
@@ -1759,7 +1759,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
goto trailer_out;
}
if (*arg == ')') {
- format_trailers_from_commit(sb, msg + c->subject_off, &opts);
+ format_trailers_from_commit(&opts, msg + c->subject_off, sb);
ret = arg - placeholder + 1;
}
trailer_out:
@@ -2077,11 +2077,11 @@ static void pp_header(struct pretty_print_context *pp,
}
}
-void pp_title_line(struct pretty_print_context *pp,
- const char **msg_p,
- struct strbuf *sb,
- const char *encoding,
- int need_8bit_cte)
+void pp_email_subject(struct pretty_print_context *pp,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *encoding,
+ int need_8bit_cte)
{
static const int max_length = 78; /* per rfc2047 */
struct strbuf title;
@@ -2091,19 +2091,14 @@ void pp_title_line(struct pretty_print_context *pp,
pp->preserve_subject ? "\n" : " ");
strbuf_grow(sb, title.len + 1024);
- if (pp->print_email_subject) {
- if (pp->rev)
- fmt_output_email_subject(sb, pp->rev);
- if (pp->encode_email_headers &&
- needs_rfc2047_encoding(title.buf, title.len))
- add_rfc2047(sb, title.buf, title.len,
- encoding, RFC2047_SUBJECT);
- else
- strbuf_add_wrapped_bytes(sb, title.buf, title.len,
+ fmt_output_email_subject(sb, pp->rev);
+ if (pp->encode_email_headers &&
+ needs_rfc2047_encoding(title.buf, title.len))
+ add_rfc2047(sb, title.buf, title.len,
+ encoding, RFC2047_SUBJECT);
+ else
+ strbuf_add_wrapped_bytes(sb, title.buf, title.len,
-last_line_length(sb), 1, max_length);
- } else {
- strbuf_addbuf(sb, &title);
- }
strbuf_addch(sb, '\n');
if (need_8bit_cte == 0) {
@@ -2126,9 +2121,8 @@ void pp_title_line(struct pretty_print_context *pp,
if (pp->after_subject) {
strbuf_addstr(sb, pp->after_subject);
}
- if (cmit_fmt_is_mail(pp->fmt)) {
- strbuf_addch(sb, '\n');
- }
+
+ strbuf_addch(sb, '\n');
if (pp->in_body_headers.nr) {
int i;
@@ -2320,7 +2314,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
}
pp_header(pp, encoding, commit, &msg, sb);
- if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) {
+ if (pp->fmt != CMIT_FMT_ONELINE && !cmit_fmt_is_mail(pp->fmt)) {
strbuf_addch(sb, '\n');
}
@@ -2328,8 +2322,11 @@ void pretty_print_commit(struct pretty_print_context *pp,
msg = skip_blank_lines(msg);
/* These formats treat the title line specially. */
- if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
- pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
+ if (pp->fmt == CMIT_FMT_ONELINE) {
+ msg = format_subject(sb, msg, " ");
+ strbuf_addch(sb, '\n');
+ } else if (cmit_fmt_is_mail(pp->fmt))
+ pp_email_subject(pp, &msg, sb, encoding, need_8bit_cte);
beginning_of_body = sb->len;
if (pp->fmt != CMIT_FMT_ONELINE)
diff --git a/pretty.h b/pretty.h
index 421209e9ec..df267afe4a 100644
--- a/pretty.h
+++ b/pretty.h
@@ -35,11 +35,10 @@ struct pretty_print_context {
*/
enum cmit_fmt fmt;
int abbrev;
- const char *after_subject;
+ char *after_subject;
int preserve_subject;
struct date_mode date_mode;
unsigned date_mode_explicit:1;
- int print_email_subject;
int expand_tabs_in_log;
int need_8bit_cte;
char *notes_message;
@@ -96,13 +95,13 @@ void pp_user_info(struct pretty_print_context *pp, const char *what,
const char *encoding);
/*
- * Format title line of commit message taken from "msg_p" and
+ * Format subject line of commit message taken from "msg_p" and
* put it into "sb".
* First line of "msg_p" is also affected.
*/
-void pp_title_line(struct pretty_print_context *pp, const char **msg_p,
- struct strbuf *sb, const char *encoding,
- int need_8bit_cte);
+void pp_email_subject(struct pretty_print_context *pp, const char **msg_p,
+ struct strbuf *sb, const char *encoding,
+ int need_8bit_cte);
/*
* Get current state of commit message from "msg_p" and continue formatting
@@ -168,7 +167,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
* a well-known sentinel date if they appear bogus.
*/
const char *show_ident_date(const struct ident_split *id,
- const struct date_mode *mode);
+ struct date_mode mode);
#endif /* PRETTY_H */
diff --git a/protocol-caps.c b/protocol-caps.c
index 808a68c974..75f4cbb0a7 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -3,7 +3,6 @@
#include "gettext.h"
#include "hex.h"
#include "pkt-line.h"
-#include "strvec.h"
#include "hash-ll.h"
#include "hex.h"
#include "object.h"
diff --git a/protocol.h b/protocol.h
index de66bf80f8..1e574bbd80 100644
--- a/protocol.h
+++ b/protocol.h
@@ -18,7 +18,7 @@
* with Linus Torvalds <torvalds@osdl.org> as the point of
* contact. September 2005.
*
- * See http://www.iana.org/assignments/port-numbers
+ * See https://www.iana.org/assignments/port-numbers
*/
#define DEFAULT_GIT_PORT 9418
diff --git a/reachable.c b/reachable.c
index 0ce8f83e56..3b85add243 100644
--- a/reachable.c
+++ b/reachable.c
@@ -2,7 +2,6 @@
#include "gettext.h"
#include "hex.h"
#include "refs.h"
-#include "tag.h"
#include "commit.h"
#include "blob.h"
#include "diff.h"
@@ -18,6 +17,7 @@
#include "pack-mtimes.h"
#include "config.h"
#include "run-command.h"
+#include "sequencer.h"
struct connectivity_progress {
struct progress *progress;
@@ -31,6 +31,52 @@ static void update_progress(struct connectivity_progress *cp)
display_progress(cp->progress, cp->count);
}
+static void add_one_file(const char *path, struct rev_info *revs)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct object_id oid;
+ struct object *object;
+
+ if (!read_oneliner(&buf, path, READ_ONELINER_SKIP_IF_EMPTY)) {
+ strbuf_release(&buf);
+ return;
+ }
+ strbuf_trim(&buf);
+ if (!get_oid_hex(buf.buf, &oid)) {
+ object = parse_object_or_die(&oid, buf.buf);
+ add_pending_object(revs, object, "");
+ }
+ strbuf_release(&buf);
+}
+
+/* Mark objects recorded in rebase state files as reachable. */
+static void add_rebase_files(struct rev_info *revs)
+{
+ struct strbuf buf = STRBUF_INIT;
+ size_t len;
+ const char *path[] = {
+ "rebase-apply/autostash",
+ "rebase-apply/orig-head",
+ "rebase-merge/autostash",
+ "rebase-merge/orig-head",
+ };
+ struct worktree **worktrees = get_worktrees();
+
+ for (struct worktree **wt = worktrees; *wt; wt++) {
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+ strbuf_complete(&buf, '/');
+ len = buf.len;
+ for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
+ strbuf_setlen(&buf, len);
+ strbuf_addstr(&buf, path[i]);
+ add_one_file(buf.buf, revs);
+ }
+ }
+ strbuf_release(&buf);
+ free_worktrees(worktrees);
+}
+
static int add_one_ref(const char *path, const struct object_id *oid,
int flag, void *cb_data)
{
@@ -323,6 +369,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
head_ref(add_one_ref, revs);
other_head_refs(add_one_ref, revs);
+ /* rebase autostash and orig-head */
+ add_rebase_files(revs);
+
/* Add all reflog info */
if (mark_reflog)
add_reflogs_to_pending(revs, 0);
diff --git a/read-cache-ll.h b/read-cache-ll.h
index 9a1a7edc5a..09414afd04 100644
--- a/read-cache-ll.h
+++ b/read-cache-ll.h
@@ -436,6 +436,14 @@ int match_stat_data_racy(const struct index_state *istate,
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 */
@@ -472,8 +480,8 @@ 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);
+ const struct pathspec *pathspec, char *ps_matched,
+ int include_sparse, int flags);
void overlay_tree_on_index(struct index_state *istate,
const char *tree_name, const char *prefix);
diff --git a/read-cache.c b/read-cache.c
index 080bd39713..e1723ad796 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -20,7 +20,6 @@
#include "oid-array.h"
#include "tree.h"
#include "commit.h"
-#include "blob.h"
#include "environment.h"
#include "gettext.h"
#include "mem-pool.h"
@@ -31,7 +30,6 @@
#include "read-cache.h"
#include "resolve-undo.h"
#include "revision.h"
-#include "run-command.h"
#include "strbuf.h"
#include "trace2.h"
#include "varint.h"
@@ -197,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)
@@ -3933,8 +3958,8 @@ static void update_callback(struct diff_queue_struct *q,
}
int add_files_to_cache(struct repository *repo, const char *prefix,
- const struct pathspec *pathspec, int include_sparse,
- int flags)
+ const struct pathspec *pathspec, char *ps_matched,
+ int include_sparse, int flags)
{
struct update_callback_data data;
struct rev_info rev;
@@ -3946,8 +3971,10 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
repo_init_revisions(repo, &rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
- if (pathspec)
+ if (pathspec) {
copy_pathspec(&rev.prune_data, pathspec);
+ rev.ps_matched = ps_matched;
+ }
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &data;
diff --git a/rebase-interactive.c b/rebase-interactive.c
index d9718409b3..c343e16fcd 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -71,14 +71,14 @@ void append_todo_help(int command_count,
if (!edit_todo) {
strbuf_addch(buf, '\n');
- strbuf_commented_addf(buf, comment_line_char,
+ strbuf_commented_addf(buf, comment_line_str,
Q_("Rebase %s onto %s (%d command)",
"Rebase %s onto %s (%d commands)",
command_count),
shortrevisions, shortonto, command_count);
}
- strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
msg = _("\nDo not remove any line. Use 'drop' "
@@ -87,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), comment_line_char);
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
if (edit_todo)
msg = _("\nYou are editing the todo file "
@@ -98,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), comment_line_char);
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_str);
}
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -130,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, comment_line_char);
+ strbuf_stripspace(&new_todo->buf, comment_line_str);
if (initial && new_todo->buf.len == 0)
return -3;
diff --git a/ref-filter.c b/ref-filter.c
index fdaabb5bb4..59ad6f54dd 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -22,16 +22,13 @@
#include "ref-filter.h"
#include "revision.h"
#include "utf8.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;
@@ -1614,6 +1611,12 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
if (formatp) {
formatp++;
parse_date_format(formatp, &date_mode);
+
+ /*
+ * If this is a sort field and a format was specified, we'll
+ * want to compare formatted date by string value.
+ */
+ v->atom->type = FIELD_STR;
}
if (!eoemail)
@@ -1624,7 +1627,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad;
- v->s = xstrdup(show_date(timestamp, tz, &date_mode));
+ v->s = xstrdup(show_date(timestamp, tz, date_mode));
v->value = timestamp;
date_mode_release(&date_mode);
return;
@@ -1988,7 +1991,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp
struct strbuf s = STRBUF_INIT;
/* Format the trailer info according to the trailer_opts given */
- format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts);
+ format_trailers_from_commit(&atom->u.contents.trailer_opts, subpos, &s);
v->s = strbuf_detach(&s, NULL);
} else if (atom->u.contents.option == C_BARE)
@@ -2212,7 +2215,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)"),
@@ -2625,6 +2628,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
each_ref_fn cb,
void *cb_data)
{
+ if (filter->kind == FILTER_REFS_KIND_MASK) {
+ /* In this case, we want to print all refs including root refs. */
+ return refs_for_each_include_root_refs(get_main_ref_store(the_repository),
+ cb, cb_data);
+ }
+
if (!filter->match_as_path) {
/*
* in this case, the patterns are applied after
@@ -2747,6 +2756,9 @@ static int ref_kind_from_refname(const char *refname)
return ref_kind[i].kind;
}
+ if (is_pseudoref(get_main_ref_store(the_repository), refname))
+ return FILTER_REFS_PSEUDOREFS;
+
return FILTER_REFS_OTHERS;
}
@@ -2778,7 +2790,16 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct
/* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
kind = filter_ref_kind(filter, refname);
- if (!(kind & filter->kind))
+
+ /*
+ * Generally HEAD refs are printed with special description denoting a rebase,
+ * detached state and so forth. This is useful when only printing the HEAD ref
+ * But when it is being printed along with other pseudorefs, it makes sense to
+ * keep the formatting consistent. So we mask the type to act like a pseudoref.
+ */
+ if (filter->kind == FILTER_REFS_KIND_MASK && kind == FILTER_REFS_DETACHED_HEAD)
+ kind = FILTER_REFS_PSEUDOREFS;
+ else if (!(kind & filter->kind))
return NULL;
if (!filter_pattern_match(filter, refname))
@@ -3044,9 +3065,15 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
else if (filter->kind == FILTER_REFS_TAGS)
ret = for_each_fullref_in("refs/tags/", fn, cb_data);
- else if (filter->kind & FILTER_REFS_ALL)
+ else if (filter->kind & FILTER_REFS_REGULAR)
ret = for_each_fullref_in_pattern(filter, fn, cb_data);
- if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
+
+ /*
+ * When printing all ref types, HEAD is already included,
+ * so we don't want to print HEAD again.
+ */
+ if (!ret && (filter->kind != FILTER_REFS_KIND_MASK) &&
+ (filter->kind & FILTER_REFS_DETACHED_HEAD))
head_ref(fn, cb_data);
}
diff --git a/ref-filter.h b/ref-filter.h
index 0ce5af58ab..0ca28d2bba 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -3,7 +3,6 @@
#include "gettext.h"
#include "oid-array.h"
-#include "refs.h"
#include "commit.h"
#include "string-list.h"
#include "strvec.h"
@@ -20,10 +19,13 @@
#define FILTER_REFS_BRANCHES 0x0004
#define FILTER_REFS_REMOTES 0x0008
#define FILTER_REFS_OTHERS 0x0010
-#define FILTER_REFS_ALL (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
+#define FILTER_REFS_REGULAR (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
#define FILTER_REFS_DETACHED_HEAD 0x0020
-#define FILTER_REFS_KIND_MASK (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD)
+#define FILTER_REFS_PSEUDOREFS 0x0040
+#define FILTER_REFS_ROOT_REFS (FILTER_REFS_DETACHED_HEAD | FILTER_REFS_PSEUDOREFS)
+#define FILTER_REFS_KIND_MASK (FILTER_REFS_REGULAR | FILTER_REFS_DETACHED_HEAD | \
+ FILTER_REFS_PSEUDOREFS)
struct atom_value;
struct ref_sorting;
diff --git a/reflog-walk.c b/reflog-walk.c
index d216f6f966..66484f4f32 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -223,7 +223,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- const struct date_mode *dmode, int force_date,
+ struct date_mode dmode, int force_date,
int shorten)
{
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -297,7 +297,7 @@ timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info)
}
void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
- const struct date_mode *dmode, int force_date)
+ struct date_mode dmode, int force_date)
{
if (reflog_info && reflog_info->last_commit_reflog) {
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
diff --git a/reflog-walk.h b/reflog-walk.h
index 4d93a26957..989583dc55 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -10,14 +10,14 @@ void reflog_walk_info_release(struct reflog_walk_info *info);
int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name);
void show_reflog_message(struct reflog_walk_info *info, int,
- const struct date_mode *, int force_date);
+ struct date_mode, int force_date);
void get_reflog_message(struct strbuf *sb,
struct reflog_walk_info *reflog_info);
const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- const struct date_mode *dmode, int force_date,
+ struct date_mode dmode, int force_date,
int shorten);
int reflog_walk_empty(struct reflog_walk_info *walk);
diff --git a/reflog.c b/reflog.c
index 9ad50e7d93..647f3ca398 100644
--- a/reflog.c
+++ b/reflog.c
@@ -6,7 +6,6 @@
#include "revision.h"
#include "tree.h"
#include "tree-walk.h"
-#include "worktree.h"
/* Remember to update object flag allocation in object.h */
#define INCOMPLETE (1u<<10)
@@ -40,7 +39,7 @@ static int tree_is_complete(const struct object_id *oid)
tree->buffer = data;
tree->size = size;
}
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!repo_has_object_file(the_repository, &entry.oid) ||
diff --git a/refs.c b/refs.c
index fcae5dddc6..55d2e0b2cb 100644
--- a/refs.c
+++ b/refs.c
@@ -33,17 +33,34 @@
/*
* List of all available backends
*/
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+ [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+ [REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
+};
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
{
- struct ref_storage_be *be;
- for (be = refs_backends; be; be = be->next)
- if (!strcmp(be->name, name))
- return be;
+ if (ref_storage_format < ARRAY_SIZE(refs_backends))
+ return refs_backends[ref_storage_format];
return NULL;
}
+unsigned int ref_storage_format_by_name(const char *name)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+ if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+ return i;
+ return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+ const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+ if (!be)
+ return "unknown";
+ return be->name;
+}
+
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refs
@@ -843,6 +860,47 @@ static int is_pseudoref_syntax(const char *refname)
return 1;
}
+int is_pseudoref(struct ref_store *refs, const char *refname)
+{
+ static const char *const irregular_pseudorefs[] = {
+ "AUTO_MERGE",
+ "BISECT_EXPECTED_REV",
+ "NOTES_MERGE_PARTIAL",
+ "NOTES_MERGE_REF",
+ "MERGE_AUTOSTASH",
+ };
+ struct object_id oid;
+ size_t i;
+
+ if (!is_pseudoref_syntax(refname))
+ return 0;
+
+ if (ends_with(refname, "_HEAD")) {
+ refs_resolve_ref_unsafe(refs, refname,
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &oid, NULL);
+ return !is_null_oid(&oid);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++)
+ if (!strcmp(refname, irregular_pseudorefs[i])) {
+ refs_resolve_ref_unsafe(refs, refname,
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &oid, NULL);
+ return !is_null_oid(&oid);
+ }
+
+ return 0;
+}
+
+int is_headref(struct ref_store *refs, const char *refname)
+{
+ if (!strcmp(refname, "HEAD"))
+ return refs_ref_exists(refs, refname);
+
+ return 0;
+}
+
static int is_current_worktree_ref(const char *ref) {
return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
}
@@ -1022,55 +1080,40 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
const char *message, void *cb_data)
{
struct read_ref_at_cb *cb = cb_data;
- int reached_count;
cb->tz = tz;
cb->date = timestamp;
- /*
- * It is not possible for cb->cnt == 0 on the first iteration because
- * that special case is handled in read_ref_at().
- */
- if (cb->cnt > 0)
- cb->cnt--;
- reached_count = cb->cnt == 0 && !is_null_oid(ooid);
- if (timestamp <= cb->at_time || reached_count) {
+ if (timestamp <= cb->at_time || cb->cnt == 0) {
set_read_ref_cutoffs(cb, timestamp, tz, message);
/*
* we have not yet updated cb->[n|o]oid so they still
* hold the values for the previous record.
*/
- if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid))
- warning(_("log for ref %s has gap after %s"),
+ if (!is_null_oid(&cb->ooid)) {
+ oidcpy(cb->oid, noid);
+ if (!oideq(&cb->ooid, noid))
+ warning(_("log for ref %s has gap after %s"),
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
- if (reached_count)
- oidcpy(cb->oid, ooid);
- else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time)
+ }
+ else if (cb->date == cb->at_time)
oidcpy(cb->oid, noid);
else if (!oideq(noid, cb->oid))
warning(_("log for ref %s unexpectedly ended on %s"),
cb->refname, show_date(cb->date, cb->tz,
DATE_MODE(RFC2822)));
+ cb->reccnt++;
+ oidcpy(&cb->ooid, ooid);
+ oidcpy(&cb->noid, noid);
cb->found_it = 1;
+ return 1;
}
cb->reccnt++;
oidcpy(&cb->ooid, ooid);
oidcpy(&cb->noid, noid);
- return cb->found_it;
-}
-
-static int read_ref_at_ent_newest(struct object_id *ooid UNUSED,
- struct object_id *noid,
- const char *email UNUSED,
- timestamp_t timestamp, int tz,
- const char *message, void *cb_data)
-{
- struct read_ref_at_cb *cb = cb_data;
-
- set_read_ref_cutoffs(cb, timestamp, tz, message);
- oidcpy(cb->oid, noid);
- /* We just want the first entry */
- return 1;
+ if (cb->cnt > 0)
+ cb->cnt--;
+ return 0;
}
static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
@@ -1082,7 +1125,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
set_read_ref_cutoffs(cb, timestamp, tz, message);
oidcpy(cb->oid, ooid);
- if (is_null_oid(cb->oid))
+ if (cb->at_time && is_null_oid(cb->oid))
oidcpy(cb->oid, noid);
/* We just want the first entry */
return 1;
@@ -1105,14 +1148,24 @@ int read_ref_at(struct ref_store *refs, const char *refname,
cb.cutoff_cnt = cutoff_cnt;
cb.oid = oid;
- if (cb.cnt == 0) {
- refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb);
- return 0;
- }
-
refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
if (!cb.reccnt) {
+ if (cnt == 0) {
+ /*
+ * The caller asked for ref@{0}, and we had no entries.
+ * It's a bit subtle, but in practice all callers have
+ * prepped the "oid" field with the current value of
+ * the ref, which is the most reasonable fallback.
+ *
+ * We'll put dummy values into the out-parameters (so
+ * they're not just uninitialized garbage), and the
+ * caller can take our return value as a hint that
+ * we did not find any such reflog.
+ */
+ set_read_ref_cutoffs(&cb, 0, 0, "empty reflog");
+ return 1;
+ }
if (flags & GET_OID_QUIETLY)
exit(128);
else
@@ -1577,10 +1630,6 @@ struct ref_iterator *refs_ref_iterator_begin(
if (trim)
iter = prefix_ref_iterator_begin(iter, "", trim);
- /* Sanity check for subclasses: */
- if (!iter->ordered)
- BUG("reference iterator is not ordered");
-
return iter;
}
@@ -1707,6 +1756,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
}
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+ void *cb_data)
+{
+ return do_for_each_ref(refs, "", NULL, fn, 0,
+ DO_FOR_EACH_INCLUDE_ROOT_REFS, cb_data);
+}
+
static int qsort_strcmp(const void *va, const void *vb)
{
const char *a = *(const char **)va;
@@ -1806,8 +1862,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);
@@ -1818,15 +1876,45 @@ done:
return result;
}
+static int is_special_ref(const char *refname)
+{
+ /*
+ * Special references are refs that have different semantics compared
+ * to "normal" refs. These refs can thus not be stored in the ref
+ * backend, but must always be accessed via the filesystem. The
+ * following refs are special:
+ *
+ * - FETCH_HEAD may contain multiple object IDs, and each one of them
+ * carries additional metadata like where it came from.
+ *
+ * - MERGE_HEAD may contain multiple object IDs when merging multiple
+ * heads.
+ *
+ * Reading, writing or deleting references must consistently go either
+ * through the filesystem (special refs) or through the reference
+ * backend (normal ones).
+ */
+ static const char * const special_refs[] = {
+ "FETCH_HEAD",
+ "MERGE_HEAD",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(special_refs); i++)
+ if (!strcmp(refname, special_refs[i]))
+ return 1;
+
+ return 0;
+}
+
int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
unsigned int *type, int *failure_errno)
{
assert(failure_errno);
- if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+ if (is_special_ref(refname))
return refs_read_special_head(ref_store, refname, oid, referent,
type, failure_errno);
- }
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
type, failure_errno);
@@ -1928,11 +2016,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
}
/* backend functions */
-int refs_init_db(struct strbuf *err)
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
{
- struct ref_store *refs = get_main_ref_store(the_repository);
-
- return refs->be->init_db(refs, err);
+ return refs->be->init_db(refs, flags, err);
}
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
@@ -2029,12 +2115,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;
@@ -2469,18 +2555,33 @@ cleanup:
return ret;
}
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+struct do_for_each_reflog_help {
+ each_reflog_fn *fn;
+ void *cb_data;
+};
+
+static int do_for_each_reflog_helper(struct repository *r UNUSED,
+ const char *refname,
+ const struct object_id *oid UNUSED,
+ int flags,
+ void *cb_data)
+{
+ struct do_for_each_reflog_help *hp = cb_data;
+ return hp->fn(refname, hp->cb_data);
+}
+
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data)
{
struct ref_iterator *iter;
- struct do_for_each_ref_help hp = { fn, cb_data };
+ struct do_for_each_reflog_help hp = { fn, cb_data };
iter = refs->be->reflog_iterator_begin(refs);
return do_for_each_repo_ref_iterator(the_repository, iter,
- do_for_each_ref_helper, &hp);
+ do_for_each_reflog_helper, &hp);
}
-int for_each_reflog(each_ref_fn fn, void *cb_data)
+int for_each_reflog(each_reflog_fn fn, void *cb_data)
{
return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
}
@@ -2599,13 +2700,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 23211a5ea1..d278775e08 100644
--- a/refs.h
+++ b/refs.h
@@ -11,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.
*
@@ -56,19 +59,13 @@ 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,
@@ -123,7 +120,9 @@ int should_autocreate_reflog(const char *refname);
int is_branch(const char *refname);
-int refs_init_db(struct strbuf *err);
+#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
/*
* Return the peeled value of the oid currently being iterated via
@@ -394,6 +393,12 @@ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
int for_each_rawref(each_ref_fn fn, void *cb_data);
/*
+ * Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
+ */
+int refs_for_each_include_root_refs(struct ref_store *refs, each_ref_fn fn,
+ void *cb_data);
+
+/*
* Normalizes partial refs to their fully qualified form.
* Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
* <prefix> will default to 'refs/' if NULL.
@@ -417,10 +422,18 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
/*
* Flags for controlling behaviour of pack_refs()
* PACK_REFS_PRUNE: Prune loose refs after packing
- * PACK_REFS_ALL: Pack _all_ refs, not just tags and already packed refs
+ * PACK_REFS_AUTO: Pack refs on a best effort basis. The heuristics and end
+ * result are decided by the ref backend. Backends may ignore
+ * this flag and fall back to a normal repack.
*/
-#define PACK_REFS_PRUNE 0x0001
-#define PACK_REFS_ALL 0x0002
+#define PACK_REFS_PRUNE (1 << 0)
+#define PACK_REFS_AUTO (1 << 1)
+
+struct pack_refs_opts {
+ unsigned int flags;
+ struct ref_exclusions *exclusions;
+ struct string_list *includes;
+};
/*
* Write a packed-refs file for the current repository.
@@ -435,7 +448,20 @@ int refs_create_reflog(struct ref_store *refs, const char *refname,
struct strbuf *err);
int safe_create_reflog(const char *refname, struct strbuf *err);
-/** Reads log for the value of ref during at_time. **/
+/**
+ * Reads log for the value of ref during at_time (in which case "cnt" should be
+ * negative) or the reflog "cnt" entries from the top (in which case "at_time"
+ * should be 0).
+ *
+ * If we found the reflog entry in question, returns 0 (and details of the
+ * entry can be found in the out-parameters).
+ *
+ * If we ran out of reflog entries, the out-parameters are filled with the
+ * details of the oldest entry we did find, and the function returns 1. Note
+ * that there is one important special case here! If the reflog was empty
+ * and the caller asked for the 0-th cnt, we will return "1" but leave the
+ * "oid" field untouched.
+ **/
int read_ref_at(struct ref_store *refs,
const char *refname, unsigned int flags,
timestamp_t at_time, int cnt,
@@ -530,11 +556,18 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
/*
+ * The signature for the callback function for the {refs_,}for_each_reflog()
+ * functions below. The memory pointed to by the refname argument is only
+ * guaranteed to be valid for the duration of a single callback invocation.
+ */
+typedef int each_reflog_fn(const char *refname, void *cb_data);
+
+/*
* Calls the specified function for each reflog file until it returns nonzero,
* and returns the value. Reflog file order is unspecified.
*/
-int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
-int for_each_reflog(each_ref_fn fn, void *cb_data);
+int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data);
+int for_each_reflog(each_reflog_fn fn, void *cb_data);
#define REFNAME_ALLOW_ONELEVEL 1
#define REFNAME_REFSPEC_PATTERN 2
@@ -1018,4 +1051,7 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT];
*/
void update_ref_namespace(enum ref_namespace namespace, char *ref);
+int is_pseudoref(struct ref_store *refs, const char *refname);
+int is_headref(struct ref_store *refs, const char *refname);
+
#endif /* REFS_H */
diff --git a/refs/debug.c b/refs/debug.c
index b7ffc4ce67..c7531b17f0 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -33,10 +33,10 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
return (struct ref_store *)res;
}
-static int debug_init_db(struct ref_store *refs, struct strbuf *err)
+static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
- int res = drefs->refs->be->init_db(drefs->refs, err);
+ int res = drefs->refs->be->init_db(drefs->refs, flags, err);
trace_printf_key(&trace_refs, "init_db: %d\n", res);
return res;
}
@@ -143,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)
{
@@ -195,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n",
diter->iter->refname);
- diter->base.ordered = diter->iter->ordered;
diter->base.refname = diter->iter->refname;
diter->base.oid = diter->iter->oid;
diter->base.flags = diter->iter->flags;
@@ -236,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
drefs->refs->be->iterator_begin(drefs->refs, prefix,
exclude_patterns, flags);
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
- base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
+ base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable);
diter->iter = res;
trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n",
prefix, flags);
@@ -440,7 +425,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,
@@ -458,7 +442,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,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index db5c0c7a72..a098d14ea0 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,5 +1,4 @@
#include "../git-compat-util.h"
-#include "../config.h"
#include "../copy.h"
#include "../environment.h"
#include "../gettext.h"
@@ -19,7 +18,6 @@
#include "../dir.h"
#include "../chdir-notify.h"
#include "../setup.h"
-#include "../worktree.h"
#include "../wrapper.h"
#include "../write-or-die.h"
#include "../revision.h"
@@ -231,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir
}
}
+static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs,
+ const char *refname,
+ struct ref_dir *dir)
+{
+ struct object_id oid;
+ int flag;
+
+ if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING,
+ &oid, &flag)) {
+ oidclr(&oid);
+ flag |= REF_ISBROKEN;
+ } else if (is_null_oid(&oid)) {
+ /*
+ * It is so astronomically unlikely
+ * that null_oid is the OID of an
+ * actual object that we consider its
+ * appearance in a loose reference
+ * file to be repo corruption
+ * (probably due to a software bug).
+ */
+ flag |= REF_ISBROKEN;
+ }
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(refname))
+ die("loose refname is dangerous: %s", refname);
+ oidclr(&oid);
+ flag |= REF_BAD_NAME | REF_ISBROKEN;
+ }
+ add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag));
+}
+
/*
* Read the loose references from the namespace dirname into dir
* (without recursing). dirname must end with '/'. dir must be the
@@ -259,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_add(&refname, dirname, dirnamelen);
while ((de = readdir(d)) != NULL) {
- struct object_id oid;
- int flag;
unsigned char dtype;
if (de->d_name[0] == '.')
@@ -276,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
create_dir_entry(dir->cache, refname.buf,
refname.len));
} else if (dtype == DT_REG) {
- if (!refs_resolve_ref_unsafe(&refs->base,
- refname.buf,
- RESOLVE_REF_READING,
- &oid, &flag)) {
- oidclr(&oid);
- flag |= REF_ISBROKEN;
- } else if (is_null_oid(&oid)) {
- /*
- * It is so astronomically unlikely
- * that null_oid is the OID of an
- * actual object that we consider its
- * appearance in a loose reference
- * file to be repo corruption
- * (probably due to a software bug).
- */
- flag |= REF_ISBROKEN;
- }
-
- if (check_refname_format(refname.buf,
- REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(refname.buf))
- die("loose refname is dangerous: %s", refname.buf);
- oidclr(&oid);
- flag |= REF_BAD_NAME | REF_ISBROKEN;
- }
- add_entry_to_dir(dir,
- create_ref_entry(refname.buf, &oid, flag));
+ loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
}
strbuf_setlen(&refname, dirnamelen);
}
@@ -313,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
add_per_worktree_entries_to_dir(dir, dirname);
}
-static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
+/*
+ * Add pseudorefs to the ref dir by parsing the directory for any files
+ * which follow the pseudoref syntax.
+ */
+static void add_pseudoref_and_head_entries(struct ref_store *ref_store,
+ struct ref_dir *dir,
+ const char *dirname)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
+ struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT;
+ struct dirent *de;
+ size_t dirnamelen;
+ DIR *d;
+
+ files_ref_path(refs, &path, dirname);
+
+ d = opendir(path.buf);
+ if (!d) {
+ strbuf_release(&path);
+ return;
+ }
+
+ strbuf_addstr(&refname, dirname);
+ dirnamelen = refname.len;
+
+ while ((de = readdir(d)) != NULL) {
+ unsigned char dtype;
+
+ if (de->d_name[0] == '.')
+ continue;
+ if (ends_with(de->d_name, ".lock"))
+ continue;
+ strbuf_addstr(&refname, de->d_name);
+
+ dtype = get_dtype(de, &path, 1);
+ if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) ||
+ is_headref(ref_store, de->d_name)))
+ loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
+
+ strbuf_setlen(&refname, dirnamelen);
+ }
+ strbuf_release(&refname);
+ strbuf_release(&path);
+ closedir(d);
+}
+
+static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs,
+ unsigned int flags)
{
if (!refs->loose) {
+ struct ref_dir *dir;
+
/*
* Mark the top-level directory complete because we
* are about to read the only subdirectory that can
@@ -326,12 +378,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
/* We're going to fill the top level ourselves: */
refs->loose->root->flag &= ~REF_INCOMPLETE;
+ dir = get_ref_dir(refs->loose->root);
+
+ if (flags & DO_FOR_EACH_INCLUDE_ROOT_REFS)
+ add_pseudoref_and_head_entries(dir->cache->ref_store, dir,
+ refs->loose->root->name);
+
/*
* Add an incomplete entry for "refs/" (to be filled
* lazily):
*/
- add_entry_to_dir(get_ref_dir(refs->loose->root),
- create_dir_entry(refs->loose, "refs/", 5));
+ add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5));
}
return refs->loose;
}
@@ -859,7 +916,7 @@ static struct ref_iterator *files_ref_iterator_begin(
* disk, and re-reads it if not.
*/
- loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
+ loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags),
prefix, ref_store->repo, 1);
/*
@@ -881,8 +938,7 @@ static struct ref_iterator *files_ref_iterator_begin(
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
- overlay_iter->ordered);
+ base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
iter->iter0 = overlay_iter;
iter->repo = ref_store->repo;
iter->flags = flags;
@@ -1220,7 +1276,7 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
- iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+ iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL,
the_repository, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
/*
@@ -1265,54 +1321,6 @@ static int files_pack_refs(struct ref_store *ref_store,
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.
@@ -2166,10 +2174,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
struct files_reflog_iterator {
struct ref_iterator base;
-
struct ref_store *ref_store;
struct dir_iterator *dir_iterator;
- struct object_id oid;
};
static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
@@ -2180,25 +2186,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
- int flags;
-
if (!S_ISREG(diter->st.st_mode))
continue;
- if (diter->basename[0] == '.')
- continue;
- if (ends_with(diter->basename, ".lock"))
+ if (check_refname_format(diter->basename,
+ REFNAME_ALLOW_ONELEVEL))
continue;
- if (!refs_resolve_ref_unsafe(iter->ref_store,
- diter->relative_path, 0,
- &iter->oid, &flags)) {
- error("bad ref for %s", diter->path.buf);
- continue;
- }
-
iter->base.refname = diter->relative_path;
- iter->base.oid = &iter->oid;
- iter->base.flags = flags;
return ITER_OK;
}
@@ -2243,7 +2237,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
strbuf_addf(&sb, "%s/logs", gitdir);
- diter = dir_iterator_begin(sb.buf, 0);
+ diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED);
if (!diter) {
strbuf_release(&sb);
return empty_ref_iterator_begin();
@@ -2252,7 +2246,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
+ base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
iter->dir_iterator = diter;
iter->ref_store = ref_store;
strbuf_release(&sb);
@@ -2260,32 +2254,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
return ref_iterator;
}
-static enum iterator_selection reflog_iterator_select(
- struct ref_iterator *iter_worktree,
- struct ref_iterator *iter_common,
- void *cb_data UNUSED)
-{
- if (iter_worktree) {
- /*
- * We're a bit loose here. We probably should ignore
- * common refs if they are accidentally added as
- * per-worktree refs.
- */
- return ITER_SELECT_0;
- } else if (iter_common) {
- if (parse_worktree_ref(iter_common->refname, NULL, NULL,
- NULL) == REF_WORKTREE_SHARED)
- return ITER_SELECT_1;
-
- /*
- * The main ref store may contain main worktree's
- * per-worktree refs, which should be ignored
- */
- return ITER_SKIP_1;
- } else
- return ITER_DONE;
-}
-
static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
{
struct files_ref_store *refs =
@@ -2296,9 +2264,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
return reflog_iterator_begin(ref_store, refs->gitcommondir);
} else {
return merge_ref_iterator_begin(
- 0, reflog_iterator_begin(ref_store, refs->base.gitdir),
+ reflog_iterator_begin(ref_store, refs->base.gitdir),
reflog_iterator_begin(ref_store, refs->gitcommondir),
- reflog_iterator_select, refs);
+ ref_iterator_select, refs);
}
}
@@ -3268,28 +3236,52 @@ static int files_reflog_expire(struct ref_store *ref_store,
return -1;
}
-static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
+static int files_init_db(struct ref_store *ref_store,
+ int flags,
+ struct strbuf *err UNUSED)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE, "init_db");
struct strbuf sb = STRBUF_INIT;
/*
- * Create .git/refs/{heads,tags}
+ * We need to create a "refs" dir in any case so that older versions of
+ * Git can tell that this is a repository. This serves two main purposes:
+ *
+ * - Clients will know to stop walking the parent-directory chain when
+ * detecting the Git repository. Otherwise they may end up detecting
+ * a Git repository in a parent directory instead.
+ *
+ * - Instead of failing to detect a repository with unknown reference
+ * format altogether, old clients will print an error saying that
+ * they do not understand the reference format extension.
*/
- files_ref_path(refs, &sb, "refs/heads");
+ strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
safe_create_dir(sb.buf, 1);
+ adjust_shared_perm(sb.buf);
- strbuf_reset(&sb);
- files_ref_path(refs, &sb, "refs/tags");
- safe_create_dir(sb.buf, 1);
+ /*
+ * There is no need to create directories for common refs when creating
+ * a worktree ref store.
+ */
+ if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+ /*
+ * Create .git/refs/{heads,tags}
+ */
+ strbuf_reset(&sb);
+ files_ref_path(refs, &sb, "refs/heads");
+ safe_create_dir(sb.buf, 1);
+
+ strbuf_reset(&sb);
+ files_ref_path(refs, &sb, "refs/tags");
+ safe_create_dir(sb.buf, 1);
+ }
strbuf_release(&sb);
return 0;
}
struct ref_storage_be refs_be_files = {
- .next = NULL,
.name = "files",
.init = files_ref_store_create,
.init_db = files_init_db,
@@ -3300,7 +3292,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,
diff --git a/refs/iterator.c b/refs/iterator.c
index 6b680f610e..9db8b056d5 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -25,11 +25,9 @@ int ref_iterator_abort(struct ref_iterator *ref_iterator)
}
void base_ref_iterator_init(struct ref_iterator *iter,
- struct ref_iterator_vtable *vtable,
- int ordered)
+ struct ref_iterator_vtable *vtable)
{
iter->vtable = vtable;
- iter->ordered = !!ordered;
iter->refname = NULL;
iter->oid = NULL;
iter->flags = 0;
@@ -74,7 +72,7 @@ struct ref_iterator *empty_ref_iterator_begin(void)
struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
+ base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable);
return ref_iterator;
}
@@ -98,6 +96,49 @@ struct merge_ref_iterator {
struct ref_iterator **current;
};
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+ struct ref_iterator *iter_common,
+ void *cb_data UNUSED)
+{
+ if (iter_worktree && !iter_common) {
+ /*
+ * Return the worktree ref if there are no more common refs.
+ */
+ return ITER_SELECT_0;
+ } else if (iter_common) {
+ /*
+ * In case we have pending worktree and common refs we need to
+ * yield them based on their lexicographical order. Worktree
+ * refs that have the same name as common refs shadow the
+ * latter.
+ */
+ if (iter_worktree) {
+ int cmp = strcmp(iter_worktree->refname,
+ iter_common->refname);
+ if (cmp < 0)
+ return ITER_SELECT_0;
+ else if (!cmp)
+ return ITER_SELECT_0_SKIP_1;
+ }
+
+ /*
+ * We now know that the lexicographically-next ref is a common
+ * ref. When the common ref is a shared one we return it.
+ */
+ if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+ NULL) == REF_WORKTREE_SHARED)
+ return ITER_SELECT_1;
+
+ /*
+ * Otherwise, if the common ref is a per-worktree ref we skip
+ * it because it would belong to the main worktree, not ours.
+ */
+ return ITER_SKIP_1;
+ } else {
+ return ITER_DONE;
+ }
+}
+
static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
@@ -207,7 +248,6 @@ static struct ref_iterator_vtable merge_ref_iterator_vtable = {
};
struct ref_iterator *merge_ref_iterator_begin(
- int ordered,
struct ref_iterator *iter0, struct ref_iterator *iter1,
ref_iterator_select_fn *select, void *cb_data)
{
@@ -222,7 +262,7 @@ struct ref_iterator *merge_ref_iterator_begin(
* references through only if they exist in both iterators.
*/
- base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
+ base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
iter->iter0 = iter0;
iter->iter1 = iter1;
iter->select = select;
@@ -271,12 +311,9 @@ struct ref_iterator *overlay_ref_iterator_begin(
} else if (is_empty_ref_iterator(back)) {
ref_iterator_abort(back);
return front;
- } else if (!front->ordered || !back->ordered) {
- BUG("overlay_ref_iterator requires ordered inputs");
}
- return merge_ref_iterator_begin(1, front, back,
- overlay_iterator_select, NULL);
+ return merge_ref_iterator_begin(front, back, overlay_iterator_select, NULL);
}
struct prefix_ref_iterator {
@@ -315,16 +352,12 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (cmp > 0) {
/*
- * If the source iterator is ordered, then we
+ * As the source iterator is ordered, we
* can stop the iteration as soon as we see a
* refname that comes after the prefix:
*/
- if (iter->iter0->ordered) {
- ok = ref_iterator_abort(iter->iter0);
- break;
- } else {
- continue;
- }
+ ok = ref_iterator_abort(iter->iter0);
+ break;
}
if (iter->trim) {
@@ -396,7 +429,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
+ base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable);
iter->iter0 = iter0;
iter->prefix = xstrdup(prefix);
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 59c78d7941..4e826c05ff 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,5 +1,4 @@
#include "../git-compat-util.h"
-#include "../alloc.h"
#include "../config.h"
#include "../gettext.h"
#include "../hash.h"
@@ -1112,7 +1111,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
+ base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
if (exclude_patterns)
populate_excluded_jump_list(iter, snapshot, exclude_patterns);
@@ -1246,6 +1245,7 @@ static const char PACKED_REFS_HEADER[] =
"# pack-refs with: peeled fully-peeled sorted \n";
static int packed_init_db(struct ref_store *ref_store UNUSED,
+ int flags UNUSED,
struct strbuf *err UNUSED)
{
/* Nothing to do. */
@@ -1688,55 +1688,6 @@ 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,
struct pack_refs_opts *pack_opts UNUSED)
{
@@ -1754,7 +1705,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,
@@ -1765,7 +1715,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,
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 6e3b725245..9f9797209a 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -1,5 +1,4 @@
#include "../git-compat-util.h"
-#include "../alloc.h"
#include "../hash.h"
#include "../refs.h"
#include "../repository.h"
@@ -487,7 +486,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1);
+ base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
iter->levels_nr = 1;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 9db8aec4da..56641aa57a 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -260,6 +260,12 @@ enum do_for_each_ref_flags {
* INCLUDE_BROKEN, since they are otherwise not included at all.
*/
DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
+
+ /*
+ * Include root refs i.e. HEAD and pseudorefs along with the regular
+ * refs.
+ */
+ DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
};
/*
@@ -312,13 +318,6 @@ enum do_for_each_ref_flags {
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
-
- /*
- * Does this `ref_iterator` iterate over references in order
- * by refname?
- */
- unsigned int ordered : 1;
-
const char *refname;
const struct object_id *oid;
unsigned int flags;
@@ -387,14 +386,21 @@ typedef enum iterator_selection ref_iterator_select_fn(
void *cb_data);
/*
+ * An implementation of ref_iterator_select_fn that merges worktree and common
+ * refs. Per-worktree refs from the common iterator are ignored, worktree refs
+ * override common refs. Refs are selected lexicographically.
+ */
+enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree,
+ struct ref_iterator *iter_common,
+ void *cb_data);
+
+/*
* Iterate over the entries from iter0 and iter1, with the values
* interleaved as directed by the select function. The iterator takes
* ownership of iter0 and iter1 and frees them when the iteration is
- * over. A derived class should set `ordered` to 1 or 0 based on
- * whether it generates its output in order by reference name.
+ * over.
*/
struct ref_iterator *merge_ref_iterator_begin(
- int ordered,
struct ref_iterator *iter0, struct ref_iterator *iter1,
ref_iterator_select_fn *select, void *cb_data);
@@ -423,8 +429,6 @@ struct ref_iterator *overlay_ref_iterator_begin(
* As an convenience to callers, if prefix is the empty string and
* trim is zero, this function returns iter0 directly, without
* wrapping it.
- *
- * The resulting ref_iterator is ordered if iter0 is.
*/
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
const char *prefix,
@@ -435,14 +439,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
/*
* Base class constructor for ref_iterators. Initialize the
* ref_iterator part of iter, setting its vtable pointer as specified.
- * `ordered` should be set to 1 if the iterator will iterate over
- * references in order by refname; otherwise it should be set to 0.
* This is meant to be called only by the initializers of derived
* classes.
*/
void base_ref_iterator_init(struct ref_iterator *iter,
- struct ref_iterator_vtable *vtable,
- int ordered);
+ struct ref_iterator_vtable *vtable);
/*
* Base class destructor for ref_iterators. Destroy the ref_iterator
@@ -529,7 +530,9 @@ typedef struct ref_store *ref_store_init_fn(struct repository *repo,
const char *gitdir,
unsigned int flags);
-typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+typedef int ref_init_db_fn(struct ref_store *refs,
+ int flags,
+ struct strbuf *err);
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,
@@ -553,8 +556,6 @@ 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);
@@ -665,7 +666,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;
@@ -677,7 +677,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;
@@ -695,6 +694,7 @@ struct ref_storage_be {
};
extern struct ref_storage_be refs_be_files;
+extern struct ref_storage_be refs_be_reftable;
extern struct ref_storage_be refs_be_packed;
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
new file mode 100644
index 0000000000..1cda48c504
--- /dev/null
+++ b/refs/reftable-backend.c
@@ -0,0 +1,2242 @@
+#include "../git-compat-util.h"
+#include "../abspath.h"
+#include "../chdir-notify.h"
+#include "../environment.h"
+#include "../gettext.h"
+#include "../hash.h"
+#include "../hex.h"
+#include "../iterator.h"
+#include "../ident.h"
+#include "../lockfile.h"
+#include "../object.h"
+#include "../path.h"
+#include "../refs.h"
+#include "../reftable/reftable-stack.h"
+#include "../reftable/reftable-record.h"
+#include "../reftable/reftable-error.h"
+#include "../reftable/reftable-iterator.h"
+#include "../reftable/reftable-merged.h"
+#include "../setup.h"
+#include "../strmap.h"
+#include "parse.h"
+#include "refs-internal.h"
+
+/*
+ * Used as a flag in ref_update::flags when the ref_update was via an
+ * update to HEAD.
+ */
+#define REF_UPDATE_VIA_HEAD (1 << 8)
+
+struct reftable_ref_store {
+ struct ref_store base;
+
+ /*
+ * The main stack refers to the common dir and thus contains common
+ * refs as well as refs of the main repository.
+ */
+ struct reftable_stack *main_stack;
+ /*
+ * The worktree stack refers to the gitdir in case the refdb is opened
+ * via a worktree. It thus contains the per-worktree refs.
+ */
+ struct reftable_stack *worktree_stack;
+ /*
+ * Map of worktree stacks by their respective worktree names. The map
+ * is populated lazily when we try to resolve `worktrees/$worktree` refs.
+ */
+ struct strmap worktree_stacks;
+ struct reftable_write_options write_options;
+
+ unsigned int store_flags;
+ int err;
+};
+
+/*
+ * Downcast ref_store to reftable_ref_store. Die if ref_store is not a
+ * reftable_ref_store. required_flags is compared with ref_store's store_flags
+ * to ensure the ref_store has all required capabilities. "caller" is used in
+ * any necessary error messages.
+ */
+static struct reftable_ref_store *reftable_be_downcast(struct ref_store *ref_store,
+ unsigned int required_flags,
+ const char *caller)
+{
+ struct reftable_ref_store *refs;
+
+ if (ref_store->be != &refs_be_reftable)
+ BUG("ref_store is type \"%s\" not \"reftables\" in %s",
+ ref_store->be->name, caller);
+
+ refs = (struct reftable_ref_store *)ref_store;
+
+ if ((refs->store_flags & required_flags) != required_flags)
+ BUG("operation %s requires abilities 0x%x, but only have 0x%x",
+ caller, required_flags, refs->store_flags);
+
+ return refs;
+}
+
+/*
+ * Some refs are global to the repository (refs/heads/{*}), while others are
+ * local to the worktree (eg. HEAD, refs/bisect/{*}). We solve this by having
+ * multiple separate databases (ie. multiple reftable/ directories), one for
+ * the shared refs, one for the current worktree refs, and one for each
+ * additional worktree. For reading, we merge the view of both the shared and
+ * the current worktree's refs, when necessary.
+ *
+ * This function also optionally assigns the rewritten reference name that is
+ * local to the stack. This translation is required when using worktree refs
+ * like `worktrees/$worktree/refs/heads/foo` as worktree stacks will store
+ * those references in their normalized form.
+ */
+static struct reftable_stack *stack_for(struct reftable_ref_store *store,
+ const char *refname,
+ const char **rewritten_ref)
+{
+ const char *wtname;
+ int wtname_len;
+
+ if (!refname)
+ return store->main_stack;
+
+ switch (parse_worktree_ref(refname, &wtname, &wtname_len, rewritten_ref)) {
+ case REF_WORKTREE_OTHER: {
+ static struct strbuf wtname_buf = STRBUF_INIT;
+ struct strbuf wt_dir = STRBUF_INIT;
+ struct reftable_stack *stack;
+
+ /*
+ * We're using a static buffer here so that we don't need to
+ * allocate the worktree name whenever we look up a reference.
+ * This could be avoided if the strmap interface knew how to
+ * handle keys with a length.
+ */
+ strbuf_reset(&wtname_buf);
+ strbuf_add(&wtname_buf, wtname, wtname_len);
+
+ /*
+ * There is an edge case here: when the worktree references the
+ * current worktree, then we set up the stack once via
+ * `worktree_stacks` and once via `worktree_stack`. This is
+ * wasteful, but in the reading case it shouldn't matter. And
+ * in the writing case we would notice that the stack is locked
+ * already and error out when trying to write a reference via
+ * both stacks.
+ */
+ stack = strmap_get(&store->worktree_stacks, wtname_buf.buf);
+ if (!stack) {
+ strbuf_addf(&wt_dir, "%s/worktrees/%s/reftable",
+ store->base.repo->commondir, wtname_buf.buf);
+
+ store->err = reftable_new_stack(&stack, wt_dir.buf,
+ store->write_options);
+ assert(store->err != REFTABLE_API_ERROR);
+ strmap_put(&store->worktree_stacks, wtname_buf.buf, stack);
+ }
+
+ strbuf_release(&wt_dir);
+ return stack;
+ }
+ case REF_WORKTREE_CURRENT:
+ /*
+ * If there is no worktree stack then we're currently in the
+ * main worktree. We thus return the main stack in that case.
+ */
+ if (!store->worktree_stack)
+ return store->main_stack;
+ return store->worktree_stack;
+ case REF_WORKTREE_MAIN:
+ case REF_WORKTREE_SHARED:
+ return store->main_stack;
+ default:
+ BUG("unhandled worktree reference type");
+ }
+}
+
+static int should_write_log(struct ref_store *refs, const char *refname)
+{
+ if (log_all_ref_updates == LOG_REFS_UNSET)
+ log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
+
+ switch (log_all_ref_updates) {
+ case LOG_REFS_NONE:
+ return refs_reflog_exists(refs, refname);
+ case LOG_REFS_ALWAYS:
+ return 1;
+ case LOG_REFS_NORMAL:
+ if (should_autocreate_reflog(refname))
+ return 1;
+ return refs_reflog_exists(refs, refname);
+ default:
+ BUG("unhandled core.logAllRefUpdates value %d", log_all_ref_updates);
+ }
+}
+
+static void fill_reftable_log_record(struct reftable_log_record *log)
+{
+ const char *info = git_committer_info(0);
+ struct ident_split split = {0};
+ int sign = 1;
+
+ if (split_ident_line(&split, info, strlen(info)))
+ BUG("failed splitting committer info");
+
+ reftable_log_record_release(log);
+ log->value_type = REFTABLE_LOG_UPDATE;
+ log->value.update.name =
+ xstrndup(split.name_begin, split.name_end - split.name_begin);
+ log->value.update.email =
+ xstrndup(split.mail_begin, split.mail_end - split.mail_begin);
+ log->value.update.time = atol(split.date_begin);
+ if (*split.tz_begin == '-') {
+ sign = -1;
+ split.tz_begin++;
+ }
+ if (*split.tz_begin == '+') {
+ sign = 1;
+ split.tz_begin++;
+ }
+
+ log->value.update.tz_offset = sign * atoi(split.tz_begin);
+}
+
+static int read_ref_without_reload(struct reftable_stack *stack,
+ const char *refname,
+ struct object_id *oid,
+ struct strbuf *referent,
+ unsigned int *type)
+{
+ struct reftable_ref_record ref = {0};
+ int ret;
+
+ ret = reftable_stack_read_ref(stack, refname, &ref);
+ if (ret)
+ goto done;
+
+ if (ref.value_type == REFTABLE_REF_SYMREF) {
+ strbuf_reset(referent);
+ strbuf_addstr(referent, ref.value.symref);
+ *type |= REF_ISSYMREF;
+ } else if (reftable_ref_record_val1(&ref)) {
+ oidread(oid, reftable_ref_record_val1(&ref));
+ } else {
+ /* We got a tombstone, which should not happen. */
+ BUG("unhandled reference value type %d", ref.value_type);
+ }
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ reftable_ref_record_release(&ref);
+ return ret;
+}
+
+static struct ref_store *reftable_be_init(struct repository *repo,
+ const char *gitdir,
+ unsigned int store_flags)
+{
+ struct reftable_ref_store *refs = xcalloc(1, sizeof(*refs));
+ struct strbuf path = STRBUF_INIT;
+ int is_worktree;
+ mode_t mask;
+
+ mask = umask(0);
+ umask(mask);
+
+ base_ref_store_init(&refs->base, repo, gitdir, &refs_be_reftable);
+ strmap_init(&refs->worktree_stacks);
+ refs->store_flags = store_flags;
+ refs->write_options.block_size = 4096;
+ refs->write_options.hash_id = repo->hash_algo->format_id;
+ refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
+ refs->write_options.disable_auto_compact =
+ !git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
+
+ /*
+ * Set up the main reftable stack that is hosted in GIT_COMMON_DIR.
+ * This stack contains both the shared and the main worktree refs.
+ *
+ * Note that we don't try to resolve the path in case we have a
+ * worktree because `get_common_dir_noenv()` already does it for us.
+ */
+ is_worktree = get_common_dir_noenv(&path, gitdir);
+ if (!is_worktree) {
+ strbuf_reset(&path);
+ strbuf_realpath(&path, gitdir, 0);
+ }
+ strbuf_addstr(&path, "/reftable");
+ refs->err = reftable_new_stack(&refs->main_stack, path.buf,
+ refs->write_options);
+ if (refs->err)
+ goto done;
+
+ /*
+ * If we're in a worktree we also need to set up the worktree reftable
+ * stack that is contained in the per-worktree GIT_DIR.
+ *
+ * Ideally, we would also add the stack to our worktree stack map. But
+ * we have no way to figure out the worktree name here and thus can't
+ * do it efficiently.
+ */
+ if (is_worktree) {
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/reftable", gitdir);
+
+ refs->err = reftable_new_stack(&refs->worktree_stack, path.buf,
+ refs->write_options);
+ if (refs->err)
+ goto done;
+ }
+
+ chdir_notify_reparent("reftables-backend $GIT_DIR", &refs->base.gitdir);
+
+done:
+ assert(refs->err != REFTABLE_API_ERROR);
+ strbuf_release(&path);
+ return &refs->base;
+}
+
+static int reftable_be_init_db(struct ref_store *ref_store,
+ int flags UNUSED,
+ struct strbuf *err UNUSED)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "init_db");
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
+ safe_create_dir(sb.buf, 1);
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
+ write_file(sb.buf, "ref: refs/heads/.invalid");
+ adjust_shared_perm(sb.buf);
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
+ safe_create_dir(sb.buf, 1);
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
+ write_file(sb.buf, "this repository uses the reftable format");
+ adjust_shared_perm(sb.buf);
+
+ strbuf_release(&sb);
+ return 0;
+}
+
+struct reftable_ref_iterator {
+ struct ref_iterator base;
+ struct reftable_ref_store *refs;
+ struct reftable_iterator iter;
+ struct reftable_ref_record ref;
+ struct object_id oid;
+
+ const char *prefix;
+ size_t prefix_len;
+ unsigned int flags;
+ int err;
+};
+
+static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
+ struct reftable_ref_iterator *iter =
+ (struct reftable_ref_iterator *)ref_iterator;
+ struct reftable_ref_store *refs = iter->refs;
+
+ while (!iter->err) {
+ int flags = 0;
+
+ iter->err = reftable_iterator_next_ref(&iter->iter, &iter->ref);
+ if (iter->err)
+ break;
+
+ /*
+ * The files backend only lists references contained in "refs/" unless
+ * the root refs are to be included. We emulate the same behaviour here.
+ */
+ if (!starts_with(iter->ref.refname, "refs/") &&
+ !(iter->flags & DO_FOR_EACH_INCLUDE_ROOT_REFS &&
+ (is_pseudoref(&iter->refs->base, iter->ref.refname) ||
+ is_headref(&iter->refs->base, iter->ref.refname)))) {
+ continue;
+ }
+
+ if (iter->prefix_len &&
+ strncmp(iter->prefix, iter->ref.refname, iter->prefix_len)) {
+ iter->err = 1;
+ break;
+ }
+
+ if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+ parse_worktree_ref(iter->ref.refname, NULL, NULL, NULL) !=
+ REF_WORKTREE_CURRENT)
+ continue;
+
+ switch (iter->ref.value_type) {
+ case REFTABLE_REF_VAL1:
+ oidread(&iter->oid, iter->ref.value.val1);
+ break;
+ case REFTABLE_REF_VAL2:
+ oidread(&iter->oid, iter->ref.value.val2.value);
+ break;
+ case REFTABLE_REF_SYMREF:
+ if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->ref.refname,
+ RESOLVE_REF_READING, &iter->oid, &flags))
+ oidclr(&iter->oid);
+ break;
+ default:
+ BUG("unhandled reference value type %d", iter->ref.value_type);
+ }
+
+ if (is_null_oid(&iter->oid))
+ flags |= REF_ISBROKEN;
+
+ if (check_refname_format(iter->ref.refname, REFNAME_ALLOW_ONELEVEL)) {
+ if (!refname_is_safe(iter->ref.refname))
+ die(_("refname is dangerous: %s"), iter->ref.refname);
+ oidclr(&iter->oid);
+ flags |= REF_BAD_NAME | REF_ISBROKEN;
+ }
+
+ if (iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS &&
+ flags & REF_ISSYMREF &&
+ flags & REF_ISBROKEN)
+ continue;
+
+ if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+ !ref_resolves_to_object(iter->ref.refname, refs->base.repo,
+ &iter->oid, flags))
+ continue;
+
+ iter->base.refname = iter->ref.refname;
+ iter->base.oid = &iter->oid;
+ iter->base.flags = flags;
+
+ break;
+ }
+
+ if (iter->err > 0) {
+ if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+ return ITER_ERROR;
+ return ITER_DONE;
+ }
+
+ if (iter->err < 0) {
+ ref_iterator_abort(ref_iterator);
+ return ITER_ERROR;
+ }
+
+ return ITER_OK;
+}
+
+static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
+ struct object_id *peeled)
+{
+ struct reftable_ref_iterator *iter =
+ (struct reftable_ref_iterator *)ref_iterator;
+
+ if (iter->ref.value_type == REFTABLE_REF_VAL2) {
+ oidread(peeled, iter->ref.value.val2.target_value);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
+ struct reftable_ref_iterator *iter =
+ (struct reftable_ref_iterator *)ref_iterator;
+ reftable_ref_record_release(&iter->ref);
+ reftable_iterator_destroy(&iter->iter);
+ free(iter);
+ return ITER_DONE;
+}
+
+static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
+ .advance = reftable_ref_iterator_advance,
+ .peel = reftable_ref_iterator_peel,
+ .abort = reftable_ref_iterator_abort
+};
+
+static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_store *refs,
+ struct reftable_stack *stack,
+ const char *prefix,
+ int flags)
+{
+ struct reftable_merged_table *merged_table;
+ struct reftable_ref_iterator *iter;
+ int ret;
+
+ iter = xcalloc(1, sizeof(*iter));
+ base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
+ iter->prefix = prefix;
+ iter->prefix_len = prefix ? strlen(prefix) : 0;
+ iter->base.oid = &iter->oid;
+ iter->flags = flags;
+ iter->refs = refs;
+
+ ret = refs->err;
+ if (ret)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ goto done;
+
+ merged_table = reftable_stack_merged_table(stack);
+
+ ret = reftable_merged_table_seek_ref(merged_table, &iter->iter, prefix);
+ if (ret)
+ goto done;
+
+done:
+ iter->err = ret;
+ return iter;
+}
+
+static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store,
+ const char *prefix,
+ const char **exclude_patterns,
+ unsigned int flags)
+{
+ struct reftable_ref_iterator *main_iter, *worktree_iter;
+ struct reftable_ref_store *refs;
+ unsigned int required_flags = REF_STORE_READ;
+
+ if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ required_flags |= REF_STORE_ODB;
+ refs = reftable_be_downcast(ref_store, required_flags, "ref_iterator_begin");
+
+ main_iter = ref_iterator_for_stack(refs, refs->main_stack, prefix, flags);
+
+ /*
+ * The worktree stack is only set when we're in an actual worktree
+ * right now. If we aren't, then we return the common reftable
+ * iterator, only.
+ */
+ if (!refs->worktree_stack)
+ return &main_iter->base;
+
+ /*
+ * Otherwise we merge both the common and the per-worktree refs into a
+ * single iterator.
+ */
+ worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags);
+ return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+ ref_iterator_select, NULL);
+}
+
+static int reftable_be_read_raw_ref(struct ref_store *ref_store,
+ const char *refname,
+ struct object_id *oid,
+ struct strbuf *referent,
+ unsigned int *type,
+ int *failure_errno)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ int ret;
+
+ if (refs->err < 0)
+ return refs->err;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ return ret;
+
+ ret = read_ref_without_reload(stack, refname, oid, referent, type);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ *failure_errno = ENOENT;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
+ const char *refname,
+ struct strbuf *referent)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "read_symbolic_ref");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct reftable_ref_record ref = {0};
+ int ret;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ return ret;
+
+ ret = reftable_stack_read_ref(stack, refname, &ref);
+ if (ret == 0 && ref.value_type == REFTABLE_REF_SYMREF)
+ strbuf_addstr(referent, ref.value.symref);
+ else
+ ret = -1;
+
+ reftable_ref_record_release(&ref);
+ return ret;
+}
+
+/*
+ * Return the refname under which update was originally requested.
+ */
+static const char *original_update_refname(struct ref_update *update)
+{
+ while (update->parent_update)
+ update = update->parent_update;
+ return update->refname;
+}
+
+struct reftable_transaction_update {
+ struct ref_update *update;
+ struct object_id current_oid;
+};
+
+struct write_transaction_table_arg {
+ struct reftable_ref_store *refs;
+ struct reftable_stack *stack;
+ struct reftable_addition *addition;
+ struct reftable_transaction_update *updates;
+ size_t updates_nr;
+ size_t updates_alloc;
+ size_t updates_expected;
+};
+
+struct reftable_transaction_data {
+ struct write_transaction_table_arg *args;
+ size_t args_nr, args_alloc;
+};
+
+static void free_transaction_data(struct reftable_transaction_data *tx_data)
+{
+ if (!tx_data)
+ return;
+ for (size_t i = 0; i < tx_data->args_nr; i++) {
+ reftable_addition_destroy(tx_data->args[i].addition);
+ free(tx_data->args[i].updates);
+ }
+ free(tx_data->args);
+ free(tx_data);
+}
+
+/*
+ * Prepare transaction update for the given reference update. This will cause
+ * us to lock the corresponding reftable stack for concurrent modification.
+ */
+static int prepare_transaction_update(struct write_transaction_table_arg **out,
+ struct reftable_ref_store *refs,
+ struct reftable_transaction_data *tx_data,
+ struct ref_update *update,
+ struct strbuf *err)
+{
+ struct reftable_stack *stack = stack_for(refs, update->refname, NULL);
+ struct write_transaction_table_arg *arg = NULL;
+ size_t i;
+ int ret;
+
+ /*
+ * Search for a preexisting stack update. If there is one then we add
+ * the update to it, otherwise we set up a new stack update.
+ */
+ for (i = 0; !arg && i < tx_data->args_nr; i++)
+ if (tx_data->args[i].stack == stack)
+ arg = &tx_data->args[i];
+
+ if (!arg) {
+ struct reftable_addition *addition;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ return ret;
+
+ ret = reftable_stack_new_addition(&addition, stack);
+ if (ret) {
+ if (ret == REFTABLE_LOCK_ERROR)
+ strbuf_addstr(err, "cannot lock references");
+ return ret;
+ }
+
+ ALLOC_GROW(tx_data->args, tx_data->args_nr + 1,
+ tx_data->args_alloc);
+ arg = &tx_data->args[tx_data->args_nr++];
+ arg->refs = refs;
+ arg->stack = stack;
+ arg->addition = addition;
+ arg->updates = NULL;
+ arg->updates_nr = 0;
+ arg->updates_alloc = 0;
+ arg->updates_expected = 0;
+ }
+
+ arg->updates_expected++;
+
+ if (out)
+ *out = arg;
+
+ return 0;
+}
+
+/*
+ * Queue a reference update for the correct stack. We potentially need to
+ * handle multiple stack updates in a single transaction when it spans across
+ * multiple worktrees.
+ */
+static int queue_transaction_update(struct reftable_ref_store *refs,
+ struct reftable_transaction_data *tx_data,
+ struct ref_update *update,
+ struct object_id *current_oid,
+ struct strbuf *err)
+{
+ struct write_transaction_table_arg *arg = NULL;
+ int ret;
+
+ if (update->backend_data)
+ BUG("reference update queued more than once");
+
+ ret = prepare_transaction_update(&arg, refs, tx_data, update, err);
+ if (ret < 0)
+ return ret;
+
+ ALLOC_GROW(arg->updates, arg->updates_nr + 1,
+ arg->updates_alloc);
+ arg->updates[arg->updates_nr].update = update;
+ oidcpy(&arg->updates[arg->updates_nr].current_oid, current_oid);
+ update->backend_data = &arg->updates[arg->updates_nr++];
+
+ return 0;
+}
+
+static int reftable_be_transaction_prepare(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
+ struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
+ struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct reftable_transaction_data *tx_data = NULL;
+ struct object_id head_oid;
+ unsigned int head_type = 0;
+ size_t i;
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ tx_data = xcalloc(1, sizeof(*tx_data));
+
+ /*
+ * Preprocess all updates. For one we check that there are no duplicate
+ * reference updates in this transaction. Second, we lock all stacks
+ * that will be modified during the transaction.
+ */
+ for (i = 0; i < transaction->nr; i++) {
+ ret = prepare_transaction_update(NULL, refs, tx_data,
+ transaction->updates[i], err);
+ if (ret)
+ goto done;
+
+ string_list_append(&affected_refnames,
+ transaction->updates[i]->refname);
+ }
+
+ /*
+ * Now that we have counted updates per stack we can preallocate their
+ * arrays. This avoids having to reallocate many times.
+ */
+ for (i = 0; i < tx_data->args_nr; i++) {
+ CALLOC_ARRAY(tx_data->args[i].updates, tx_data->args[i].updates_expected);
+ tx_data->args[i].updates_alloc = tx_data->args[i].updates_expected;
+ }
+
+ /*
+ * Fail if a refname appears more than once in the transaction.
+ * This code is taken from the files backend and is a good candidate to
+ * be moved into the generic layer.
+ */
+ string_list_sort(&affected_refnames);
+ if (ref_update_reject_duplicates(&affected_refnames, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto done;
+ }
+
+ ret = read_ref_without_reload(stack_for(refs, "HEAD", NULL), "HEAD", &head_oid,
+ &head_referent, &head_type);
+ if (ret < 0)
+ goto done;
+ ret = 0;
+
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *u = transaction->updates[i];
+ struct object_id current_oid = {0};
+ struct reftable_stack *stack;
+ const char *rewritten_ref;
+
+ stack = stack_for(refs, u->refname, &rewritten_ref);
+
+ /* Verify that the new object ID is valid. */
+ if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
+ !(u->flags & REF_SKIP_OID_VERIFICATION) &&
+ !(u->flags & REF_LOG_ONLY)) {
+ struct object *o = parse_object(refs->base.repo, &u->new_oid);
+ if (!o) {
+ strbuf_addf(err,
+ _("trying to write ref '%s' with nonexistent object %s"),
+ u->refname, oid_to_hex(&u->new_oid));
+ ret = -1;
+ goto done;
+ }
+
+ if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
+ strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
+ oid_to_hex(&u->new_oid), u->refname);
+ ret = -1;
+ goto done;
+ }
+ }
+
+ /*
+ * When we update the reference that HEAD points to we enqueue
+ * a second log-only update for HEAD so that its reflog is
+ * updated accordingly.
+ */
+ if (head_type == REF_ISSYMREF &&
+ !(u->flags & REF_LOG_ONLY) &&
+ !(u->flags & REF_UPDATE_VIA_HEAD) &&
+ !strcmp(rewritten_ref, head_referent.buf)) {
+ struct ref_update *new_update;
+
+ /*
+ * First make sure that HEAD is not already in the
+ * transaction. This check is O(lg N) in the transaction
+ * size, but it happens at most once per transaction.
+ */
+ if (string_list_has_string(&affected_refnames, "HEAD")) {
+ /* An entry already existed */
+ strbuf_addf(err,
+ _("multiple updates for 'HEAD' (including one "
+ "via its referent '%s') are not allowed"),
+ u->refname);
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto done;
+ }
+
+ new_update = ref_transaction_add_update(
+ transaction, "HEAD",
+ u->flags | REF_LOG_ONLY | REF_NO_DEREF,
+ &u->new_oid, &u->old_oid, u->msg);
+ string_list_insert(&affected_refnames, new_update->refname);
+ }
+
+ ret = read_ref_without_reload(stack, rewritten_ref,
+ &current_oid, &referent, &u->type);
+ if (ret < 0)
+ goto done;
+ if (ret > 0 && (!(u->flags & REF_HAVE_OLD) || is_null_oid(&u->old_oid))) {
+ /*
+ * The reference does not exist, and we either have no
+ * old object ID or expect the reference to not exist.
+ * We can thus skip below safety checks as well as the
+ * symref splitting. But we do want to verify that
+ * there is no conflicting reference here so that we
+ * can output a proper error message instead of failing
+ * at a later point.
+ */
+ ret = refs_verify_refname_available(ref_store, u->refname,
+ &affected_refnames, NULL, err);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * There is no need to write the reference deletion
+ * when the reference in question doesn't exist.
+ */
+ if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
+ ret = queue_transaction_update(refs, tx_data, u,
+ &current_oid, err);
+ if (ret)
+ goto done;
+ }
+
+ continue;
+ }
+ if (ret > 0) {
+ /* The reference does not exist, but we expected it to. */
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "unable to resolve reference '%s'"),
+ original_update_refname(u), u->refname);
+ ret = -1;
+ goto done;
+ }
+
+ if (u->type & REF_ISSYMREF) {
+ /*
+ * The reftable stack is locked at this point already,
+ * so it is safe to call `refs_resolve_ref_unsafe()`
+ * here without causing races.
+ */
+ const char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, 0,
+ &current_oid, NULL);
+
+ if (u->flags & REF_NO_DEREF) {
+ if (u->flags & REF_HAVE_OLD && !resolved) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "error reading reference"), u->refname);
+ ret = -1;
+ goto done;
+ }
+ } else {
+ struct ref_update *new_update;
+ int new_flags;
+
+ new_flags = u->flags;
+ if (!strcmp(rewritten_ref, "HEAD"))
+ new_flags |= REF_UPDATE_VIA_HEAD;
+
+ /*
+ * If we are updating a symref (eg. HEAD), we should also
+ * update the branch that the symref points to.
+ *
+ * This is generic functionality, and would be better
+ * done in refs.c, but the current implementation is
+ * intertwined with the locking in files-backend.c.
+ */
+ new_update = ref_transaction_add_update(
+ transaction, referent.buf, new_flags,
+ &u->new_oid, &u->old_oid, u->msg);
+ new_update->parent_update = u;
+
+ /*
+ * Change the symbolic ref update to log only. Also, it
+ * doesn't need to check its old OID value, as that will be
+ * done when new_update is processed.
+ */
+ u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
+ u->flags &= ~REF_HAVE_OLD;
+
+ if (string_list_has_string(&affected_refnames, new_update->refname)) {
+ strbuf_addf(err,
+ _("multiple updates for '%s' (including one "
+ "via symref '%s') are not allowed"),
+ referent.buf, u->refname);
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto done;
+ }
+ string_list_insert(&affected_refnames, new_update->refname);
+ }
+ }
+
+ /*
+ * Verify that the old object matches our expectations. Note
+ * that the error messages here do not make a lot of sense in
+ * the context of the reftable backend as we never lock
+ * individual refs. But the error messages match what the files
+ * backend returns, which keeps our tests happy.
+ */
+ if (u->flags & REF_HAVE_OLD && !oideq(&current_oid, &u->old_oid)) {
+ if (is_null_oid(&u->old_oid))
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "reference already exists"),
+ original_update_refname(u));
+ else if (is_null_oid(&current_oid))
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "reference is missing but expected %s"),
+ original_update_refname(u),
+ oid_to_hex(&u->old_oid));
+ else
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "is at %s but expected %s"),
+ original_update_refname(u),
+ oid_to_hex(&current_oid),
+ oid_to_hex(&u->old_oid));
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * If all of the following conditions are true:
+ *
+ * - We're not about to write a symref.
+ * - We're not about to write a log-only entry.
+ * - Old and new object ID are different.
+ *
+ * Then we're essentially doing a no-op update that can be
+ * skipped. This is not only for the sake of efficiency, but
+ * also skips writing unneeded reflog entries.
+ */
+ if ((u->type & REF_ISSYMREF) ||
+ (u->flags & REF_LOG_ONLY) ||
+ (u->flags & REF_HAVE_NEW && !oideq(&current_oid, &u->new_oid))) {
+ ret = queue_transaction_update(refs, tx_data, u,
+ &current_oid, err);
+ if (ret)
+ goto done;
+ }
+ }
+
+ transaction->backend_data = tx_data;
+ transaction->state = REF_TRANSACTION_PREPARED;
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ if (ret < 0) {
+ free_transaction_data(tx_data);
+ transaction->state = REF_TRANSACTION_CLOSED;
+ if (!err->len)
+ strbuf_addf(err, _("reftable: transaction prepare: %s"),
+ reftable_error_str(ret));
+ }
+ string_list_clear(&affected_refnames, 0);
+ strbuf_release(&referent);
+ strbuf_release(&head_referent);
+
+ return ret;
+}
+
+static int reftable_be_transaction_abort(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct reftable_transaction_data *tx_data = transaction->backend_data;
+ free_transaction_data(tx_data);
+ transaction->state = REF_TRANSACTION_CLOSED;
+ return 0;
+}
+
+static int transaction_update_cmp(const void *a, const void *b)
+{
+ return strcmp(((struct reftable_transaction_update *)a)->update->refname,
+ ((struct reftable_transaction_update *)b)->update->refname);
+}
+
+static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
+{
+ struct write_transaction_table_arg *arg = cb_data;
+ struct reftable_merged_table *mt =
+ reftable_stack_merged_table(arg->stack);
+ uint64_t ts = reftable_stack_next_update_index(arg->stack);
+ struct reftable_log_record *logs = NULL;
+ size_t logs_nr = 0, logs_alloc = 0, i;
+ int ret = 0;
+
+ QSORT(arg->updates, arg->updates_nr, transaction_update_cmp);
+
+ reftable_writer_set_limits(writer, ts, ts);
+
+ for (i = 0; i < arg->updates_nr; i++) {
+ struct reftable_transaction_update *tx_update = &arg->updates[i];
+ struct ref_update *u = tx_update->update;
+
+ /*
+ * Write a reflog entry when updating a ref to point to
+ * something new in either of the following cases:
+ *
+ * - The reference is about to be deleted. We always want to
+ * delete the reflog in that case.
+ * - REF_FORCE_CREATE_REFLOG is set, asking us to always create
+ * the reflog entry.
+ * - `core.logAllRefUpdates` tells us to create the reflog for
+ * the given ref.
+ */
+ if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
+ struct reftable_log_record log = {0};
+ struct reftable_iterator it = {0};
+
+ /*
+ * When deleting refs we also delete all reflog entries
+ * with them. While it is not strictly required to
+ * delete reflogs together with their refs, this
+ * matches the behaviour of the files backend.
+ *
+ * Unfortunately, we have no better way than to delete
+ * all reflog entries one by one.
+ */
+ ret = reftable_merged_table_seek_log(mt, &it, u->refname);
+ while (ret == 0) {
+ struct reftable_log_record *tombstone;
+
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ break;
+ if (ret > 0 || strcmp(log.refname, u->refname)) {
+ ret = 0;
+ break;
+ }
+
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ tombstone = &logs[logs_nr++];
+ tombstone->refname = xstrdup(u->refname);
+ tombstone->value_type = REFTABLE_LOG_DELETION;
+ tombstone->update_index = log.update_index;
+ }
+
+ reftable_log_record_release(&log);
+ reftable_iterator_destroy(&it);
+
+ if (ret)
+ goto done;
+ } else if (u->flags & REF_HAVE_NEW &&
+ (u->flags & REF_FORCE_CREATE_REFLOG ||
+ should_write_log(&arg->refs->base, u->refname))) {
+ struct reftable_log_record *log;
+
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ log = &logs[logs_nr++];
+ memset(log, 0, sizeof(*log));
+
+ fill_reftable_log_record(log);
+ log->update_index = ts;
+ log->refname = xstrdup(u->refname);
+ memcpy(log->value.update.new_hash, u->new_oid.hash, GIT_MAX_RAWSZ);
+ memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
+ log->value.update.message =
+ xstrndup(u->msg, arg->refs->write_options.block_size / 2);
+ }
+
+ if (u->flags & REF_LOG_ONLY)
+ continue;
+
+ if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
+ struct reftable_ref_record ref = {
+ .refname = (char *)u->refname,
+ .update_index = ts,
+ .value_type = REFTABLE_REF_DELETION,
+ };
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret < 0)
+ goto done;
+ } else if (u->flags & REF_HAVE_NEW) {
+ struct reftable_ref_record ref = {0};
+ struct object_id peeled;
+ int peel_error;
+
+ ref.refname = (char *)u->refname;
+ ref.update_index = ts;
+
+ peel_error = peel_object(&u->new_oid, &peeled);
+ if (!peel_error) {
+ ref.value_type = REFTABLE_REF_VAL2;
+ memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
+ memcpy(ref.value.val2.value, u->new_oid.hash, GIT_MAX_RAWSZ);
+ } else if (!is_null_oid(&u->new_oid)) {
+ ref.value_type = REFTABLE_REF_VAL1;
+ memcpy(ref.value.val1, u->new_oid.hash, GIT_MAX_RAWSZ);
+ }
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret < 0)
+ goto done;
+ }
+ }
+
+ /*
+ * Logs are written at the end so that we do not have intermixed ref
+ * and log blocks.
+ */
+ if (logs) {
+ ret = reftable_writer_add_logs(writer, logs, logs_nr);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ for (i = 0; i < logs_nr; i++)
+ reftable_log_record_release(&logs[i]);
+ free(logs);
+ return ret;
+}
+
+static int reftable_be_transaction_finish(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct reftable_transaction_data *tx_data = transaction->backend_data;
+ int ret = 0;
+
+ for (size_t i = 0; i < tx_data->args_nr; i++) {
+ ret = reftable_addition_add(tx_data->args[i].addition,
+ write_transaction_table, &tx_data->args[i]);
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_addition_commit(tx_data->args[i].addition);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ free_transaction_data(tx_data);
+ transaction->state = REF_TRANSACTION_CLOSED;
+
+ if (ret) {
+ strbuf_addf(err, _("reftable: transaction failure: %s"),
+ reftable_error_str(ret));
+ return -1;
+ }
+ return ret;
+}
+
+static int reftable_be_initial_transaction_commit(struct ref_store *ref_store UNUSED,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ return ref_transaction_commit(transaction, err);
+}
+
+static int reftable_be_pack_refs(struct ref_store *ref_store,
+ struct pack_refs_opts *opts)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB, "pack_refs");
+ struct reftable_stack *stack;
+ int ret;
+
+ if (refs->err)
+ return refs->err;
+
+ stack = refs->worktree_stack;
+ if (!stack)
+ stack = refs->main_stack;
+
+ if (opts->flags & PACK_REFS_AUTO)
+ ret = reftable_stack_auto_compact(stack);
+ else
+ ret = reftable_stack_compact_all(stack, NULL);
+ if (ret < 0) {
+ ret = error(_("unable to compact stack: %s"),
+ reftable_error_str(ret));
+ goto out;
+ }
+
+ ret = reftable_stack_clean(stack);
+ if (ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+struct write_create_symref_arg {
+ struct reftable_ref_store *refs;
+ struct reftable_stack *stack;
+ const char *refname;
+ const char *target;
+ const char *logmsg;
+};
+
+static int write_create_symref_table(struct reftable_writer *writer, void *cb_data)
+{
+ struct write_create_symref_arg *create = cb_data;
+ uint64_t ts = reftable_stack_next_update_index(create->stack);
+ struct reftable_ref_record ref = {
+ .refname = (char *)create->refname,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *)create->target,
+ .update_index = ts,
+ };
+ struct reftable_log_record log = {0};
+ struct object_id new_oid;
+ struct object_id old_oid;
+ int ret;
+
+ reftable_writer_set_limits(writer, ts, ts);
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret)
+ return ret;
+
+ /*
+ * Note that it is important to try and resolve the reference before we
+ * write the log entry. This is because `should_write_log()` will munge
+ * `core.logAllRefUpdates`, which is undesirable when we create a new
+ * repository because it would be written into the config. As HEAD will
+ * not resolve for new repositories this ordering will ensure that this
+ * never happens.
+ */
+ if (!create->logmsg ||
+ !refs_resolve_ref_unsafe(&create->refs->base, create->target,
+ RESOLVE_REF_READING, &new_oid, NULL) ||
+ !should_write_log(&create->refs->base, create->refname))
+ return 0;
+
+ fill_reftable_log_record(&log);
+ log.refname = xstrdup(create->refname);
+ log.update_index = ts;
+ log.value.update.message = xstrndup(create->logmsg,
+ create->refs->write_options.block_size / 2);
+ memcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);
+ if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
+ RESOLVE_REF_READING, &old_oid, NULL))
+ memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
+
+ ret = reftable_writer_add_log(writer, &log);
+ reftable_log_record_release(&log);
+ return ret;
+}
+
+static int reftable_be_create_symref(struct ref_store *ref_store,
+ const char *refname,
+ const char *target,
+ const char *logmsg)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_symref");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct write_create_symref_arg arg = {
+ .refs = refs,
+ .stack = stack,
+ .refname = refname,
+ .target = target,
+ .logmsg = logmsg,
+ };
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ goto done;
+
+ ret = reftable_stack_add(stack, &write_create_symref_table, &arg);
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ if (ret)
+ error("unable to write symref for %s: %s", refname,
+ reftable_error_str(ret));
+ return ret;
+}
+
+struct write_copy_arg {
+ struct reftable_ref_store *refs;
+ struct reftable_stack *stack;
+ const char *oldname;
+ const char *newname;
+ const char *logmsg;
+ int delete_old;
+};
+
+static int write_copy_table(struct reftable_writer *writer, void *cb_data)
+{
+ struct write_copy_arg *arg = cb_data;
+ uint64_t deletion_ts, creation_ts;
+ struct reftable_merged_table *mt = reftable_stack_merged_table(arg->stack);
+ struct reftable_ref_record old_ref = {0}, refs[2] = {0};
+ struct reftable_log_record old_log = {0}, *logs = NULL;
+ struct reftable_iterator it = {0};
+ struct string_list skip = STRING_LIST_INIT_NODUP;
+ struct strbuf errbuf = STRBUF_INIT;
+ size_t logs_nr = 0, logs_alloc = 0, i;
+ int ret;
+
+ if (reftable_stack_read_ref(arg->stack, arg->oldname, &old_ref)) {
+ ret = error(_("refname %s not found"), arg->oldname);
+ goto done;
+ }
+ if (old_ref.value_type == REFTABLE_REF_SYMREF) {
+ ret = error(_("refname %s is a symbolic ref, copying it is not supported"),
+ arg->oldname);
+ goto done;
+ }
+
+ /*
+ * There's nothing to do in case the old and new name are the same, so
+ * we exit early in that case.
+ */
+ if (!strcmp(arg->oldname, arg->newname)) {
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * Verify that the new refname is available.
+ */
+ string_list_insert(&skip, arg->oldname);
+ ret = refs_verify_refname_available(&arg->refs->base, arg->newname,
+ NULL, &skip, &errbuf);
+ if (ret < 0) {
+ error("%s", errbuf.buf);
+ goto done;
+ }
+
+ /*
+ * When deleting the old reference we have to use two update indices:
+ * once to delete the old ref and its reflog, and once to create the
+ * new ref and its reflog. They need to be staged with two separate
+ * indices because the new reflog needs to encode both the deletion of
+ * the old branch and the creation of the new branch, and we cannot do
+ * two changes to a reflog in a single update.
+ */
+ deletion_ts = creation_ts = reftable_stack_next_update_index(arg->stack);
+ if (arg->delete_old)
+ creation_ts++;
+ reftable_writer_set_limits(writer, deletion_ts, creation_ts);
+
+ /*
+ * Add the new reference. If this is a rename then we also delete the
+ * old reference.
+ */
+ refs[0] = old_ref;
+ refs[0].refname = (char *)arg->newname;
+ refs[0].update_index = creation_ts;
+ if (arg->delete_old) {
+ refs[1].refname = (char *)arg->oldname;
+ refs[1].value_type = REFTABLE_REF_DELETION;
+ refs[1].update_index = deletion_ts;
+ }
+ ret = reftable_writer_add_refs(writer, refs, arg->delete_old ? 2 : 1);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * When deleting the old branch we need to create a reflog entry on the
+ * new branch name that indicates that the old branch has been deleted
+ * and then recreated. This is a tad weird, but matches what the files
+ * backend does.
+ */
+ if (arg->delete_old) {
+ struct strbuf head_referent = STRBUF_INIT;
+ struct object_id head_oid;
+ int append_head_reflog;
+ unsigned head_type = 0;
+
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+ fill_reftable_log_record(&logs[logs_nr]);
+ logs[logs_nr].refname = (char *)arg->newname;
+ logs[logs_nr].update_index = deletion_ts;
+ logs[logs_nr].value.update.message =
+ xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
+ memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
+ logs_nr++;
+
+ ret = read_ref_without_reload(arg->stack, "HEAD", &head_oid, &head_referent, &head_type);
+ if (ret < 0)
+ goto done;
+ append_head_reflog = (head_type & REF_ISSYMREF) && !strcmp(head_referent.buf, arg->oldname);
+ strbuf_release(&head_referent);
+
+ /*
+ * The files backend uses `refs_delete_ref()` to delete the old
+ * branch name, which will append a reflog entry for HEAD in
+ * case it points to the old branch.
+ */
+ if (append_head_reflog) {
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ logs[logs_nr] = logs[logs_nr - 1];
+ logs[logs_nr].refname = "HEAD";
+ logs_nr++;
+ }
+ }
+
+ /*
+ * Create the reflog entry for the newly created branch.
+ */
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+ fill_reftable_log_record(&logs[logs_nr]);
+ logs[logs_nr].refname = (char *)arg->newname;
+ logs[logs_nr].update_index = creation_ts;
+ logs[logs_nr].value.update.message =
+ xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
+ memcpy(logs[logs_nr].value.update.new_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
+ logs_nr++;
+
+ /*
+ * In addition to writing the reflog entry for the new branch, we also
+ * copy over all log entries from the old reflog. Last but not least,
+ * when renaming we also have to delete all the old reflog entries.
+ */
+ ret = reftable_merged_table_seek_log(mt, &it, arg->oldname);
+ if (ret < 0)
+ goto done;
+
+ while (1) {
+ ret = reftable_iterator_next_log(&it, &old_log);
+ if (ret < 0)
+ goto done;
+ if (ret > 0 || strcmp(old_log.refname, arg->oldname)) {
+ ret = 0;
+ break;
+ }
+
+ free(old_log.refname);
+
+ /*
+ * Copy over the old reflog entry with the new refname.
+ */
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ logs[logs_nr] = old_log;
+ logs[logs_nr].refname = (char *)arg->newname;
+ logs_nr++;
+
+ /*
+ * Delete the old reflog entry in case we are renaming.
+ */
+ if (arg->delete_old) {
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
+ logs[logs_nr].refname = (char *)arg->oldname;
+ logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
+ logs[logs_nr].update_index = old_log.update_index;
+ logs_nr++;
+ }
+
+ /*
+ * Transfer ownership of the log record we're iterating over to
+ * the array of log records. Otherwise, the pointers would get
+ * free'd or reallocated by the iterator.
+ */
+ memset(&old_log, 0, sizeof(old_log));
+ }
+
+ ret = reftable_writer_add_logs(writer, logs, logs_nr);
+ if (ret < 0)
+ goto done;
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ reftable_iterator_destroy(&it);
+ string_list_clear(&skip, 0);
+ strbuf_release(&errbuf);
+ for (i = 0; i < logs_nr; i++) {
+ if (!strcmp(logs[i].refname, "HEAD"))
+ continue;
+ logs[i].refname = NULL;
+ reftable_log_record_release(&logs[i]);
+ }
+ free(logs);
+ reftable_ref_record_release(&old_ref);
+ reftable_log_record_release(&old_log);
+ return ret;
+}
+
+static int reftable_be_rename_ref(struct ref_store *ref_store,
+ const char *oldrefname,
+ const char *newrefname,
+ const char *logmsg)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
+ struct reftable_stack *stack = stack_for(refs, newrefname, &newrefname);
+ struct write_copy_arg arg = {
+ .refs = refs,
+ .stack = stack,
+ .oldname = oldrefname,
+ .newname = newrefname,
+ .logmsg = logmsg,
+ .delete_old = 1,
+ };
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ goto done;
+ ret = reftable_stack_add(stack, &write_copy_table, &arg);
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ return ret;
+}
+
+static int reftable_be_copy_ref(struct ref_store *ref_store,
+ const char *oldrefname,
+ const char *newrefname,
+ const char *logmsg)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "copy_ref");
+ struct reftable_stack *stack = stack_for(refs, newrefname, &newrefname);
+ struct write_copy_arg arg = {
+ .refs = refs,
+ .stack = stack,
+ .oldname = oldrefname,
+ .newname = newrefname,
+ .logmsg = logmsg,
+ };
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ goto done;
+ ret = reftable_stack_add(stack, &write_copy_table, &arg);
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ return ret;
+}
+
+struct reftable_reflog_iterator {
+ struct ref_iterator base;
+ struct reftable_ref_store *refs;
+ struct reftable_iterator iter;
+ struct reftable_log_record log;
+ struct strbuf last_name;
+ int err;
+};
+
+static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
+{
+ struct reftable_reflog_iterator *iter =
+ (struct reftable_reflog_iterator *)ref_iterator;
+
+ while (!iter->err) {
+ iter->err = reftable_iterator_next_log(&iter->iter, &iter->log);
+ if (iter->err)
+ break;
+
+ /*
+ * We want the refnames that we have reflogs for, so we skip if
+ * we've already produced this name. This could be faster by
+ * seeking directly to reflog@update_index==0.
+ */
+ if (!strcmp(iter->log.refname, iter->last_name.buf))
+ continue;
+
+ if (check_refname_format(iter->log.refname,
+ REFNAME_ALLOW_ONELEVEL))
+ continue;
+
+ strbuf_reset(&iter->last_name);
+ strbuf_addstr(&iter->last_name, iter->log.refname);
+ iter->base.refname = iter->log.refname;
+
+ break;
+ }
+
+ if (iter->err > 0) {
+ if (ref_iterator_abort(ref_iterator) != ITER_DONE)
+ return ITER_ERROR;
+ return ITER_DONE;
+ }
+
+ if (iter->err < 0) {
+ ref_iterator_abort(ref_iterator);
+ return ITER_ERROR;
+ }
+
+ return ITER_OK;
+}
+
+static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator,
+ struct object_id *peeled)
+{
+ BUG("reftable reflog iterator cannot be peeled");
+ return -1;
+}
+
+static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+{
+ struct reftable_reflog_iterator *iter =
+ (struct reftable_reflog_iterator *)ref_iterator;
+ reftable_log_record_release(&iter->log);
+ reftable_iterator_destroy(&iter->iter);
+ strbuf_release(&iter->last_name);
+ free(iter);
+ return ITER_DONE;
+}
+
+static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
+ .advance = reftable_reflog_iterator_advance,
+ .peel = reftable_reflog_iterator_peel,
+ .abort = reftable_reflog_iterator_abort
+};
+
+static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
+ struct reftable_stack *stack)
+{
+ struct reftable_merged_table *merged_table;
+ struct reftable_reflog_iterator *iter;
+ int ret;
+
+ iter = xcalloc(1, sizeof(*iter));
+ base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable);
+ strbuf_init(&iter->last_name, 0);
+ iter->refs = refs;
+
+ ret = refs->err;
+ if (ret)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret < 0)
+ goto done;
+
+ merged_table = reftable_stack_merged_table(stack);
+
+ ret = reftable_merged_table_seek_log(merged_table, &iter->iter, "");
+ if (ret < 0)
+ goto done;
+
+done:
+ iter->err = ret;
+ return iter;
+}
+
+static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store *ref_store)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_iterator_begin");
+ struct reftable_reflog_iterator *main_iter, *worktree_iter;
+
+ main_iter = reflog_iterator_for_stack(refs, refs->main_stack);
+ if (!refs->worktree_stack)
+ return &main_iter->base;
+
+ worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack);
+
+ return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base,
+ ref_iterator_select, NULL);
+}
+
+static int yield_log_record(struct reftable_log_record *log,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct object_id old_oid, new_oid;
+ const char *full_committer;
+
+ oidread(&old_oid, log->value.update.old_hash);
+ oidread(&new_oid, log->value.update.new_hash);
+
+ /*
+ * When both the old object ID and the new object ID are null
+ * then this is the reflog existence marker. The caller must
+ * not be aware of it.
+ */
+ if (is_null_oid(&old_oid) && is_null_oid(&new_oid))
+ return 0;
+
+ full_committer = fmt_ident(log->value.update.name, log->value.update.email,
+ WANT_COMMITTER_IDENT, NULL, IDENT_NO_DATE);
+ return fn(&old_oid, &new_oid, full_committer,
+ log->value.update.time, log->value.update.tz_offset,
+ log->value.update.message, cb_data);
+}
+
+static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent_reverse");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_log_record log = {0};
+ struct reftable_iterator it = {0};
+ int ret;
+
+ if (refs->err < 0)
+ return refs->err;
+
+ mt = reftable_stack_merged_table(stack);
+ ret = reftable_merged_table_seek_log(mt, &it, refname);
+ while (!ret) {
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ break;
+ if (ret > 0 || strcmp(log.refname, refname)) {
+ ret = 0;
+ break;
+ }
+
+ ret = yield_log_record(&log, fn, cb_data);
+ if (ret)
+ break;
+ }
+
+ reftable_log_record_release(&log);
+ reftable_iterator_destroy(&it);
+ return ret;
+}
+
+static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "for_each_reflog_ent");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_log_record *logs = NULL;
+ struct reftable_iterator it = {0};
+ size_t logs_alloc = 0, logs_nr = 0, i;
+ int ret;
+
+ if (refs->err < 0)
+ return refs->err;
+
+ mt = reftable_stack_merged_table(stack);
+ ret = reftable_merged_table_seek_log(mt, &it, refname);
+ while (!ret) {
+ struct reftable_log_record log = {0};
+
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ goto done;
+ if (ret > 0 || strcmp(log.refname, refname)) {
+ reftable_log_record_release(&log);
+ ret = 0;
+ break;
+ }
+
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ logs[logs_nr++] = log;
+ }
+
+ for (i = logs_nr; i--;) {
+ ret = yield_log_record(&logs[i], fn, cb_data);
+ if (ret)
+ goto done;
+ }
+
+done:
+ reftable_iterator_destroy(&it);
+ for (i = 0; i < logs_nr; i++)
+ reftable_log_record_release(&logs[i]);
+ free(logs);
+ return ret;
+}
+
+static int reftable_be_reflog_exists(struct ref_store *ref_store,
+ const char *refname)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_READ, "reflog_exists");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
+ struct reftable_log_record log = {0};
+ struct reftable_iterator it = {0};
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_merged_table_seek_log(mt, &it, refname);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * Check whether we get at least one log record for the given ref name.
+ * If so, the reflog exists, otherwise it doesn't.
+ */
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ goto done;
+ if (ret > 0) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = strcmp(log.refname, refname) == 0;
+
+done:
+ reftable_iterator_destroy(&it);
+ reftable_log_record_release(&log);
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+struct write_reflog_existence_arg {
+ struct reftable_ref_store *refs;
+ const char *refname;
+ struct reftable_stack *stack;
+};
+
+static int write_reflog_existence_table(struct reftable_writer *writer,
+ void *cb_data)
+{
+ struct write_reflog_existence_arg *arg = cb_data;
+ uint64_t ts = reftable_stack_next_update_index(arg->stack);
+ struct reftable_log_record log = {0};
+ int ret;
+
+ ret = reftable_stack_read_log(arg->stack, arg->refname, &log);
+ if (ret <= 0)
+ goto done;
+
+ reftable_writer_set_limits(writer, ts, ts);
+
+ /*
+ * The existence entry has both old and new object ID set to the the
+ * null object ID. Our iterators are aware of this and will not present
+ * them to their callers.
+ */
+ log.refname = xstrdup(arg->refname);
+ log.update_index = ts;
+ log.value_type = REFTABLE_LOG_UPDATE;
+ ret = reftable_writer_add_log(writer, &log);
+
+done:
+ assert(ret != REFTABLE_API_ERROR);
+ reftable_log_record_release(&log);
+ return ret;
+}
+
+static int reftable_be_create_reflog(struct ref_store *ref_store,
+ const char *refname,
+ struct strbuf *errmsg)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct write_reflog_existence_arg arg = {
+ .refs = refs,
+ .stack = stack,
+ .refname = refname,
+ };
+ int ret;
+
+ ret = refs->err;
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ goto done;
+
+ ret = reftable_stack_add(stack, &write_reflog_existence_table, &arg);
+
+done:
+ return ret;
+}
+
+struct write_reflog_delete_arg {
+ struct reftable_stack *stack;
+ const char *refname;
+};
+
+static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_data)
+{
+ struct write_reflog_delete_arg *arg = cb_data;
+ struct reftable_merged_table *mt =
+ reftable_stack_merged_table(arg->stack);
+ struct reftable_log_record log = {0}, tombstone = {0};
+ struct reftable_iterator it = {0};
+ uint64_t ts = reftable_stack_next_update_index(arg->stack);
+ int ret;
+
+ reftable_writer_set_limits(writer, ts, ts);
+
+ /*
+ * In order to delete a table we need to delete all reflog entries one
+ * by one. This is inefficient, but the reftable format does not have a
+ * better marker right now.
+ */
+ ret = reftable_merged_table_seek_log(mt, &it, arg->refname);
+ while (ret == 0) {
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ break;
+ if (ret > 0 || strcmp(log.refname, arg->refname)) {
+ ret = 0;
+ break;
+ }
+
+ tombstone.refname = (char *)arg->refname;
+ tombstone.value_type = REFTABLE_LOG_DELETION;
+ tombstone.update_index = log.update_index;
+
+ ret = reftable_writer_add_log(writer, &tombstone);
+ }
+
+ reftable_log_record_release(&log);
+ reftable_iterator_destroy(&it);
+ return ret;
+}
+
+static int reftable_be_delete_reflog(struct ref_store *ref_store,
+ const char *refname)
+{
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "delete_reflog");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct write_reflog_delete_arg arg = {
+ .stack = stack,
+ .refname = refname,
+ };
+ int ret;
+
+ ret = reftable_stack_reload(stack);
+ if (ret)
+ return ret;
+ ret = reftable_stack_add(stack, &write_reflog_delete_table, &arg);
+
+ assert(ret != REFTABLE_API_ERROR);
+ return ret;
+}
+
+struct reflog_expiry_arg {
+ struct reftable_stack *stack;
+ struct reftable_log_record *records;
+ struct object_id update_oid;
+ const char *refname;
+ size_t len;
+};
+
+static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_data)
+{
+ struct reflog_expiry_arg *arg = cb_data;
+ uint64_t ts = reftable_stack_next_update_index(arg->stack);
+ uint64_t live_records = 0;
+ size_t i;
+ int ret;
+
+ for (i = 0; i < arg->len; i++)
+ if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
+ live_records++;
+
+ reftable_writer_set_limits(writer, ts, ts);
+
+ if (!is_null_oid(&arg->update_oid)) {
+ struct reftable_ref_record ref = {0};
+ struct object_id peeled;
+
+ ref.refname = (char *)arg->refname;
+ ref.update_index = ts;
+
+ if (!peel_object(&arg->update_oid, &peeled)) {
+ ref.value_type = REFTABLE_REF_VAL2;
+ memcpy(ref.value.val2.target_value, peeled.hash, GIT_MAX_RAWSZ);
+ memcpy(ref.value.val2.value, arg->update_oid.hash, GIT_MAX_RAWSZ);
+ } else {
+ ref.value_type = REFTABLE_REF_VAL1;
+ memcpy(ref.value.val1, arg->update_oid.hash, GIT_MAX_RAWSZ);
+ }
+
+ ret = reftable_writer_add_ref(writer, &ref);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * When there are no more entries left in the reflog we empty it
+ * completely, but write a placeholder reflog entry that indicates that
+ * the reflog still exists.
+ */
+ if (!live_records) {
+ struct reftable_log_record log = {
+ .refname = (char *)arg->refname,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .update_index = ts,
+ };
+
+ ret = reftable_writer_add_log(writer, &log);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < arg->len; i++) {
+ ret = reftable_writer_add_log(writer, &arg->records[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int reftable_be_reflog_expire(struct ref_store *ref_store,
+ const char *refname,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ /*
+ * For log expiry, we write tombstones for every single reflog entry
+ * that is to be expired. This means that the entries are still
+ * retrievable by delving into the stack, and expiring entries
+ * paradoxically takes extra memory. This memory is only reclaimed when
+ * compacting the reftable stack.
+ *
+ * It would be better if the refs backend supported an API that sets a
+ * criterion for all refs, passing the criterion to pack_refs().
+ *
+ * On the plus side, because we do the expiration per ref, we can easily
+ * insert the reflog existence dummies.
+ */
+ struct reftable_ref_store *refs =
+ reftable_be_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
+ struct reftable_stack *stack = stack_for(refs, refname, &refname);
+ struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
+ struct reftable_log_record *logs = NULL;
+ struct reftable_log_record *rewritten = NULL;
+ struct reftable_ref_record ref_record = {0};
+ struct reftable_iterator it = {0};
+ struct reftable_addition *add = NULL;
+ struct reflog_expiry_arg arg = {0};
+ struct object_id oid = {0};
+ uint8_t *last_hash = NULL;
+ size_t logs_nr = 0, logs_alloc = 0, i;
+ int ret;
+
+ if (refs->err < 0)
+ return refs->err;
+
+ ret = reftable_stack_reload(stack);
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_merged_table_seek_log(mt, &it, refname);
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_new_addition(&add, stack);
+ if (ret < 0)
+ goto done;
+
+ ret = reftable_stack_read_ref(stack, refname, &ref_record);
+ if (ret < 0)
+ goto done;
+ if (reftable_ref_record_val1(&ref_record))
+ oidread(&oid, reftable_ref_record_val1(&ref_record));
+ prepare_fn(refname, &oid, policy_cb_data);
+
+ while (1) {
+ struct reftable_log_record log = {0};
+ struct object_id old_oid, new_oid;
+
+ ret = reftable_iterator_next_log(&it, &log);
+ if (ret < 0)
+ goto done;
+ if (ret > 0 || strcmp(log.refname, refname)) {
+ reftable_log_record_release(&log);
+ break;
+ }
+
+ oidread(&old_oid, log.value.update.old_hash);
+ oidread(&new_oid, log.value.update.new_hash);
+
+ /*
+ * Skip over the reflog existence marker. We will add it back
+ * in when there are no live reflog records.
+ */
+ if (is_null_oid(&old_oid) && is_null_oid(&new_oid)) {
+ reftable_log_record_release(&log);
+ continue;
+ }
+
+ ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
+ logs[logs_nr++] = log;
+ }
+
+ /*
+ * We need to rewrite all reflog entries according to the pruning
+ * callback function:
+ *
+ * - If a reflog entry shall be pruned we mark the record for
+ * deletion.
+ *
+ * - Otherwise we may have to rewrite the chain of reflog entries so
+ * that gaps created by just-deleted records get backfilled.
+ */
+ CALLOC_ARRAY(rewritten, logs_nr);
+ for (i = logs_nr; i--;) {
+ struct reftable_log_record *dest = &rewritten[i];
+ struct object_id old_oid, new_oid;
+
+ *dest = logs[i];
+ oidread(&old_oid, logs[i].value.update.old_hash);
+ oidread(&new_oid, logs[i].value.update.new_hash);
+
+ if (should_prune_fn(&old_oid, &new_oid, logs[i].value.update.email,
+ (timestamp_t)logs[i].value.update.time,
+ logs[i].value.update.tz_offset,
+ logs[i].value.update.message,
+ policy_cb_data)) {
+ dest->value_type = REFTABLE_LOG_DELETION;
+ } else {
+ if ((flags & EXPIRE_REFLOGS_REWRITE) && last_hash)
+ memcpy(dest->value.update.old_hash, last_hash, GIT_MAX_RAWSZ);
+ last_hash = logs[i].value.update.new_hash;
+ }
+ }
+
+ if (flags & EXPIRE_REFLOGS_UPDATE_REF && last_hash &&
+ reftable_ref_record_val1(&ref_record))
+ oidread(&arg.update_oid, last_hash);
+
+ arg.records = rewritten;
+ arg.len = logs_nr;
+ arg.stack = stack,
+ arg.refname = refname,
+
+ ret = reftable_addition_add(add, &write_reflog_expiry_table, &arg);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * Future improvement: we could skip writing records that were
+ * not changed.
+ */
+ if (!(flags & EXPIRE_REFLOGS_DRY_RUN))
+ ret = reftable_addition_commit(add);
+
+done:
+ if (add)
+ cleanup_fn(policy_cb_data);
+ assert(ret != REFTABLE_API_ERROR);
+
+ reftable_ref_record_release(&ref_record);
+ reftable_iterator_destroy(&it);
+ reftable_addition_destroy(add);
+ for (i = 0; i < logs_nr; i++)
+ reftable_log_record_release(&logs[i]);
+ free(logs);
+ free(rewritten);
+ return ret;
+}
+
+struct ref_storage_be refs_be_reftable = {
+ .name = "reftable",
+ .init = reftable_be_init,
+ .init_db = reftable_be_init_db,
+ .transaction_prepare = reftable_be_transaction_prepare,
+ .transaction_finish = reftable_be_transaction_finish,
+ .transaction_abort = reftable_be_transaction_abort,
+ .initial_transaction_commit = reftable_be_initial_transaction_commit,
+
+ .pack_refs = reftable_be_pack_refs,
+ .create_symref = reftable_be_create_symref,
+ .rename_ref = reftable_be_rename_ref,
+ .copy_ref = reftable_be_copy_ref,
+
+ .iterator_begin = reftable_be_iterator_begin,
+ .read_raw_ref = reftable_be_read_raw_ref,
+ .read_symbolic_ref = reftable_be_read_symbolic_ref,
+
+ .reflog_iterator_begin = reftable_be_reflog_iterator_begin,
+ .for_each_reflog_ent = reftable_be_for_each_reflog_ent,
+ .for_each_reflog_ent_reverse = reftable_be_for_each_reflog_ent_reverse,
+ .reflog_exists = reftable_be_reflog_exists,
+ .create_reflog = reftable_be_create_reflog,
+ .delete_reflog = reftable_be_delete_reflog,
+ .reflog_expire = reftable_be_reflog_expire,
+};
diff --git a/reftable/basics.c b/reftable/basics.c
index f761e48028..fea711db7e 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -27,7 +27,7 @@ void put_be16(uint8_t *out, uint16_t i)
out[1] = (uint8_t)(i & 0xff);
}
-int binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
+size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
{
size_t lo = 0;
size_t hi = sz;
@@ -39,8 +39,11 @@ int binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
*/
while (hi - lo > 1) {
size_t mid = lo + (hi - lo) / 2;
+ int ret = f(mid, args);
+ if (ret < 0)
+ return sz;
- if (f(mid, args))
+ if (ret > 0)
hi = mid;
else
lo = mid;
@@ -64,12 +67,11 @@ void free_names(char **a)
reftable_free(a);
}
-int names_length(char **names)
+size_t names_length(char **names)
{
char **p = names;
- for (; *p; p++) {
- /* empty */
- }
+ while (*p)
+ p++;
return p - names;
}
@@ -89,17 +91,13 @@ void parse_names(char *buf, int size, char ***namesp)
next = end;
}
if (p < next) {
- if (names_len == names_cap) {
- names_cap = 2 * names_cap + 1;
- names = reftable_realloc(
- names, names_cap * sizeof(*names));
- }
+ REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap);
names[names_len++] = xstrdup(p);
}
p = next + 1;
}
- names = reftable_realloc(names, (names_len + 1) * sizeof(*names));
+ REFTABLE_REALLOC_ARRAY(names, names_len + 1);
names[names_len] = NULL;
*namesp = names;
}
diff --git a/reftable/basics.h b/reftable/basics.h
index 096b36862b..523ecd5307 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -22,13 +22,14 @@ uint32_t get_be24(uint8_t *in);
void put_be16(uint8_t *out, uint16_t i);
/*
- * find smallest index i in [0, sz) at which f(i) is true, assuming
- * that f is ascending. Return sz if f(i) is false for all indices.
+ * find smallest index i in [0, sz) at which `f(i) > 0`, assuming that f is
+ * ascending. Return sz if `f(i) == 0` for all indices. The search is aborted
+ * and `sz` is returned in case `f(i) < 0`.
*
* Contrary to bsearch(3), this returns something useful if the argument is not
* found.
*/
-int binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
+size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
/*
* Frees a NULL terminated array of malloced strings. The array itself is also
@@ -44,14 +45,27 @@ void parse_names(char *buf, int size, char ***namesp);
int names_equal(char **a, char **b);
/* returns the array size of a NULL-terminated array of strings. */
-int names_length(char **names);
+size_t names_length(char **names);
/* Allocation routines; they invoke the functions set through
* reftable_set_alloc() */
void *reftable_malloc(size_t sz);
void *reftable_realloc(void *p, size_t sz);
void reftable_free(void *p);
-void *reftable_calloc(size_t sz);
+void *reftable_calloc(size_t nelem, size_t elsize);
+
+#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
+#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
+#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
+#define REFTABLE_ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ alloc = 2 * (alloc) + 1; \
+ if (alloc < (nr)) \
+ alloc = (nr); \
+ REFTABLE_REALLOC_ARRAY(x, alloc); \
+ } \
+ } while (0)
/* Find the longest shared prefix size of `a` and `b` */
struct strbuf;
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 1fcd229725..997c4d9e01 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -12,40 +12,47 @@ https://developers.google.com/open-source/licenses/bsd
#include "test_framework.h"
#include "reftable-tests.h"
-struct binsearch_args {
- int key;
- int *arr;
+struct integer_needle_lesseq_args {
+ int needle;
+ int *haystack;
};
-static int binsearch_func(size_t i, void *void_args)
+static int integer_needle_lesseq(size_t i, void *_args)
{
- struct binsearch_args *args = void_args;
-
- return args->key < args->arr[i];
+ struct integer_needle_lesseq_args *args = _args;
+ return args->needle <= args->haystack[i];
}
static void test_binsearch(void)
{
- int arr[] = { 2, 4, 6, 8, 10 };
- size_t sz = ARRAY_SIZE(arr);
- struct binsearch_args args = {
- .arr = arr,
+ int haystack[] = { 2, 4, 6, 8, 10 };
+ struct {
+ int needle;
+ size_t expected_idx;
+ } testcases[] = {
+ {-9000, 0},
+ {-1, 0},
+ {0, 0},
+ {2, 0},
+ {3, 1},
+ {4, 1},
+ {7, 3},
+ {9, 4},
+ {10, 4},
+ {11, 5},
+ {9000, 5},
};
+ size_t i = 0;
- int i = 0;
- for (i = 1; i < 11; i++) {
- int res;
- args.key = i;
- res = binsearch(sz, &binsearch_func, &args);
+ for (i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct integer_needle_lesseq_args args = {
+ .haystack = haystack,
+ .needle = testcases[i].needle,
+ };
+ size_t idx;
- if (res < sz) {
- EXPECT(args.key < arr[res]);
- if (res > 0) {
- EXPECT(args.key >= arr[res - 1]);
- }
- } else {
- EXPECT(args.key == 10 || args.key == 11);
- }
+ idx = binsearch(ARRAY_SIZE(haystack), &integer_needle_lesseq, &args);
+ EXPECT(idx == testcases[i].expected_idx);
}
}
diff --git a/reftable/block.c b/reftable/block.c
index 34d4d07369..3e87460cba 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -51,12 +51,7 @@ static int block_writer_register_restart(struct block_writer *w, int n,
if (2 + 3 * rlen + n > w->block_size - w->next)
return -1;
if (is_restart) {
- if (w->restart_len == w->restart_cap) {
- w->restart_cap = w->restart_cap * 2 + 1;
- w->restarts = reftable_realloc(
- w->restarts, sizeof(uint32_t) * w->restart_cap);
- }
-
+ REFTABLE_ALLOC_GROW(w->restarts, w->restart_len + 1, w->restart_cap);
w->restarts[w->restart_len++] = w->next;
}
@@ -148,8 +143,10 @@ int block_writer_finish(struct block_writer *w)
int block_header_skip = 4 + w->header_off;
uLongf src_len = w->next - block_header_skip;
uLongf dest_cap = src_len * 1.001 + 12;
+ uint8_t *compressed;
+
+ REFTABLE_ALLOC_ARRAY(compressed, dest_cap);
- uint8_t *compressed = reftable_malloc(dest_cap);
while (1) {
uLongf out_dest_len = dest_cap;
int zresult = compress2(compressed, &out_dest_len,
@@ -178,11 +175,6 @@ int block_writer_finish(struct block_writer *w)
return w->next;
}
-uint8_t block_reader_type(struct block_reader *r)
-{
- return r->block.data[r->header_off];
-}
-
int block_reader_init(struct block_reader *br, struct reftable_block *block,
uint32_t header_off, uint32_t table_block_size,
int hash_size)
@@ -194,7 +186,8 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
uint16_t restart_count = 0;
uint32_t restart_start = 0;
uint8_t *restart_bytes = NULL;
- uint8_t *uncompressed = NULL;
+
+ reftable_block_done(&br->block);
if (!reftable_is_block_type(typ)) {
err = REFTABLE_FORMAT_ERROR;
@@ -202,37 +195,57 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
}
if (typ == BLOCK_TYPE_LOG) {
- int block_header_skip = 4 + header_off;
- uLongf dst_len = sz - block_header_skip; /* total size of dest
- buffer. */
- uLongf src_len = block->len - block_header_skip;
- /* Log blocks specify the *uncompressed* size in their header.
- */
- uncompressed = reftable_malloc(sz);
+ uint32_t block_header_skip = 4 + header_off;
+ uLong dst_len = sz - block_header_skip;
+ uLong src_len = block->len - block_header_skip;
+
+ /* Log blocks specify the *uncompressed* size in their header. */
+ REFTABLE_ALLOC_GROW(br->uncompressed_data, sz,
+ br->uncompressed_cap);
/* Copy over the block header verbatim. It's not compressed. */
- memcpy(uncompressed, block->data, block_header_skip);
+ memcpy(br->uncompressed_data, block->data, block_header_skip);
- /* Uncompress */
- if (Z_OK !=
- uncompress2(uncompressed + block_header_skip, &dst_len,
- block->data + block_header_skip, &src_len)) {
+ if (!br->zstream) {
+ REFTABLE_CALLOC_ARRAY(br->zstream, 1);
+ err = inflateInit(br->zstream);
+ } else {
+ err = inflateReset(br->zstream);
+ }
+ if (err != Z_OK) {
+ err = REFTABLE_ZLIB_ERROR;
+ goto done;
+ }
+
+ br->zstream->next_in = block->data + block_header_skip;
+ br->zstream->avail_in = src_len;
+ br->zstream->next_out = br->uncompressed_data + block_header_skip;
+ br->zstream->avail_out = dst_len;
+
+ /*
+ * We know both input as well as output size, and we know that
+ * the sizes should never be bigger than `uInt_MAX` because
+ * blocks can at most be 16MB large. We can thus use `Z_FINISH`
+ * here to instruct zlib to inflate the data in one go, which
+ * is more efficient than using `Z_NO_FLUSH`.
+ */
+ err = inflate(br->zstream, Z_FINISH);
+ if (err != Z_STREAM_END) {
err = REFTABLE_ZLIB_ERROR;
goto done;
}
+ err = 0;
- if (dst_len + block_header_skip != sz) {
+ if (br->zstream->total_out + block_header_skip != sz) {
err = REFTABLE_FORMAT_ERROR;
goto done;
}
/* We're done with the input data. */
reftable_block_done(block);
- block->data = uncompressed;
- uncompressed = NULL;
+ block->data = br->uncompressed_data;
block->len = sz;
- block->source = malloc_block_source();
- full_block_size = src_len + block_header_skip;
+ full_block_size = src_len + block_header_skip - br->zstream->avail_in;
} else if (full_block_size == 0) {
full_block_size = sz;
} else if (sz < full_block_size && sz < block->len &&
@@ -260,176 +273,242 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
br->restart_bytes = restart_bytes;
done:
- reftable_free(uncompressed);
return err;
}
-static uint32_t block_reader_restart_offset(struct block_reader *br, int i)
+void block_reader_release(struct block_reader *br)
+{
+ inflateEnd(br->zstream);
+ reftable_free(br->zstream);
+ reftable_free(br->uncompressed_data);
+ reftable_block_done(&br->block);
+}
+
+uint8_t block_reader_type(const struct block_reader *r)
+{
+ return r->block.data[r->header_off];
+}
+
+int block_reader_first_key(const struct block_reader *br, struct strbuf *key)
+{
+ int off = br->header_off + 4, n;
+ struct string_view in = {
+ .buf = br->block.data + off,
+ .len = br->block_len - off,
+ };
+ uint8_t extra = 0;
+
+ strbuf_reset(key);
+
+ n = reftable_decode_key(key, &extra, in);
+ if (n < 0)
+ return n;
+ if (!key->len)
+ return REFTABLE_FORMAT_ERROR;
+
+ return 0;
+}
+
+static uint32_t block_reader_restart_offset(const struct block_reader *br, int i)
{
return get_be24(br->restart_bytes + 3 * i);
}
-void block_reader_start(struct block_reader *br, struct block_iter *it)
+void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
{
- it->br = br;
+ it->block = br->block.data;
+ it->block_len = br->block_len;
+ it->hash_size = br->hash_size;
strbuf_reset(&it->last_key);
it->next_off = br->header_off + 4;
}
-struct restart_find_args {
+struct restart_needle_less_args {
int error;
- struct strbuf key;
- struct block_reader *r;
+ struct strbuf needle;
+ const struct block_reader *reader;
};
-static int restart_key_less(size_t idx, void *args)
+static int restart_needle_less(size_t idx, void *_args)
{
- struct restart_find_args *a = args;
- uint32_t off = block_reader_restart_offset(a->r, idx);
+ struct restart_needle_less_args *args = _args;
+ uint32_t off = block_reader_restart_offset(args->reader, idx);
struct string_view in = {
- .buf = a->r->block.data + off,
- .len = a->r->block_len - off,
+ .buf = args->reader->block.data + off,
+ .len = args->reader->block_len - off,
};
-
- /* the restart key is verbatim in the block, so this could avoid the
- alloc for decoding the key */
- struct strbuf rkey = STRBUF_INIT;
- struct strbuf last_key = STRBUF_INIT;
- uint8_t unused_extra;
- int n = reftable_decode_key(&rkey, &unused_extra, last_key, in);
- int result;
- if (n < 0) {
- a->error = 1;
+ uint64_t prefix_len, suffix_len;
+ uint8_t extra;
+ int n;
+
+ /*
+ * Records at restart points are stored without prefix compression, so
+ * there is no need to fully decode the record key here. This removes
+ * the need for allocating memory.
+ */
+ n = reftable_decode_keylen(in, &prefix_len, &suffix_len, &extra);
+ if (n < 0 || prefix_len) {
+ args->error = 1;
return -1;
}
- result = strbuf_cmp(&a->key, &rkey);
- strbuf_release(&rkey);
- return result;
-}
+ string_view_consume(&in, n);
+ if (suffix_len > in.len) {
+ args->error = 1;
+ return -1;
+ }
-void block_iter_copy_from(struct block_iter *dest, struct block_iter *src)
-{
- dest->br = src->br;
- dest->next_off = src->next_off;
- strbuf_reset(&dest->last_key);
- strbuf_addbuf(&dest->last_key, &src->last_key);
+ n = memcmp(args->needle.buf, in.buf,
+ args->needle.len < suffix_len ? args->needle.len : suffix_len);
+ if (n)
+ return n < 0;
+ return args->needle.len < suffix_len;
}
int block_iter_next(struct block_iter *it, struct reftable_record *rec)
{
struct string_view in = {
- .buf = it->br->block.data + it->next_off,
- .len = it->br->block_len - it->next_off,
+ .buf = (unsigned char *) it->block + it->next_off,
+ .len = it->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)
+ if (it->next_off >= it->block_len)
return 1;
- n = reftable_decode_key(&key, &extra, it->last_key, in);
+ n = reftable_decode_key(&it->last_key, &extra, in);
if (n < 0)
return -1;
-
- if (!key.len)
+ if (!it->last_key.len)
return REFTABLE_FORMAT_ERROR;
string_view_consume(&in, n);
- n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
+ n = reftable_record_decode(rec, it->last_key, extra, in, it->hash_size,
+ &it->scratch);
if (n < 0)
return -1;
string_view_consume(&in, n);
- strbuf_reset(&it->last_key);
- strbuf_addbuf(&it->last_key, &key);
it->next_off += start.len - in.len;
- strbuf_release(&key);
return 0;
}
-int block_reader_first_key(struct block_reader *br, struct strbuf *key)
+void block_iter_reset(struct block_iter *it)
{
- struct strbuf empty = STRBUF_INIT;
- int off = br->header_off + 4;
- struct string_view in = {
- .buf = br->block.data + off,
- .len = br->block_len - off,
- };
-
- uint8_t extra = 0;
- int n = reftable_decode_key(key, &extra, empty, in);
- if (n < 0)
- return n;
- if (!key->len)
- return REFTABLE_FORMAT_ERROR;
-
- return 0;
-}
-
-int block_iter_seek(struct block_iter *it, struct strbuf *want)
-{
- return block_reader_seek(it->br, it, want);
+ strbuf_reset(&it->last_key);
+ it->next_off = 0;
+ it->block = NULL;
+ it->block_len = 0;
+ it->hash_size = 0;
}
void block_iter_close(struct block_iter *it)
{
strbuf_release(&it->last_key);
+ strbuf_release(&it->scratch);
}
-int block_reader_seek(struct block_reader *br, struct block_iter *it,
- struct strbuf *want)
+int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
+ struct strbuf *want)
{
- struct restart_find_args args = {
- .key = *want,
- .r = br,
+ struct restart_needle_less_args args = {
+ .needle = *want,
+ .reader = br,
};
- struct reftable_record rec = reftable_new_record(block_reader_type(br));
- struct strbuf key = STRBUF_INIT;
+ struct reftable_record rec;
int err = 0;
- struct block_iter next = {
- .last_key = STRBUF_INIT,
- };
-
- int i = binsearch(br->restart_count, &restart_key_less, &args);
+ size_t i;
+
+ /*
+ * Perform a binary search over the block's restart points, which
+ * avoids doing a linear scan over the whole block. Like this, we
+ * identify the section of the block that should contain our key.
+ *
+ * Note that we explicitly search for the first restart point _greater_
+ * than the sought-after record, not _greater or equal_ to it. In case
+ * the sought-after record is located directly at the restart point we
+ * would otherwise start doing the linear search at the preceding
+ * restart point. While that works alright, we would end up scanning
+ * too many record.
+ */
+ i = binsearch(br->restart_count, &restart_needle_less, &args);
if (args.error) {
err = REFTABLE_FORMAT_ERROR;
goto done;
}
- it->br = br;
- if (i > 0) {
- i--;
- it->next_off = block_reader_restart_offset(br, i);
- } else {
+ /*
+ * Now there are multiple cases:
+ *
+ * - `i == 0`: The wanted record is smaller than the record found at
+ * the first restart point. As the first restart point is the first
+ * record in the block, our wanted record cannot be located in this
+ * block at all. We still need to position the iterator so that the
+ * next call to `block_iter_next()` will yield an end-of-iterator
+ * signal.
+ *
+ * - `i == restart_count`: The wanted record was not found at any of
+ * the restart points. As there is no restart point at the end of
+ * the section the record may thus be contained in the last block.
+ *
+ * - `i > 0`: The wanted record must be contained in the section
+ * before the found restart point. We thus do a linear search
+ * starting from the preceding restart point.
+ */
+ if (i > 0)
+ it->next_off = block_reader_restart_offset(br, i - 1);
+ else
it->next_off = br->header_off + 4;
- }
-
- /* We're looking for the last entry less/equal than the wanted key, so
- we have to go one entry too far and then back up.
- */
+ it->block = br->block.data;
+ it->block_len = br->block_len;
+ it->hash_size = br->hash_size;
+
+ reftable_record_init(&rec, block_reader_type(br));
+
+ /*
+ * We're looking for the last entry less than the wanted key so that
+ * the next call to `block_reader_next()` would yield the wanted
+ * record. We thus don't want to position our reader at the sought
+ * after record, but one before. To do so, we have to go one entry too
+ * far and then back up.
+ */
while (1) {
- block_iter_copy_from(&next, it);
- err = block_iter_next(&next, &rec);
+ size_t prev_off = it->next_off;
+
+ err = block_iter_next(it, &rec);
if (err < 0)
goto done;
-
- reftable_record_key(&rec, &key);
- if (err > 0 || strbuf_cmp(&key, want) >= 0) {
+ if (err > 0) {
+ it->next_off = prev_off;
err = 0;
goto done;
}
- block_iter_copy_from(it, &next);
+ /*
+ * Check whether the current key is greater or equal to the
+ * sought-after key. In case it is greater we know that the
+ * record does not exist in the block and can thus abort early.
+ * In case it is equal to the sought-after key we have found
+ * the desired record.
+ *
+ * Note that we store the next record's key record directly in
+ * `last_key` without restoring the key of the preceding record
+ * in case we need to go one record back. This is safe to do as
+ * `block_iter_next()` would return the ref whose key is equal
+ * to `last_key` now, and naturally all keys share a prefix
+ * with themselves.
+ */
+ reftable_record_key(&rec, &it->last_key);
+ if (strbuf_cmp(&it->last_key, want) >= 0) {
+ it->next_off = prev_off;
+ goto done;
+ }
}
done:
- strbuf_release(&key);
- strbuf_release(&next.last_key);
reftable_record_release(&rec);
-
return err;
}
diff --git a/reftable/block.h b/reftable/block.h
index 87c77539b5..ea4384a7e2 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -56,6 +56,8 @@ int block_writer_finish(struct block_writer *w);
/* clears out internally allocated block_writer members. */
void block_writer_release(struct block_writer *bw);
+struct z_stream;
+
/* Read a block. */
struct block_reader {
/* offset of the block header; nonzero for the first block in a
@@ -66,6 +68,11 @@ struct block_reader {
struct reftable_block block;
int hash_size;
+ /* Uncompressed data for log entries. */
+ z_stream *zstream;
+ unsigned char *uncompressed_data;
+ size_t uncompressed_cap;
+
/* size of the data, excluding restart data. */
uint32_t block_len;
uint8_t *restart_bytes;
@@ -76,41 +83,49 @@ struct block_reader {
uint32_t full_block_size;
};
+/* 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,
+ int hash_size);
+
+void block_reader_release(struct block_reader *br);
+
+/* Returns the block type (eg. 'r' for refs) */
+uint8_t block_reader_type(const struct block_reader *r);
+
+/* Decodes the first key in the block */
+int block_reader_first_key(const struct block_reader *br, struct strbuf *key);
+
/* Iterate over entries in a block */
struct block_iter {
/* offset within the block of the next entry to read. */
uint32_t next_off;
- struct block_reader *br;
+ const unsigned char *block;
+ size_t block_len;
+ int hash_size;
/* key for last entry we read. */
struct strbuf last_key;
+ struct strbuf scratch;
};
-/* 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,
- int hash_size);
+#define BLOCK_ITER_INIT { \
+ .last_key = STRBUF_INIT, \
+ .scratch = STRBUF_INIT, \
+}
/* Position `it` at start of the block */
-void block_reader_start(struct block_reader *br, struct block_iter *it);
+void block_iter_seek_start(struct block_iter *it, const struct block_reader *br);
/* Position `it` to the `want` key in the block */
-int block_reader_seek(struct block_reader *br, struct block_iter *it,
- struct strbuf *want);
-
-/* Returns the block type (eg. 'r' for refs) */
-uint8_t block_reader_type(struct block_reader *r);
-
-/* Decodes the first key in the block */
-int block_reader_first_key(struct block_reader *br, struct strbuf *key);
-
-void block_iter_copy_from(struct block_iter *dest, struct block_iter *src);
+int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
+ struct strbuf *want);
/* return < 0 for error, 0 for OK, > 0 for EOF. */
int block_iter_next(struct block_iter *it, struct reftable_record *rec);
-/* Seek to `want` with in the block pointed to by `it` */
-int block_iter_seek(struct block_iter *it, struct strbuf *want);
+/* Reset the block iterator to pristine state without releasing its memory. */
+void block_iter_reset(struct block_iter *it);
/* deallocate memory for `it`. The block reader and its block is left intact. */
void block_iter_close(struct block_iter *it);
diff --git a/reftable/block_test.c b/reftable/block_test.c
index cb88af4a56..26a9cfbc83 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -32,11 +32,11 @@ 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;
- block.data = reftable_calloc(block_size);
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
block.len = block_size;
block.source = malloc_block_source();
block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
@@ -49,13 +49,11 @@ 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);
@@ -71,7 +69,7 @@ static void test_block_read_write(void)
block_reader_init(&br, &block, header_off, block_size, GIT_SHA1_RAWSZ);
- block_reader_start(&br, &it);
+ block_iter_seek_start(&it, &br);
while (1) {
int r = block_iter_next(&it, &rec);
@@ -87,11 +85,11 @@ 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]);
- n = block_reader_seek(&br, &it, &want);
+ n = block_iter_seek_key(&it, &br, &want);
EXPECT(n == 0);
n = block_iter_next(&it, &rec);
@@ -100,7 +98,7 @@ static void test_block_read_write(void)
EXPECT_STREQ(names[i], rec.u.ref.refname);
want.len--;
- n = block_reader_seek(&br, &it, &want);
+ n = block_iter_seek_key(&it, &br, &want);
EXPECT(n == 0);
n = block_iter_next(&it, &rec);
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index 8331b34e82..eeed254ba9 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -29,7 +29,7 @@ static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
{
struct strbuf *b = v;
assert(off + size <= b->len);
- dest->data = reftable_calloc(size);
+ REFTABLE_CALLOC_ARRAY(dest->data, size);
memcpy(dest->data, b->buf + off, size);
dest->len = size;
return size;
@@ -76,8 +76,8 @@ struct reftable_block_source malloc_block_source(void)
}
struct file_block_source {
- int fd;
uint64_t size;
+ unsigned char *data;
};
static uint64_t file_size(void *b)
@@ -87,19 +87,12 @@ static uint64_t file_size(void *b)
static void file_return_block(void *b, struct reftable_block *dest)
{
- if (dest->len)
- memset(dest->data, 0xff, dest->len);
- reftable_free(dest->data);
}
-static void file_close(void *b)
+static void file_close(void *v)
{
- int fd = ((struct file_block_source *)b)->fd;
- if (fd > 0) {
- close(fd);
- ((struct file_block_source *)b)->fd = 0;
- }
-
+ struct file_block_source *b = v;
+ munmap(b->data, b->size);
reftable_free(b);
}
@@ -108,9 +101,7 @@ static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
{
struct file_block_source *b = v;
assert(off + size <= b->size);
- dest->data = reftable_malloc(size);
- if (pread(b->fd, dest->data, size, off) != size)
- return -1;
+ dest->data = b->data + off;
dest->len = size;
return size;
}
@@ -125,26 +116,26 @@ static struct reftable_block_source_vtable file_vtable = {
int reftable_block_source_from_file(struct reftable_block_source *bs,
const char *name)
{
- struct stat st = { 0 };
- int err = 0;
- int fd = open(name, O_RDONLY);
- struct file_block_source *p = NULL;
+ struct file_block_source *p;
+ struct stat st;
+ int fd;
+
+ fd = open(name, O_RDONLY);
if (fd < 0) {
- if (errno == ENOENT) {
+ if (errno == ENOENT)
return REFTABLE_NOT_EXIST_ERROR;
- }
return -1;
}
- err = fstat(fd, &st);
- if (err < 0) {
+ if (fstat(fd, &st) < 0) {
close(fd);
return REFTABLE_IO_ERROR;
}
- p = reftable_calloc(sizeof(struct file_block_source));
+ REFTABLE_CALLOC_ARRAY(p, 1);
p->size = st.st_size;
- p->fd = fd;
+ p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
assert(!bs->ops);
bs->ops = &file_vtable;
diff --git a/reftable/dump.c b/reftable/dump.c
index ce936b4e18..26e0393c7d 100644
--- a/reftable/dump.c
+++ b/reftable/dump.c
@@ -11,14 +11,12 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-blocksource.h"
#include "reftable-error.h"
-#include "reftable-merged.h"
#include "reftable-record.h"
#include "reftable-tests.h"
#include "reftable-writer.h"
#include "reftable-iterator.h"
#include "reftable-reader.h"
#include "reftable-stack.h"
-#include "reftable-generic.h"
#include <stddef.h>
#include <stdio.h>
diff --git a/reftable/error.c b/reftable/error.c
index 0d1766735e..cfb7a0fda4 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -22,7 +22,7 @@ const char *reftable_error_str(int err)
case REFTABLE_NOT_EXIST_ERROR:
return "file does not exist";
case REFTABLE_LOCK_ERROR:
- return "data is outdated";
+ return "data is locked";
case REFTABLE_API_ERROR:
return "misuse of the reftable API";
case REFTABLE_ZLIB_ERROR:
@@ -35,6 +35,8 @@ const char *reftable_error_str(int err)
return "invalid refname";
case REFTABLE_ENTRY_TOO_BIG_ERROR:
return "entry too large";
+ case REFTABLE_OUTDATED_ERROR:
+ return "data concurrently modified";
case -1:
return "general error";
default:
diff --git a/reftable/generic.c b/reftable/generic.c
index 57f8032db9..b9f1c7c18a 100644
--- a/reftable/generic.c
+++ b/reftable/generic.c
@@ -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"
diff --git a/reftable/iter.c b/reftable/iter.c
index a8d174c040..aa9ac199b1 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -16,11 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
#include "reader.h"
#include "reftable-error.h"
-int iterator_is_null(struct reftable_iterator *it)
-{
- return !it->ops;
-}
-
static void filtering_ref_iterator_close(void *iter_arg)
{
struct filtering_ref_iterator *fri = iter_arg;
@@ -120,7 +115,7 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
/* indexed block does not exist. */
return REFTABLE_FORMAT_ERROR;
}
- block_reader_start(&it->block_reader, &it->cur);
+ block_iter_seek_start(&it->cur, &it->block_reader);
return 0;
}
@@ -160,8 +155,7 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
int oid_len, uint64_t *offsets, int offset_len)
{
struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT;
- struct indexed_table_ref_iter *itr =
- reftable_calloc(sizeof(struct indexed_table_ref_iter));
+ struct indexed_table_ref_iter *itr = reftable_calloc(1, sizeof(*itr));
int err = 0;
*itr = empty;
diff --git a/reftable/iter.h b/reftable/iter.h
index 09eb0cbfa5..537431baba 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -16,10 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-iterator.h"
#include "reftable-generic.h"
-/* Returns true for a zeroed out iterator, such as the one returned from
- * iterator_destroy. */
-int iterator_is_null(struct reftable_iterator *it);
-
/* iterator that produces only ref records that point to `oid` */
struct filtering_ref_iterator {
int double_check;
@@ -53,10 +49,10 @@ 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);
diff --git a/reftable/merged.c b/reftable/merged.c
index 5ded470c08..f85a24c678 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -11,33 +11,45 @@ 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"
#include "reftable-error.h"
#include "system.h"
+struct merged_subiter {
+ struct reftable_iterator iter;
+ struct reftable_record rec;
+};
+
+struct merged_iter {
+ struct merged_subiter *subiters;
+ struct merged_iter_pqueue pq;
+ uint32_t hash_id;
+ size_t stack_len;
+ uint8_t typ;
+ int suppress_deletions;
+ ssize_t advance_index;
+};
+
static int merged_iter_init(struct merged_iter *mi)
{
- int i = 0;
- for (i = 0; i < mi->stack_len; i++) {
- struct reftable_record rec = reftable_new_record(mi->typ);
- int err = iterator_next(&mi->stack[i], &rec);
- if (err < 0) {
+ for (size_t i = 0; i < mi->stack_len; i++) {
+ struct pq_entry e = {
+ .index = i,
+ .rec = &mi->subiters[i].rec,
+ };
+ int err;
+
+ reftable_record_init(&mi->subiters[i].rec, mi->typ);
+ err = iterator_next(&mi->subiters[i].iter,
+ &mi->subiters[i].rec);
+ if (err < 0)
return err;
- }
+ if (err > 0)
+ continue;
- if (err > 0) {
- reftable_iterator_destroy(&mi->stack[i]);
- reftable_record_release(&rec);
- } else {
- struct pq_entry e = {
- .rec = rec,
- .index = i,
- };
- merged_iter_pqueue_add(&mi->pq, &e);
- }
+ merged_iter_pqueue_add(&mi->pq, &e);
}
return 0;
@@ -46,56 +58,68 @@ static int merged_iter_init(struct merged_iter *mi)
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
- int i = 0;
+
merged_iter_pqueue_release(&mi->pq);
- for (i = 0; i < mi->stack_len; i++) {
- reftable_iterator_destroy(&mi->stack[i]);
+ for (size_t i = 0; i < mi->stack_len; i++) {
+ reftable_iterator_destroy(&mi->subiters[i].iter);
+ reftable_record_release(&mi->subiters[i].rec);
}
- reftable_free(mi->stack);
+ reftable_free(mi->subiters);
}
-static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
- size_t idx)
+static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
{
struct pq_entry e = {
- .rec = reftable_new_record(mi->typ),
.index = idx,
+ .rec = &mi->subiters[idx].rec,
};
- int err = iterator_next(&mi->stack[idx], &e.rec);
- if (err < 0)
- return err;
+ int err;
- if (err > 0) {
- reftable_iterator_destroy(&mi->stack[idx]);
- reftable_record_release(&e.rec);
- return 0;
- }
+ err = iterator_next(&mi->subiters[idx].iter, &mi->subiters[idx].rec);
+ if (err)
+ return err;
merged_iter_pqueue_add(&mi->pq, &e);
return 0;
}
-static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
-{
- if (iterator_is_null(&mi->stack[idx]))
- return 0;
- return merged_iter_advance_nonnull_subiter(mi, idx);
-}
-
static int merged_iter_next_entry(struct merged_iter *mi,
struct reftable_record *rec)
{
- struct strbuf entry_key = STRBUF_INIT;
struct pq_entry entry = { 0 };
- int err = 0;
+ int err = 0, empty;
+
+ empty = merged_iter_pqueue_is_empty(mi->pq);
+
+ if (mi->advance_index >= 0) {
+ /*
+ * When there are no pqueue entries then we only have a single
+ * subiter left. There is no need to use the pqueue in that
+ * case anymore as we know that the subiter will return entries
+ * in the correct order already.
+ *
+ * While this may sound like a very specific edge case, it may
+ * happen more frequently than you think. Most repositories
+ * will end up having a single large base table that contains
+ * most of the refs. It's thus likely that we exhaust all
+ * subiters but the one from that base ref.
+ */
+ if (empty)
+ return iterator_next(&mi->subiters[mi->advance_index].iter,
+ rec);
+
+ err = merged_iter_advance_subiter(mi, mi->advance_index);
+ if (err < 0)
+ return err;
+ if (!err)
+ empty = 0;
+ mi->advance_index = -1;
+ }
- if (merged_iter_pqueue_is_empty(mi->pq))
+ if (empty)
return 1;
entry = merged_iter_pqueue_remove(&mi->pq);
- err = merged_iter_advance_subiter(mi, entry.index);
- if (err < 0)
- return err;
/*
One can also use reftable as datacenter-local storage, where the ref
@@ -105,57 +129,38 @@ 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);
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;
-
- reftable_record_key(&top.rec, &k);
+ int cmp;
- cmp = strbuf_cmp(&k, &entry_key);
- strbuf_release(&k);
-
- if (cmp > 0) {
+ cmp = reftable_record_cmp(top.rec, entry.rec);
+ if (cmp > 0)
break;
- }
merged_iter_pqueue_remove(&mi->pq);
err = merged_iter_advance_subiter(mi, top.index);
- if (err < 0) {
+ if (err < 0)
return err;
- }
- reftable_record_release(&top.rec);
}
- reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
- reftable_record_release(&entry.rec);
- strbuf_release(&entry_key);
+ mi->advance_index = entry.index;
+ SWAP(*rec, *entry.rec);
return 0;
}
-static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
+static int merged_iter_next_void(void *p, struct reftable_record *rec)
{
+ struct merged_iter *mi = p;
while (1) {
int err = merged_iter_next_entry(mi, rec);
- if (err == 0 && mi->suppress_deletions &&
- reftable_record_is_deletion(rec)) {
+ if (err)
+ return err;
+ if (mi->suppress_deletions && reftable_record_is_deletion(rec))
continue;
- }
-
- return err;
+ return 0;
}
}
-static int merged_iter_next_void(void *p, struct reftable_record *rec)
-{
- struct merged_iter *mi = p;
- if (merged_iter_pqueue_is_empty(mi->pq))
- return 1;
-
- return merged_iter_next(mi, rec);
-}
-
static struct reftable_iterator_vtable merged_iter_vtable = {
.next = &merged_iter_next_void,
.close = &merged_iter_close,
@@ -170,14 +175,14 @@ static void iterator_from_merged_iter(struct reftable_iterator *it,
}
int reftable_new_merged_table(struct reftable_merged_table **dest,
- struct reftable_table *stack, int n,
+ struct reftable_table *stack, size_t n,
uint32_t hash_id)
{
struct reftable_merged_table *m = NULL;
uint64_t last_max = 0;
uint64_t first_min = 0;
- int i = 0;
- for (i = 0; i < n; i++) {
+
+ for (size_t i = 0; i < n; i++) {
uint64_t min = reftable_table_min_update_index(&stack[i]);
uint64_t max = reftable_table_max_update_index(&stack[i]);
@@ -192,7 +197,7 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
}
}
- m = reftable_calloc(sizeof(struct reftable_merged_table));
+ REFTABLE_CALLOC_ARRAY(m, 1);
m->stack = stack;
m->stack_len = n;
m->min = first_min;
@@ -241,48 +246,37 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
struct reftable_iterator *it,
struct reftable_record *rec)
{
- struct reftable_iterator *iters = reftable_calloc(
- sizeof(struct reftable_iterator) * mt->stack_len);
struct merged_iter merged = {
- .stack = iters,
.typ = reftable_record_type(rec),
.hash_id = mt->hash_id,
.suppress_deletions = mt->suppress_deletions,
+ .advance_index = -1,
};
- int n = 0;
- int err = 0;
- int i = 0;
- for (i = 0; i < mt->stack_len && err == 0; i++) {
- int e = reftable_table_seek_record(&mt->stack[i], &iters[n],
- rec);
- if (e < 0) {
- err = e;
- }
- if (e == 0) {
- n++;
- }
- }
- if (err < 0) {
- int i = 0;
- for (i = 0; i < n; i++) {
- reftable_iterator_destroy(&iters[i]);
- }
- reftable_free(iters);
- return err;
+ struct merged_iter *p;
+ int err;
+
+ REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
+ for (size_t i = 0; i < mt->stack_len; i++) {
+ err = reftable_table_seek_record(&mt->stack[i],
+ &merged.subiters[merged.stack_len].iter, rec);
+ if (err < 0)
+ goto out;
+ if (!err)
+ merged.stack_len++;
}
- merged.stack_len = n;
err = merged_iter_init(&merged);
- if (err < 0) {
+ if (err < 0)
+ goto out;
+
+ p = reftable_malloc(sizeof(struct merged_iter));
+ *p = merged;
+ iterator_from_merged_iter(it, p);
+
+out:
+ if (err < 0)
merged_iter_close(&merged);
- return err;
- } else {
- struct merged_iter *p =
- reftable_malloc(sizeof(struct merged_iter));
- *p = merged;
- iterator_from_merged_iter(it, p);
- }
- return 0;
+ return err;
}
int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
diff --git a/reftable/merged.h b/reftable/merged.h
index 7d9f95d27e..a2571dbc99 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -9,7 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
#ifndef MERGED_H
#define MERGED_H
-#include "pq.h"
+#include "system.h"
struct reftable_merged_table {
struct reftable_table *stack;
@@ -24,15 +24,6 @@ struct reftable_merged_table {
uint64_t max;
};
-struct merged_iter {
- struct reftable_iterator *stack;
- uint32_t hash_id;
- size_t stack_len;
- uint8_t typ;
- int suppress_deletions;
- struct merged_iter_pqueue pq;
-};
-
void merged_table_release(struct reftable_merged_table *mt);
#endif
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index d08c16abef..530fc82d1c 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -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"
@@ -43,7 +42,7 @@ static void write_test_table(struct strbuf *buf,
}
}
- w = reftable_new_writer(&strbuf_add_void, buf, &opts);
+ w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
reftable_writer_set_limits(w, min, max);
for (i = 0; i < n; i++) {
@@ -71,7 +70,7 @@ static void write_test_log_table(struct strbuf *buf,
.exact_log_message = 1,
};
struct reftable_writer *w = NULL;
- w = reftable_new_writer(&strbuf_add_void, buf, &opts);
+ w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
reftable_writer_set_limits(w, update_index, update_index);
for (i = 0; i < n; i++) {
@@ -89,16 +88,17 @@ static struct reftable_merged_table *
merged_table_from_records(struct reftable_ref_record **refs,
struct reftable_block_source **source,
struct reftable_reader ***readers, int *sizes,
- struct strbuf *buf, int n)
+ struct strbuf *buf, size_t n)
{
- int i = 0;
struct reftable_merged_table *mt = NULL;
+ struct reftable_table *tabs;
int err;
- struct reftable_table *tabs =
- reftable_calloc(n * sizeof(struct reftable_table));
- *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
- *source = reftable_calloc(n * sizeof(**source));
- for (i = 0; i < n; i++) {
+
+ REFTABLE_CALLOC_ARRAY(tabs, n);
+ REFTABLE_CALLOC_ARRAY(*readers, n);
+ REFTABLE_CALLOC_ARRAY(*source, n);
+
+ for (size_t i = 0; i < n; i++) {
write_test_table(&buf[i], refs[i], sizes[i]);
block_source_from_strbuf(&(*source)[i], &buf[i]);
@@ -123,13 +123,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 +163,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 +193,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 },
},
};
@@ -236,14 +232,10 @@ static void test_merged(void)
while (len < 100) { /* cap loops/recursion. */
struct reftable_ref_record ref = { NULL };
int err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
+ if (err > 0)
break;
- }
- if (len == cap) {
- cap = 2 * cap + 1;
- out = reftable_realloc(
- out, sizeof(struct reftable_ref_record) * cap);
- }
+
+ REFTABLE_ALLOC_GROW(out, len + 1, cap);
out[len++] = ref;
}
reftable_iterator_destroy(&it);
@@ -270,16 +262,17 @@ static struct reftable_merged_table *
merged_table_from_log_records(struct reftable_log_record **logs,
struct reftable_block_source **source,
struct reftable_reader ***readers, int *sizes,
- struct strbuf *buf, int n)
+ struct strbuf *buf, size_t n)
{
- int i = 0;
struct reftable_merged_table *mt = NULL;
+ struct reftable_table *tabs;
int err;
- struct reftable_table *tabs =
- reftable_calloc(n * sizeof(struct reftable_table));
- *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
- *source = reftable_calloc(n * sizeof(**source));
- for (i = 0; i < n; i++) {
+
+ REFTABLE_CALLOC_ARRAY(tabs, n);
+ REFTABLE_CALLOC_ARRAY(*readers, n);
+ REFTABLE_CALLOC_ARRAY(*source, n);
+
+ for (size_t i = 0; i < n; i++) {
write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
block_source_from_strbuf(&(*source)[i], &buf[i]);
@@ -296,16 +289,13 @@ merged_table_from_log_records(struct reftable_log_record **logs,
static void test_merged_logs(void)
{
- uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
- uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
- uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 };
struct reftable_log_record r1[] = {
{
.refname = "a",
.update_index = 2,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
- .old_hash = hash2,
+ .old_hash = { 2 },
/* deletion */
.name = "jane doe",
.email = "jane@invalid",
@@ -317,8 +307,8 @@ static void test_merged_logs(void)
.update_index = 1,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
- .old_hash = hash1,
- .new_hash = hash2,
+ .old_hash = { 1 },
+ .new_hash = { 2 },
.name = "jane doe",
.email = "jane@invalid",
.message = "message1",
@@ -331,7 +321,7 @@ static void test_merged_logs(void)
.update_index = 3,
.value_type = REFTABLE_LOG_UPDATE,
.value.update = {
- .new_hash = hash3,
+ .new_hash = { 3 },
.name = "jane doe",
.email = "jane@invalid",
.message = "message3",
@@ -373,14 +363,10 @@ static void test_merged_logs(void)
while (len < 100) { /* cap loops/recursion. */
struct reftable_log_record log = { NULL };
int err = reftable_iterator_next_log(&it, &log);
- if (err > 0) {
+ if (err > 0)
break;
- }
- if (len == cap) {
- cap = 2 * cap + 1;
- out = reftable_realloc(
- out, sizeof(struct reftable_log_record) * cap);
- }
+
+ REFTABLE_ALLOC_GROW(out, len + 1, cap);
out[len++] = log;
}
reftable_iterator_destroy(&it);
@@ -417,7 +403,7 @@ static void test_default_write_opts(void)
struct reftable_write_options opts = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record rec = {
.refname = "master",
@@ -425,7 +411,7 @@ static void test_default_write_opts(void)
};
int err;
struct reftable_block_source source = { NULL };
- struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
+ struct reftable_table *tab = reftable_calloc(1, sizeof(*tab));
uint32_t hash_id;
struct reftable_reader *rd = NULL;
struct reftable_merged_table *merged = NULL;
diff --git a/reftable/pq.c b/reftable/pq.c
index dcefeb793a..7fb45d8c60 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -14,33 +14,12 @@ https://developers.google.com/open-source/licenses/bsd
int pq_less(struct pq_entry *a, struct pq_entry *b)
{
- struct strbuf ak = STRBUF_INIT;
- struct strbuf bk = STRBUF_INIT;
- int cmp = 0;
- reftable_record_key(&a->rec, &ak);
- reftable_record_key(&b->rec, &bk);
-
- cmp = strbuf_cmp(&ak, &bk);
-
- strbuf_release(&ak);
- strbuf_release(&bk);
-
+ int cmp = reftable_record_cmp(a->rec, b->rec);
if (cmp == 0)
return a->index > b->index;
-
return cmp < 0;
}
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
-{
- return pq.heap[0];
-}
-
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
-{
- return pq.len == 0;
-}
-
struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
{
int i = 0;
@@ -75,13 +54,9 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
{
int i = 0;
- if (pq->len == pq->cap) {
- pq->cap = 2 * pq->cap + 1;
- pq->heap = reftable_realloc(pq->heap,
- pq->cap * sizeof(struct pq_entry));
- }
-
+ REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap);
pq->heap[pq->len++] = *e;
+
i = pq->len - 1;
while (i > 0) {
int j = (i - 1) / 2;
@@ -97,10 +72,6 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
{
- int i = 0;
- for (i = 0; i < pq->len; i++) {
- reftable_record_release(&pq->heap[i].rec);
- }
FREE_AND_NULL(pq->heap);
- pq->len = pq->cap = 0;
+ memset(pq, 0, sizeof(*pq));
}
diff --git a/reftable/pq.h b/reftable/pq.h
index e85bac9b52..f796c23179 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -12,8 +12,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "record.h"
struct pq_entry {
- int index;
- struct reftable_record rec;
+ size_t index;
+ struct reftable_record *rec;
};
struct merged_iter_pqueue {
@@ -22,12 +22,20 @@ struct merged_iter_pqueue {
size_t cap;
};
-struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
-int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq);
void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
int pq_less(struct pq_entry *a, struct pq_entry *b);
+static inline struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
+{
+ return pq.heap[0];
+}
+
+static inline int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
+{
+ return pq.len == 0;
+}
+
#endif
diff --git a/reftable/pq_test.c b/reftable/pq_test.c
index 011b5c7502..b7d3c80cc7 100644
--- a/reftable/pq_test.c
+++ b/reftable/pq_test.c
@@ -27,48 +27,43 @@ void merged_iter_pqueue_check(struct merged_iter_pqueue pq)
static void test_pq(void)
{
- char *names[54] = { NULL };
- int N = ARRAY_SIZE(names) - 1;
-
struct merged_iter_pqueue pq = { NULL };
+ struct reftable_record recs[54];
+ int N = ARRAY_SIZE(recs) - 1, i;
char *last = NULL;
- int i = 0;
for (i = 0; i < N; i++) {
- char name[100];
- snprintf(name, sizeof(name), "%02d", i);
- names[i] = xstrdup(name);
+ struct strbuf refname = STRBUF_INIT;
+ strbuf_addf(&refname, "%02d", i);
+
+ reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ recs[i].u.ref.refname = strbuf_detach(&refname, NULL);
}
i = 1;
do {
- struct pq_entry e = { .rec = { .type = BLOCK_TYPE_REF,
- .u.ref = {
- .refname = names[i],
- } } };
+ struct pq_entry e = {
+ .rec = &recs[i],
+ };
+
merged_iter_pqueue_add(&pq, &e);
merged_iter_pqueue_check(pq);
+
i = (i * 7) % N;
} while (i != 1);
while (!merged_iter_pqueue_is_empty(pq)) {
struct pq_entry e = merged_iter_pqueue_remove(&pq);
- struct reftable_record *rec = &e.rec;
merged_iter_pqueue_check(pq);
- EXPECT(reftable_record_type(rec) == BLOCK_TYPE_REF);
- if (last) {
- EXPECT(strcmp(last, rec->u.ref.refname) < 0);
- }
- // this is names[i], so don't dealloc.
- last = rec->u.ref.refname;
- rec->u.ref.refname = NULL;
- reftable_record_release(rec);
- }
- for (i = 0; i < N; i++) {
- reftable_free(names[i]);
+ EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
+ if (last)
+ EXPECT(strcmp(last, e.rec->u.ref.refname) < 0);
+ last = e.rec->u.ref.refname;
}
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
merged_iter_pqueue_release(&pq);
}
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
index bcb82530d6..44b84a125e 100644
--- a/reftable/publicbasics.c
+++ b/reftable/publicbasics.c
@@ -37,8 +37,9 @@ void reftable_free(void *p)
free(p);
}
-void *reftable_calloc(size_t sz)
+void *reftable_calloc(size_t nelem, size_t elsize)
{
+ size_t sz = st_mult(nelem, elsize);
void *p = reftable_malloc(sz);
memset(p, 0, sz);
return p;
diff --git a/reftable/reader.c b/reftable/reader.c
index b4db23ce18..481dff10d4 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
{
@@ -221,22 +220,12 @@ struct table_iter {
struct reftable_reader *r;
uint8_t typ;
uint64_t block_off;
+ struct block_reader br;
struct block_iter bi;
int is_finished;
};
-#define TABLE_ITER_INIT \
- { \
- .bi = {.last_key = STRBUF_INIT } \
- }
-
-static void table_iter_copy_from(struct table_iter *dest,
- struct table_iter *src)
-{
- dest->r = src->r;
- dest->typ = src->typ;
- dest->block_off = src->block_off;
- dest->is_finished = src->is_finished;
- block_iter_copy_from(&dest->bi, &src->bi);
+#define TABLE_ITER_INIT { \
+ .bi = BLOCK_ITER_INIT \
}
static int table_iter_next_in_block(struct table_iter *ti,
@@ -252,14 +241,8 @@ static int table_iter_next_in_block(struct table_iter *ti,
static void table_iter_block_done(struct table_iter *ti)
{
- if (!ti->bi.br) {
- return;
- }
- reftable_block_done(&ti->bi.br->block);
- FREE_AND_NULL(ti->bi.br);
-
- ti->bi.last_key.len = 0;
- ti->bi.next_off = 0;
+ block_reader_release(&ti->br);
+ block_iter_reset(&ti->bi);
}
static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off,
@@ -323,32 +306,27 @@ done:
return err;
}
-static int table_iter_next_block(struct table_iter *dest,
- struct table_iter *src)
+static void table_iter_close(struct table_iter *ti)
{
- uint64_t next_block_off = src->block_off + src->bi.br->full_block_size;
- struct block_reader br = { 0 };
- int err = 0;
+ table_iter_block_done(ti);
+ block_iter_close(&ti->bi);
+}
- dest->r = src->r;
- dest->typ = src->typ;
- dest->block_off = next_block_off;
+static int table_iter_next_block(struct table_iter *ti)
+{
+ uint64_t next_block_off = ti->block_off + ti->br.full_block_size;
+ int err;
- err = reader_init_block_reader(src->r, &br, next_block_off, src->typ);
- if (err > 0) {
- dest->is_finished = 1;
- return 1;
- }
- if (err != 0)
+ err = reader_init_block_reader(ti->r, &ti->br, next_block_off, ti->typ);
+ if (err > 0)
+ ti->is_finished = 1;
+ if (err)
return err;
- else {
- struct block_reader *brp =
- reftable_malloc(sizeof(struct block_reader));
- *brp = br;
- dest->is_finished = 0;
- block_reader_start(brp, &dest->bi);
- }
+ ti->block_off = next_block_off;
+ ti->is_finished = 0;
+ block_iter_seek_start(&ti->bi, &ti->br);
+
return 0;
}
@@ -358,27 +336,30 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
return REFTABLE_API_ERROR;
while (1) {
- struct table_iter next = TABLE_ITER_INIT;
- int err = 0;
- if (ti->is_finished) {
+ int err;
+
+ if (ti->is_finished)
return 1;
- }
+ /*
+ * Check whether the current block still has more records. If
+ * so, return it. If the iterator returns positive then the
+ * current block has been exhausted.
+ */
err = table_iter_next_in_block(ti, rec);
- if (err <= 0) {
+ if (err <= 0)
return err;
- }
- err = table_iter_next_block(&next, ti);
- if (err != 0) {
+ /*
+ * Otherwise, we need to continue to the next block in the
+ * table and retry. If there are no more blocks then the
+ * iterator is drained.
+ */
+ err = table_iter_next_block(ti);
+ if (err) {
ti->is_finished = 1;
- }
- table_iter_block_done(ti);
- if (err != 0) {
return err;
}
- table_iter_copy_from(ti, &next);
- block_iter_close(&next.bi);
}
}
@@ -387,16 +368,14 @@ static int table_iter_next_void(void *ti, struct reftable_record *rec)
return table_iter_next(ti, rec);
}
-static void table_iter_close(void *p)
+static void table_iter_close_void(void *ti)
{
- struct table_iter *ti = p;
- table_iter_block_done(ti);
- block_iter_close(&ti->bi);
+ table_iter_close(ti);
}
static struct reftable_iterator_vtable table_iter_vtable = {
.next = &table_iter_next_void,
- .close = &table_iter_close,
+ .close = &table_iter_close_void,
};
static void iterator_from_table_iter(struct reftable_iterator *it,
@@ -411,19 +390,16 @@ static int reader_table_iter_at(struct reftable_reader *r,
struct table_iter *ti, uint64_t off,
uint8_t typ)
{
- struct block_reader br = { 0 };
- struct block_reader *brp = NULL;
+ int err;
- int err = reader_init_block_reader(r, &br, off, typ);
+ err = reader_init_block_reader(r, &ti->br, off, typ);
if (err != 0)
return err;
- brp = reftable_malloc(sizeof(struct block_reader));
- *brp = br;
ti->r = r;
- ti->typ = block_reader_type(brp);
+ ti->typ = block_reader_type(&ti->br);
ti->block_off = off;
- block_reader_start(brp, &ti->bi);
+ block_iter_seek_start(&ti->bi, &ti->br);
return 0;
}
@@ -446,25 +422,54 @@ static int reader_start(struct reftable_reader *r, struct table_iter *ti,
static int reader_seek_linear(struct table_iter *ti,
struct reftable_record *want)
{
- struct reftable_record rec =
- reftable_new_record(reftable_record_type(want));
struct strbuf want_key = STRBUF_INIT;
struct strbuf got_key = STRBUF_INIT;
- struct table_iter next = TABLE_ITER_INIT;
+ struct reftable_record rec;
int err = -1;
+ reftable_record_init(&rec, reftable_record_type(want));
reftable_record_key(want, &want_key);
+ /*
+ * First we need to locate the block that must contain our record. To
+ * do so we scan through blocks linearly until we find the first block
+ * whose first key is bigger than our wanted key. Once we have found
+ * that block we know that the key must be contained in the preceding
+ * block.
+ *
+ * This algorithm is somewhat unfortunate because it means that we
+ * always have to seek one block too far and then back up. But as we
+ * can only decode the _first_ key of a block but not its _last_ key we
+ * have no other way to do this.
+ */
while (1) {
- err = table_iter_next_block(&next, ti);
+ struct table_iter next = *ti;
+
+ /*
+ * We must be careful to not modify underlying data of `ti`
+ * because we may find that `next` does not contain our desired
+ * block, but that `ti` does. In that case, we would discard
+ * `next` and continue with `ti`.
+ *
+ * This also means that we cannot reuse allocated memory for
+ * `next` here. While it would be great if we could, it should
+ * in practice not be too bad given that we should only ever
+ * end up doing linear seeks with at most three blocks. As soon
+ * as we have more than three blocks we would have an index, so
+ * we would not do a linear search there anymore.
+ */
+ memset(&next.br.block, 0, sizeof(next.br.block));
+ next.br.zstream = NULL;
+ next.br.uncompressed_data = NULL;
+ next.br.uncompressed_cap = 0;
+
+ err = table_iter_next_block(&next);
if (err < 0)
goto done;
-
- if (err > 0) {
+ if (err > 0)
break;
- }
- err = block_reader_first_key(next.bi.br, &got_key);
+ err = block_reader_first_key(&next.br, &got_key);
if (err < 0)
goto done;
@@ -474,16 +479,20 @@ static int reader_seek_linear(struct table_iter *ti,
}
table_iter_block_done(ti);
- table_iter_copy_from(ti, &next);
+ *ti = next;
}
- err = block_iter_seek(&ti->bi, &want_key);
+ /*
+ * We have located the block that must contain our record, so we seek
+ * the wanted key inside of it. If the block does not contain our key
+ * we know that the corresponding record does not exist.
+ */
+ err = block_iter_seek_key(&ti->bi, &ti->br, &want_key);
if (err < 0)
goto done;
err = 0;
done:
- block_iter_close(&next.bi);
reftable_record_release(&rec);
strbuf_release(&want_key);
strbuf_release(&got_key);
@@ -502,6 +511,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
.u.idx = { .last_key = STRBUF_INIT },
};
struct table_iter index_iter = TABLE_ITER_INIT;
+ struct table_iter empty = TABLE_ITER_INIT;
struct table_iter next = TABLE_ITER_INIT;
int err = 0;
@@ -510,10 +520,39 @@ static int reader_seek_indexed(struct reftable_reader *r,
if (err < 0)
goto done;
+ /*
+ * The index may consist of multiple levels, where each level may have
+ * multiple index blocks. We start by doing a linear search in the
+ * highest layer that identifies the relevant index block as well as
+ * the record inside that block that corresponds to our wanted key.
+ */
err = reader_seek_linear(&index_iter, &want_index);
+ if (err < 0)
+ goto done;
+
+ /*
+ * Traverse down the levels until we find a non-index entry.
+ */
while (1) {
+ /*
+ * In case we seek a record that does not exist the index iter
+ * will tell us that the iterator is over. This works because
+ * the last index entry of the current level will contain the
+ * last key it knows about. So in case our seeked key is larger
+ * than the last indexed key we know that it won't exist.
+ *
+ * There is one subtlety in the layout of the index section
+ * that makes this work as expected: the highest-level index is
+ * at end of the section and will point backwards and thus we
+ * start reading from the end of the index section, not the
+ * beginning.
+ *
+ * If that wasn't the case and the order was reversed then the
+ * linear seek would seek into the lower levels and traverse
+ * all levels of the index only to find out that the key does
+ * not exist.
+ */
err = table_iter_next(&index_iter, &index_result);
- table_iter_block_done(&index_iter);
if (err != 0)
goto done;
@@ -522,7 +561,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
if (err != 0)
goto done;
- err = block_iter_seek(&next.bi, &want_index.u.idx.last_key);
+ err = block_iter_seek_key(&next.bi, &next.br, &want_index.u.idx.last_key);
if (err < 0)
goto done;
@@ -536,19 +575,20 @@ static int reader_seek_indexed(struct reftable_reader *r,
break;
}
- table_iter_copy_from(&index_iter, &next);
+ table_iter_close(&index_iter);
+ index_iter = next;
+ next = empty;
}
if (err == 0) {
- struct table_iter empty = TABLE_ITER_INIT;
- struct table_iter *malloced =
- reftable_calloc(sizeof(struct table_iter));
- *malloced = empty;
- table_iter_copy_from(malloced, &next);
+ struct table_iter *malloced = reftable_calloc(1, sizeof(*malloced));
+ *malloced = next;
+ next = empty;
iterator_from_table_iter(it, malloced);
}
+
done:
- block_iter_close(&next.bi);
+ table_iter_close(&next);
table_iter_close(&index_iter);
reftable_record_release(&want_index);
reftable_record_release(&index_result);
@@ -562,25 +602,28 @@ static int reader_seek_internal(struct reftable_reader *r,
struct reftable_reader_offsets *offs =
reader_offsets_for(r, reftable_record_type(rec));
uint64_t idx = offs->index_offset;
- struct table_iter ti = TABLE_ITER_INIT;
- int err = 0;
+ struct table_iter ti = TABLE_ITER_INIT, *p;
+ int err;
+
if (idx > 0)
return reader_seek_indexed(r, it, rec);
err = reader_start(r, &ti, reftable_record_type(rec), 0);
if (err < 0)
- return err;
+ goto out;
+
err = reader_seek_linear(&ti, rec);
if (err < 0)
- return err;
- else {
- struct table_iter *p =
- reftable_malloc(sizeof(struct table_iter));
- *p = ti;
- iterator_from_table_iter(it, p);
- }
+ goto out;
- return 0;
+ REFTABLE_ALLOC_ARRAY(p, 1);
+ *p = ti;
+ iterator_from_table_iter(it, p);
+
+out:
+ if (err)
+ table_iter_close(&ti);
+ return err;
}
static int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
@@ -637,8 +680,7 @@ void reader_close(struct reftable_reader *r)
int reftable_new_reader(struct reftable_reader **p,
struct reftable_block_source *src, char const *name)
{
- struct reftable_reader *rd =
- reftable_calloc(sizeof(struct reftable_reader));
+ struct reftable_reader *rd = reftable_calloc(1, sizeof(*rd));
int err = init_reader(rd, src, name);
if (err == 0) {
*p = rd;
@@ -713,7 +755,7 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
uint8_t *oid)
{
struct table_iter ti_empty = TABLE_ITER_INIT;
- struct table_iter *ti = reftable_calloc(sizeof(struct table_iter));
+ struct table_iter *ti = reftable_calloc(1, sizeof(*ti));
struct filtering_ref_iterator *filter = NULL;
struct filtering_ref_iterator empty = FILTERING_REF_ITERATOR_INIT;
int oid_len = hash_size(r->hash_id);
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index 469ab79a5a..a6dbd214c5 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -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"
@@ -52,26 +51,25 @@ static void write_table(char ***names, struct strbuf *buf, int N,
.hash_id = hash_id,
};
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
struct reftable_ref_record ref = { NULL };
int i = 0, n;
struct reftable_log_record log = { NULL };
const struct reftable_stats *stats = NULL;
- *names = reftable_calloc(sizeof(char *) * (N + 1));
+
+ REFTABLE_CALLOC_ARRAY(*names, N + 1);
+
reftable_writer_set_limits(w, update_index, update_index);
for (i = 0; i < N; i++) {
- uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
char name[100];
int n;
- set_test_hash(hash, i);
-
snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
ref.refname = name;
ref.update_index = update_index;
ref.value_type = REFTABLE_REF_VAL1;
- ref.value.val1 = hash;
+ set_test_hash(ref.value.val1, i);
(*names)[i] = xstrdup(name);
n = reftable_writer_add_ref(w, &ref);
@@ -79,18 +77,15 @@ static void write_table(char ***names, struct strbuf *buf, int N,
}
for (i = 0; i < N; i++) {
- uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
char name[100];
int n;
- set_test_hash(hash, i);
-
snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
log.refname = name;
log.update_index = update_index;
log.value_type = REFTABLE_LOG_UPDATE;
- log.value.update.new_hash = hash;
+ set_test_hash(log.value.update.new_hash, i);
log.value.update.message = "message";
n = reftable_writer_add_log(w, &log);
@@ -134,18 +129,15 @@ static void test_log_buffer_size(void)
.message = "commit: 9\n",
} } };
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
/* This tests buffer extension for log compression. Must use a random
hash, to ensure that the compressed part is larger than the original.
*/
- uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
- hash1[i] = (uint8_t)(rand() % 256);
- hash2[i] = (uint8_t)(rand() % 256);
+ log.value.update.old_hash[i] = (uint8_t)(git_rand() % 256);
+ log.value.update.new_hash[i] = (uint8_t)(git_rand() % 256);
}
- log.value.update.old_hash = hash1;
- log.value.update.new_hash = hash2;
reftable_writer_set_limits(w, update_index, update_index);
err = reftable_writer_add_log(w, &log);
EXPECT_ERR(err);
@@ -163,25 +155,26 @@ static void test_log_overflow(void)
.block_size = ARRAY_SIZE(msg),
};
int err;
- struct reftable_log_record
- log = { .refname = "refs/heads/master",
- .update_index = 0xa,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = { .update = {
- .name = "Han-Wen Nienhuys",
- .email = "hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- .message = msg,
- } } };
+ struct reftable_log_record log = {
+ .refname = "refs/heads/master",
+ .update_index = 0xa,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .old_hash = { 1 },
+ .new_hash = { 2 },
+ .name = "Han-Wen Nienhuys",
+ .email = "hanwen@google.com",
+ .tz_offset = 100,
+ .time = 0x5e430672,
+ .message = msg,
+ },
+ },
+ };
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
-
- uint8_t hash1[GIT_SHA1_RAWSZ] = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
memset(msg, 'x', sizeof(msg) - 1);
- log.value.update.old_hash = hash1;
- log.value.update.new_hash = hash2;
reftable_writer_set_limits(w, update_index, update_index);
err = reftable_writer_add_log(w, &log);
EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
@@ -192,7 +185,7 @@ static void test_log_overflow(void)
static void test_log_write_read(void)
{
int N = 2;
- char **names = reftable_calloc(sizeof(char *) * (N + 1));
+ char **names = reftable_calloc(N + 1, sizeof(*names));
int err;
struct reftable_write_options opts = {
.block_size = 256,
@@ -206,7 +199,7 @@ static void test_log_write_read(void)
struct reftable_block_source source = { NULL };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
const struct reftable_stats *stats = NULL;
reftable_writer_set_limits(w, 0, N);
for (i = 0; i < N; i++) {
@@ -221,16 +214,13 @@ static void test_log_write_read(void)
EXPECT_ERR(err);
}
for (i = 0; i < N; i++) {
- uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
struct reftable_log_record log = { NULL };
- set_test_hash(hash1, i);
- set_test_hash(hash2, i + 1);
log.refname = names[i];
log.update_index = i;
log.value_type = REFTABLE_LOG_UPDATE;
- log.value.update.old_hash = hash1;
- log.value.update.new_hash = hash2;
+ set_test_hash(log.value.update.old_hash, i);
+ set_test_hash(log.value.update.new_hash, i + 1);
err = reftable_writer_add_log(w, &log);
EXPECT_ERR(err);
@@ -298,20 +288,17 @@ static void test_log_zlib_corruption(void)
struct reftable_block_source source = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
const struct reftable_stats *stats = NULL;
- uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
- uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
char message[100] = { 0 };
int err, i, n;
-
struct reftable_log_record log = {
.refname = "refname",
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
- .new_hash = hash1,
- .old_hash = hash2,
+ .new_hash = { 1 },
+ .old_hash = { 2 },
.name = "My Name",
.email = "myname@invalid",
.message = message,
@@ -320,7 +307,7 @@ 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);
@@ -523,7 +510,7 @@ static void test_table_read_write_seek_index(void)
static void test_table_refs_for(int indexed)
{
int N = 50;
- char **want_names = reftable_calloc(sizeof(char *) * (N + 1));
+ char **want_names = reftable_calloc(N + 1, sizeof(*want_names));
int want_names_len = 0;
uint8_t want_hash[GIT_SHA1_RAWSZ];
@@ -539,7 +526,7 @@ static void test_table_refs_for(int indexed)
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_iterator it = { NULL };
int j;
@@ -550,8 +537,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 +546,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 +556,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);
}
}
@@ -636,7 +619,7 @@ static void test_write_empty_table(void)
struct reftable_write_options opts = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_block_source source = { NULL };
struct reftable_reader *rd = NULL;
struct reftable_ref_record rec = { NULL };
@@ -674,12 +657,11 @@ 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};
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record ref = {
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
- .value.val1 = hash,
+ .value.val1 = {42},
};
int err;
int i;
@@ -710,12 +692,11 @@ 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};
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record ref = {
.update_index = 1,
.value_type = REFTABLE_REF_VAL1,
- .value.val1 = hash,
+ .value.val1 = {42},
};
int err;
int i;
@@ -745,7 +726,7 @@ static void test_write_empty_key(void)
struct reftable_write_options opts = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record ref = {
.refname = "",
.update_index = 1,
@@ -768,7 +749,7 @@ static void test_write_key_order(void)
struct reftable_write_options opts = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record refs[2] = {
{
.refname = "b",
@@ -798,6 +779,138 @@ 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, &noop_flush, &writer_buf, &opts);
+ reftable_writer_set_limits(writer, 1, 1);
+ for (i = 0; i < 100; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {i},
+ };
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "refs/heads/%04d", i);
+ ref.refname = buf.buf,
+
+ err = reftable_writer_add_ref(writer, &ref);
+ EXPECT_ERR(err);
+ }
+
+ for (i = 0; i < 100; i++) {
+ struct reftable_log_record log = {
+ .update_index = 1,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value.update = {
+ .old_hash = { i },
+ .new_hash = { i },
+ },
+ };
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "refs/heads/%04d", i);
+ log.refname = buf.buf,
+
+ err = reftable_writer_add_log(writer, &log);
+ EXPECT_ERR(err);
+ }
+
+ reftable_writer_close(writer);
+
+ /*
+ * The written data should be sufficiently large to result in indices
+ * for each of the block types.
+ */
+ stats = reftable_writer_stats(writer);
+ EXPECT(stats->ref_stats.index_offset > 0);
+ EXPECT(stats->obj_stats.index_offset > 0);
+ EXPECT(stats->log_stats.index_offset > 0);
+
+ block_source_from_strbuf(&source, &writer_buf);
+ err = reftable_new_reader(&reader, &source, "filename");
+ EXPECT_ERR(err);
+
+ /*
+ * Seeking the log uses the log index now. In case there is any
+ * confusion regarding indices we would notice here.
+ */
+ err = reftable_reader_seek_log(reader, &it, "");
+ EXPECT_ERR(err);
+
+ reftable_iterator_destroy(&it);
+ reftable_writer_free(writer);
+ reftable_reader_free(reader);
+ strbuf_release(&writer_buf);
+ strbuf_release(&buf);
+}
+
+static void test_write_multi_level_index(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 100,
+ };
+ struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_iterator it = { 0 };
+ const struct reftable_stats *stats;
+ struct reftable_writer *writer;
+ struct reftable_reader *reader;
+ int err;
+
+ writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts);
+ reftable_writer_set_limits(writer, 1, 1);
+ for (size_t i = 0; i < 200; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {i},
+ };
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "refs/heads/%03" PRIuMAX, (uintmax_t)i);
+ ref.refname = buf.buf,
+
+ err = reftable_writer_add_ref(writer, &ref);
+ EXPECT_ERR(err);
+ }
+ reftable_writer_close(writer);
+
+ /*
+ * The written refs should be sufficiently large to result in a
+ * multi-level index.
+ */
+ stats = reftable_writer_stats(writer);
+ EXPECT(stats->ref_stats.max_index_level == 2);
+
+ block_source_from_strbuf(&source, &writer_buf);
+ err = reftable_new_reader(&reader, &source, "filename");
+ EXPECT_ERR(err);
+
+ /*
+ * Seeking the last ref should work as expected.
+ */
+ err = reftable_reader_seek_ref(reader, &it, "refs/heads/199");
+ EXPECT_ERR(err);
+
+ reftable_iterator_destroy(&it);
+ reftable_writer_free(writer);
+ reftable_reader_free(reader);
+ strbuf_release(&writer_buf);
+ strbuf_release(&buf);
+}
+
static void test_corrupt_table_empty(void)
{
struct strbuf buf = STRBUF_INIT;
@@ -847,5 +960,7 @@ 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);
+ RUN_TEST(test_write_multi_level_index);
return 0;
}
diff --git a/reftable/record.c b/reftable/record.c
index fbaa1fbef5..5506f3e913 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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:
@@ -159,34 +159,49 @@ int reftable_encode_key(int *restart, struct string_view dest,
return start.len - dest.len;
}
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
- struct strbuf last_key, struct string_view in)
+int reftable_decode_keylen(struct string_view in,
+ uint64_t *prefix_len,
+ uint64_t *suffix_len,
+ uint8_t *extra)
{
- int start_len = in.len;
- uint64_t prefix_len = 0;
- uint64_t suffix_len = 0;
- int n = get_var_int(&prefix_len, &in);
+ size_t start_len = in.len;
+ int n;
+
+ n = get_var_int(prefix_len, &in);
if (n < 0)
return -1;
string_view_consume(&in, n);
- if (prefix_len > last_key.len)
- return -1;
-
- n = get_var_int(&suffix_len, &in);
+ n = get_var_int(suffix_len, &in);
if (n <= 0)
return -1;
string_view_consume(&in, n);
- *extra = (uint8_t)(suffix_len & 0x7);
- suffix_len >>= 3;
+ *extra = (uint8_t)(*suffix_len & 0x7);
+ *suffix_len >>= 3;
+
+ return start_len - in.len;
+}
+
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+ struct string_view in)
+{
+ int start_len = in.len;
+ uint64_t prefix_len = 0;
+ uint64_t suffix_len = 0;
+ int n;
+
+ n = reftable_decode_keylen(in, &prefix_len, &suffix_len, extra);
+ if (n < 0)
+ return -1;
+ string_view_consume(&in, n);
- if (in.len < suffix_len)
+ if (in.len < suffix_len ||
+ prefix_len > last_key->len)
return -1;
- strbuf_reset(key);
- strbuf_add(key, last_key.buf, prefix_len);
- strbuf_add(key, in.buf, suffix_len);
+ strbuf_setlen(last_key, prefix_len);
+ strbuf_add(last_key, in.buf, suffix_len);
string_view_consume(&in, suffix_len);
return start_len - in.len;
@@ -205,27 +220,36 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
{
struct reftable_ref_record *ref = rec;
const struct reftable_ref_record *src = src_rec;
+ char *refname = NULL;
+ size_t refname_cap = 0;
+
assert(hash_size > 0);
- /* This is simple and correct, but we could probably reuse the hash
- * fields. */
+ SWAP(refname, ref->refname);
+ SWAP(refname_cap, ref->refname_cap);
reftable_ref_record_release(ref);
+ SWAP(ref->refname, refname);
+ SWAP(ref->refname_cap, refname_cap);
+
if (src->refname) {
- ref->refname = xstrdup(src->refname);
+ size_t refname_len = strlen(src->refname);
+
+ REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
+ ref->refname_cap);
+ memcpy(ref->refname, src->refname, refname_len);
+ ref->refname[refname_len] = 0;
}
+
ref->update_index = src->update_index;
ref->value_type = src->value_type;
switch (src->value_type) {
case REFTABLE_REF_DELETION:
break;
case REFTABLE_REF_VAL1:
- ref->value.val1 = reftable_malloc(hash_size);
memcpy(ref->value.val1, src->value.val1, hash_size);
break;
case REFTABLE_REF_VAL2:
- ref->value.val2.value = reftable_malloc(hash_size);
memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
- ref->value.val2.target_value = reftable_malloc(hash_size);
memcpy(ref->value.val2.target_value,
src->value.val2.target_value, hash_size);
break;
@@ -242,7 +266,7 @@ 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 +323,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;
@@ -369,24 +390,33 @@ static int reftable_ref_record_encode(const void *rec, struct string_view s,
static int reftable_ref_record_decode(void *rec, struct strbuf key,
uint8_t val_type, struct string_view in,
- int hash_size)
+ int hash_size, struct strbuf *scratch)
{
struct reftable_ref_record *r = rec;
struct string_view start = in;
uint64_t update_index = 0;
- int n = get_var_int(&update_index, &in);
+ const char *refname = NULL;
+ size_t refname_cap = 0;
+ int n;
+
+ assert(hash_size > 0);
+
+ n = get_var_int(&update_index, &in);
if (n < 0)
return n;
string_view_consume(&in, n);
+ SWAP(refname, r->refname);
+ SWAP(refname_cap, r->refname_cap);
reftable_ref_record_release(r);
+ SWAP(r->refname, refname);
+ SWAP(r->refname_cap, refname_cap);
- assert(hash_size > 0);
-
- r->refname = reftable_realloc(r->refname, key.len + 1);
+ REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
memcpy(r->refname, key.buf, key.len);
- r->update_index = update_index;
r->refname[key.len] = 0;
+
+ r->update_index = update_index;
r->value_type = val_type;
switch (val_type) {
case REFTABLE_REF_VAL1:
@@ -394,7 +424,6 @@ 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,23 +433,20 @@ 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;
case REFTABLE_REF_SYMREF: {
- struct strbuf dest = STRBUF_INIT;
- int n = decode_string(&dest, in);
+ int n = decode_string(scratch, in);
if (n < 0) {
return -1;
}
string_view_consume(&in, n);
- r->value.symref = dest.buf;
+ r->value.symref = strbuf_detach(scratch, NULL);
} break;
case REFTABLE_REF_DELETION:
@@ -439,7 +465,6 @@ static int reftable_ref_record_is_deletion_void(const void *p)
(const struct reftable_ref_record *)p);
}
-
static int reftable_ref_record_equal_void(const void *a,
const void *b, int hash_size)
{
@@ -448,6 +473,13 @@ static int reftable_ref_record_equal_void(const void *a,
return reftable_ref_record_equal(ra, rb, hash_size);
}
+static int reftable_ref_record_cmp_void(const void *_a, const void *_b)
+{
+ const struct reftable_ref_record *a = _a;
+ const struct reftable_ref_record *b = _b;
+ return strcmp(a->refname, b->refname);
+}
+
static void reftable_ref_record_print_void(const void *rec,
int hash_size)
{
@@ -464,6 +496,7 @@ static struct reftable_record_vtable reftable_ref_record_vtable = {
.release = &reftable_ref_record_release_void,
.is_deletion = &reftable_ref_record_is_deletion_void,
.equal = &reftable_ref_record_equal_void,
+ .cmp = &reftable_ref_record_cmp_void,
.print = &reftable_ref_record_print_void,
};
@@ -506,12 +539,13 @@ static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
(const struct reftable_obj_record *)src_rec;
reftable_obj_record_release(obj);
- obj->hash_prefix = reftable_malloc(src->hash_prefix_len);
+
+ REFTABLE_ALLOC_ARRAY(obj->hash_prefix, src->hash_prefix_len);
obj->hash_prefix_len = src->hash_prefix_len;
if (src->hash_prefix_len)
memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
- obj->offsets = reftable_malloc(src->offset_len * sizeof(uint64_t));
+ REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
obj->offset_len = src->offset_len;
COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
}
@@ -560,7 +594,7 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
static int reftable_obj_record_decode(void *rec, struct strbuf key,
uint8_t val_type, struct string_view in,
- int hash_size)
+ int hash_size, struct strbuf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_obj_record *r = rec;
@@ -568,7 +602,10 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
int n = 0;
uint64_t last;
int j;
- r->hash_prefix = reftable_malloc(key.len);
+
+ reftable_obj_record_release(r);
+
+ REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
memcpy(r->hash_prefix, key.buf, key.len);
r->hash_prefix_len = key.len;
@@ -586,7 +623,7 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
if (count == 0)
return start.len - in.len;
- r->offsets = reftable_malloc(count * sizeof(uint64_t));
+ REFTABLE_ALLOC_ARRAY(r->offsets, count);
r->offset_len = count;
n = get_var_int(&r->offsets[0], &in);
@@ -634,6 +671,25 @@ static int reftable_obj_record_equal_void(const void *a, const void *b, int hash
return 1;
}
+static int reftable_obj_record_cmp_void(const void *_a, const void *_b)
+{
+ const struct reftable_obj_record *a = _a;
+ const struct reftable_obj_record *b = _b;
+ int cmp;
+
+ cmp = memcmp(a->hash_prefix, b->hash_prefix,
+ a->hash_prefix_len > b->hash_prefix_len ?
+ a->hash_prefix_len : b->hash_prefix_len);
+ if (cmp)
+ return cmp;
+
+ /*
+ * When the prefix is the same then the object record that is longer is
+ * considered to be bigger.
+ */
+ return a->hash_prefix_len - b->hash_prefix_len;
+}
+
static struct reftable_record_vtable reftable_obj_record_vtable = {
.key = &reftable_obj_record_key,
.type = BLOCK_TYPE_OBJ,
@@ -644,6 +700,7 @@ static struct reftable_record_vtable reftable_obj_record_vtable = {
.release = &reftable_obj_record_release,
.is_deletion = &not_a_deletion,
.equal = &reftable_obj_record_equal_void,
+ .cmp = &reftable_obj_record_cmp_void,
.print = &reftable_obj_record_print,
};
@@ -723,16 +780,10 @@ static void reftable_log_record_copy_from(void *rec, const void *src_rec,
xstrdup(dst->value.update.message);
}
- if (dst->value.update.new_hash) {
- dst->value.update.new_hash = reftable_malloc(hash_size);
- memcpy(dst->value.update.new_hash,
- src->value.update.new_hash, hash_size);
- }
- if (dst->value.update.old_hash) {
- dst->value.update.old_hash = reftable_malloc(hash_size);
- memcpy(dst->value.update.old_hash,
- src->value.update.old_hash, hash_size);
- }
+ memcpy(dst->value.update.new_hash,
+ src->value.update.new_hash, hash_size);
+ memcpy(dst->value.update.old_hash,
+ src->value.update.old_hash, hash_size);
break;
}
}
@@ -750,8 +801,6 @@ void reftable_log_record_release(struct reftable_log_record *r)
case REFTABLE_LOG_DELETION:
break;
case REFTABLE_LOG_UPDATE:
- reftable_free(r->value.update.new_hash);
- reftable_free(r->value.update.old_hash);
reftable_free(r->value.update.name);
reftable_free(r->value.update.email);
reftable_free(r->value.update.message);
@@ -768,33 +817,20 @@ static uint8_t reftable_log_record_val_type(const void *rec)
return reftable_log_record_is_deletion(log) ? 0 : 1;
}
-static uint8_t zero[GIT_SHA256_RAWSZ] = { 0 };
-
static int reftable_log_record_encode(const void *rec, struct string_view s,
int hash_size)
{
const struct reftable_log_record *r = rec;
struct string_view start = s;
int n = 0;
- uint8_t *oldh = NULL;
- uint8_t *newh = NULL;
if (reftable_log_record_is_deletion(r))
return 0;
- oldh = r->value.update.old_hash;
- newh = r->value.update.new_hash;
- if (!oldh) {
- oldh = zero;
- }
- if (!newh) {
- newh = zero;
- }
-
if (s.len < 2 * hash_size)
return -1;
- memcpy(s.buf, oldh, hash_size);
- memcpy(s.buf + hash_size, newh, hash_size);
+ memcpy(s.buf, r->value.update.old_hash, hash_size);
+ memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
string_view_consume(&s, 2 * hash_size);
n = encode_string(r->value.update.name ? r->value.update.name : "", s);
@@ -830,19 +866,18 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
static int reftable_log_record_decode(void *rec, struct strbuf key,
uint8_t val_type, struct string_view in,
- int hash_size)
+ int hash_size, struct strbuf *scratch)
{
struct string_view start = in;
struct reftable_log_record *r = rec;
uint64_t max = 0;
uint64_t ts = 0;
- struct strbuf dest = STRBUF_INIT;
int n;
if (key.len <= 9 || key.buf[key.len - 9] != 0)
return REFTABLE_FORMAT_ERROR;
- r->refname = reftable_realloc(r->refname, key.len - 8);
+ REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
memcpy(r->refname, key.buf, key.len - 8);
ts = get_be64(key.buf + key.len - 8);
@@ -851,9 +886,8 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
if (val_type != r->value_type) {
switch (r->value_type) {
case REFTABLE_LOG_UPDATE:
- FREE_AND_NULL(r->value.update.old_hash);
- FREE_AND_NULL(r->value.update.new_hash);
FREE_AND_NULL(r->value.update.message);
+ r->value.update.message_cap = 0;
FREE_AND_NULL(r->value.update.email);
FREE_AND_NULL(r->value.update.name);
break;
@@ -869,36 +903,43 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
if (in.len < 2 * hash_size)
return REFTABLE_FORMAT_ERROR;
- r->value.update.old_hash =
- reftable_realloc(r->value.update.old_hash, hash_size);
- r->value.update.new_hash =
- reftable_realloc(r->value.update.new_hash, hash_size);
-
memcpy(r->value.update.old_hash, in.buf, hash_size);
memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
string_view_consume(&in, 2 * hash_size);
- n = decode_string(&dest, in);
+ n = decode_string(scratch, in);
if (n < 0)
goto done;
string_view_consume(&in, n);
- r->value.update.name =
- reftable_realloc(r->value.update.name, dest.len + 1);
- memcpy(r->value.update.name, dest.buf, dest.len);
- r->value.update.name[dest.len] = 0;
+ /*
+ * In almost all cases we can expect the reflog name to not change for
+ * reflog entries as they are tied to the local identity, not to the
+ * target commits. As an optimization for this common case we can thus
+ * skip copying over the name in case it's accurate already.
+ */
+ if (!r->value.update.name ||
+ strcmp(r->value.update.name, scratch->buf)) {
+ r->value.update.name =
+ reftable_realloc(r->value.update.name, scratch->len + 1);
+ memcpy(r->value.update.name, scratch->buf, scratch->len);
+ r->value.update.name[scratch->len] = 0;
+ }
- strbuf_reset(&dest);
- n = decode_string(&dest, in);
+ n = decode_string(scratch, in);
if (n < 0)
goto done;
string_view_consume(&in, n);
- r->value.update.email =
- reftable_realloc(r->value.update.email, dest.len + 1);
- memcpy(r->value.update.email, dest.buf, dest.len);
- r->value.update.email[dest.len] = 0;
+ /* Same as above, but for the reflog email. */
+ if (!r->value.update.email ||
+ strcmp(r->value.update.email, scratch->buf)) {
+ r->value.update.email =
+ reftable_realloc(r->value.update.email, scratch->len + 1);
+ memcpy(r->value.update.email, scratch->buf, scratch->len);
+ r->value.update.email[scratch->len] = 0;
+ }
ts = 0;
n = get_var_int(&ts, &in);
@@ -912,22 +953,19 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
r->value.update.tz_offset = get_be16(in.buf);
string_view_consume(&in, 2);
- strbuf_reset(&dest);
- n = decode_string(&dest, in);
+ n = decode_string(scratch, in);
if (n < 0)
goto done;
string_view_consume(&in, n);
- r->value.update.message =
- reftable_realloc(r->value.update.message, dest.len + 1);
- memcpy(r->value.update.message, dest.buf, dest.len);
- r->value.update.message[dest.len] = 0;
+ REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
+ r->value.update.message_cap);
+ memcpy(r->value.update.message, scratch->buf, scratch->len);
+ r->value.update.message[scratch->len] = 0;
- strbuf_release(&dest);
return start.len - in.len;
done:
- strbuf_release(&dest);
return REFTABLE_FORMAT_ERROR;
}
@@ -943,17 +981,6 @@ static int null_streq(char *a, char *b)
return 0 == strcmp(a, b);
}
-static int zero_hash_eq(uint8_t *a, uint8_t *b, int sz)
-{
- if (!a)
- a = zero;
-
- if (!b)
- b = zero;
-
- return !memcmp(a, b, sz);
-}
-
static int reftable_log_record_equal_void(const void *a,
const void *b, int hash_size)
{
@@ -962,6 +989,22 @@ static int reftable_log_record_equal_void(const void *a,
hash_size);
}
+static int reftable_log_record_cmp_void(const void *_a, const void *_b)
+{
+ const struct reftable_log_record *a = _a;
+ const struct reftable_log_record *b = _b;
+ int cmp = strcmp(a->refname, b->refname);
+ if (cmp)
+ return cmp;
+
+ /*
+ * Note that the comparison here is reversed. This is because the
+ * update index is reversed when comparing keys. For reference, see how
+ * we handle this in reftable_log_record_key()`.
+ */
+ return b->update_index - a->update_index;
+}
+
int reftable_log_record_equal(const struct reftable_log_record *a,
const struct reftable_log_record *b, int hash_size)
{
@@ -981,10 +1024,10 @@ int reftable_log_record_equal(const struct reftable_log_record *a,
b->value.update.email) &&
null_streq(a->value.update.message,
b->value.update.message) &&
- zero_hash_eq(a->value.update.old_hash,
- b->value.update.old_hash, hash_size) &&
- zero_hash_eq(a->value.update.new_hash,
- b->value.update.new_hash, hash_size);
+ !memcmp(a->value.update.old_hash,
+ b->value.update.old_hash, hash_size) &&
+ !memcmp(a->value.update.new_hash,
+ b->value.update.new_hash, hash_size);
}
abort();
@@ -1011,6 +1054,7 @@ static struct reftable_record_vtable reftable_log_record_vtable = {
.release = &reftable_log_record_release_void,
.is_deletion = &reftable_log_record_is_deletion_void,
.equal = &reftable_log_record_equal_void,
+ .cmp = &reftable_log_record_cmp_void,
.print = &reftable_log_record_print_void,
};
@@ -1061,7 +1105,7 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
static int reftable_index_record_decode(void *rec, struct strbuf key,
uint8_t val_type, struct string_view in,
- int hash_size)
+ int hash_size, struct strbuf *scratch UNUSED)
{
struct string_view start = in;
struct reftable_index_record *r = rec;
@@ -1086,6 +1130,13 @@ static int reftable_index_record_equal(const void *a, const void *b, int hash_si
return ia->offset == ib->offset && !strbuf_cmp(&ia->last_key, &ib->last_key);
}
+static int reftable_index_record_cmp(const void *_a, const void *_b)
+{
+ const struct reftable_index_record *a = _a;
+ const struct reftable_index_record *b = _b;
+ return strbuf_cmp(&a->last_key, &b->last_key);
+}
+
static void reftable_index_record_print(const void *rec, int hash_size)
{
const struct reftable_index_record *idx = rec;
@@ -1103,6 +1154,7 @@ static struct reftable_record_vtable reftable_index_record_vtable = {
.release = &reftable_index_record_release,
.is_deletion = &not_a_deletion,
.equal = &reftable_index_record_equal,
+ .cmp = &reftable_index_record_cmp,
.print = &reftable_index_record_print,
};
@@ -1111,11 +1163,6 @@ void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
reftable_record_vtable(rec)->key(reftable_record_data(rec), dest);
}
-uint8_t reftable_record_type(struct reftable_record *rec)
-{
- return rec->type;
-}
-
int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
int hash_size)
{
@@ -1139,10 +1186,12 @@ uint8_t reftable_record_val_type(struct reftable_record *rec)
}
int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
- uint8_t extra, struct string_view src, int hash_size)
+ uint8_t extra, struct string_view src, int hash_size,
+ struct strbuf *scratch)
{
return reftable_record_vtable(rec)->decode(reftable_record_data(rec),
- key, extra, src, hash_size);
+ key, extra, src, hash_size,
+ scratch);
}
void reftable_record_release(struct reftable_record *rec)
@@ -1156,6 +1205,14 @@ int reftable_record_is_deletion(struct reftable_record *rec)
reftable_record_data(rec));
}
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b)
+{
+ if (a->type != b->type)
+ BUG("cannot compare reftable records of different type");
+ return reftable_record_vtable(a)->cmp(
+ reftable_record_data(a), reftable_record_data(b));
+}
+
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size)
{
if (a->type != b->type)
@@ -1164,7 +1221,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);
@@ -1229,12 +1286,6 @@ int reftable_log_record_is_deletion(const struct reftable_log_record *log)
return (log->value_type == REFTABLE_LOG_DELETION);
}
-void string_view_consume(struct string_view *s, int n)
-{
- s->buf += n;
- s->len -= n;
-}
-
static void *reftable_record_data(struct reftable_record *rec)
{
switch (rec->type) {
@@ -1266,45 +1317,22 @@ reftable_record_vtable(struct reftable_record *rec)
abort();
}
-struct reftable_record reftable_new_record(uint8_t typ)
+void reftable_record_init(struct reftable_record *rec, uint8_t typ)
{
- struct reftable_record clean = {
- .type = typ,
- };
+ memset(rec, 0, sizeof(*rec));
+ rec->type = typ;
- /* the following is involved, but the naive solution (just return
- * `clean` as is, except for BLOCK_TYPE_INDEX), returns a garbage
- * clean.u.obj.offsets pointer on Windows VS CI. Go figure.
- */
switch (typ) {
- case BLOCK_TYPE_OBJ:
- {
- struct reftable_obj_record obj = { 0 };
- clean.u.obj = obj;
- break;
- }
- case BLOCK_TYPE_INDEX:
- {
- struct reftable_index_record idx = {
- .last_key = STRBUF_INIT,
- };
- clean.u.idx = idx;
- break;
- }
case BLOCK_TYPE_REF:
- {
- struct reftable_ref_record ref = { 0 };
- clean.u.ref = ref;
- break;
- }
case BLOCK_TYPE_LOG:
- {
- struct reftable_log_record log = { 0 };
- clean.u.log = log;
- break;
- }
+ case BLOCK_TYPE_OBJ:
+ return;
+ case BLOCK_TYPE_INDEX:
+ strbuf_init(&rec->u.idx.last_key, 0);
+ return;
+ default:
+ BUG("unhandled record type");
}
- return clean;
}
void reftable_record_print(struct reftable_record *rec, int hash_size)
diff --git a/reftable/record.h b/reftable/record.h
index fd80cd451d..d778133e6e 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -25,7 +25,11 @@ struct string_view {
};
/* Advance `s.buf` by `n`, and decrease length. */
-void string_view_consume(struct string_view *s, int n);
+static inline void string_view_consume(struct string_view *s, int n)
+{
+ s->buf += n;
+ s->len -= n;
+}
/* utilities for de/encoding varints */
@@ -51,7 +55,8 @@ struct reftable_record_vtable {
/* decode data from `src` into the record. */
int (*decode)(void *rec, struct strbuf key, uint8_t extra,
- struct string_view src, int hash_size);
+ struct string_view src, int hash_size,
+ struct strbuf *scratch);
/* deallocate and null the record. */
void (*release)(void *rec);
@@ -62,6 +67,12 @@ struct reftable_record_vtable {
/* Are two records equal? This assumes they have the same type. Returns 0 for non-equal. */
int (*equal)(const void *a, const void *b, int hash_size);
+ /*
+ * Compare keys of two records with each other. The records must have
+ * the same type.
+ */
+ int (*cmp)(const void *a, const void *b);
+
/* Print on stdout, for debugging. */
void (*print)(const void *rec, int hash_size);
};
@@ -69,18 +80,24 @@ struct reftable_record_vtable {
/* returns true for recognized block types. Block start with the block type. */
int reftable_is_block_type(uint8_t typ);
-/* return an initialized record for the given type */
-struct reftable_record reftable_new_record(uint8_t typ);
-
/* Encode `key` into `dest`. Sets `is_restart` to indicate a restart. Returns
* number of bytes written. */
int reftable_encode_key(int *is_restart, struct string_view dest,
struct strbuf prev_key, struct strbuf key,
uint8_t extra);
-/* Decode into `key` and `extra` from `in` */
-int reftable_decode_key(struct strbuf *key, uint8_t *extra,
- struct strbuf last_key, struct string_view in);
+/* Decode a record's key lengths. */
+int reftable_decode_keylen(struct string_view in,
+ uint64_t *prefix_len,
+ uint64_t *suffix_len,
+ uint8_t *extra);
+
+/*
+ * Decode into `last_key` and `extra` from `in`. `last_key` is expected to
+ * contain the decoded key of the preceding record, if any.
+ */
+int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
+ struct string_view in);
/* reftable_index_record are used internally to speed up lookups. */
struct reftable_index_record {
@@ -100,8 +117,8 @@ struct reftable_obj_record {
/* record is a generic wrapper for different types of records. It is normally
* created on the stack, or embedded within another struct. If the type is
* known, a fresh instance can be initialized explicitly. Otherwise, use
- * reftable_new_record() to initialize generically (as the index_record is not
- * valid as 0-initialized structure)
+ * `reftable_record_init()` to initialize generically (as the index_record is
+ * not valid as 0-initialized structure)
*/
struct reftable_record {
uint8_t type;
@@ -113,11 +130,14 @@ struct reftable_record {
} u;
};
+/* Initialize the reftable record for the given type */
+void reftable_record_init(struct reftable_record *rec, uint8_t typ);
+
/* see struct record_vtable */
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, int hash_size);
void reftable_record_print(struct reftable_record *rec, int hash_size);
void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
-uint8_t reftable_record_type(struct reftable_record *rec);
void reftable_record_copy_from(struct reftable_record *rec,
struct reftable_record *src, int hash_size);
uint8_t reftable_record_val_type(struct reftable_record *rec);
@@ -125,9 +145,14 @@ int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
int hash_size);
int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
uint8_t extra, struct string_view src,
- int hash_size);
+ int hash_size, struct strbuf *scratch);
int reftable_record_is_deletion(struct reftable_record *rec);
+static inline uint8_t reftable_record_type(struct reftable_record *rec)
+{
+ return rec->type;
+}
+
/* frees and zeroes out the embedded record */
void reftable_record_release(struct reftable_record *rec);
diff --git a/reftable/record_test.c b/reftable/record_test.c
index 70ae78feca..c158ee79ff 100644
--- a/reftable/record_test.c
+++ b/reftable/record_test.c
@@ -16,11 +16,11 @@
static void test_copy(struct reftable_record *rec)
{
- struct reftable_record copy = { 0 };
+ struct reftable_record copy;
uint8_t typ;
typ = reftable_record_type(rec);
- copy = reftable_new_record(typ);
+ reftable_record_init(&copy, typ);
reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
/* do it twice to catch memory leaks */
reftable_record_copy_from(&copy, rec, GIT_SHA1_RAWSZ);
@@ -99,6 +99,7 @@ static void set_hash(uint8_t *h, int j)
static void test_reftable_ref_record_roundtrip(void)
{
+ struct strbuf scratch = STRBUF_INIT;
int i = 0;
for (i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
@@ -119,15 +120,10 @@ 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:
@@ -145,7 +141,7 @@ static void test_reftable_ref_record_roundtrip(void)
EXPECT(n > 0);
/* decode into a non-zero reftable_record to test for leaks. */
- m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ);
+ m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ, &scratch);
EXPECT(n == m);
EXPECT(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
@@ -155,6 +151,8 @@ static void test_reftable_ref_record_roundtrip(void)
strbuf_release(&key);
reftable_record_release(&out);
}
+
+ strbuf_release(&scratch);
}
static void test_reftable_log_record_equal(void)
@@ -180,7 +178,6 @@ static void test_reftable_log_record_equal(void)
static void test_reftable_log_record_roundtrip(void)
{
int i;
-
struct reftable_log_record in[] = {
{
.refname = xstrdup("refs/heads/master"),
@@ -188,8 +185,6 @@ static void test_reftable_log_record_roundtrip(void)
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
- .old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
- .new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
.name = xstrdup("han-wen"),
.email = xstrdup("hanwen@google.com"),
.message = xstrdup("test"),
@@ -207,15 +202,10 @@ static void test_reftable_log_record_roundtrip(void)
.refname = xstrdup("branch"),
.update_index = 33,
.value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
- .new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
- /* rest of fields left empty. */
- },
- },
}
};
+ struct strbuf scratch = STRBUF_INIT;
+
set_test_hash(in[0].value.update.new_hash, 1);
set_test_hash(in[0].value.update.old_hash, 2);
set_test_hash(in[2].value.update.new_hash, 3);
@@ -236,8 +226,6 @@ static void test_reftable_log_record_roundtrip(void)
.value_type = REFTABLE_LOG_UPDATE,
.value = {
.update = {
- .new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
- .old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
.name = xstrdup("old name"),
.email = xstrdup("old@email"),
.message = xstrdup("old message"),
@@ -257,7 +245,7 @@ static void test_reftable_log_record_roundtrip(void)
EXPECT(n >= 0);
valtype = reftable_record_val_type(&rec);
m = reftable_record_decode(&out, key, valtype, dest,
- GIT_SHA1_RAWSZ);
+ GIT_SHA1_RAWSZ, &scratch);
EXPECT(n == m);
EXPECT(reftable_log_record_equal(&in[i], &out.u.log,
@@ -266,6 +254,8 @@ static void test_reftable_log_record_roundtrip(void)
strbuf_release(&key);
reftable_record_release(&out);
}
+
+ strbuf_release(&scratch);
}
static void test_u24_roundtrip(void)
@@ -300,7 +290,8 @@ static void test_key_roundtrip(void)
EXPECT(!restart);
EXPECT(n > 0);
- m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
+ strbuf_addstr(&roundtrip, "refs/heads/master");
+ m = reftable_decode_key(&roundtrip, &rt_extra, dest);
EXPECT(n == m);
EXPECT(0 == strbuf_cmp(&key, &roundtrip));
EXPECT(rt_extra == extra);
@@ -314,23 +305,27 @@ static void test_reftable_obj_record_roundtrip(void)
{
uint8_t testHash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 4, 0 };
uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
- struct reftable_obj_record recs[3] = { {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 3,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 9,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- } };
+ struct reftable_obj_record recs[3] = {
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 3,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 9,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ },
+ };
+ struct strbuf scratch = STRBUF_INIT;
int i = 0;
+
for (i = 0; i < ARRAY_SIZE(recs); i++) {
uint8_t buffer[1024] = { 0 };
struct string_view dest = {
@@ -354,13 +349,15 @@ static void test_reftable_obj_record_roundtrip(void)
EXPECT(n > 0);
extra = reftable_record_val_type(&in);
m = reftable_record_decode(&out, key, extra, dest,
- GIT_SHA1_RAWSZ);
+ GIT_SHA1_RAWSZ, &scratch);
EXPECT(n == m);
EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
strbuf_release(&key);
reftable_record_release(&out);
}
+
+ strbuf_release(&scratch);
}
static void test_reftable_index_record_roundtrip(void)
@@ -377,6 +374,7 @@ static void test_reftable_index_record_roundtrip(void)
.buf = buffer,
.len = sizeof(buffer),
};
+ struct strbuf scratch = STRBUF_INIT;
struct strbuf key = STRBUF_INIT;
struct reftable_record out = {
.type = BLOCK_TYPE_INDEX,
@@ -394,13 +392,15 @@ static void test_reftable_index_record_roundtrip(void)
EXPECT(n > 0);
extra = reftable_record_val_type(&in);
- m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ);
+ m = reftable_record_decode(&out, key, extra, dest, GIT_SHA1_RAWSZ,
+ &scratch);
EXPECT(m == n);
EXPECT(reftable_record_equal(&in, &out, GIT_SHA1_RAWSZ));
reftable_record_release(&out);
strbuf_release(&key);
+ strbuf_release(&scratch);
strbuf_release(&in.u.idx.last_key);
}
diff --git a/reftable/refname.c b/reftable/refname.c
index 9573496932..bbfde15754 100644
--- a/reftable/refname.c
+++ b/reftable/refname.c
@@ -12,15 +12,15 @@
#include "refname.h"
#include "reftable-iterator.h"
-struct find_arg {
- char **names;
- const char *want;
+struct refname_needle_lesseq_args {
+ char **haystack;
+ const char *needle;
};
-static int find_name(size_t k, void *arg)
+static int refname_needle_lesseq(size_t k, void *_args)
{
- struct find_arg *f_arg = arg;
- return strcmp(f_arg->names[k], f_arg->want) >= 0;
+ struct refname_needle_lesseq_args *args = _args;
+ return strcmp(args->needle, args->haystack[k]) <= 0;
}
static int modification_has_ref(struct modification *mod, const char *name)
@@ -29,25 +29,23 @@ static int modification_has_ref(struct modification *mod, const char *name)
int err = 0;
if (mod->add_len > 0) {
- struct find_arg arg = {
- .names = mod->add,
- .want = name,
+ struct refname_needle_lesseq_args args = {
+ .haystack = mod->add,
+ .needle = name,
};
- int idx = binsearch(mod->add_len, find_name, &arg);
- if (idx < mod->add_len && !strcmp(mod->add[idx], name)) {
+ size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
+ if (idx < mod->add_len && !strcmp(mod->add[idx], name))
return 0;
- }
}
if (mod->del_len > 0) {
- struct find_arg arg = {
- .names = mod->del,
- .want = name,
+ struct refname_needle_lesseq_args args = {
+ .haystack = mod->del,
+ .needle = name,
};
- int idx = binsearch(mod->del_len, find_name, &arg);
- if (idx < mod->del_len && !strcmp(mod->del[idx], name)) {
+ size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
+ if (idx < mod->del_len && !strcmp(mod->del[idx], name))
return 1;
- }
}
err = reftable_table_read_ref(&mod->tab, name, &ref);
@@ -73,11 +71,11 @@ static int modification_has_ref_with_prefix(struct modification *mod,
int err = 0;
if (mod->add_len > 0) {
- struct find_arg arg = {
- .names = mod->add,
- .want = prefix,
+ struct refname_needle_lesseq_args args = {
+ .haystack = mod->add,
+ .needle = prefix,
};
- int idx = binsearch(mod->add_len, find_name, &arg);
+ size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
if (idx < mod->add_len &&
!strncmp(prefix, mod->add[idx], strlen(prefix)))
goto done;
@@ -92,15 +90,14 @@ static int modification_has_ref_with_prefix(struct modification *mod,
goto done;
if (mod->del_len > 0) {
- struct find_arg arg = {
- .names = mod->del,
- .want = ref.refname,
+ struct refname_needle_lesseq_args args = {
+ .haystack = mod->del,
+ .needle = ref.refname,
};
- int idx = binsearch(mod->del_len, find_name, &arg);
+ size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
if (idx < mod->del_len &&
- !strcmp(ref.refname, mod->del[idx])) {
+ !strcmp(ref.refname, mod->del[idx]))
continue;
- }
}
if (strncmp(ref.refname, prefix, strlen(prefix))) {
@@ -140,8 +137,8 @@ int validate_ref_record_addition(struct reftable_table tab,
{
struct modification mod = {
.tab = tab,
- .add = reftable_calloc(sizeof(char *) * sz),
- .del = reftable_calloc(sizeof(char *) * sz),
+ .add = reftable_calloc(sz, sizeof(*mod.add)),
+ .del = reftable_calloc(sz, sizeof(*mod.del)),
};
int i = 0;
int err = 0;
diff --git a/reftable/refname_test.c b/reftable/refname_test.c
index 8645cd93bb..b9cc62554e 100644
--- a/reftable/refname_test.c
+++ b/reftable/refname_test.c
@@ -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"
@@ -31,7 +30,7 @@ static void test_conflict(void)
struct reftable_write_options opts = { 0 };
struct strbuf buf = STRBUF_INIT;
struct reftable_writer *w =
- reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
struct reftable_ref_record rec = {
.refname = "a/b",
.value_type = REFTABLE_REF_SYMREF,
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 4c457aaaf8..e9b07c9f36 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -25,7 +25,7 @@ enum reftable_error {
*/
REFTABLE_NOT_EXIST_ERROR = -4,
- /* Trying to write out-of-date data. */
+ /* Trying to access locked data. */
REFTABLE_LOCK_ERROR = -5,
/* Misuse of the API:
@@ -57,6 +57,9 @@ enum reftable_error {
/* Entry does not fit. This can happen when writing outsize reflog
messages. */
REFTABLE_ENTRY_TOO_BIG_ERROR = -11,
+
+ /* Trying to write out-of-date data. */
+ REFTABLE_OUTDATED_ERROR = -12,
};
/* convert the numeric error code to a string. The string should not be
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 1a6d16915a..c91a2d83a2 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -33,7 +33,7 @@ struct reftable_table;
the stack array.
*/
int reftable_new_merged_table(struct reftable_merged_table **dest,
- struct reftable_table *stack, int n,
+ struct reftable_table *stack, size_t n,
uint32_t hash_id);
/* returns an iterator positioned just before 'name' */
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 67104f8fbf..2a2943cd13 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -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>
/*
@@ -21,6 +22,7 @@ https://developers.google.com/open-source/licenses/bsd
/* reftable_ref_record holds a ref database entry target_value */
struct reftable_ref_record {
char *refname; /* Name of the ref, malloced. */
+ size_t refname_cap;
uint64_t update_index; /* Logical timestamp at which this value is
* written */
@@ -38,10 +40,10 @@ 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 +51,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);
@@ -72,6 +74,7 @@ int reftable_ref_record_equal(const struct reftable_ref_record *a,
/* reftable_log_record holds a reflog entry */
struct reftable_log_record {
char *refname;
+ size_t refname_cap;
uint64_t update_index; /* logical timestamp of a transactional update.
*/
@@ -86,13 +89,14 @@ struct reftable_log_record {
union {
struct {
- uint8_t *new_hash;
- uint8_t *old_hash;
+ unsigned char new_hash[GIT_MAX_RAWSZ];
+ unsigned char old_hash[GIT_MAX_RAWSZ];
char *name;
char *email;
uint64_t time;
int16_t tz_offset;
char *message;
+ size_t message_cap;
} update;
} value;
};
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index db8de197f6..155bf0bbe2 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -46,6 +46,9 @@ struct reftable_write_options {
* is a single line, and add '\n' if missing.
*/
unsigned exact_log_message : 1;
+
+ /* boolean: Prevent auto-compaction of tables. */
+ unsigned disable_auto_compact : 1;
};
/* reftable_block_stats holds statistics for a single block type */
@@ -88,6 +91,7 @@ struct reftable_stats {
/* reftable_new_writer creates a new writer */
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
+ int (*flush_func)(void *),
void *writer_arg, struct reftable_write_options *opts);
/* Set the range of update indices for the records we will add. When writing a
diff --git a/reftable/stack.c b/reftable/stack.c
index ddbdf1b9c8..80266bcbab 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -8,6 +8,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "stack.h"
+#include "../write-or-die.h"
#include "system.h"
#include "merged.h"
#include "reader.h"
@@ -16,13 +17,15 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-record.h"
#include "reftable-merged.h"
#include "writer.h"
+#include "tempfile.h"
static int stack_try_add(struct reftable_stack *st,
int (*write_table)(struct reftable_writer *wr,
void *arg),
void *arg);
static int stack_write_compact(struct reftable_stack *st,
- struct reftable_writer *wr, int first, int last,
+ struct reftable_writer *wr,
+ size_t first, size_t last,
struct reftable_log_expiry_config *config);
static int stack_check_addition(struct reftable_stack *st,
const char *new_tab_name);
@@ -42,14 +45,20 @@ static 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);
+}
+
+static int reftable_fd_flush(void *arg)
+{
+ int *fdp = (int *)arg;
+
+ return fsync_component(FSYNC_COMPONENT_REFERENCE, *fdp);
}
int reftable_new_stack(struct reftable_stack **dest, const char *dir,
struct reftable_write_options config)
{
- struct reftable_stack *p =
- reftable_calloc(sizeof(struct reftable_stack));
+ struct reftable_stack *p = reftable_calloc(1, sizeof(*p));
struct strbuf list_file_name = STRBUF_INIT;
int err = 0;
@@ -64,6 +73,7 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir,
strbuf_addstr(&list_file_name, "/tables.list");
p->list_file = strbuf_detach(&list_file_name, NULL);
+ p->list_fd = -1;
p->reftable_dir = xstrdup(dir);
p->config = config;
@@ -91,8 +101,8 @@ static int fd_read_lines(int fd, char ***namesp)
goto done;
}
- buf = reftable_malloc(size + 1);
- if (read(fd, buf, size) != size) {
+ REFTABLE_ALLOC_ARRAY(buf, size + 1);
+ if (read_in_full(fd, buf, size) != size) {
err = REFTABLE_IO_ERROR;
goto done;
}
@@ -111,7 +121,7 @@ int read_lines(const char *filename, char ***namesp)
int err = 0;
if (fd < 0) {
if (errno == ENOENT) {
- *namesp = reftable_calloc(sizeof(char *));
+ REFTABLE_CALLOC_ARRAY(*namesp, 1);
return 0;
}
@@ -173,6 +183,12 @@ void reftable_stack_destroy(struct reftable_stack *st)
st->readers_len = 0;
FREE_AND_NULL(st->readers);
}
+
+ if (st->list_fd >= 0) {
+ close(st->list_fd);
+ st->list_fd = -1;
+ }
+
FREE_AND_NULL(st->list_file);
FREE_AND_NULL(st->reftable_dir);
reftable_free(st);
@@ -182,8 +198,7 @@ void reftable_stack_destroy(struct reftable_stack *st)
static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
int cur_len)
{
- struct reftable_reader **cur =
- reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
+ struct reftable_reader **cur = reftable_calloc(cur_len, sizeof(*cur));
int i = 0;
for (i = 0; i < cur_len; i++) {
cur[i] = st->readers[i];
@@ -194,17 +209,18 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
int reuse_open)
{
- int cur_len = !st->merged ? 0 : st->merged->stack_len;
+ size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
struct reftable_reader **cur = stack_copy_readers(st, cur_len);
- int err = 0;
- int names_len = names_length(names);
+ size_t names_len = names_length(names);
struct reftable_reader **new_readers =
- reftable_calloc(sizeof(struct reftable_reader *) * names_len);
+ reftable_calloc(names_len, sizeof(*new_readers));
struct reftable_table *new_tables =
- reftable_calloc(sizeof(struct reftable_table) * names_len);
- int new_readers_len = 0;
+ reftable_calloc(names_len, sizeof(*new_tables));
+ size_t new_readers_len = 0;
struct reftable_merged_table *new_merged = NULL;
- int i;
+ struct strbuf table_path = STRBUF_INIT;
+ int err = 0;
+ size_t i;
while (*names) {
struct reftable_reader *rd = NULL;
@@ -212,24 +228,20 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
/* this is linear; we assume compaction keeps the number of
tables under control so this is not quadratic. */
- int j = 0;
- for (j = 0; reuse_open && j < cur_len; j++) {
- if (cur[j] && 0 == strcmp(cur[j]->name, name)) {
- rd = cur[j];
- cur[j] = NULL;
+ for (i = 0; reuse_open && i < cur_len; i++) {
+ if (cur[i] && 0 == strcmp(cur[i]->name, name)) {
+ rd = cur[i];
+ cur[i] = NULL;
break;
}
}
if (!rd) {
struct reftable_block_source src = { NULL };
- struct strbuf table_path = STRBUF_INIT;
stack_filename(&table_path, st, name);
err = reftable_block_source_from_file(&src,
table_path.buf);
- strbuf_release(&table_path);
-
if (err < 0)
goto done;
@@ -267,16 +279,13 @@ 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 +297,7 @@ done:
reftable_free(new_readers);
reftable_free(new_tables);
reftable_free(cur);
+ strbuf_release(&table_path);
return err;
}
@@ -306,69 +316,134 @@ static int tv_cmp(struct timeval *a, struct timeval *b)
static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
int reuse_open)
{
- struct timeval deadline = { 0 };
- int err = gettimeofday(&deadline, NULL);
+ char **names = NULL, **names_after = NULL;
+ struct timeval deadline;
int64_t delay = 0;
- int tries = 0;
- if (err < 0)
- return err;
+ int tries = 0, err;
+ int fd = -1;
+ err = gettimeofday(&deadline, NULL);
+ if (err < 0)
+ goto out;
deadline.tv_sec += 3;
+
while (1) {
- char **names = NULL;
- char **names_after = NULL;
- struct timeval now = { 0 };
- int err = gettimeofday(&now, NULL);
- int err2 = 0;
- if (err < 0) {
- return err;
- }
+ struct timeval now;
+
+ err = gettimeofday(&now, NULL);
+ if (err < 0)
+ goto out;
- /* Only look at deadlines after the first few times. This
- simplifies debugging in GDB */
+ /*
+ * Only look at deadlines after the first few times. This
+ * simplifies debugging in GDB.
+ */
tries++;
- if (tries > 3 && tv_cmp(&now, &deadline) >= 0) {
- break;
- }
+ if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
+ goto out;
- err = read_lines(st->list_file, &names);
- if (err < 0) {
- free_names(names);
- return err;
- }
- err = reftable_stack_reload_once(st, names, reuse_open);
- if (err == 0) {
- free_names(names);
- break;
- }
- if (err != REFTABLE_NOT_EXIST_ERROR) {
- free_names(names);
- return err;
- }
+ fd = open(st->list_file, O_RDONLY);
+ if (fd < 0) {
+ if (errno != ENOENT) {
+ err = REFTABLE_IO_ERROR;
+ goto out;
+ }
- /* err == REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
- writer. Check if there was one by checking if the name list
- changed.
- */
- err2 = read_lines(st->list_file, &names_after);
- if (err2 < 0) {
- free_names(names);
- return err2;
+ REFTABLE_CALLOC_ARRAY(names, 1);
+ } else {
+ err = fd_read_lines(fd, &names);
+ if (err < 0)
+ goto out;
}
+ err = reftable_stack_reload_once(st, names, reuse_open);
+ if (!err)
+ break;
+ if (err != REFTABLE_NOT_EXIST_ERROR)
+ goto out;
+
+ /*
+ * REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
+ * writer. Check if there was one by checking if the name list
+ * changed.
+ */
+ err = read_lines(st->list_file, &names_after);
+ if (err < 0)
+ goto out;
if (names_equal(names_after, names)) {
- free_names(names);
- free_names(names_after);
- return err;
+ err = REFTABLE_NOT_EXIST_ERROR;
+ goto out;
}
+
free_names(names);
+ names = NULL;
free_names(names_after);
+ names_after = NULL;
+ close(fd);
+ fd = -1;
delay = delay + (delay * rand()) / RAND_MAX + 1;
sleep_millisec(delay);
}
- return 0;
+out:
+ /*
+ * Invalidate the stat cache. It is sufficient to only close the file
+ * descriptor and keep the cached stat info because we never use the
+ * latter when the former is negative.
+ */
+ if (st->list_fd >= 0) {
+ close(st->list_fd);
+ st->list_fd = -1;
+ }
+
+ /*
+ * Cache stat information in case it provides a useful signal to us.
+ * According to POSIX, "The st_ino and st_dev fields taken together
+ * uniquely identify the file within the system." That being said,
+ * Windows is not POSIX compliant and we do not have these fields
+ * available. So the information we have there is insufficient to
+ * determine whether two file descriptors point to the same file.
+ *
+ * While we could fall back to using other signals like the file's
+ * mtime, those are not sufficient to avoid races. We thus refrain from
+ * using the stat cache on such systems and fall back to the secondary
+ * caching mechanism, which is to check whether contents of the file
+ * have changed.
+ *
+ * On other systems which are POSIX compliant we must keep the file
+ * descriptor open. This is to avoid a race condition where two
+ * processes access the reftable stack at the same point in time:
+ *
+ * 1. A reads the reftable stack and caches its stat info.
+ *
+ * 2. B updates the stack, appending a new table to "tables.list".
+ * This will both use a new inode and result in a different file
+ * size, thus invalidating A's cache in theory.
+ *
+ * 3. B decides to auto-compact the stack and merges two tables. The
+ * file size now matches what A has cached again. Furthermore, the
+ * filesystem may decide to recycle the inode number of the file
+ * we have replaced in (2) because it is not in use anymore.
+ *
+ * 4. A reloads the reftable stack. Neither the inode number nor the
+ * file size changed. If the timestamps did not change either then
+ * we think the cached copy of our stack is up-to-date.
+ *
+ * By keeping the file descriptor open the inode number cannot be
+ * recycled, mitigating the race.
+ */
+ if (!err && fd >= 0 && !fstat(fd, &st->list_st) &&
+ st->list_st.st_dev && st->list_st.st_ino) {
+ st->list_fd = fd;
+ fd = -1;
+ }
+
+ if (fd >= 0)
+ close(fd);
+ free_names(names);
+ free_names(names_after);
+ return err;
}
/* -1 = error
@@ -377,8 +452,44 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
static int stack_uptodate(struct reftable_stack *st)
{
char **names = NULL;
- int err = read_lines(st->list_file, &names);
+ int err;
int i = 0;
+
+ /*
+ * When we have cached stat information available then we use it to
+ * verify whether the file has been rewritten.
+ *
+ * Note that we explicitly do not want to use `stat_validity_check()`
+ * and friends here because they may end up not comparing the `st_dev`
+ * and `st_ino` fields. These functions thus cannot guarantee that we
+ * indeed still have the same file.
+ */
+ if (st->list_fd >= 0) {
+ struct stat list_st;
+
+ if (stat(st->list_file, &list_st) < 0) {
+ /*
+ * It's fine for "tables.list" to not exist. In that
+ * case, we have to refresh when the loaded stack has
+ * any readers.
+ */
+ if (errno == ENOENT)
+ return !!st->readers_len;
+ return REFTABLE_IO_ERROR;
+ }
+
+ /*
+ * When "tables.list" refers to the same file we can assume
+ * that it didn't change. This is because we always use
+ * rename(3P) to update the file and never write to it
+ * directly.
+ */
+ if (st->list_st.st_dev == list_st.st_dev &&
+ st->list_st.st_ino == list_st.st_ino)
+ return 0;
+ }
+
+ err = read_lines(st->list_file, &names);
if (err < 0)
return err;
@@ -418,25 +529,22 @@ int reftable_stack_add(struct reftable_stack *st,
{
int err = stack_try_add(st, write, arg);
if (err < 0) {
- if (err == REFTABLE_LOCK_ERROR) {
+ if (err == REFTABLE_OUTDATED_ERROR) {
/* Ignore error return, we want to propagate
- REFTABLE_LOCK_ERROR.
+ REFTABLE_OUTDATED_ERROR.
*/
reftable_stack_reload(st);
}
return err;
}
- if (!st->disable_auto_compact)
- return reftable_stack_auto_compact(st);
-
return 0;
}
static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
{
char buf[100];
- uint32_t rnd = (uint32_t)rand();
+ uint32_t rnd = (uint32_t)git_rand();
snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
min, max, rnd);
strbuf_reset(dest);
@@ -444,33 +552,27 @@ 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;
- int new_tables_len;
+ size_t new_tables_len, new_tables_cap;
uint64_t next_update_index;
};
-#define REFTABLE_ADDITION_INIT \
- { \
- .lock_file_name = STRBUF_INIT \
- }
+#define REFTABLE_ADDITION_INIT {0}
static int reftable_stack_init_addition(struct reftable_addition *add,
struct reftable_stack *st)
{
+ struct strbuf lock_file_name = STRBUF_INIT;
int err = 0;
add->stack = st;
- strbuf_reset(&add->lock_file_name);
- strbuf_addstr(&add->lock_file_name, st->list_file);
- strbuf_addstr(&add->lock_file_name, ".lock");
+ strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
- add->lock_file_fd = open(add->lock_file_name.buf,
- O_EXCL | O_CREAT | O_WRONLY, 0666);
- if (add->lock_file_fd < 0) {
+ add->lock_file = create_tempfile(lock_file_name.buf);
+ if (!add->lock_file) {
if (errno == EEXIST) {
err = REFTABLE_LOCK_ERROR;
} else {
@@ -479,7 +581,7 @@ 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;
}
@@ -488,9 +590,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
err = stack_uptodate(st);
if (err < 0)
goto done;
-
- if (err > 1) {
- err = REFTABLE_LOCK_ERROR;
+ if (err > 0) {
+ err = REFTABLE_OUTDATED_ERROR;
goto done;
}
@@ -499,13 +600,15 @@ done:
if (err) {
reftable_addition_close(add);
}
+ strbuf_release(&lock_file_name);
return err;
}
static void reftable_addition_close(struct reftable_addition *add)
{
- int i = 0;
struct strbuf nm = STRBUF_INIT;
+ size_t i;
+
for (i = 0; i < add->new_tables_len; i++) {
stack_filename(&nm, add->stack, add->new_tables[i]);
unlink(nm.buf);
@@ -515,16 +618,9 @@ static void reftable_addition_close(struct reftable_addition *add)
reftable_free(add->new_tables);
add->new_tables = NULL;
add->new_tables_len = 0;
+ add->new_tables_cap = 0;
- if (add->lock_file_fd > 0) {
- close(add->lock_file_fd);
- add->lock_file_fd = 0;
- }
- if (add->lock_file_name.len > 0) {
- unlink(add->lock_file_name.buf);
- strbuf_release(&add->lock_file_name);
- }
-
+ delete_tempfile(&add->lock_file);
strbuf_release(&nm);
}
@@ -540,8 +636,10 @@ void reftable_addition_destroy(struct reftable_addition *add)
int reftable_addition_commit(struct reftable_addition *add)
{
struct strbuf table_list = STRBUF_INIT;
- int i = 0;
+ int lock_file_fd = get_tempfile_fd(add->lock_file);
int err = 0;
+ size_t i;
+
if (add->new_tables_len == 0)
goto done;
@@ -554,36 +652,48 @@ 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;
- }
+ fsync_component_or_die(FSYNC_COMPONENT_REFERENCE, lock_file_fd,
+ get_tempfile_path(add->lock_file));
- err = rename(add->lock_file_name.buf, add->stack->list_file);
+ err = rename_tempfile(&add->lock_file, add->stack->list_file);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
/* success, no more state to clean up. */
- strbuf_release(&add->lock_file_name);
- for (i = 0; i < add->new_tables_len; i++) {
+ for (i = 0; i < add->new_tables_len; i++)
reftable_free(add->new_tables[i]);
- }
reftable_free(add->new_tables);
add->new_tables = NULL;
add->new_tables_len = 0;
+ add->new_tables_cap = 0;
+
+ err = reftable_stack_reload_maybe_reuse(add->stack, 1);
+ if (err)
+ goto done;
+
+ if (!add->stack->config.disable_auto_compact) {
+ /*
+ * Auto-compact the stack to keep the number of tables in
+ * control. It is possible that a concurrent writer is already
+ * trying to compact parts of the stack, which would lead to a
+ * `REFTABLE_LOCK_ERROR` because parts of the stack are locked
+ * already. This is a benign error though, so we ignore it.
+ */
+ err = reftable_stack_auto_compact(add->stack);
+ if (err < 0 && err != REFTABLE_LOCK_ERROR)
+ goto done;
+ err = 0;
+ }
- err = reftable_stack_reload(add->stack);
done:
reftable_addition_close(add);
return err;
@@ -594,7 +704,7 @@ int reftable_stack_new_addition(struct reftable_addition **dest,
{
int err = 0;
struct reftable_addition empty = REFTABLE_ADDITION_INIT;
- *dest = reftable_calloc(sizeof(**dest));
+ REFTABLE_CALLOC_ARRAY(*dest, 1);
**dest = empty;
err = reftable_stack_init_addition(*dest, st);
if (err) {
@@ -613,10 +723,6 @@ static int stack_try_add(struct reftable_stack *st,
int err = reftable_stack_init_addition(&add, st);
if (err < 0)
goto done;
- if (err > 0) {
- err = REFTABLE_LOCK_ERROR;
- goto done;
- }
err = reftable_addition_add(&add, write_table, arg);
if (err < 0)
@@ -637,8 +743,9 @@ int reftable_addition_add(struct reftable_addition *add,
struct strbuf tab_file_name = STRBUF_INIT;
struct strbuf next_name = STRBUF_INIT;
struct reftable_writer *wr = NULL;
+ struct tempfile *tab_file = NULL;
int err = 0;
- int tab_fd = 0;
+ int tab_fd;
strbuf_reset(&next_name);
format_name(&next_name, add->next_update_index, add->next_update_index);
@@ -646,18 +753,21 @@ int reftable_addition_add(struct reftable_addition *add,
stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
- tab_fd = mkstemp(temp_tab_file_name.buf);
- if (tab_fd < 0) {
+ tab_file = mks_tempfile(temp_tab_file_name.buf);
+ if (!tab_file) {
err = REFTABLE_IO_ERROR;
goto done;
}
if (add->stack->config.default_permissions) {
- if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
+ if (chmod(get_tempfile_path(tab_file),
+ add->stack->config.default_permissions)) {
err = REFTABLE_IO_ERROR;
goto done;
}
}
- wr = reftable_new_writer(reftable_fd_write, &tab_fd,
+ tab_fd = get_tempfile_fd(tab_file);
+
+ wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
&add->stack->config);
err = write_table(wr, arg);
if (err < 0)
@@ -671,14 +781,13 @@ int reftable_addition_add(struct reftable_addition *add,
if (err < 0)
goto done;
- err = close(tab_fd);
- tab_fd = 0;
+ err = close_tempfile_gently(tab_file);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
- err = stack_check_addition(add->stack, temp_tab_file_name.buf);
+ err = stack_check_addition(add->stack, get_tempfile_path(tab_file));
if (err < 0)
goto done;
@@ -689,33 +798,23 @@ int reftable_addition_add(struct reftable_addition *add,
format_name(&next_name, wr->min_update_index, wr->max_update_index);
strbuf_addstr(&next_name, ".ref");
-
stack_filename(&tab_file_name, add->stack, next_name.buf);
/*
On windows, this relies on rand() picking a unique destination name.
Maybe we should do retry loop as well?
*/
- err = rename(temp_tab_file_name.buf, tab_file_name.buf);
+ err = rename_tempfile(&tab_file, tab_file_name.buf);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
- add->new_tables = reftable_realloc(add->new_tables,
- sizeof(*add->new_tables) *
- (add->new_tables_len + 1));
- add->new_tables[add->new_tables_len] = strbuf_detach(&next_name, NULL);
- add->new_tables_len++;
+ REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
+ add->new_tables_cap);
+ add->new_tables[add->new_tables_len++] = strbuf_detach(&next_name, NULL);
done:
- if (tab_fd > 0) {
- close(tab_fd);
- tab_fd = 0;
- }
- if (temp_tab_file_name.len > 0) {
- unlink(temp_tab_file_name.buf);
- }
-
+ delete_tempfile(&tab_file);
strbuf_release(&temp_tab_file_name);
strbuf_release(&tab_file_name);
strbuf_release(&next_name);
@@ -732,66 +831,77 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
return 1;
}
-static int stack_compact_locked(struct reftable_stack *st, int first, int last,
- struct strbuf *temp_tab,
- struct reftable_log_expiry_config *config)
+static int stack_compact_locked(struct reftable_stack *st,
+ size_t first, size_t last,
+ struct reftable_log_expiry_config *config,
+ struct tempfile **tab_file_out)
{
struct strbuf next_name = STRBUF_INIT;
- int tab_fd = -1;
+ struct strbuf tab_file_path = STRBUF_INIT;
struct reftable_writer *wr = NULL;
- int err = 0;
+ struct tempfile *tab_file;
+ int tab_fd, err = 0;
format_name(&next_name,
reftable_reader_min_update_index(st->readers[first]),
reftable_reader_max_update_index(st->readers[last]));
+ stack_filename(&tab_file_path, st, next_name.buf);
+ strbuf_addstr(&tab_file_path, ".temp.XXXXXX");
- stack_filename(temp_tab, st, next_name.buf);
- strbuf_addstr(temp_tab, ".temp.XXXXXX");
+ tab_file = mks_tempfile(tab_file_path.buf);
+ if (!tab_file) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+ tab_fd = get_tempfile_fd(tab_file);
- tab_fd = mkstemp(temp_tab->buf);
- wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config);
+ if (st->config.default_permissions &&
+ chmod(get_tempfile_path(tab_file), st->config.default_permissions) < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+ wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush,
+ &tab_fd, &st->config);
err = stack_write_compact(st, wr, first, last, config);
if (err < 0)
goto done;
+
err = reftable_writer_close(wr);
if (err < 0)
goto done;
- err = close(tab_fd);
- tab_fd = 0;
+ err = close_tempfile_gently(tab_file);
+ if (err < 0)
+ goto done;
+
+ *tab_file_out = tab_file;
+ tab_file = NULL;
done:
+ delete_tempfile(&tab_file);
reftable_writer_free(wr);
- if (tab_fd > 0) {
- close(tab_fd);
- tab_fd = 0;
- }
- if (err != 0 && temp_tab->len > 0) {
- unlink(temp_tab->buf);
- strbuf_release(temp_tab);
- }
strbuf_release(&next_name);
+ strbuf_release(&tab_file_path);
return err;
}
static int stack_write_compact(struct reftable_stack *st,
- struct reftable_writer *wr, int first, int last,
+ struct reftable_writer *wr,
+ size_t first, size_t last,
struct reftable_log_expiry_config *config)
{
- int subtabs_len = last - first + 1;
+ size_t subtabs_len = last - first + 1;
struct reftable_table *subtabs = reftable_calloc(
- sizeof(struct reftable_table) * (last - first + 1));
+ last - first + 1, sizeof(*subtabs));
struct reftable_merged_table *mt = NULL;
- int err = 0;
struct reftable_iterator it = { NULL };
struct reftable_ref_record ref = { NULL };
struct reftable_log_record log = { NULL };
-
uint64_t entries = 0;
+ int err = 0;
- int i = 0, j = 0;
- for (i = first, j = 0; i <= last; i++) {
+ for (size_t i = first, j = 0; i <= last; i++) {
struct reftable_reader *t = st->readers[i];
reftable_table_from_reader(&subtabs[j++], t);
st->stats.bytes += t->size;
@@ -816,18 +926,16 @@ 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 +950,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 +967,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++;
}
@@ -878,27 +984,28 @@ done:
return err;
}
-/* < 0: error. 0 == OK, > 0 attempt failed; could retry. */
-static int stack_compact_range(struct reftable_stack *st, int first, int last,
+/*
+ * Compact all tables in the range `[first, last)` into a single new table.
+ *
+ * This function returns `0` on success or a code `< 0` on failure. When the
+ * stack or any of the tables in the specified range are already locked then
+ * this function returns `REFTABLE_LOCK_ERROR`. This is a benign error that
+ * callers can either ignore, or they may choose to retry compaction after some
+ * amount of time.
+ */
+static int stack_compact_range(struct reftable_stack *st,
+ size_t first, size_t last,
struct reftable_log_expiry_config *expiry)
{
- struct strbuf temp_tab_file_name = STRBUF_INIT;
+ struct strbuf tables_list_buf = STRBUF_INIT;
struct strbuf new_table_name = STRBUF_INIT;
- struct strbuf lock_file_name = STRBUF_INIT;
- struct strbuf ref_list_contents = STRBUF_INIT;
struct strbuf new_table_path = STRBUF_INIT;
- int err = 0;
- int have_lock = 0;
- int lock_file_fd = -1;
- int compact_count = last - first + 1;
- char **listp = NULL;
- char **delete_on_success =
- reftable_calloc(sizeof(char *) * (compact_count + 1));
- char **subtable_locks =
- reftable_calloc(sizeof(char *) * (compact_count + 1));
- int i = 0;
- int j = 0;
- int is_empty_table = 0;
+ struct strbuf table_name = STRBUF_INIT;
+ struct lock_file tables_list_lock = LOCK_INIT;
+ struct lock_file *table_locks = NULL;
+ struct tempfile *new_table = NULL;
+ int is_empty_table = 0, err = 0;
+ size_t i;
if (first > last || (!expiry && first == last)) {
err = 0;
@@ -907,196 +1014,200 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
st->stats.attempts++;
- strbuf_reset(&lock_file_name);
- strbuf_addstr(&lock_file_name, st->list_file);
- strbuf_addstr(&lock_file_name, ".lock");
-
- lock_file_fd =
- open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
- if (lock_file_fd < 0) {
- if (errno == EEXIST) {
- err = 1;
- } else {
+ /*
+ * Hold the lock so that we can read "tables.list" and lock all tables
+ * which are part of the user-specified range.
+ */
+ err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+ LOCK_NO_DEREF);
+ if (err < 0) {
+ if (errno == EEXIST)
+ err = REFTABLE_LOCK_ERROR;
+ else
err = REFTABLE_IO_ERROR;
- }
goto done;
}
- /* Don't want to write to the lock for now. */
- close(lock_file_fd);
- lock_file_fd = -1;
- have_lock = 1;
err = stack_uptodate(st);
- if (err != 0)
+ if (err)
goto done;
- for (i = first, j = 0; i <= last; i++) {
- struct strbuf subtab_file_name = STRBUF_INIT;
- struct strbuf subtab_lock = STRBUF_INIT;
- int sublock_file_fd = -1;
-
- stack_filename(&subtab_file_name, st,
- reader_name(st->readers[i]));
-
- strbuf_reset(&subtab_lock);
- strbuf_addbuf(&subtab_lock, &subtab_file_name);
- strbuf_addstr(&subtab_lock, ".lock");
-
- sublock_file_fd = open(subtab_lock.buf,
- O_EXCL | O_CREAT | O_WRONLY, 0666);
- if (sublock_file_fd >= 0) {
- close(sublock_file_fd);
- } else if (sublock_file_fd < 0) {
- if (errno == EEXIST) {
- err = 1;
- } else {
+ /*
+ * Lock all tables in the user-provided range. This is the slice of our
+ * stack which we'll compact.
+ */
+ REFTABLE_CALLOC_ARRAY(table_locks, last - first + 1);
+ for (i = first; i <= last; i++) {
+ stack_filename(&table_name, st, reader_name(st->readers[i]));
+
+ err = hold_lock_file_for_update(&table_locks[i - first],
+ table_name.buf, LOCK_NO_DEREF);
+ if (err < 0) {
+ if (errno == EEXIST)
+ err = REFTABLE_LOCK_ERROR;
+ else
err = REFTABLE_IO_ERROR;
- }
+ goto done;
}
- subtable_locks[j] = subtab_lock.buf;
- delete_on_success[j] = subtab_file_name.buf;
- j++;
-
- if (err != 0)
+ /*
+ * We need to close the lockfiles as we might otherwise easily
+ * run into file descriptor exhaustion when we compress a lot
+ * of tables.
+ */
+ err = close_lock_file_gently(&table_locks[i - first]);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
goto done;
+ }
}
- err = unlink(lock_file_name.buf);
- if (err < 0)
+ /*
+ * We have locked all tables in our range and can thus release the
+ * "tables.list" lock while compacting the locked tables. This allows
+ * concurrent updates to the stack to proceed.
+ */
+ err = rollback_lock_file(&tables_list_lock);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
goto done;
- have_lock = 0;
-
- err = stack_compact_locked(st, first, last, &temp_tab_file_name,
- expiry);
- /* Compaction + tombstones can create an empty table out of non-empty
- * tables. */
- is_empty_table = (err == REFTABLE_EMPTY_TABLE_ERROR);
- if (is_empty_table) {
- err = 0;
}
- if (err < 0)
- goto done;
- lock_file_fd =
- open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
- if (lock_file_fd < 0) {
- if (errno == EEXIST) {
- err = 1;
- } else {
+ /*
+ * Compact the now-locked tables into a new table. Note that compacting
+ * these tables may end up with an empty new table in case tombstones
+ * end up cancelling out all refs in that range.
+ */
+ err = stack_compact_locked(st, first, last, expiry, &new_table);
+ if (err < 0) {
+ if (err != REFTABLE_EMPTY_TABLE_ERROR)
+ goto done;
+ is_empty_table = 1;
+ }
+
+ /*
+ * Now that we have written the new, compacted table we need to re-lock
+ * "tables.list". We'll then replace the compacted range of tables with
+ * the new table.
+ */
+ err = hold_lock_file_for_update(&tables_list_lock, st->list_file,
+ LOCK_NO_DEREF);
+ if (err < 0) {
+ if (errno == EEXIST)
+ err = REFTABLE_LOCK_ERROR;
+ else
err = REFTABLE_IO_ERROR;
- }
goto done;
}
- have_lock = 1;
+
if (st->config.default_permissions) {
- if (chmod(lock_file_name.buf, st->config.default_permissions) < 0) {
+ if (chmod(get_lock_file_path(&tables_list_lock),
+ st->config.default_permissions) < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
}
- format_name(&new_table_name, st->readers[first]->min_update_index,
- st->readers[last]->max_update_index);
- strbuf_addstr(&new_table_name, ".ref");
-
- stack_filename(&new_table_path, st, new_table_name.buf);
-
+ /*
+ * If the resulting compacted table is not empty, then we need to move
+ * it into place now.
+ */
if (!is_empty_table) {
- /* retry? */
- err = rename(temp_tab_file_name.buf, new_table_path.buf);
+ format_name(&new_table_name, st->readers[first]->min_update_index,
+ st->readers[last]->max_update_index);
+ strbuf_addstr(&new_table_name, ".ref");
+ stack_filename(&new_table_path, st, new_table_name.buf);
+
+ err = rename_tempfile(&new_table, new_table_path.buf);
if (err < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
}
- for (i = 0; i < first; i++) {
- strbuf_addstr(&ref_list_contents, st->readers[i]->name);
- strbuf_addstr(&ref_list_contents, "\n");
- }
- if (!is_empty_table) {
- strbuf_addbuf(&ref_list_contents, &new_table_name);
- strbuf_addstr(&ref_list_contents, "\n");
- }
- for (i = last + 1; i < st->merged->stack_len; i++) {
- strbuf_addstr(&ref_list_contents, st->readers[i]->name);
- strbuf_addstr(&ref_list_contents, "\n");
- }
-
- err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
+ /*
+ * Write the new "tables.list" contents with the compacted table we
+ * have just written. In case the compacted table became empty we
+ * simply skip writing it.
+ */
+ for (i = 0; i < first; i++)
+ strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+ if (!is_empty_table)
+ strbuf_addf(&tables_list_buf, "%s\n", new_table_name.buf);
+ for (i = last + 1; i < st->merged->stack_len; i++)
+ strbuf_addf(&tables_list_buf, "%s\n", st->readers[i]->name);
+
+ err = write_in_full(get_lock_file_fd(&tables_list_lock),
+ tables_list_buf.buf, tables_list_buf.len);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
goto done;
}
- err = close(lock_file_fd);
- lock_file_fd = -1;
+
+ err = fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&tables_list_lock));
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
goto done;
}
- err = rename(lock_file_name.buf, st->list_file);
+ err = commit_lock_file(&tables_list_lock);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
goto done;
}
- have_lock = 0;
- /* Reload the stack before deleting. On windows, we can only delete the
- files after we closed them.
- */
+ /*
+ * Reload the stack before deleting the compacted tables. We can only
+ * delete the files after we closed them on Windows, so this needs to
+ * happen first.
+ */
err = reftable_stack_reload_maybe_reuse(st, first < last);
+ if (err < 0)
+ goto done;
- listp = delete_on_success;
- while (*listp) {
- if (strcmp(*listp, new_table_path.buf)) {
- unlink(*listp);
- }
- listp++;
+ /*
+ * Delete the old tables. They may still be in use by concurrent
+ * readers, so it is expected that unlinking tables may fail.
+ */
+ for (i = first; i <= last; i++) {
+ struct lock_file *table_lock = &table_locks[i - first];
+ char *table_path = get_locked_file_path(table_lock);
+ unlink(table_path);
+ free(table_path);
}
done:
- free_names(delete_on_success);
+ rollback_lock_file(&tables_list_lock);
+ for (i = first; table_locks && i <= last; i++)
+ rollback_lock_file(&table_locks[i - first]);
+ reftable_free(table_locks);
- listp = subtable_locks;
- while (*listp) {
- unlink(*listp);
- listp++;
- }
- free_names(subtable_locks);
- if (lock_file_fd >= 0) {
- close(lock_file_fd);
- lock_file_fd = -1;
- }
- if (have_lock) {
- unlink(lock_file_name.buf);
- }
+ delete_tempfile(&new_table);
strbuf_release(&new_table_name);
strbuf_release(&new_table_path);
- strbuf_release(&ref_list_contents);
- strbuf_release(&temp_tab_file_name);
- strbuf_release(&lock_file_name);
+
+ strbuf_release(&tables_list_buf);
+ strbuf_release(&table_name);
return err;
}
int reftable_stack_compact_all(struct reftable_stack *st,
struct reftable_log_expiry_config *config)
{
- return stack_compact_range(st, 0, st->merged->stack_len - 1, config);
+ return stack_compact_range(st, 0, st->merged->stack_len ?
+ st->merged->stack_len - 1 : 0, config);
}
-static int stack_compact_range_stats(struct reftable_stack *st, int first,
- int last,
+static int stack_compact_range_stats(struct reftable_stack *st,
+ size_t first, size_t last,
struct reftable_log_expiry_config *config)
{
int err = stack_compact_range(st, first, last, config);
- if (err > 0) {
+ if (err == REFTABLE_LOCK_ERROR)
st->stats.failures++;
- }
return err;
}
@@ -1105,84 +1216,82 @@ static int segment_size(struct segment *s)
return s->end - s->start;
}
-int fastlog2(uint64_t sz)
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n)
{
- int l = 0;
- if (sz == 0)
- return 0;
- for (; sz; sz /= 2) {
- l++;
- }
- return l - 1;
-}
-
-struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n)
-{
- struct segment *segs = reftable_calloc(sizeof(struct segment) * n);
- int next = 0;
- struct segment cur = { 0 };
- int i = 0;
+ struct segment seg = { 0 };
+ uint64_t bytes;
+ size_t i;
- if (n == 0) {
- *seglen = 0;
- return segs;
- }
- for (i = 0; i < n; i++) {
- int log = fastlog2(sizes[i]);
- if (cur.log != log && cur.bytes > 0) {
- struct segment fresh = {
- .start = i,
- };
+ /*
+ * If there are no tables or only a single one then we don't have to
+ * compact anything. The sequence is geometric by definition already.
+ */
+ if (n <= 1)
+ return seg;
- segs[next++] = cur;
- cur = fresh;
+ /*
+ * Find the ending table of the compaction segment needed to restore the
+ * geometric sequence. Note that the segment end is exclusive.
+ *
+ * To do so, we iterate backwards starting from the most recent table
+ * until a valid segment end is found. If the preceding table is smaller
+ * than the current table multiplied by the geometric factor (2), the
+ * compaction segment end has been identified.
+ *
+ * Tables after the ending point are not added to the byte count because
+ * they are already valid members of the geometric sequence. Due to the
+ * properties of a geometric sequence, it is not possible for the sum of
+ * these tables to exceed the value of the ending point table.
+ *
+ * Example table size sequence requiring no compaction:
+ * 64, 32, 16, 8, 4, 2, 1
+ *
+ * Example table size sequence where compaction segment end is set to
+ * the last table. Since the segment end is exclusive, the last table is
+ * excluded during subsequent compaction and the table with size 3 is
+ * the final table included:
+ * 64, 32, 16, 8, 4, 3, 1
+ */
+ for (i = n - 1; i > 0; i--) {
+ if (sizes[i - 1] < sizes[i] * 2) {
+ seg.end = i + 1;
+ bytes = sizes[i];
+ break;
}
-
- cur.log = log;
- cur.end = i + 1;
- cur.bytes += sizes[i];
}
- segs[next++] = cur;
- *seglen = next;
- return segs;
-}
-struct segment suggest_compaction_segment(uint64_t *sizes, int n)
-{
- int seglen = 0;
- struct segment *segs = sizes_to_segments(&seglen, sizes, n);
- struct segment min_seg = {
- .log = 64,
- };
- int i = 0;
- for (i = 0; i < seglen; i++) {
- if (segment_size(&segs[i]) == 1) {
- continue;
- }
-
- if (segs[i].log < min_seg.log) {
- min_seg = segs[i];
- }
- }
+ /*
+ * Find the starting table of the compaction segment by iterating
+ * through the remaining tables and keeping track of the accumulated
+ * size of all tables seen from the segment end table. The previous
+ * table is compared to the accumulated size because the tables from the
+ * segment end are merged backwards recursively.
+ *
+ * Note that we keep iterating even after we have found the first
+ * starting point. This is because there may be tables in the stack
+ * preceding that first starting point which violate the geometric
+ * sequence.
+ *
+ * Example compaction segment start set to table with size 32:
+ * 128, 32, 16, 8, 4, 3, 1
+ */
+ for (; i > 0; i--) {
+ uint64_t curr = bytes;
+ bytes += sizes[i - 1];
- while (min_seg.start > 0) {
- int prev = min_seg.start - 1;
- if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev])) {
- break;
+ if (sizes[i - 1] < curr * 2) {
+ seg.start = i - 1;
+ seg.bytes = bytes;
}
-
- min_seg.start = prev;
- min_seg.bytes += sizes[prev];
}
- reftable_free(segs);
- return min_seg;
+ return seg;
}
static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
{
uint64_t *sizes =
- reftable_calloc(sizeof(uint64_t) * st->merged->stack_len);
+ reftable_calloc(st->merged->stack_len, sizeof(*sizes));
int version = (st->config.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
int overhead = header_size(version) - 1;
int i = 0;
@@ -1281,17 +1390,12 @@ static int stack_check_addition(struct reftable_stack *st,
while (1) {
struct reftable_ref_record ref = { NULL };
err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0) {
+ if (err > 0)
break;
- }
if (err < 0)
goto done;
- if (len >= cap) {
- cap = 2 * cap + 1;
- refs = reftable_realloc(refs, cap * sizeof(refs[0]));
- }
-
+ REFTABLE_ALLOC_GROW(refs, len + 1, cap);
refs[len++] = ref;
}
diff --git a/reftable/stack.h b/reftable/stack.h
index f57005846e..d43efa4760 100644
--- a/reftable/stack.h
+++ b/reftable/stack.h
@@ -14,9 +14,11 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-stack.h"
struct reftable_stack {
+ struct stat list_st;
char *list_file;
+ int list_fd;
+
char *reftable_dir;
- int disable_auto_compact;
struct reftable_write_options config;
@@ -29,13 +31,10 @@ struct reftable_stack {
int read_lines(const char *filename, char ***lines);
struct segment {
- int start, end;
- int log;
+ size_t start, end;
uint64_t bytes;
};
-int fastlog2(uint64_t sz);
-struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n);
-struct segment suggest_compaction_segment(uint64_t *sizes, int n);
+struct segment suggest_compaction_segment(uint64_t *sizes, size_t n);
#endif
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index d0b717510f..1df3ffce52 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -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"
@@ -39,7 +38,17 @@ static int count_dir_entries(const char *dirname)
return 0;
while ((d = readdir(dir))) {
- if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
+ /*
+ * Besides skipping over "." and "..", we also need to
+ * skip over other files that have a leading ".". This
+ * is due to behaviour of NFS, which will rename files
+ * to ".nfs*" to emulate delete-on-last-close.
+ *
+ * In any case this should be fine as the reftable
+ * library will never write files with leading dots
+ * anyway.
+ */
+ if (starts_with(d->d_name, "."))
continue;
len++;
}
@@ -78,7 +87,7 @@ 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);
@@ -233,7 +242,7 @@ static void test_reftable_stack_uptodate(void)
EXPECT_ERR(err);
err = reftable_stack_add(st2, &write_test_ref, &ref2);
- EXPECT(err == REFTABLE_LOCK_ERROR);
+ EXPECT(err == REFTABLE_OUTDATED_ERROR);
err = reftable_stack_reload(st2);
EXPECT_ERR(err);
@@ -289,6 +298,104 @@ 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->config.disable_auto_compact = i != n;
+
+ err = reftable_stack_new_addition(&add, st);
+ EXPECT_ERR(err);
+
+ err = reftable_addition_add(add, &write_test_ref, &ref);
+ EXPECT_ERR(err);
+
+ err = reftable_addition_commit(add);
+ EXPECT_ERR(err);
+
+ reftable_addition_destroy(add);
+
+ /*
+ * The stack length should grow continuously for all runs where
+ * auto compaction is disabled. When enabled, we should merge
+ * all tables in the stack.
+ */
+ if (i != n)
+ EXPECT(st->merged->stack_len == i + 1);
+ else
+ EXPECT(st->merged->stack_len == 1);
+ }
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+static void test_reftable_stack_auto_compaction_fails_gracefully(void)
+{
+ struct reftable_ref_record ref = {
+ .refname = "refs/heads/master",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {0x01},
+ };
+ struct reftable_write_options cfg = {0};
+ struct reftable_stack *st;
+ struct strbuf table_path = STRBUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+ int err;
+
+ err = reftable_new_stack(&st, dir, cfg);
+ EXPECT_ERR(err);
+
+ err = reftable_stack_add(st, write_test_ref, &ref);
+ EXPECT_ERR(err);
+ EXPECT(st->merged->stack_len == 1);
+ EXPECT(st->stats.attempts == 0);
+ EXPECT(st->stats.failures == 0);
+
+ /*
+ * Lock the newly written table such that it cannot be compacted.
+ * Adding a new table to the stack should not be impacted by this, even
+ * though auto-compaction will now fail.
+ */
+ strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
+ write_file_buf(table_path.buf, "", 0);
+
+ ref.update_index = 2;
+ err = reftable_stack_add(st, write_test_ref, &ref);
+ EXPECT_ERR(err);
+ EXPECT(st->merged->stack_len == 2);
+ EXPECT(st->stats.attempts == 1);
+ EXPECT(st->stats.failures == 1);
+
+ reftable_stack_destroy(st);
+ strbuf_release(&table_path);
+ clear_dir(dir);
+}
+
static void test_reftable_stack_validate_refname(void)
{
struct reftable_write_options cfg = { 0 };
@@ -389,18 +496,19 @@ static void test_reftable_stack_add(void)
int err = 0;
struct reftable_write_options cfg = {
.exact_log_message = 1,
+ .default_permissions = 0660,
+ .disable_auto_compact = 1,
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
-
struct reftable_ref_record refs[2] = { { NULL } };
struct reftable_log_record logs[2] = { { NULL } };
+ struct strbuf path = STRBUF_INIT;
+ struct stat stat_result;
int N = ARRAY_SIZE(refs);
-
err = reftable_new_stack(&st, dir, cfg);
EXPECT_ERR(err);
- st->disable_auto_compact = 1;
for (i = 0; i < N; i++) {
char buf[256];
@@ -408,14 +516,11 @@ 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);
logs[i].update_index = N + i + 1;
logs[i].value_type = REFTABLE_LOG_UPDATE;
-
- logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
logs[i].value.update.email = xstrdup("identity@invalid");
set_test_hash(logs[i].value.update.new_hash, i);
}
@@ -456,12 +561,32 @@ static void test_reftable_stack_add(void)
reftable_log_record_release(&dest);
}
+#ifndef GIT_WINDOWS_NATIVE
+ strbuf_addstr(&path, dir);
+ strbuf_addstr(&path, "/tables.list");
+ err = stat(path.buf, &stat_result);
+ EXPECT(!err);
+ EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+
+ strbuf_reset(&path);
+ strbuf_addstr(&path, dir);
+ strbuf_addstr(&path, "/");
+ /* do not try at home; not an external API for reftable. */
+ strbuf_addstr(&path, st->readers[0]->name);
+ err = stat(path.buf, &stat_result);
+ EXPECT(!err);
+ EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+#else
+ (void) stat_result;
+#endif
+
/* cleanup */
reftable_stack_destroy(st);
for (i = 0; i < N; i++) {
reftable_ref_record_release(&refs[i]);
reftable_log_record_release(&logs[i]);
}
+ strbuf_release(&path);
clear_dir(dir);
}
@@ -473,16 +598,17 @@ static void test_reftable_stack_log_normalize(void)
};
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
-
- uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
-
- struct reftable_log_record input = { .refname = "branch",
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = { .update = {
- .new_hash = h1,
- .old_hash = h2,
- } } };
+ struct reftable_log_record input = {
+ .refname = "branch",
+ .update_index = 1,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .new_hash = { 1 },
+ .old_hash = { 2 },
+ },
+ },
+ };
struct reftable_log_record dest = {
.update_index = 0,
};
@@ -545,7 +671,6 @@ 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);
}
@@ -554,8 +679,6 @@ static void test_reftable_stack_tombstone(void)
logs[i].update_index = 42;
if (i % 2 == 0) {
logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.new_hash =
- reftable_malloc(GIT_SHA1_RAWSZ);
set_test_hash(logs[i].value.update.new_hash, i);
logs[i].value.update.email =
xstrdup("identity@invalid");
@@ -647,60 +770,13 @@ static void test_reftable_stack_hash_id(void)
clear_dir(dir);
}
-static void test_log2(void)
-{
- EXPECT(1 == fastlog2(3));
- EXPECT(2 == fastlog2(4));
- EXPECT(2 == fastlog2(5));
-}
-
-static void test_sizes_to_segments(void)
-{
- uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
- /* .................0 1 2 3 4 5 */
-
- int seglen = 0;
- struct segment *segs =
- sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
- EXPECT(segs[2].log == 3);
- EXPECT(segs[2].start == 5);
- EXPECT(segs[2].end == 6);
-
- EXPECT(segs[1].log == 2);
- EXPECT(segs[1].start == 2);
- EXPECT(segs[1].end == 5);
- reftable_free(segs);
-}
-
-static void test_sizes_to_segments_empty(void)
-{
- int seglen = 0;
- struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
- EXPECT(seglen == 0);
- reftable_free(segs);
-}
-
-static void test_sizes_to_segments_all_equal(void)
-{
- uint64_t sizes[] = { 5, 5 };
-
- int seglen = 0;
- struct segment *segs =
- sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
- EXPECT(seglen == 1);
- EXPECT(segs[0].start == 0);
- EXPECT(segs[0].end == 2);
- reftable_free(segs);
-}
-
static void test_suggest_compaction_segment(void)
{
- uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
- /* .................0 1 2 3 4 5 6 */
+ uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
struct segment min =
suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
- EXPECT(min.start == 2);
- EXPECT(min.end == 7);
+ EXPECT(min.start == 1);
+ EXPECT(min.end == 10);
}
static void test_suggest_compaction_segment_nothing(void)
@@ -738,7 +814,6 @@ static void test_reflog_expire(void)
logs[i].update_index = i;
logs[i].value_type = REFTABLE_LOG_UPDATE;
logs[i].value.update.time = i;
- logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
logs[i].value.update.email = xstrdup("identity@invalid");
set_test_hash(logs[i].value.update.new_hash, i);
}
@@ -812,9 +887,21 @@ static void test_empty_add(void)
reftable_stack_destroy(st2);
}
+static int fastlog2(uint64_t sz)
+{
+ int l = 0;
+ if (sz == 0)
+ return 0;
+ for (; sz; sz /= 2)
+ l++;
+ return l - 1;
+}
+
static void test_reftable_stack_auto_compaction(void)
{
- struct reftable_write_options cfg = { 0 };
+ struct reftable_write_options cfg = {
+ .disable_auto_compact = 1,
+ };
struct reftable_stack *st = NULL;
char *dir = get_tmp_dir(__LINE__);
@@ -824,7 +911,6 @@ static void test_reftable_stack_auto_compaction(void)
err = reftable_new_stack(&st, dir, cfg);
EXPECT_ERR(err);
- st->disable_auto_compact = 1; /* call manually below for coverage. */
for (i = 0; i < N; i++) {
char name[100];
struct reftable_ref_record ref = {
@@ -850,6 +936,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->config.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 };
@@ -952,7 +1086,6 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
int stack_test_main(int argc, const char *argv[])
{
RUN_TEST(test_empty_add);
- RUN_TEST(test_log2);
RUN_TEST(test_names_equal);
RUN_TEST(test_parse_names);
RUN_TEST(test_read_file);
@@ -960,6 +1093,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,12 +1101,11 @@ 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_auto_compaction_fails_gracefully);
RUN_TEST(test_reftable_stack_update_index_check);
RUN_TEST(test_reftable_stack_uptodate);
RUN_TEST(test_reftable_stack_validate_refname);
- RUN_TEST(test_sizes_to_segments);
- RUN_TEST(test_sizes_to_segments_all_equal);
- RUN_TEST(test_sizes_to_segments_empty);
RUN_TEST(test_suggest_compaction_segment);
RUN_TEST(test_suggest_compaction_segment_nothing);
return 0;
diff --git a/reftable/system.h b/reftable/system.h
index 6b74a81514..5d8b6dede5 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -12,7 +12,9 @@ https://developers.google.com/open-source/licenses/bsd
/* This header glues the reftable library to the rest of Git */
#include "git-compat-util.h"
+#include "lockfile.h"
#include "strbuf.h"
+#include "tempfile.h"
#include "hash-ll.h" /* hash ID, sizes.*/
#include "dir.h" /* remove_dir_recursively, for tests.*/
diff --git a/reftable/test_framework.c b/reftable/test_framework.c
index 84ac972cad..4066924eee 100644
--- a/reftable/test_framework.c
+++ b/reftable/test_framework.c
@@ -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)
{
@@ -21,3 +20,8 @@ ssize_t strbuf_add_void(void *b, const void *data, size_t sz)
strbuf_add(b, data, sz);
return sz;
}
+
+int noop_flush(void *arg)
+{
+ return 0;
+}
diff --git a/reftable/test_framework.h b/reftable/test_framework.h
index 774cb275bf..687390f9c2 100644
--- a/reftable/test_framework.h
+++ b/reftable/test_framework.h
@@ -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); \
@@ -50,4 +56,6 @@ void set_test_hash(uint8_t *p, int i);
*/
ssize_t strbuf_add_void(void *b, const void *data, size_t sz);
+int noop_flush(void *);
+
#endif
diff --git a/reftable/tree.c b/reftable/tree.c
index a5bf880985..528f33ae38 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -20,8 +20,8 @@ struct tree_node *tree_search(void *key, struct tree_node **rootp,
if (!insert) {
return NULL;
} else {
- struct tree_node *n =
- reftable_calloc(sizeof(struct tree_node));
+ struct tree_node *n;
+ REFTABLE_CALLOC_ARRAY(n, 1);
n->key = key;
*rootp = n;
return *rootp;
diff --git a/reftable/tree_test.c b/reftable/tree_test.c
index ac3a045ad4..6961a657ad 100644
--- a/reftable/tree_test.c
+++ b/reftable/tree_test.c
@@ -9,8 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "tree.h"
-#include "basics.h"
-#include "record.h"
#include "test_framework.h"
#include "reftable-tests.h"
diff --git a/reftable/writer.c b/reftable/writer.c
index 2e322a5683..1d9ff0fbfa 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -49,7 +49,7 @@ static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len,
{
int n = 0;
if (w->pending_padding > 0) {
- uint8_t *zeroed = reftable_calloc(w->pending_padding);
+ uint8_t *zeroed = reftable_calloc(w->pending_padding, sizeof(*zeroed));
int n = w->write(w->write_arg, zeroed, w->pending_padding);
if (n < 0)
return n;
@@ -121,10 +121,10 @@ static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
struct reftable_writer *
reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
+ int (*flush_func)(void *),
void *writer_arg, struct reftable_write_options *opts)
{
- struct reftable_writer *wp =
- reftable_calloc(sizeof(struct reftable_writer));
+ struct reftable_writer *wp = reftable_calloc(1, sizeof(*wp));
strbuf_init(&wp->block_writer_data.last_key, 0);
options_set_defaults(opts);
if (opts->block_size >= (1 << 24)) {
@@ -132,10 +132,11 @@ reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
abort();
}
wp->last_key = reftable_empty_strbuf;
- wp->block = reftable_calloc(opts->block_size);
+ REFTABLE_CALLOC_ARRAY(wp->block, opts->block_size);
wp->write = writer_func;
wp->write_arg = writer_arg;
wp->opts = *opts;
+ wp->flush = flush_func;
writer_reinit_block_writer(wp, BLOCK_TYPE_REF);
return wp;
@@ -200,12 +201,7 @@ static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash)
return;
}
- if (key->offset_len == key->offset_cap) {
- key->offset_cap = 2 * key->offset_cap + 1;
- key->offsets = reftable_realloc(
- key->offsets, sizeof(uint64_t) * key->offset_cap);
- }
-
+ REFTABLE_ALLOC_GROW(key->offsets, key->offset_len + 1, key->offset_cap);
key->offsets[key->offset_len++] = off;
}
@@ -377,20 +373,39 @@ int reftable_writer_add_logs(struct reftable_writer *w,
static int writer_finish_section(struct reftable_writer *w)
{
+ struct reftable_block_stats *bstats = NULL;
uint8_t typ = block_writer_type(w->block_writer);
uint64_t index_start = 0;
int max_level = 0;
- int threshold = w->opts.unpadded ? 1 : 3;
+ size_t threshold = w->opts.unpadded ? 1 : 3;
int before_blocks = w->stats.idx_stats.blocks;
- int err = writer_flush_block(w);
- int i = 0;
- struct reftable_block_stats *bstats = NULL;
+ int err;
+
+ err = writer_flush_block(w);
if (err < 0)
return err;
+ /*
+ * When the section we are about to index has a lot of blocks then the
+ * index itself may span across multiple blocks, as well. This would
+ * require a linear scan over index blocks only to find the desired
+ * indexed block, which is inefficient. Instead, we write a multi-level
+ * index where index records of level N+1 will refer to index blocks of
+ * level N. This isn't constant time, either, but at least logarithmic.
+ *
+ * This loop handles writing this multi-level index. Note that we write
+ * the lowest-level index pointing to the indexed blocks first. We then
+ * continue writing additional index levels until the current level has
+ * less blocks than the threshold so that the highest level will be at
+ * the end of the index section.
+ *
+ * Readers are thus required to start reading the index section from
+ * its end, which is why we set `index_start` to the beginning of the
+ * last index section.
+ */
while (w->index_len > threshold) {
struct reftable_index_record *idx = NULL;
- int idx_len = 0;
+ size_t i, idx_len;
max_level++;
index_start = w->next;
@@ -409,35 +424,28 @@ static int writer_finish_section(struct reftable_writer *w)
.idx = idx[i],
},
};
- if (block_writer_add(w->block_writer, &rec) == 0) {
- continue;
- }
- err = writer_flush_block(w);
+ err = writer_add_record(w, &rec);
if (err < 0)
return err;
+ }
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_flush_block(w);
+ if (err < 0)
+ return err;
- err = block_writer_add(w->block_writer, &rec);
- if (err != 0) {
- /* write into fresh block should always succeed
- */
- abort();
- }
- }
- for (i = 0; i < idx_len; i++) {
+ for (i = 0; i < idx_len; i++)
strbuf_release(&idx[i].last_key);
- }
reftable_free(idx);
}
+ /*
+ * The index may still contain a number of index blocks lower than the
+ * threshold. Clear it so that these entries don't leak into the next
+ * index section.
+ */
writer_clear_index(w);
- err = writer_flush_block(w);
- if (err < 0)
- return err;
-
bstats = writer_reftable_block_stats(w, typ);
bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
bstats->index_offset = index_start;
@@ -603,6 +611,12 @@ int reftable_writer_close(struct reftable_writer *w)
put_be32(p, crc32(0, footer, p - footer));
p += 4;
+ err = w->flush(w->write_arg);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
err = padded_write(w, footer, footer_size(writer_version(w)), 0);
if (err < 0)
goto done;
@@ -622,11 +636,8 @@ done:
static void writer_clear_index(struct reftable_writer *w)
{
- int i = 0;
- for (i = 0; i < w->index_len; i++) {
+ for (size_t i = 0; i < w->index_len; i++)
strbuf_release(&w->index[i].last_key);
- }
-
FREE_AND_NULL(w->index);
w->index_len = 0;
w->index_cap = 0;
@@ -674,12 +685,7 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)
if (err < 0)
return err;
- if (w->index_cap == w->index_len) {
- w->index_cap = 2 * w->index_cap + 1;
- w->index = reftable_realloc(
- w->index,
- sizeof(struct reftable_index_record) * w->index_cap);
- }
+ REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
ir.offset = w->next;
strbuf_reset(&ir.last_key);
diff --git a/reftable/writer.h b/reftable/writer.h
index 09b88673d9..8d0df9cc52 100644
--- a/reftable/writer.h
+++ b/reftable/writer.h
@@ -16,6 +16,7 @@ https://developers.google.com/open-source/licenses/bsd
struct reftable_writer {
ssize_t (*write)(void *, const void *, size_t);
+ int (*flush)(void *);
void *write_arg;
int pending_padding;
struct strbuf last_key;
diff --git a/remote-curl.c b/remote-curl.c
index ef05752ca5..0b6d7815fd 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "git-curl-compat.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
@@ -8,11 +9,9 @@
#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"
@@ -22,6 +21,7 @@
#include "quote.h"
#include "trace2.h"
#include "transport.h"
+#include "url.h"
#include "write-or-die.h"
static struct remote *remote;
@@ -212,14 +212,9 @@ static int set_option(const char *name, const char *value)
options.filter = xstrdup(value);
return 0;
} else if (!strcmp(name, "object-format")) {
- int algo;
options.object_format = 1;
- if (strcmp(value, "true")) {
- algo = hash_algo_by_name(value);
- if (algo == GIT_HASH_UNKNOWN)
- die("unknown object format '%s'", value);
- options.hash_algo = &hash_algos[algo];
- }
+ if (strcmp(value, "true"))
+ die(_("unknown value for object-format: %s"), value);
return 0;
} else {
return 1 /* unsupported */;
@@ -961,7 +956,9 @@ retry:
/* The request body is large and the size cannot be predicted.
* We must use chunked encoding to send it.
*/
+#ifdef GIT_CURL_NEED_TRANSFER_ENCODING_HEADER
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+#endif
rpc->initial_buffer = 1;
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
@@ -1447,8 +1444,14 @@ static int stateless_connect(const char *service_name)
* establish a stateless connection, otherwise we need to tell the
* client to fallback to using other transport helper functions to
* complete their request.
+ *
+ * The "git-upload-archive" service is a read-only operation. Fallback
+ * to use "git-upload-pack" service to discover protocol version.
*/
- discover = discover_refs(service_name, 0);
+ if (!strcmp(service_name, "git-upload-archive"))
+ discover = discover_refs("git-upload-pack", 0);
+ else
+ discover = discover_refs(service_name, 0);
if (discover->version != protocol_v2) {
printf("fallback\n");
fflush(stdout);
@@ -1486,9 +1489,11 @@ static int stateless_connect(const char *service_name)
/*
* Dump the capability listing that we got from the server earlier
- * during the info/refs request.
+ * during the info/refs request. This does not work with the
+ * "git-upload-archive" service.
*/
- write_or_die(rpc.in, discover->buf, discover->len);
+ if (strcmp(service_name, "git-upload-archive"))
+ write_or_die(rpc.in, discover->buf, discover->len);
/* Until we see EOF keep sending POSTs */
while (1) {
@@ -1564,8 +1569,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 ")) {
diff --git a/remote.c b/remote.c
index abb24822be..2b650b813b 100644
--- a/remote.c
+++ b/remote.c
@@ -15,7 +15,6 @@
#include "diff.h"
#include "revision.h"
#include "dir.h"
-#include "tag.h"
#include "setup.h"
#include "string-list.h"
#include "strvec.h"
@@ -106,7 +105,7 @@ static int remotes_hash_cmp(const void *cmp_data UNUSED,
b = container_of(entry_or_key, const struct remote, ent);
if (key)
- return strncmp(a->name, key->str, key->len) || a->name[key->len];
+ return !!xstrncmpz(a->name, key->str, key->len);
else
return strcmp(a->name, b->name);
}
@@ -190,8 +189,7 @@ static int branches_hash_cmp(const void *cmp_data UNUSED,
b = container_of(entry_or_key, const struct branch, ent);
if (key)
- return strncmp(a->name, key->str, key->len) ||
- a->name[key->len];
+ return !!xstrncmpz(a->name, key->str, key->len);
else
return strcmp(a->name, b->name);
}
@@ -509,7 +507,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;
@@ -518,7 +516,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) &&
@@ -561,7 +559,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,
@@ -587,7 +585,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,
@@ -599,7 +597,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) {
@@ -709,7 +707,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);
}
@@ -722,7 +726,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);
}
@@ -738,7 +742,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 =
@@ -1831,7 +1835,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
@@ -1973,7 +1977,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)
@@ -2675,7 +2679,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 = repo_in_merge_bases_many(the_repository, commit, size, chunk)))
+ if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk, 0)))
break;
}
diff --git a/remote.h b/remote.h
index cdc8b1db42..dfd4837e60 100644
--- a/remote.h
+++ b/remote.h
@@ -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);
@@ -400,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;
diff --git a/repo-settings.c b/repo-settings.c
index 525f69c0c7..a0b590bc6c 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -2,7 +2,6 @@
#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)
@@ -44,6 +43,7 @@ void prepare_repo_settings(struct repository *r)
if (experimental) {
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
r->settings.pack_use_bitmap_boundary_traversal = 1;
+ r->settings.pack_use_multi_pack_reuse = 1;
}
if (manyfiles) {
r->settings.index_version = 4;
diff --git a/repository.c b/repository.c
index a7679ceeaa..e15b416944 100644
--- a/repository.c
+++ b/repository.c
@@ -14,6 +14,7 @@
#include "read-cache-ll.h"
#include "remote.h"
#include "setup.h"
+#include "loose.h"
#include "submodule-config.h"
#include "sparse-index.h"
#include "trace2.h"
@@ -104,6 +105,20 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
repo->hash_algo = &hash_algos[hash_algo];
}
+void repo_set_compat_hash_algo(struct repository *repo, int algo)
+{
+ if (hash_algo_by_ptr(repo->hash_algo) == algo)
+ BUG("hash_algo and compat_hash_algo match");
+ repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL;
+ if (repo->compat_hash_algo)
+ repo_read_loose_object_map(repo);
+}
+
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+ repo->ref_storage_format = format;
+}
+
/*
* Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
* Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +199,8 @@ int repo_init(struct repository *repo,
goto error;
repo_set_hash_algo(repo, format.hash_algo);
+ repo_set_compat_hash_algo(repo, format.compat_hash_algo);
+ repo_set_ref_storage_format(repo, format.ref_storage_format);
repo->repository_format_worktree_config = format.worktree_config;
/* take ownership of format.partial_clone */
@@ -193,6 +210,9 @@ int repo_init(struct repository *repo,
if (worktree)
repo_set_worktree(repo, worktree);
+ if (repo->compat_hash_algo)
+ repo_read_loose_object_map(repo);
+
clear_repository_format(&format);
return 0;
@@ -256,8 +276,6 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
FREE_AND_NULL(cache->merge_rr);
FREE_AND_NULL(cache->merge_mode);
FREE_AND_NULL(cache->merge_head);
- FREE_AND_NULL(cache->merge_autostash);
- FREE_AND_NULL(cache->auto_merge);
FREE_AND_NULL(cache->fetch_head);
FREE_AND_NULL(cache->shallow);
}
diff --git a/repository.h b/repository.h
index 5f18486f64..268436779c 100644
--- a/repository.h
+++ b/repository.h
@@ -24,6 +24,10 @@ enum fetch_negotiation_setting {
FETCH_NEGOTIATION_NOOP,
};
+#define REF_STORAGE_FORMAT_UNKNOWN 0
+#define REF_STORAGE_FORMAT_FILES 1
+#define REF_STORAGE_FORMAT_REFTABLE 2
+
struct repo_settings {
int initialized;
@@ -36,6 +40,7 @@ struct repo_settings {
int sparse_index;
int pack_read_reverse_index;
int pack_use_bitmap_boundary_traversal;
+ int pack_use_multi_pack_reuse;
/*
* Does this repository have core.useReplaceRefs=true (on by
@@ -64,8 +69,6 @@ struct repo_path_cache {
char *merge_rr;
char *merge_mode;
char *merge_head;
- char *merge_autostash;
- char *auto_merge;
char *fetch_head;
char *shallow;
};
@@ -160,6 +163,12 @@ struct repository {
/* Repository's current hash algorithm, as serialized on disk. */
const struct git_hash_algo *hash_algo;
+ /* Repository's compatibility hash algorithm. */
+ const struct git_hash_algo *compat_hash_algo;
+
+ /* Repository's reference storage format, as serialized on disk. */
+ unsigned int ref_storage_format;
+
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@@ -199,6 +208,8 @@ 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_compat_hash_algo(struct repository *repo, int compat_algo);
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
void initialize_the_repository(void);
RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree);
diff --git a/rerere.c b/rerere.c
index 09e1941285..13c94ded03 100644
--- a/rerere.c
+++ b/rerere.c
@@ -12,12 +12,10 @@
#include "dir.h"
#include "resolve-undo.h"
#include "merge-ll.h"
-#include "attr.h"
#include "path.h"
#include "pathspec.h"
#include "object-file.h"
#include "object-store-ll.h"
-#include "hash-lookup.h"
#include "strmap.h"
#define RESOLVED 0
@@ -975,6 +973,9 @@ static int handle_cache(struct index_state *istate,
mmfile[i].ptr = repo_read_object_file(the_repository,
&ce->oid, &type,
&size);
+ if (!mmfile[i].ptr)
+ die(_("unable to read %s"),
+ oid_to_hex(&ce->oid));
mmfile[i].size = size;
}
}
diff --git a/reset.c b/reset.c
index 48da0adf85..d619cb7115 100644
--- a/reset.c
+++ b/reset.c
@@ -6,7 +6,6 @@
#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"
@@ -158,6 +157,11 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
}
tree = parse_tree_indirect(oid);
+ if (!tree) {
+ ret = error(_("unable to read tree (%s)"), oid_to_hex(oid));
+ goto leave_reset_head;
+ }
+
prime_cache_tree(r, r->index, tree);
if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
diff --git a/revision.c b/revision.c
index 00d5c29bfc..7e45f765d9 100644
--- a/revision.c
+++ b/revision.c
@@ -21,12 +21,10 @@
#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"
@@ -83,7 +81,7 @@ static void mark_tree_contents_uninteresting(struct repository *r,
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
@@ -190,7 +188,7 @@ static void add_children_by_path(struct repository *r,
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
@@ -383,13 +381,18 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
object = parse_object_with_flags(revs->repo, oid,
revs->verify_objects ? 0 :
- PARSE_OBJECT_SKIP_HASH_CHECK);
+ PARSE_OBJECT_SKIP_HASH_CHECK |
+ PARSE_OBJECT_DISCARD_TREE);
if (!object) {
if (revs->ignore_missing)
- return object;
+ return NULL;
if (revs->exclude_promisor_objects && is_promisor_object(oid))
return NULL;
+ if (revs->do_not_die_on_missing_objects) {
+ oidset_insert(&revs->missing_commits, oid);
+ return NULL;
+ }
die("bad object %s", name);
}
object->flags |= flags;
@@ -417,15 +420,21 @@ static struct commit *handle_commit(struct rev_info *revs,
*/
while (object->type == OBJ_TAG) {
struct tag *tag = (struct tag *) object;
+ struct object_id *oid;
if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
- object = parse_object(revs->repo, get_tagged_oid(tag));
+ oid = get_tagged_oid(tag);
+ object = parse_object(revs->repo, oid);
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
if (revs->exclude_promisor_objects &&
is_promisor_object(&tag->tagged->oid))
return NULL;
+ if (revs->do_not_die_on_missing_objects && oid) {
+ oidset_insert(&revs->missing_commits, oid);
+ return NULL;
+ }
die("bad object %s", oid_to_hex(&tag->tagged->oid));
}
object->flags |= flags;
@@ -1688,9 +1697,7 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
return 0;
}
-static int handle_one_reflog(const char *refname_in_wt,
- const struct object_id *oid UNUSED,
- int flag UNUSED, void *cb_data)
+static int handle_one_reflog(const char *refname_in_wt, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct strbuf refname = STRBUF_INIT;
@@ -1949,6 +1956,7 @@ void repo_init_revisions(struct repository *r,
init_display_notes(&revs->notes_opt);
list_objects_filter_init(&revs->filter);
init_ref_exclusions(&revs->ref_excludes);
+ oidset_init(&revs->missing_commits, 0);
}
static void add_pending_commit_list(struct rev_info *revs,
@@ -1963,11 +1971,31 @@ static void add_pending_commit_list(struct rev_info *revs,
}
}
+static const char *lookup_other_head(struct object_id *oid)
+{
+ int i;
+ static const char *const other_head[] = {
+ "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
+ };
+
+ for (i = 0; i < ARRAY_SIZE(other_head); i++)
+ if (!read_ref_full(other_head[i],
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ oid, NULL)) {
+ if (is_null_oid(oid))
+ die(_("%s exists but is a symbolic ref"), other_head[i]);
+ return other_head[i];
+ }
+
+ die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+}
+
static void prepare_show_merge(struct rev_info *revs)
{
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
struct commit *head, *other;
struct object_id oid;
+ const char *other_name;
const char **prune = NULL;
int i, prune_num = 1; /* counting terminating NULL */
struct index_state *istate = revs->repo->index;
@@ -1975,12 +2003,12 @@ static void prepare_show_merge(struct rev_info *revs)
if (repo_get_oid(the_repository, "HEAD", &oid))
die("--merge without HEAD?");
head = lookup_commit_or_die(&oid, "HEAD");
- if (repo_get_oid(the_repository, "MERGE_HEAD", &oid))
- die("--merge without MERGE_HEAD?");
- other = lookup_commit_or_die(&oid, "MERGE_HEAD");
+ other_name = lookup_other_head(&oid);
+ other = lookup_commit_or_die(&oid, other_name);
add_pending_object(revs, &head->object, "HEAD");
- add_pending_object(revs, &other->object, "MERGE_HEAD");
- bases = repo_get_merge_bases(the_repository, head, other);
+ add_pending_object(revs, &other->object, other_name);
+ if (repo_get_merge_bases(the_repository, head, other, &bases) < 0)
+ exit(128);
add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
free_commit_list(bases);
@@ -2068,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
} else {
/* A...B -- find merge bases between the two */
struct commit *a, *b;
- struct commit_list *exclude;
+ struct commit_list *exclude = NULL;
a = lookup_commit_reference(revs->repo, &a_obj->oid);
b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric);
- exclude = repo_get_merge_bases(the_repository, a, b);
+ if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
+ free_commit_list(exclude);
+ return -1;
+ }
add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
flags_exclude);
add_pending_commit_list(revs, exclude, flags_exclude);
@@ -2180,13 +2211,18 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
if (revarg_opt & REVARG_COMMITTISH)
get_sha1_flags |= GET_OID_COMMITTISH;
+ /*
+ * Even if revs->do_not_die_on_missing_objects is set, we
+ * should error out if we can't even get an oid, as
+ * `--missing=print` should be able to report missing oids.
+ */
if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, &oid, flags ^ local_flags);
if (!object)
- return revs->ignore_missing ? 0 : -1;
+ return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
free(oc.path);
@@ -2223,6 +2259,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)
@@ -2249,29 +2306,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);
@@ -2283,7 +2338,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);
@@ -2303,7 +2358,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
struct commit *c;
struct object_id oid;
- const char *msg = _("could not get commit for ancestry-path argument %s");
+ const char *msg = _("could not get commit for --ancestry-path argument %s");
revs->ancestry_path = 1;
revs->simplify_history = 0;
@@ -2371,11 +2426,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")) {
@@ -2384,8 +2439,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)
@@ -2709,7 +2764,8 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) {
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --branches"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--branches");
handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) {
@@ -2720,12 +2776,14 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
revs->bisect = 1;
} else if (!strcmp(arg, "--tags")) {
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --tags"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--tags");
handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--remotes")) {
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --remotes"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--remotes");
handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
clear_ref_exclusions(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
@@ -2743,21 +2801,24 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
} else if (skip_prefix(arg, "--branches=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --branches"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--branches");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --tags"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--tags");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
struct all_refs_cb cb;
if (revs->ref_excludes.hidden_refs_configured)
- return error(_("--exclude-hidden cannot be used together with --remotes"));
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--remotes");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
clear_ref_exclusions(&revs->ref_excludes);
@@ -3036,8 +3097,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)
@@ -3048,11 +3107,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)
@@ -3810,8 +3868,6 @@ int prepare_revision_walk(struct rev_info *revs)
FOR_EACH_OBJECT_PROMISOR_ONLY);
}
- oidset_init(&revs->missing_commits, 0);
-
if (!revs->reflog_info)
prepare_to_use_bloom_filter(revs);
if (!revs->unsorted_input)
diff --git a/revision.h b/revision.h
index 94c43138bc..0e470d1df1 100644
--- a/revision.h
+++ b/revision.h
@@ -142,6 +142,7 @@ struct rev_info {
/* Basic information */
const char *prefix;
const char *def;
+ char *ps_matched; /* optionally record matches of prune_data */
struct pathspec prune_data;
/*
diff --git a/run-command.c b/run-command.c
index a558042c87..0e7435718a 100644
--- a/run-command.c
+++ b/run-command.c
@@ -14,9 +14,7 @@
#include "quote.h"
#include "config.h"
#include "packfile.h"
-#include "hook.h"
#include "compat/nonblock.h"
-#include "alloc.h"
void child_process_init(struct child_process *child)
{
diff --git a/send-pack.c b/send-pack.c
index 89aca9d829..37f59d4f66 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -4,7 +4,6 @@
#include "date.h"
#include "gettext.h"
#include "hex.h"
-#include "refs.h"
#include "object-store-ll.h"
#include "pkt-line.h"
#include "sideband.h"
@@ -12,7 +11,6 @@
#include "remote.h"
#include "connect.h"
#include "send-pack.h"
-#include "quote.h"
#include "transport.h"
#include "version.h"
#include "oid-array.h"
diff --git a/sequencer.c b/sequencer.c
index d584cac8ed..2c19846385 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -15,10 +15,8 @@
#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"
@@ -39,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"
@@ -238,34 +235,29 @@ 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")) {
@@ -340,12 +332,12 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
sb->buf[sb->len - ignore_footer] = '\0';
}
- trailer_info_get(&info, sb->buf, &opts);
+ trailer_info_get(&opts, sb->buf, &info);
if (ignore_footer)
sb->buf[sb->len - ignore_footer] = saved_char;
- if (info.trailer_start == info.trailer_end)
+ if (info.trailer_block_start == info.trailer_block_end)
return 0;
for (i = 0; i < info.trailer_nr; i++)
@@ -469,41 +461,56 @@ static void free_message(struct commit *commit, struct commit_message *msg)
repo_unuse_commit_buffer(the_repository, commit, msg->message);
}
+const char *rebase_resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
static void print_advice(struct repository *r, int show_hint,
struct replay_opts *opts)
{
- char *msg = getenv("GIT_CHERRY_PICK_HELP");
+ const char *msg;
+
+ if (is_rebase_i(opts))
+ msg = rebase_resolvemsg;
+ else
+ msg = getenv("GIT_CHERRY_PICK_HELP");
if (msg) {
- advise("%s\n", msg);
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
/*
* A conflict has occurred but the porcelain
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
return;
}
if (show_hint) {
if (opts->no_commit)
- advise(_("after resolving the conflicts, mark the corrected paths\n"
- "with 'git add <paths>' or 'git rm <paths>'"));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("after resolving the conflicts, mark the corrected paths\n"
+ "with 'git add <paths>' or 'git rm <paths>'"));
else if (opts->action == REPLAY_PICK)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git cherry-pick --continue\".\n"
- "You can instead skip this commit with \"git cherry-pick --skip\".\n"
- "To abort and get back to the state before \"git cherry-pick\",\n"
- "run \"git cherry-pick --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git cherry-pick --continue\".\n"
+ "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+ "To abort and get back to the state before \"git cherry-pick\",\n"
+ "run \"git cherry-pick --abort\"."));
else if (opts->action == REPLAY_REVERT)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git revert --continue\".\n"
- "You can instead skip this commit with \"git revert --skip\".\n"
- "To abort and get back to the state before \"git revert\",\n"
- "run \"git revert --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git revert --continue\".\n"
+ "You can instead skip this commit with \"git revert --skip\".\n"
+ "To abort and get back to the state before \"git revert\",\n"
+ "run \"git revert --abort\"."));
else
BUG("unexpected pick action in print_advice()");
}
@@ -671,15 +678,15 @@ void append_conflicts_hint(struct index_state *istate,
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
strbuf_addch(msgbuf, '\n');
wt_status_append_cut_line(msgbuf);
- strbuf_addch(msgbuf, comment_line_char);
+ strbuf_addstr(msgbuf, comment_line_str);
}
strbuf_addch(msgbuf, '\n');
- strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n");
+ strbuf_commented_addf(msgbuf, comment_line_str, "Conflicts:\n");
for (i = 0; i < istate->cache_nr;) {
const struct cache_entry *ce = istate->cache[i++];
if (ce_stage(ce)) {
- strbuf_commented_addf(msgbuf, comment_line_char,
+ strbuf_commented_addf(msgbuf, comment_line_str,
"\t%s\n", ce->name);
while (i < istate->cache_nr &&
!strcmp(ce->name, istate->cache[i]->name))
@@ -715,6 +722,8 @@ static int do_recursive_merge(struct repository *r,
o.show_rename_progress = 1;
head_tree = parse_tree_indirect(head);
+ if (!head_tree)
+ return error(_("unable to read tree (%s)"), oid_to_hex(head));
next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
@@ -778,29 +787,42 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
static int is_index_unchanged(struct repository *r)
{
struct object_id head_oid, *cache_tree_oid;
+ const struct object_id *head_tree_oid;
struct commit *head_commit;
struct index_state *istate = r->index;
+ const char *head_name;
+
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+ /* Check to see if this is an unborn branch */
+ head_name = resolve_ref_unsafe("HEAD",
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, NULL);
+ if (!head_name ||
+ !starts_with(head_name, "refs/heads/") ||
+ !is_null_oid(&head_oid))
+ return error(_("could not resolve HEAD commit"));
+ head_tree_oid = the_hash_algo->empty_tree;
+ } else {
+ head_commit = lookup_commit(r, &head_oid);
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
- return error(_("could not resolve HEAD commit"));
-
- head_commit = lookup_commit(r, &head_oid);
+ /*
+ * If head_commit is NULL, check_commit, called from
+ * lookup_commit, would have indicated that head_commit is not
+ * a commit object already. repo_parse_commit() will return failure
+ * without further complaints in such a case. Otherwise, if
+ * the commit is invalid, repo_parse_commit() will complain. So
+ * there is nothing for us to say here. Just return failure.
+ */
+ if (repo_parse_commit(r, head_commit))
+ return -1;
- /*
- * If head_commit is NULL, check_commit, called from
- * lookup_commit, would have indicated that head_commit is not
- * a commit object already. repo_parse_commit() will return failure
- * without further complaints in such a case. Otherwise, if
- * the commit is invalid, repo_parse_commit() will complain. So
- * there is nothing for us to say here. Just return failure.
- */
- if (repo_parse_commit(r, head_commit))
- return -1;
+ head_tree_oid = get_commit_tree_oid(head_commit);
+ }
if (!(cache_tree_oid = get_cache_tree_oid(istate)))
return -1;
- return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+ return oideq(cache_tree_oid, head_tree_oid);
}
static int write_author_script(const char *message)
@@ -1160,7 +1182,7 @@ void cleanup_message(struct strbuf *msgbuf,
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 ? comment_line_char : '\0');
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
}
/*
@@ -1192,7 +1214,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
return 0;
strbuf_stripspace(&tmpl,
- cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
@@ -1565,7 +1587,7 @@ static int try_to_commit(struct repository *r,
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
strbuf_stripspace(msg,
- cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+ cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
res = 1; /* run 'git commit' to display error message */
goto out;
@@ -1675,7 +1697,7 @@ static int do_commit(struct repository *r,
strbuf_release(&sb);
if (!res) {
refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0);
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF);
unlink(git_path_merge_msg(r));
if (!is_rebase_i(opts))
print_commit_summary(r, NULL, &oid,
@@ -1727,34 +1749,25 @@ static int allow_empty(struct repository *r,
int index_unchanged, originally_empty;
/*
- * Four cases:
- *
- * (1) we do not allow empty at all and error out.
- *
- * (2) we allow ones that were initially empty, and
- * just drop the ones that become empty
- *
- * (3) we allow ones that were initially empty, but
- * halt for the ones that become empty;
+ * For a commit that is initially empty, allow_empty determines if it
+ * should be kept or not
*
- * (4) we allow both.
+ * For a commit that becomes empty, keep_redundant_commits and
+ * drop_redundant_commits determine whether the commit should be kept or
+ * dropped. If neither is specified, halt.
*/
- if (!opts->allow_empty)
- return 0; /* let "git commit" barf as necessary */
-
index_unchanged = is_index_unchanged(r);
if (index_unchanged < 0)
return index_unchanged;
if (!index_unchanged)
return 0; /* we do not have to say --allow-empty */
- if (opts->keep_redundant_commits)
- return 1;
-
originally_empty = is_original_commit_empty(commit);
if (originally_empty < 0)
return originally_empty;
if (originally_empty)
+ return opts->allow_empty;
+ else if (opts->keep_redundant_commits)
return 1;
else if (opts->drop_redundant_commits)
return 2;
@@ -1787,6 +1800,8 @@ static const char *command_to_string(const enum todo_command command)
{
if (command < TODO_COMMENT)
return todo_command_info[command].str;
+ if (command == TODO_COMMENT)
+ return comment_line_str;
die(_("unknown command: %d"), command);
}
@@ -1794,7 +1809,7 @@ static char command_to_char(const enum todo_command command)
{
if (command < TODO_COMMENT)
return todo_command_info[command].c;
- return comment_line_char;
+ return 0;
}
static int is_noop(const enum todo_command command)
@@ -1848,7 +1863,7 @@ static int is_fixup_flag(enum todo_command command, unsigned flag)
static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
{
const char *s = str;
- while (len > 0 && s[0] == comment_line_char) {
+ while (starts_with_mem(s, len, comment_line_str)) {
size_t count;
const char *n = memchr(s, '\n', len);
if (!n)
@@ -1859,7 +1874,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, comment_line_char);
+ strbuf_add_commented_lines(buf, s, len, comment_line_str);
}
/* Does the current fixup chain contain a squash command? */
@@ -1954,11 +1969,11 @@ static int append_squash_message(struct strbuf *buf, const char *body,
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
commented_len = commit_subject_length(body);
- strbuf_addf(buf, "\n%c ", comment_line_char);
+ strbuf_addf(buf, "\n%s ", comment_line_str);
strbuf_addf(buf, _(nth_commit_msg_fmt),
++opts->current_fixup_count + 1);
strbuf_addstr(buf, "\n\n");
- strbuf_add_commented_lines(buf, body, commented_len, comment_line_char);
+ strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
/* buf->buf may be reallocated so store an offset into the buffer */
fixup_off = buf->len;
strbuf_addstr(buf, body + commented_len);
@@ -2011,10 +2026,10 @@ static int update_squash_messages(struct repository *r,
return error(_("could not read '%s'"),
rebase_path_squash_msg());
- eol = buf.buf[0] != comment_line_char ?
+ eol = !starts_with(buf.buf, comment_line_str) ?
buf.buf : strchrnul(buf.buf, '\n');
- strbuf_addf(&header, "%c ", comment_line_char);
+ strbuf_addf(&header, "%s ", comment_line_str);
strbuf_addf(&header, _(combined_commit_msg_fmt),
opts->current_fixup_count + 2);
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
@@ -2040,16 +2055,16 @@ static int update_squash_messages(struct repository *r,
repo_unuse_commit_buffer(r, head_commit, head_message);
return error(_("cannot write '%s'"), rebase_path_fixup_msg());
}
- strbuf_addf(&buf, "%c ", comment_line_char);
+ strbuf_addf(&buf, "%s ", comment_line_str);
strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
- strbuf_addf(&buf, "\n%c ", comment_line_char);
+ strbuf_addf(&buf, "\n%s ", comment_line_str);
strbuf_addstr(&buf, is_fixup_flag(command, flag) ?
_(skip_first_commit_msg_str) :
_(first_commit_msg_str));
strbuf_addstr(&buf, "\n\n");
if (is_fixup_flag(command, flag))
strbuf_add_commented_lines(&buf, body, strlen(body),
- comment_line_char);
+ comment_line_str);
else
strbuf_addstr(&buf, body);
@@ -2064,12 +2079,12 @@ static int update_squash_messages(struct repository *r,
if (command == TODO_SQUASH || is_fixup_flag(command, flag)) {
res = append_squash_message(&buf, body, command, opts, flag);
} else if (command == TODO_FIXUP) {
- strbuf_addf(&buf, "\n%c ", comment_line_char);
+ strbuf_addf(&buf, "\n%s ", comment_line_str);
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
++opts->current_fixup_count + 1);
strbuf_addstr(&buf, "\n\n");
strbuf_add_commented_lines(&buf, body, strlen(body),
- comment_line_char);
+ comment_line_str);
} else
return error(_("unknown command: %d"), command);
repo_unuse_commit_buffer(r, commit, message);
@@ -2414,9 +2429,10 @@ static int do_pick_commit(struct repository *r,
} else if (allow == 2) {
drop_commit = 1;
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
unlink(git_path_merge_msg(r));
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
fprintf(stderr,
_("dropping %s %s -- patch contents already upstream\n"),
oid_to_hex(&commit->object.oid), msg.subject);
@@ -2569,7 +2585,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
/* left-trim */
bol += strspn(bol, " \t");
- if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
+ if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
item->command = TODO_COMMENT;
item->commit = NULL;
item->arg_offset = bol - buf;
@@ -2810,7 +2826,7 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
if (!refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0) &&
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF) &&
verbose)
warning(_("cancelling a cherry picking in progress"));
opts.action = REPLAY_PICK;
@@ -2819,14 +2835,15 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
- NULL, 0) &&
+ NULL, REF_NO_DEREF) &&
verbose)
warning(_("cancelling a revert in progress"));
opts.action = REPLAY_REVERT;
need_cleanup = 1;
}
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
if (!need_cleanup)
return;
@@ -2932,6 +2949,9 @@ static int populate_opts_cb(const char *key, const char *value,
else if (!strcmp(key, "options.allow-empty-message"))
opts->allow_empty_message =
git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+ else if (!strcmp(key, "options.drop-redundant-commits"))
+ opts->drop_redundant_commits =
+ git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
else if (!strcmp(key, "options.keep-redundant-commits"))
opts->keep_redundant_commits =
git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -3466,54 +3486,57 @@ static int save_opts(struct replay_opts *opts)
if (opts->no_commit)
res |= git_config_set_in_file_gently(opts_file,
- "options.no-commit", "true");
+ "options.no-commit", NULL, "true");
if (opts->edit >= 0)
- res |= git_config_set_in_file_gently(opts_file, "options.edit",
+ res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
opts->edit ? "true" : "false");
if (opts->allow_empty)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-empty", "true");
+ "options.allow-empty", NULL, "true");
if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-empty-message", "true");
+ "options.allow-empty-message", NULL, "true");
+ if (opts->drop_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.drop-redundant-commits", NULL, "true");
if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file,
- "options.keep-redundant-commits", "true");
+ "options.keep-redundant-commits", NULL, "true");
if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file,
- "options.signoff", "true");
+ "options.signoff", NULL, "true");
if (opts->record_origin)
res |= git_config_set_in_file_gently(opts_file,
- "options.record-origin", "true");
+ "options.record-origin", NULL, "true");
if (opts->allow_ff)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-ff", "true");
+ "options.allow-ff", NULL, "true");
if (opts->mainline) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%d", opts->mainline);
res |= git_config_set_in_file_gently(opts_file,
- "options.mainline", buf.buf);
+ "options.mainline", NULL, buf.buf);
strbuf_release(&buf);
}
if (opts->strategy)
res |= git_config_set_in_file_gently(opts_file,
- "options.strategy", opts->strategy);
+ "options.strategy", NULL, opts->strategy);
if (opts->gpg_sign)
res |= git_config_set_in_file_gently(opts_file,
- "options.gpg-sign", opts->gpg_sign);
+ "options.gpg-sign", NULL, opts->gpg_sign);
for (size_t i = 0; i < opts->xopts.nr; i++)
res |= git_config_set_multivar_in_file_gently(opts_file,
"options.strategy-option",
- opts->xopts.v[i], "^$", 0);
+ opts->xopts.v[i], "^$", NULL, 0);
if (opts->allow_rerere_auto)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-rerere-auto",
+ "options.allow-rerere-auto", NULL,
opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
"true" : "false");
if (opts->explicit_cleanup)
res |= git_config_set_in_file_gently(opts_file,
- "options.default-msg-cleanup",
+ "options.default-msg-cleanup", NULL,
describe_cleanup_mode(opts->default_msg_cleanup));
return res;
}
@@ -3647,6 +3670,7 @@ static int do_exec(struct repository *r, const char *command_line)
fprintf(stderr, _("Executing: %s\n"), command_line);
cmd.use_shell = 1;
strvec_push(&cmd.args, command_line);
+ strvec_push(&cmd.env, "GIT_CHERRY_PICK_HELP");
status = run_command(&cmd);
/* force re-reading of the cache */
@@ -3887,6 +3911,8 @@ static int do_reset(struct repository *r,
}
tree = parse_tree_indirect(&oid);
+ if (!tree)
+ return error(_("unable to read tree (%s)"), oid_to_hex(&oid));
prime_cache_tree(r, r->index, tree);
if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
@@ -3913,7 +3939,7 @@ static int do_merge(struct repository *r,
int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
- struct commit_list *bases, *j;
+ struct commit_list *bases = NULL, *j;
struct commit_list *to_merge = NULL, **tail = &to_merge;
const char *strategy = !opts->xopts.nr &&
(!opts->strategy ||
@@ -4124,7 +4150,7 @@ static int do_merge(struct repository *r,
strbuf_release(&ref_name);
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
rollback_lock_file(&lock);
ret = run_command(&cmd);
@@ -4139,7 +4165,11 @@ static int do_merge(struct repository *r,
}
merge_commit = to_merge->item;
- bases = repo_get_merge_bases(r, head_commit, merge_commit);
+ if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) {
+ ret = -1;
+ goto leave_merge;
+ }
+
if (bases && oideq(&merge_commit->object.oid,
&bases->item->object.oid)) {
ret = 0;
@@ -4469,12 +4499,17 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
return -1;
}
-void create_autostash(struct repository *r, const char *path)
+static void create_autostash_internal(struct repository *r,
+ const char *path,
+ const char *refname)
{
struct strbuf buf = STRBUF_INIT;
struct lock_file lock_file = LOCK_INIT;
int fd;
+ if (path && refname)
+ BUG("can only pass path or refname");
+
fd = repo_hold_locked_index(r, &lock_file, 0);
refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
if (0 <= fd)
@@ -4501,10 +4536,16 @@ void create_autostash(struct repository *r, const char *path)
strbuf_reset(&buf);
strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
- if (safe_create_leading_directories_const(path))
- die(_("Could not create directory for '%s'"),
- path);
- write_file(path, "%s", oid_to_hex(&oid));
+ if (path) {
+ if (safe_create_leading_directories_const(path))
+ die(_("Could not create directory for '%s'"),
+ path);
+ write_file(path, "%s", oid_to_hex(&oid));
+ } else {
+ refs_update_ref(get_main_ref_store(r), "", refname,
+ &oid, null_oid(), 0, UPDATE_REFS_DIE_ON_ERR);
+ }
+
printf(_("Created autostash: %s\n"), buf.buf);
if (reset_head(r, &ropts) < 0)
die(_("could not reset --hard"));
@@ -4515,6 +4556,16 @@ void create_autostash(struct repository *r, const char *path)
strbuf_release(&buf);
}
+void create_autostash(struct repository *r, const char *path)
+{
+ create_autostash_internal(r, path, NULL);
+}
+
+void create_autostash_ref(struct repository *r, const char *refname)
+{
+ create_autostash_internal(r, NULL, refname);
+}
+
static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -4592,6 +4643,41 @@ int apply_autostash_oid(const char *stash_oid)
return apply_save_autostash_oid(stash_oid, 1);
}
+static int apply_save_autostash_ref(struct repository *r, const char *refname,
+ int attempt_apply)
+{
+ struct object_id stash_oid;
+ char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+ int flag, ret;
+
+ if (!refs_ref_exists(get_main_ref_store(r), refname))
+ return 0;
+
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname,
+ RESOLVE_REF_READING, &stash_oid, &flag))
+ return -1;
+ if (flag & REF_ISSYMREF)
+ return error(_("autostash reference is a symref"));
+
+ oid_to_hex_r(stash_oid_hex, &stash_oid);
+ ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
+
+ refs_delete_ref(get_main_ref_store(r), "", refname,
+ &stash_oid, REF_NO_DEREF);
+
+ return ret;
+}
+
+int save_autostash_ref(struct repository *r, const char *refname)
+{
+ return apply_save_autostash_ref(r, refname, 0);
+}
+
+int apply_autostash_ref(struct repository *r, const char *refname)
+{
+ return apply_save_autostash_ref(r, refname, 1);
+}
+
static int checkout_onto(struct repository *r, struct replay_opts *opts,
const char *onto_name, const struct object_id *onto,
const struct object_id *orig_head)
@@ -4774,8 +4860,10 @@ static int pick_commits(struct repository *r,
}
unlink(rebase_path_author_script());
unlink(git_path_merge_head(r));
- unlink(git_path_auto_merge(r));
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(r), "", "REBASE_HEAD",
+ NULL, REF_NO_DEREF);
if (item->command == TODO_BREAK) {
if (!opts->verbose)
@@ -5116,7 +5204,7 @@ static int commit_staged_changes(struct repository *r,
if (refs_ref_exists(get_main_ref_store(r),
"CHERRY_PICK_HEAD") &&
refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0))
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF))
return error(_("could not remove CHERRY_PICK_HEAD"));
if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
return error_errno(_("could not remove '%s'"),
@@ -5130,7 +5218,8 @@ static int commit_staged_changes(struct repository *r,
return error(_("could not commit staged changes."));
unlink(rebase_path_amend());
unlink(git_path_merge_head(r));
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
if (final_fixup) {
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
@@ -5605,8 +5694,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
oid_to_hex(&commit->object.oid),
oneline.buf);
if (is_empty)
- strbuf_addf(&buf, " %c empty",
- comment_line_char);
+ strbuf_addf(&buf, " %s empty",
+ comment_line_str);
FLEX_ALLOC_STR(entry, string, buf.buf);
oidcpy(&entry->entry.oid, &commit->object.oid);
@@ -5696,7 +5785,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
entry = oidmap_get(&state.commit2label, &commit->object.oid);
if (entry)
- strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+ strbuf_addf(out, "\n%s Branch %s\n", comment_line_str, entry->string);
else
strbuf_addch(out, '\n');
@@ -5833,7 +5922,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
oid_to_hex(&commit->object.oid));
pretty_print_commit(&pp, commit, out);
if (is_empty)
- strbuf_addf(out, " %c empty", comment_line_char);
+ strbuf_addf(out, " %s empty", comment_line_str);
strbuf_addch(out, '\n');
}
if (skipped_commit)
diff --git a/sequencer.h b/sequencer.h
index 913a0f652d..437eabd38a 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -14,6 +14,8 @@ const char *rebase_path_todo(void);
const char *rebase_path_todo_backup(void);
const char *rebase_path_dropped(void);
+extern const char *rebase_resolvemsg;
+
#define APPEND_SIGNOFF_DEDUP (1u << 0)
enum replay_action {
@@ -225,9 +227,12 @@ void commit_post_rewrite(struct repository *r,
const struct object_id *new_head);
void create_autostash(struct repository *r, const char *path);
+void create_autostash_ref(struct repository *r, const char *refname);
int save_autostash(const char *path);
+int save_autostash_ref(struct repository *r, const char *refname);
int apply_autostash(const char *path);
int apply_autostash_oid(const char *stash_oid);
+int apply_autostash_ref(struct repository *r, const char *refname);
#define SUMMARY_INITIAL_COMMIT (1 << 0)
#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
diff --git a/serve.c b/serve.c
index a1d71134d4..aa651b73e9 100644
--- a/serve.c
+++ b/serve.c
@@ -12,6 +12,7 @@
#include "trace2.h"
static int advertise_sid = -1;
+static int advertise_object_info = -1;
static int client_hash_algo = GIT_HASH_SHA1;
static int always_advertise(struct repository *r UNUSED,
@@ -67,6 +68,17 @@ static void session_id_receive(struct repository *r UNUSED,
trace2_data_string("transfer", NULL, "client-sid", client_sid);
}
+static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED)
+{
+ if (advertise_object_info == -1 &&
+ repo_config_get_bool(r, "transfer.advertiseobjectinfo",
+ &advertise_object_info)) {
+ /* disabled by default */
+ advertise_object_info = 0;
+ }
+ return advertise_object_info;
+}
+
struct protocol_capability {
/*
* The name of the capability. The server uses this name when
@@ -135,7 +147,7 @@ static struct protocol_capability capabilities[] = {
},
{
.name = "object-info",
- .advertise = always_advertise,
+ .advertise = object_info_advertise,
.command = cap_object_info,
},
{
diff --git a/setup.c b/setup.c
index fc592dc6dd..f4b32f76e3 100644
--- a/setup.c
+++ b/setup.c
@@ -13,7 +13,6 @@
#include "string-list.h"
#include "chdir-notify.h"
#include "path.h"
-#include "promisor-remote.h"
#include "quote.h"
#include "trace2.h"
#include "worktree.h"
@@ -559,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")) {
@@ -590,6 +591,36 @@ static enum extension_result handle_extension(const char *var,
"extensions.objectformat", value);
data->hash_algo = format;
return EXTENSION_OK;
+ } else if (!strcmp(ext, "compatobjectformat")) {
+ struct string_list_item *item;
+ int format;
+
+ if (!value)
+ return config_error_nonbool(var);
+ format = hash_algo_by_name(value);
+ if (format == GIT_HASH_UNKNOWN)
+ return error(_("invalid value for '%s': '%s'"),
+ "extensions.compatobjectformat", value);
+ /* For now only support compatObjectFormat being specified once. */
+ for_each_string_list_item(item, &data->v1_only_extensions) {
+ if (!strcmp(item->string, "compatobjectformat"))
+ return error(_("'%s' already specified as '%s'"),
+ "extensions.compatobjectformat",
+ hash_algos[data->compat_hash_algo].name);
+ }
+ data->compat_hash_algo = format;
+ return EXTENSION_OK;
+ } else if (!strcmp(ext, "refstorage")) {
+ unsigned int format;
+
+ if (!value)
+ return config_error_nonbool(var);
+ format = ref_storage_format_by_name(value);
+ if (format == REF_STORAGE_FORMAT_UNKNOWN)
+ return error(_("invalid value for '%s': '%s'"),
+ "extensions.refstorage", value);
+ data->ref_storage_format = format;
+ return EXTENSION_OK;
}
return EXTENSION_UNKNOWN;
}
@@ -1231,6 +1262,32 @@ static const char *allowed_bare_repo_to_string(
return NULL;
}
+static int is_implicit_bare_repo(const char *path)
+{
+ /*
+ * what we found is a ".git" directory at the root of
+ * the working tree.
+ */
+ if (ends_with_path_components(path, ".git"))
+ return 1;
+
+ /*
+ * we are inside $GIT_DIR of a secondary worktree of a
+ * non-bare repository.
+ */
+ if (strstr(path, "/.git/worktrees/"))
+ return 1;
+
+ /*
+ * we are inside $GIT_DIR of a worktree of a non-embedded
+ * submodule, whose superproject is not a bare repository.
+ */
+ if (strstr(path, "/.git/modules/"))
+ return 1;
+
+ return 0;
+}
+
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
@@ -1359,7 +1416,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
if (is_git_directory(dir->buf)) {
trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
- if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
+ if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
+ !is_implicit_bare_repo(dir->buf))
return GIT_DIR_DISALLOWED_BARE;
if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
return GIT_DIR_INVALID_OWNERSHIP;
@@ -1564,6 +1622,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_compat_hash_algo(the_repository,
+ repo_fmt.compat_hash_algo);
+ repo_set_ref_storage_format(the_repository,
+ repo_fmt.ref_storage_format);
the_repository->repository_format_worktree_config =
repo_fmt.worktree_config;
/* take ownership of repo_fmt.partial_clone */
@@ -1657,6 +1719,9 @@ 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_compat_hash_algo(the_repository, fmt->compat_hash_algo);
+ repo_set_ref_storage_format(the_repository,
+ fmt->ref_storage_format);
the_repository->repository_format_worktree_config =
fmt->worktree_config;
the_repository->repository_format_partial_clone =
@@ -1865,12 +1930,22 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1;
}
-void initialize_repository_version(int hash_algo, int reinit)
+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)
+ /*
+ * Note that we initialize the repository version to 1 when the ref
+ * storage format is unknown. This is on purpose so that we can add the
+ * correct object format to the config during git-clone(1). The format
+ * version will get adjusted by git-clone(1) once it has learned about
+ * the remote repository's format.
+ */
+ if (hash_algo != GIT_HASH_SHA1 ||
+ ref_storage_format != REF_STORAGE_FORMAT_FILES)
repo_version = GIT_REPO_VERSION_READ;
/* This forces creation of new config file */
@@ -1878,28 +1953,76 @@ void initialize_repository_version(int hash_algo, int reinit)
"%d", repo_version);
git_config_set("core.repositoryformatversion", repo_version_string);
- if (hash_algo != GIT_HASH_SHA1)
+ if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)
git_config_set("extensions.objectformat",
hash_algos[hash_algo].name);
else if (reinit)
git_config_set_gently("extensions.objectformat", NULL);
+
+ if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+ git_config_set("extensions.refstorage",
+ ref_storage_format_to_name(ref_storage_format));
+}
+
+static int is_reinit(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ char junk[2];
+ int ret;
+
+ git_path_buf(&buf, "HEAD");
+ ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
+ strbuf_release(&buf);
+ return ret;
+}
+
+void create_reference_database(unsigned int ref_storage_format,
+ const char *initial_branch, int quiet)
+{
+ struct strbuf err = STRBUF_INIT;
+ int reinit = is_reinit();
+
+ repo_set_ref_storage_format(the_repository, ref_storage_format);
+ if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
+ die("failed to set up refs db: %s", err.buf);
+
+ /*
+ * Point the HEAD symref to the initial branch with if HEAD does
+ * not yet exist.
+ */
+ if (!reinit) {
+ char *ref;
+
+ if (!initial_branch)
+ initial_branch = git_default_branch_name(quiet);
+
+ ref = xstrfmt("refs/heads/%s", initial_branch);
+ if (check_refname_format(ref, 0) < 0)
+ die(_("invalid initial branch name: '%s'"),
+ initial_branch);
+
+ if (create_symref("HEAD", ref, NULL) < 0)
+ exit(1);
+ free(ref);
+ }
+
+ if (reinit && initial_branch)
+ warning(_("re-init: ignored --initial-branch=%s"),
+ initial_branch);
+
+ strbuf_release(&err);
}
static int create_default_files(const char *template_path,
const char *original_git_dir,
- const char *initial_branch,
const struct repository_format *fmt,
- int prev_bare_repository,
- int init_shared_repository,
- int quiet)
+ int init_shared_repository)
{
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();
@@ -1919,40 +2042,16 @@ static int create_default_files(const char *template_path,
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.
- */
+
+ is_bare_repository_cfg = !work_tree;
/*
* We would have created the above under user's umask -- under
@@ -1962,40 +2061,7 @@ static int create_default_files(const char *template_path,
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);
+ initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
/* Check filemode trustability */
path = git_path_buf(&buf, "config");
@@ -2108,15 +2174,35 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
}
+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, const char *initial_branch,
+ 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;
@@ -2142,7 +2228,6 @@ int init_db(const char *git_dir, const char *real_git_dir,
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
@@ -2152,16 +2237,21 @@ int init_db(const char *git_dir, const char *real_git_dir,
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,
- initial_branch, &repo_fmt,
- prev_bare_repository,
- init_shared_repository,
- flags & INIT_DB_QUIET);
- if (reinit && initial_branch)
- warning(_("re-init: ignored --initial-branch=%s"),
- initial_branch);
+ &repo_fmt, init_shared_repository);
+
+ /*
+ * Now that we have set up both the hash algorithm and the ref storage
+ * format we can update the repository's settings accordingly.
+ */
+ repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+ repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+ if (!(flags & INIT_DB_SKIP_REFDB))
+ create_reference_database(repo_fmt.ref_storage_format,
+ initial_branch, flags & INIT_DB_QUIET);
create_object_directory();
if (get_shared_repository()) {
diff --git a/setup.h b/setup.h
index b48cf1c43b..d88bb37aaf 100644
--- a/setup.h
+++ b/setup.h
@@ -115,6 +115,8 @@ struct repository_format {
int worktree_config;
int is_bare;
int hash_algo;
+ int compat_hash_algo;
+ unsigned int ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@@ -131,6 +133,7 @@ struct repository_format {
.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, \
}
@@ -169,14 +172,20 @@ int verify_repository_format(const struct repository_format *format,
*/
void check_repository_format(struct repository_format *fmt);
-#define INIT_DB_QUIET 0x0001
-#define INIT_DB_EXIST_OK 0x0002
+#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, int reinit);
+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!!
diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c
index 133496bd4d..f69fd16610 100644
--- a/sh-i18n--envsubst.c
+++ b/sh-i18n--envsubst.c
@@ -31,7 +31,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>. */
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
/* closeout.c - close standard output and standard error
Copyright (C) 1998-2007 Free Software Foundation, Inc.
@@ -47,7 +47,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>. */
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdio.h>
diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c
index dede2cbddf..f993ef9c69 100644
--- a/sha1dc/sha1.c
+++ b/sha1dc/sha1.c
@@ -88,7 +88,7 @@
/*
* Should define Big Endian for a whitelist of known processors. See
* https://sourceforge.net/p/predef/wiki/Endianness/ and
- * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ * https://web.archive.org/web/20140421151132/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html
*/
#define SHA1DC_BIGENDIAN
diff --git a/shallow.c b/shallow.c
index ac728cdd77..7ff50dd0da 100644
--- a/shallow.c
+++ b/shallow.c
@@ -7,7 +7,6 @@
#include "commit.h"
#include "tag.h"
#include "pkt-line.h"
-#include "remote.h"
#include "refs.h"
#include "oid-array.h"
#include "path.h"
@@ -795,12 +794,16 @@ static void post_assign_shallow(struct shallow_info *info,
if (!*bitmap)
continue;
for (j = 0; j < bitmap_nr; j++)
- if (bitmap[0][j] &&
- /* Step 7, reachability test at commit level */
- !repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits)) {
- update_refstatus(ref_status, info->ref->nr, *bitmap);
- dst++;
- break;
+ if (bitmap[0][j]) {
+ /* Step 7, reachability test at commit level */
+ int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1);
+ if (ret < 0)
+ exit(128);
+ if (!ret) {
+ update_refstatus(ref_status, info->ref->nr, *bitmap);
+ dst++;
+ break;
+ }
}
}
info->nr_ours = dst;
@@ -828,7 +831,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
si->reachable[c] = repo_in_merge_bases_many(the_repository,
commit,
si->nr_commits,
- si->commits);
+ si->commits,
+ 1);
+ if (si->reachable[c] < 0)
+ exit(128);
si->need_reachability_test[c] = 0;
}
return si->reachable[c];
diff --git a/shared.mak b/shared.mak
index aeb80fc4d5..29bebd30d8 100644
--- a/shared.mak
+++ b/shared.mak
@@ -108,3 +108,11 @@ endif
define mkdir_p_parent_template
$(if $(wildcard $(@D)),,$(QUIET_MKDIR_P_PARENT)$(shell mkdir -p $(@D)))
endef
+
+## Getting sick of writing -L$(SOMELIBDIR) $(CC_LD_DYNPATH)$(SOMELIBDIR)?
+## Write $(call libpath_template,$(SOMELIBDIR)) instead, perhaps?
+## With CC_LD_DYNPATH set to either an empty string or to "-L", the
+## the directory is not shown the second time.
+define libpath_template
+-L$(1) $(if $(filter-out -L,$(CC_LD_DYNPATH)),$(CC_LD_DYNPATH)$(1))
+endef
diff --git a/shell.c b/shell.c
index 5c67e7bd97..2ece8b16e2 100644
--- a/shell.c
+++ b/shell.c
@@ -4,7 +4,6 @@
#include "strbuf.h"
#include "run-command.h"
#include "alias.h"
-#include "prompt.h"
#define COMMAND_DIR "git-shell-commands"
#define HELP_COMMAND COMMAND_DIR "/help"
diff --git a/sideband.c b/sideband.c
index 6cbfd391c4..5d8907151f 100644
--- a/sideband.c
+++ b/sideband.c
@@ -69,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)
{
@@ -217,7 +220,7 @@ int demultiplex_sideband(const char *me, int status,
}
strbuf_addch(scratch, *brk);
- xwrite(2, scratch->buf, scratch->len);
+ write_in_full(2, scratch->buf, scratch->len);
strbuf_reset(scratch);
b = brk + 1;
@@ -244,7 +247,7 @@ cleanup:
die("%s", scratch->buf);
if (scratch->len) {
strbuf_addch(scratch, '\n');
- xwrite(2, scratch->buf, scratch->len);
+ write_in_full(2, scratch->buf, scratch->len);
}
strbuf_release(scratch);
return 1;
diff --git a/sparse-index.c b/sparse-index.c
index 3578feb283..e48e40cae7 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -579,8 +579,9 @@ void expand_to_path(struct index_state *istate,
replace++;
temp = *replace;
*replace = '\0';
+ substr_len = replace - path_mutable.buf;
if (index_file_exists(istate, path_mutable.buf,
- path_mutable.len, icase)) {
+ substr_len, icase)) {
/*
* We found a parent directory in the name-hash
* hashtable, because only sparse directory entries
@@ -593,7 +594,6 @@ void expand_to_path(struct index_state *istate,
}
*replace = temp;
- substr_len = replace - path_mutable.buf;
}
cleanup:
diff --git a/statinfo.c b/statinfo.c
index 9367ca099c..3c6bc049c1 100644
--- a/statinfo.c
+++ b/statinfo.c
@@ -31,6 +31,33 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
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;
diff --git a/statinfo.h b/statinfo.h
index 700f502ac0..5b21a30f90 100644
--- a/statinfo.h
+++ b/statinfo.h
@@ -47,6 +47,14 @@ struct stat_validity {
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,
diff --git a/strbuf.c b/strbuf.c
index 7827178d8e..1492a08225 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -24,6 +24,17 @@ int istarts_with(const char *str, const char *prefix)
return 0;
}
+int starts_with_mem(const char *str, size_t len, const char *prefix)
+{
+ const char *end = str + len;
+ for (; ; str++, prefix++) {
+ if (!*prefix)
+ return 1;
+ else if (str == end || *str != *prefix)
+ return 0;
+ }
+}
+
int skip_to_optional_arg_default(const char *str, const char *prefix,
const char **arg, const char *def)
{
@@ -340,18 +351,17 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
}
static void add_lines(struct strbuf *out,
- const char *prefix1,
- const char *prefix2,
- const char *buf, size_t size)
+ const char *prefix,
+ const char *buf, size_t size,
+ int space_after_prefix)
{
while (size) {
- const char *prefix;
const char *next = memchr(buf, '\n', size);
next = next ? (next + 1) : (buf + size);
- prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t'))
- ? prefix2 : prefix1);
strbuf_addstr(out, prefix);
+ if (space_after_prefix && buf[0] != '\n' && buf[0] != '\t')
+ strbuf_addch(out, ' ');
strbuf_add(out, buf, next - buf);
size -= next - buf;
buf = next;
@@ -360,19 +370,12 @@ static void add_lines(struct strbuf *out,
}
void strbuf_add_commented_lines(struct strbuf *out, const char *buf,
- size_t size, char comment_line_char)
+ size_t size, const char *comment_prefix)
{
- static char prefix1[3];
- static char prefix2[2];
-
- if (prefix1[0] != comment_line_char) {
- xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char);
- xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char);
- }
- add_lines(out, prefix1, prefix2, buf, size);
+ add_lines(out, comment_prefix, buf, size, 1);
}
-void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix,
const char *fmt, ...)
{
va_list params;
@@ -383,7 +386,7 @@ void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
strbuf_vaddf(&buf, fmt, params);
va_end(params);
- strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_line_char);
+ strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_prefix);
if (incomplete_line)
sb->buf[--sb->len] = '\0';
@@ -442,6 +445,26 @@ size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder)
return 0;
}
+void strbuf_expand_bad_format(const char *format, const char *command)
+{
+ const char *end;
+
+ if (*format != '(')
+ /* TRANSLATORS: The first %s is a command like "ls-tree". */
+ die(_("bad %s format: element '%s' does not start with '('"),
+ command, format);
+
+ end = strchr(format + 1, ')');
+ if (!end)
+ /* TRANSLATORS: The first %s is a command like "ls-tree". */
+ die(_("bad %s format: element '%s' does not end in ')'"),
+ command, format);
+
+ /* TRANSLATORS: %s is a command like "ls-tree". */
+ die(_("bad %s format: %%%.*s"),
+ command, (int)(end - format + 1), format);
+}
+
void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
{
size_t i, len = src->len;
@@ -750,7 +773,7 @@ ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
void strbuf_add_lines(struct strbuf *out, const char *prefix,
const char *buf, size_t size)
{
- add_lines(out, prefix, NULL, buf, size);
+ add_lines(out, prefix, buf, size, 0);
}
void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
@@ -1005,10 +1028,10 @@ static size_t cleanup(char *line, size_t len)
*
* If last line does not have a newline at the end, one is added.
*
- * Pass a non-NUL comment_line_char to skip every line starting
+ * Pass a non-NULL comment_prefix to skip every line starting
* with it.
*/
-void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
+void strbuf_stripspace(struct strbuf *sb, const char *comment_prefix)
{
size_t empties = 0;
size_t i, j, len, newlen;
@@ -1021,8 +1044,8 @@ void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
eol = memchr(sb->buf + i, '\n', sb->len - i);
len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
- if (comment_line_char && len &&
- sb->buf[i] == comment_line_char) {
+ if (comment_prefix && len &&
+ starts_with(sb->buf + i, comment_prefix)) {
newlen = 0;
continue;
}
diff --git a/strbuf.h b/strbuf.h
index e959caca87..97fa4a3d01 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -288,7 +288,7 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
*/
void strbuf_add_commented_lines(struct strbuf *out,
const char *buf, size_t size,
- char comment_line_char);
+ const char *comment_prefix);
/**
@@ -338,6 +338,11 @@ size_t strbuf_expand_literal(struct strbuf *sb, const char *placeholder);
int strbuf_expand_step(struct strbuf *sb, const char **formatp);
/**
+ * Used with `strbuf_expand_step` to report unknown placeholders.
+ */
+void strbuf_expand_bad_format(const char *format, const char *command);
+
+/**
* Append the contents of one strbuf to another, quoting any
* percent signs ("%") into double-percents ("%%") in the
* destination. This is useful for literal data to be fed to either
@@ -379,7 +384,7 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
* blank to the buffer.
*/
__attribute__((format (printf, 3, 4)))
-void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, const char *fmt, ...);
+void strbuf_commented_addf(struct strbuf *sb, const char *comment_prefix, const char *fmt, ...);
__attribute__((format (printf,2,0)))
void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
@@ -513,11 +518,11 @@ int strbuf_getcwd(struct strbuf *sb);
int strbuf_normalize_path(struct strbuf *sb);
/**
- * Strip whitespace from a buffer. If comment_line_char is non-NUL,
+ * Strip whitespace from a buffer. If comment_prefix is non-NULL,
* then lines beginning with that character are considered comments,
* thus removed.
*/
-void strbuf_stripspace(struct strbuf *buf, char comment_line_char);
+void strbuf_stripspace(struct strbuf *buf, const char *comment_prefix);
static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
{
@@ -673,6 +678,7 @@ char *xstrfmt(const char *fmt, ...);
int starts_with(const char *str, const char *prefix);
int istarts_with(const char *str, const char *prefix);
+int starts_with_mem(const char *str, size_t len, const char *prefix);
/*
* If the string "str" is the same as the string in "prefix", then the "arg"
diff --git a/strvec.h b/strvec.h
index 9f55c8766b..4715d3e51f 100644
--- a/strvec.h
+++ b/strvec.h
@@ -4,8 +4,8 @@
/**
* The strvec API allows one to dynamically build and store
* NULL-terminated arrays of strings. A strvec maintains the invariant that the
- * `items` member always points to a non-NULL array, and that the array is
- * always NULL-terminated at the element pointed to by `items[nr]`. This
+ * `v` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `v[nr]`. This
* makes the result suitable for passing to functions expecting to receive
* argv from main().
*
@@ -22,7 +22,7 @@ extern const char *empty_strvec[];
/**
* A single array. This should be initialized by assignment from
- * `STRVEC_INIT`, or by calling `strvec_init`. The `items`
+ * `STRVEC_INIT`, or by calling `strvec_init`. The `v`
* member contains the actual array; the `nr` member contains the
* number of elements in the array, not including the terminating
* NULL.
@@ -80,7 +80,7 @@ void strvec_split(struct strvec *, const char *);
void strvec_clear(struct strvec *);
/**
- * Disconnect the `items` member from the `strvec` struct and
+ * Disconnect the `v` member from the `strvec` struct and
* return it. The caller is responsible for freeing the memory used
* by the array, and by the strings it references. After detaching,
* the `strvec` is in a reinitialized state and can be pushed
diff --git a/submodule-config.c b/submodule-config.c
index 6a48fd12f6..11428b4ada 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -14,6 +14,8 @@
#include "parse-options.h"
#include "thread-utils.h"
#include "tree-walk.h"
+#include "url.h"
+#include "urlmatch.h"
/*
* submodule cache lookup structure
@@ -228,6 +230,144 @@ in_component:
return 0;
}
+static int starts_with_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
+}
+
+static int starts_with_dot_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
+}
+
+static int submodule_url_is_relative(const char *url)
+{
+ return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
+}
+
+/*
+ * Count directory components that a relative submodule URL should chop
+ * from the remote_url it is to be resolved against.
+ *
+ * In other words, this counts "../" components at the start of a
+ * submodule URL.
+ *
+ * Returns the number of directory components to chop and writes a
+ * pointer to the next character of url after all leading "./" and
+ * "../" components to out.
+ */
+static int count_leading_dotdots(const char *url, const char **out)
+{
+ int result = 0;
+ while (1) {
+ if (starts_with_dot_dot_slash(url)) {
+ result++;
+ url += strlen("../");
+ continue;
+ }
+ if (starts_with_dot_slash(url)) {
+ url += strlen("./");
+ continue;
+ }
+ *out = url;
+ return result;
+ }
+}
+/*
+ * Check whether a transport is implemented by git-remote-curl.
+ *
+ * If it is, returns 1 and writes the URL that would be passed to
+ * git-remote-curl to the "out" parameter.
+ *
+ * Otherwise, returns 0 and leaves "out" untouched.
+ *
+ * Examples:
+ * http::https://example.com/repo.git -> 1, https://example.com/repo.git
+ * https://example.com/repo.git -> 1, https://example.com/repo.git
+ * git://example.com/repo.git -> 0
+ *
+ * This is for use in checking for previously exploitable bugs that
+ * required a submodule URL to be passed to git-remote-curl.
+ */
+static int url_to_curl_url(const char *url, const char **out)
+{
+ /*
+ * We don't need to check for case-aliases, "http.exe", and so
+ * on because in the default configuration, is_transport_allowed
+ * prevents URLs with those schemes from being cloned
+ * automatically.
+ */
+ if (skip_prefix(url, "http::", out) ||
+ skip_prefix(url, "https::", out) ||
+ skip_prefix(url, "ftp::", out) ||
+ skip_prefix(url, "ftps::", out))
+ return 1;
+ if (starts_with(url, "http://") ||
+ starts_with(url, "https://") ||
+ starts_with(url, "ftp://") ||
+ starts_with(url, "ftps://")) {
+ *out = url;
+ return 1;
+ }
+ return 0;
+}
+
+int check_submodule_url(const char *url)
+{
+ const char *curl_url;
+
+ if (looks_like_command_line_option(url))
+ return -1;
+
+ if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
+ char *decoded;
+ const char *next;
+ int has_nl;
+
+ /*
+ * This could be appended to an http URL and url-decoded;
+ * check for malicious characters.
+ */
+ decoded = url_decode(url);
+ has_nl = !!strchr(decoded, '\n');
+
+ free(decoded);
+ if (has_nl)
+ return -1;
+
+ /*
+ * URLs which escape their root via "../" can overwrite
+ * the host field and previous components, resolving to
+ * URLs like https::example.com/submodule.git and
+ * https:///example.com/submodule.git that were
+ * susceptible to CVE-2020-11008.
+ */
+ if (count_leading_dotdots(url, &next) > 0 &&
+ (*next == ':' || *next == '/'))
+ return -1;
+ }
+
+ else if (url_to_curl_url(url, &curl_url)) {
+ int ret = 0;
+ char *normalized = url_normalize(curl_url, NULL);
+ if (normalized) {
+ char *decoded = url_decode(normalized);
+ if (strchr(decoded, '\n'))
+ ret = -1;
+ free(normalized);
+ free(decoded);
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
static int name_and_item_from_var(const char *var, struct strbuf *name,
struct strbuf *item)
{
@@ -516,7 +656,9 @@ static int parse_config(const char *var, const char *value,
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 {
@@ -836,7 +978,7 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value)
{
int ret;
- ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
+ ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value);
if (ret < 0)
/* Maybe the user already did that, don't error out here */
warning(_("Could not update .gitmodules entry %s"), key);
diff --git a/submodule-config.h b/submodule-config.h
index 2a37689cc2..b6133af71b 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -2,9 +2,7 @@
#define SUBMODULE_CONFIG_CACHE_H
#include "config.h"
-#include "hashmap.h"
#include "submodule.h"
-#include "strbuf.h"
#include "tree-walk.h"
/**
@@ -91,6 +89,9 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value);
*/
int check_submodule_name(const char *name);
+/* Returns 0 if the URL valid per RFC3986 and -1 otherwise. */
+int check_submodule_url(const char *url);
+
/*
* Note: these helper functions exist solely to maintain backward
* compatibility with 'fetch' and 'update_clone' storing configuration in
diff --git a/submodule.c b/submodule.c
index e603a19a87..ce2d032521 100644
--- a/submodule.c
+++ b/submodule.c
@@ -17,10 +17,8 @@
#include "string-list.h"
#include "oid-array.h"
#include "strvec.h"
-#include "blob.h"
#include "thread-utils.h"
#include "path.h"
-#include "quote.h"
#include "remote.h"
#include "worktree.h"
#include "parse-options.h"
@@ -30,7 +28,6 @@
#include "commit-reach.h"
#include "read-cache-ll.h"
#include "setup.h"
-#include "shallow.h"
#include "trace2.h"
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
@@ -595,7 +592,12 @@ static void show_submodule_header(struct diff_options *o,
(!is_null_oid(two) && !*right))
message = "(commits not present)";
- *merge_bases = repo_get_merge_bases(sub, *left, *right);
+ *merge_bases = NULL;
+ if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) {
+ message = "(corrupt repository)";
+ goto output_header;
+ }
+
if (*merge_bases) {
if ((*merge_bases)->item == *left)
fast_forward = 1;
@@ -1690,8 +1692,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
task = get_fetch_task_from_changed(spf, err);
if (task) {
- struct strbuf submodule_prefix = STRBUF_INIT;
-
child_process_init(cp);
cp->dir = task->repo->gitdir;
prepare_submodule_repo_env_in_gitdir(&cp->env);
@@ -1701,15 +1701,11 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
strvec_pushv(&cp->args, task->git_args.v);
strvec_pushv(&cp->args, spf->args.v);
strvec_push(&cp->args, task->default_argv);
- strvec_push(&cp->args, "--submodule-prefix");
+ strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+ spf->prefix, task->sub->path);
- strbuf_addf(&submodule_prefix, "%s%s/",
- spf->prefix,
- task->sub->path);
- strvec_push(&cp->args, submodule_prefix.buf);
*task_cb = task;
- strbuf_release(&submodule_prefix);
string_list_insert(&spf->seen_submodule_names, task->sub->name);
return 1;
}
@@ -1717,12 +1713,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
if (spf->oid_fetch_tasks_nr) {
struct fetch_task *task =
spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1];
- struct strbuf submodule_prefix = STRBUF_INIT;
spf->oid_fetch_tasks_nr--;
- strbuf_addf(&submodule_prefix, "%s%s/",
- spf->prefix, task->sub->path);
-
child_process_init(cp);
prepare_submodule_repo_env_in_gitdir(&cp->env);
cp->git_cmd = 1;
@@ -1731,8 +1723,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
strvec_init(&cp->args);
strvec_pushv(&cp->args, spf->args.v);
strvec_push(&cp->args, "on-demand");
- strvec_push(&cp->args, "--submodule-prefix");
- strvec_push(&cp->args, submodule_prefix.buf);
+ strvec_pushf(&cp->args, "--submodule-prefix=%s%s/",
+ spf->prefix, task->sub->path);
/* NEEDSWORK: have get_default_remote from submodule--helper */
strvec_push(&cp->args, "origin");
@@ -1740,7 +1732,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
append_oid_to_argv, &cp->args);
*task_cb = task;
- strbuf_release(&submodule_prefix);
return 1;
}
@@ -2055,7 +2046,7 @@ void submodule_unset_core_worktree(const struct submodule *sub)
submodule_name_to_gitdir(&config_path, the_repository, sub->name);
strbuf_addstr(&config_path, "/config");
- if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL))
+ if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL))
warning(_("Could not unset core.worktree setting in submodule '%s'"),
sub->path);
diff --git a/t/Makefile b/t/Makefile
index 225aaf78ed..2d95046f26 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -6,6 +9,7 @@ include ../shared.mak
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak.uname
-include ../config.mak.autogen
-include ../config.mak
@@ -42,14 +46,16 @@ TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
-UNIT_TESTS = $(sort $(filter-out %.pdb unit-tests/bin/t-basic%,$(wildcard unit-tests/bin/t-*)))
+UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c)
+UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES))
+UNIT_TESTS = $(sort $(filter-out unit-tests/bin/t-basic%,$(UNIT_TEST_PROGRAMS)))
# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
# checks all tests in all scripts via a single invocation, so tell individual
# scripts not to run the external "chainlint.pl" script themselves
CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
-all: $(DEFAULT_TEST_TARGET)
+all:: $(DEFAULT_TEST_TARGET)
test: pre-clean check-chainlint $(TEST_LINT)
$(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
@@ -103,20 +109,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 | \
- sed -e 's/^[1-9][0-9]* //;/^[ ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
- if test -f ../GIT-BUILD-OPTIONS; then \
- . ../GIT-BUILD-OPTIONS; \
- fi && \
- if test -x ../git$$X; then \
- DIFFW="../git$$X --no-pager diff -w --no-index"; \
- else \
- DIFFW="diff -w -u"; \
- fi && \
- $$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+ sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+ diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
test-lint-filenames
diff --git a/t/README b/t/README
index a0ebe29484..d9e0e07506 100644
--- a/t/README
+++ b/t/README
@@ -32,7 +32,14 @@ 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
+t/Makefile defines a target for each test file, such that you can also use
+shell pattern matching to run a subset of the tests:
+
+ make *checkout*
+
+will run all tests with 'checkout' in their filename.
+
+Since the tests all output TAP (see https://testanything.org) they can
be run with any TAP harness. Here's an example of parallel testing
powered by a recent version of prove(1):
@@ -479,6 +486,9 @@ 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_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.
@@ -721,6 +731,26 @@ The "do's:"
Note that we still &&-chain the loop to propagate failures from
earlier commands.
+ - Repeat tests with slightly different arguments in a loop.
+
+ In some cases it may make sense to re-run the same set of tests with
+ different options or commands to ensure that the command behaves
+ despite the different parameters. This can be achieved by looping
+ around a specific parameter:
+
+ for arg in '' "--foo"
+ do
+ test_expect_success "test command ${arg:-without arguments}" '
+ command $arg
+ '
+ done
+
+ Note that while the test title uses double quotes ("), the test body
+ should continue to use single quotes (') to avoid breakage in case the
+ values contain e.g. quoting characters. The loop variable will be
+ accessible regardless of the single quotes as the test body is passed
+ to `eval`.
+
And here are the "don'ts:"
@@ -1278,7 +1308,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:
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 5e21e84f38..87572459e4 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -532,7 +532,7 @@ test_expect_success 'blame -L :funcname with userdiff driver' '
"$(cat file.template)" &&
test_commit --author "B <B@test.git>" \
"change" "$fortran_file" \
- "$(cat file.template | sed -e s/ChangeMe/IWasChanged/)" &&
+ "$(sed -e s/ChangeMe/IWasChanged/ file.template)" &&
check_count -f "$fortran_file" -L:RIGHT A 3 B 1
'
diff --git a/t/chainlint/blank-line-before-esac.expect b/t/chainlint/blank-line-before-esac.expect
index 48ed4eb124..056e03003d 100644
--- a/t/chainlint/blank-line-before-esac.expect
+++ b/t/chainlint/blank-line-before-esac.expect
@@ -1,11 +1,11 @@
-test_done ( ) {
+test_done () {
case "$test_failure" in
- 0 )
+ 0)
test_at_end_hook_
exit 0 ;;
- * )
+ *)
if test $test_external_has_tap -eq 0
then
say_color error "# failed $test_failure among $msg"
@@ -14,5 +14,5 @@ test_done ( ) {
exit 1 ;;
- esac
+ esac
}
diff --git a/t/chainlint/blank-line.expect b/t/chainlint/blank-line.expect
index f76fde1ffb..b47827d749 100644
--- a/t/chainlint/blank-line.expect
+++ b/t/chainlint/blank-line.expect
@@ -1,4 +1,8 @@
(
+
nothing &&
+
something
+
+
)
diff --git a/t/chainlint/block.expect b/t/chainlint/block.expect
index a3bcea492a..1c87326364 100644
--- a/t/chainlint/block.expect
+++ b/t/chainlint/block.expect
@@ -12,9 +12,9 @@
) &&
{
- echo a ; ?!AMP?! echo b
+ echo a; ?!AMP?! echo b
} &&
-{ echo a ; ?!AMP?! echo b ; } &&
+{ echo a; ?!AMP?! echo b; } &&
{
echo "${var}9" &&
diff --git a/t/chainlint/chain-break-background.expect b/t/chainlint/chain-break-background.expect
index 28f9114f42..20d0bb5333 100644
--- a/t/chainlint/chain-break-background.expect
+++ b/t/chainlint/chain-break-background.expect
@@ -1,9 +1,9 @@
JGIT_DAEMON_PID= &&
git init --bare empty.git &&
-> empty.git/git-daemon-export-ok &&
+>empty.git/git-daemon-export-ok &&
mkfifo jgit_daemon_output &&
{
- jgit daemon --port="$JGIT_DAEMON_PORT" . > jgit_daemon_output &
+ jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
JGIT_DAEMON_PID=$!
} &&
test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
diff --git a/t/chainlint/chain-break-return-exit.expect b/t/chainlint/chain-break-return-exit.expect
index 1732d221c3..4cd18e2edf 100644
--- a/t/chainlint/chain-break-return-exit.expect
+++ b/t/chainlint/chain-break-return-exit.expect
@@ -1,16 +1,16 @@
case "$(git ls-files)" in
-one ) echo pass one ;;
-* ) echo bad one ; return 1 ;;
+one) echo pass one ;;
+*) echo bad one; return 1 ;;
esac &&
(
case "$(git ls-files)" in
- two ) echo pass two ;;
- * ) echo bad two ; exit 1 ;;
-esac
+ two) echo pass two ;;
+ *) echo bad two; exit 1 ;;
+ esac
) &&
case "$(git ls-files)" in
-dir/two"$LF"one ) echo pass both ;;
-* ) echo bad ; return 1 ;;
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
esac &&
for i in 1 2 3 4 ; do
diff --git a/t/chainlint/chain-break-status.expect b/t/chainlint/chain-break-status.expect
index f4bada9463..e6b3b2193e 100644
--- a/t/chainlint/chain-break-status.expect
+++ b/t/chainlint/chain-break-status.expect
@@ -1,7 +1,7 @@
-OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
-{ test-tool sigchain > actual ; ret=$? ; } &&
+{ test-tool sigchain >actual; ret=$?; } &&
{
test_match_signal 15 "$ret" ||
test "$ret" = 3
diff --git a/t/chainlint/chained-subshell.expect b/t/chainlint/chained-subshell.expect
index af0369d328..83810ea7ec 100644
--- a/t/chainlint/chained-subshell.expect
+++ b/t/chainlint/chained-subshell.expect
@@ -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)
diff --git a/t/chainlint/command-substitution-subsubshell.expect b/t/chainlint/command-substitution-subsubshell.expect
index ab2f79e845..ec42f2c30c 100644
--- a/t/chainlint/command-substitution-subsubshell.expect
+++ b/t/chainlint/command-substitution-subsubshell.expect
@@ -1,2 +1,2 @@
-OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
diff --git a/t/chainlint/dqstring-line-splice.expect b/t/chainlint/dqstring-line-splice.expect
index bf9ced60d4..37eab80738 100644
--- a/t/chainlint/dqstring-line-splice.expect
+++ b/t/chainlint/dqstring-line-splice.expect
@@ -1,3 +1,5 @@
-echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' > expect &&
-test_must_fail git commit --fixup=reword:HEAD~ $1 2 > actual &&
+
+echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
test_cmp expect actual
+
diff --git a/t/chainlint/dqstring-no-interpolate.expect b/t/chainlint/dqstring-no-interpolate.expect
index 10724987a5..087eda15e4 100644
--- a/t/chainlint/dqstring-no-interpolate.expect
+++ b/t/chainlint/dqstring-no-interpolate.expect
@@ -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
+
diff --git a/t/chainlint/empty-here-doc.expect b/t/chainlint/empty-here-doc.expect
index e8733c97c6..8507721192 100644
--- a/t/chainlint/empty-here-doc.expect
+++ b/t/chainlint/empty-here-doc.expect
@@ -1,4 +1,4 @@
-git ls-tree $tree path > current &&
-cat > expected <<\EOF &&
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
EOF
test_output
diff --git a/t/chainlint/exclamation.expect b/t/chainlint/exclamation.expect
index 2d961a58c6..765a35bb4c 100644
--- a/t/chainlint/exclamation.expect
+++ b/t/chainlint/exclamation.expect
@@ -1,4 +1,4 @@
-if ! condition ; then echo nope ; else yep ; fi &&
+if ! condition; then echo nope; else yep; fi &&
test_prerequisite !MINGW &&
mail uucp!address &&
echo !whatever!
diff --git a/t/chainlint/for-loop-abbreviated.expect b/t/chainlint/for-loop-abbreviated.expect
index a21007a63f..02c0d15cca 100644
--- a/t/chainlint/for-loop-abbreviated.expect
+++ b/t/chainlint/for-loop-abbreviated.expect
@@ -1,5 +1,5 @@
for it
do
- path=$(expr "$it" : ( [^:]*) ) &&
+ path=$(expr "$it" : ([^:]*)) &&
git update-index --add "$path" || exit
done
diff --git a/t/chainlint/for-loop.expect b/t/chainlint/for-loop.expect
index d65c82129a..d2237f1e38 100644
--- a/t/chainlint/for-loop.expect
+++ b/t/chainlint/for-loop.expect
@@ -6,6 +6,7 @@
bar
EOF
done ?!AMP?!
+
for i in a b c; do
echo $i &&
cat $i ?!LOOP?!
diff --git a/t/chainlint/function.expect b/t/chainlint/function.expect
index a14388e6b9..dd7c997a3c 100644
--- a/t/chainlint/function.expect
+++ b/t/chainlint/function.expect
@@ -1,8 +1,8 @@
-sha1_file ( ) {
+sha1_file() {
echo "$*" | sed "s#..#.git/objects/&/#"
} &&
-remove_object ( ) {
+remove_object() {
file=$(sha1_file "$*") &&
test -e "$file" ?!AMP?!
rm -f "$file"
diff --git a/t/chainlint/here-doc.expect b/t/chainlint/here-doc.expect
index 1df3f78282..91b961242a 100644
--- a/t/chainlint/here-doc.expect
+++ b/t/chainlint/here-doc.expect
@@ -1,6 +1,6 @@
boodle wobba \
- gorgo snoot \
- wafta snurb <<EOF &&
+ gorgo snoot \
+ wafta snurb <<EOF &&
quoth the raven,
nevermore...
EOF
diff --git a/t/chainlint/loop-detect-status.expect b/t/chainlint/loop-detect-status.expect
index 24da9e86d5..7ce3a34806 100644
--- a/t/chainlint/loop-detect-status.expect
+++ b/t/chainlint/loop-detect-status.expect
@@ -1,18 +1,18 @@
-( while test $i -le $blobcount
-do
- printf "Generating blob $i/$blobcount\r" >& 2 &&
+(while test $i -le $blobcount
+ do
+ printf "Generating blob $i/$blobcount\r" >&2 &&
printf "blob\nmark :$i\ndata $blobsize\n" &&
#test-tool genrandom $i $blobsize &&
printf "%-${blobsize}s" $i &&
echo "M 100644 :$i $i" >> commit &&
i=$(($i+1)) ||
echo $? > exit-status
-done &&
-echo "commit refs/heads/main" &&
-echo "author A U Thor <author@email.com> 123456789 +0000" &&
-echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
-echo "data 5" &&
-echo ">2gb" &&
-cat commit ) |
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
git fast-import --big-file-threshold=2 &&
test ! -f exit-status
diff --git a/t/chainlint/nested-cuddled-subshell.expect b/t/chainlint/nested-cuddled-subshell.expect
index 2a86885ee6..3836049cc4 100644
--- a/t/chainlint/nested-cuddled-subshell.expect
+++ b/t/chainlint/nested-cuddled-subshell.expect
@@ -2,18 +2,24 @@
(cd foo &&
bar
) &&
+
(cd foo &&
bar
) ?!AMP?!
+
(
cd foo &&
bar) &&
+
(
cd foo &&
bar) ?!AMP?!
+
(cd foo &&
bar) &&
+
(cd foo &&
bar) ?!AMP?!
+
foobar
)
diff --git a/t/chainlint/nested-loop-detect-failure.expect b/t/chainlint/nested-loop-detect-failure.expect
index 4793a0e8e1..3461df40e5 100644
--- a/t/chainlint/nested-loop-detect-failure.expect
+++ b/t/chainlint/nested-loop-detect-failure.expect
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
do
- for j in 0 1 2 3 4 5 6 7 8 9 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" ?!LOOP?!
+ echo "$i$j" >"path$i$j" ?!LOOP?!
done ?!LOOP?!
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
do
- for j in 0 1 2 3 4 5 6 7 8 9 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" || return 1
+ echo "$i$j" >"path$i$j" || return 1
done
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
do
- for j in 0 1 2 3 4 5 6 7 8 9 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" ?!LOOP?!
+ echo "$i$j" >"path$i$j" ?!LOOP?!
done || return 1
done &&
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
do
- for j in 0 1 2 3 4 5 6 7 8 9 ;
+ for j in 0 1 2 3 4 5 6 7 8 9;
do
- echo "$i$j" > "path$i$j" || return 1
+ echo "$i$j" >"path$i$j" || return 1
done || return 1
done
diff --git a/t/chainlint/nested-subshell.expect b/t/chainlint/nested-subshell.expect
index 02e0a9f1bb..73ff28546a 100644
--- a/t/chainlint/nested-subshell.expect
+++ b/t/chainlint/nested-subshell.expect
@@ -4,6 +4,7 @@
echo a &&
echo b
) >file &&
+
cd foo &&
(
echo a ?!AMP?!
diff --git a/t/chainlint/pipe.expect b/t/chainlint/pipe.expect
index 2cfc028297..811971b1a3 100644
--- a/t/chainlint/pipe.expect
+++ b/t/chainlint/pipe.expect
@@ -2,7 +2,9 @@
foo |
bar |
baz &&
+
fish |
cow ?!AMP?!
+
sunder
)
diff --git a/t/chainlint/subshell-here-doc.expect b/t/chainlint/subshell-here-doc.expect
index 52789278d1..75d6f607e2 100644
--- a/t/chainlint/subshell-here-doc.expect
+++ b/t/chainlint/subshell-here-doc.expect
@@ -1,7 +1,7 @@
(
echo wobba \
- gorgo snoot \
- wafta snurb <<-EOF &&
+ gorgo snoot \
+ wafta snurb <<-EOF &&
quoth the raven,
nevermore...
EOF
diff --git a/t/chainlint/subshell-one-liner.expect b/t/chainlint/subshell-one-liner.expect
index b7015361bf..8f694990e8 100644
--- a/t/chainlint/subshell-one-liner.expect
+++ b/t/chainlint/subshell-one-liner.expect
@@ -2,13 +2,18 @@
(foo && bar) &&
(foo && bar) |
(foo && bar) >baz &&
+
(foo; ?!AMP?! bar) &&
(foo; ?!AMP?! bar) |
(foo; ?!AMP?! bar) >baz &&
+
(foo || exit 1) &&
(foo || exit 1) |
(foo || exit 1) >baz &&
+
(foo && bar) ?!AMP?!
+
(foo && bar; ?!AMP?! baz) ?!AMP?!
+
foobar
)
diff --git a/t/chainlint/t7900-subtree.expect b/t/chainlint/t7900-subtree.expect
index 71b3b3bc20..02f3129232 100644
--- a/t/chainlint/t7900-subtree.expect
+++ b/t/chainlint/t7900-subtree.expect
@@ -15,6 +15,7 @@ main-sub4" &&
$chkms
TXT
) &&
+
subfiles=$(git ls-files) &&
check_equal "$subfiles" "$chkms
$chks"
diff --git a/t/chainlint/token-pasting.expect b/t/chainlint/token-pasting.expect
index 342360bcd0..6a387917a7 100644
--- a/t/chainlint/token-pasting.expect
+++ b/t/chainlint/token-pasting.expect
@@ -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/while-loop.expect b/t/chainlint/while-loop.expect
index 1f5eaea0fd..06c1567f48 100644
--- a/t/chainlint/while-loop.expect
+++ b/t/chainlint/while-loop.expect
@@ -6,6 +6,7 @@
bar
EOF
done ?!AMP?!
+
while true; do
echo foo &&
cat bar ?!LOOP?!
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl
index dd8107cd7d..b2b28c2ced 100755
--- a/t/check-non-portable-shell.pl
+++ b/t/check-non-portable-shell.pl
@@ -47,6 +47,8 @@ while (<>) {
/\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)';
+ /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and
+ err q(quote "$val" in 'local var=$val');
/^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
$line = '';
diff --git a/t/helper/test-bundle-uri.c b/t/helper/test-bundle-uri.c
index 475058592d..09dc78733c 100644
--- a/t/helper/test-bundle-uri.c
+++ b/t/helper/test-bundle-uri.c
@@ -5,9 +5,7 @@
#include "strbuf.h"
#include "string-list.h"
#include "transport.h"
-#include "ref-filter.h"
#include "remote.h"
-#include "refs.h"
enum input_mode {
KEY_VALUE_PAIRS,
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644
index e5659df40b..0000000000
--- a/t/helper/test-ctype.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "test-tool.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
- printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
- rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
- /*
- * We can't find NUL using strchr. Accept it as the first
- * character in the spec -- there are no empty classes.
- */
- if (ch == '\0')
- return ch == *s;
- if (*s == '\0')
- s++;
- return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) { \
- int i; \
- for (i = 0; i < 256; i++) { \
- if (is_in(s, i) != t(i)) \
- report_error(#t, i); \
- } \
- if (t(EOF)) \
- report_error(#t, EOF); \
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-#define ASCII \
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
- "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
- "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
- "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
- "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
- "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
- "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
-#define CNTRL \
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
- "\x7f"
-
-int cmd__ctype(int argc UNUSED, const char **argv UNUSED)
-{
- TEST_CLASS(isdigit, DIGIT);
- TEST_CLASS(isspace, " \n\r\t");
- TEST_CLASS(isalpha, LOWER UPPER);
- TEST_CLASS(isalnum, LOWER UPPER DIGIT);
- TEST_CLASS(is_glob_special, "*?[\\");
- TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
- TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
- TEST_CLASS(isascii, ASCII);
- TEST_CLASS(islower, LOWER);
- TEST_CLASS(isupper, UPPER);
- TEST_CLASS(iscntrl, CNTRL);
- TEST_CLASS(ispunct, PUNCT);
- TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
- TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
-
- return rc;
-}
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 0683d46574..f25512de9a 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -52,7 +52,7 @@ static void show_dates(const char **argv, const char *format)
arg++;
tz = atoi(arg);
- printf("%s -> %s\n", *argv, show_date(t, tz, &mode));
+ printf("%s -> %s\n", *argv, show_date(t, tz, mode));
}
date_mode_release(&mode);
diff --git a/t/helper/test-delete-gpgsig.c b/t/helper/test-delete-gpgsig.c
new file mode 100644
index 0000000000..e36831af03
--- /dev/null
+++ b/t/helper/test-delete-gpgsig.c
@@ -0,0 +1,62 @@
+#include "test-tool.h"
+#include "gpg-interface.h"
+#include "strbuf.h"
+
+
+int cmd__delete_gpgsig(int argc, const char **argv)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *pattern = "gpgsig";
+ const char *bufptr, *tail, *eol;
+ int deleting = 0;
+ size_t plen;
+
+ if (argc >= 2) {
+ pattern = argv[1];
+ argv++;
+ argc--;
+ }
+
+ plen = strlen(pattern);
+ strbuf_read(&buf, 0, 0);
+
+ if (!strcmp(pattern, "trailer")) {
+ size_t payload_size = parse_signed_buffer(buf.buf, buf.len);
+ fwrite(buf.buf, 1, payload_size, stdout);
+ fflush(stdout);
+ return 0;
+ }
+
+ bufptr = buf.buf;
+ tail = bufptr + buf.len;
+
+ while (bufptr < tail) {
+ /* Find the end of the line */
+ eol = memchr(bufptr, '\n', tail - bufptr);
+ if (!eol)
+ eol = tail;
+
+ /* Drop continuation lines */
+ if (deleting && (bufptr < eol) && (bufptr[0] == ' ')) {
+ bufptr = eol + 1;
+ continue;
+ }
+ deleting = 0;
+
+ /* Does the line match the prefix? */
+ if (((bufptr + plen) < eol) &&
+ !memcmp(bufptr, pattern, plen) &&
+ (bufptr[plen] == ' ')) {
+ deleting = 1;
+ bufptr = eol + 1;
+ continue;
+ }
+
+ /* Print all other lines */
+ fwrite(bufptr, 1, (eol - bufptr) + 1, stdout);
+ bufptr = eol + 1;
+ }
+ fflush(stdout);
+
+ return 0;
+}
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644
index cac20a72b3..0000000000
--- a/t/helper/test-fast-rebase.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * "git fast-rebase" builtin command
- *
- * FAST: Forking Any Subprocesses (is) Taboo
- *
- * This is meant SOLELY as a demo of what is possible. sequencer.c and
- * rebase.c should be refactored to use the ideas here, rather than attempting
- * to extend this file to replace those (unless Phillip or Dscho say that
- * refactoring is too hard and we need a clean slate, but I'm guessing that
- * refactoring is the better route).
- */
-
-#define USE_THE_INDEX_VARIABLE
-#include "test-tool.h"
-#include "cache-tree.h"
-#include "commit.h"
-#include "environment.h"
-#include "gettext.h"
-#include "hash.h"
-#include "hex.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "object-name.h"
-#include "read-cache-ll.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "setup.h"
-#include "strvec.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;
- 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;
-}
-
-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 (repo_get_oid(the_repository, "HEAD", &head))
- die(_("Cannot read HEAD"));
- assert(oideq(&onto->object.oid, &head));
-
- repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
- if (repo_read_index(the_repository) < 0)
- BUG("Could not read index");
-
- repo_init_revisions(the_repository, &revs, NULL);
- revs.verbose_header = 1;
- revs.max_parents = 1;
- revs.cherry_mark = 1;
- revs.limited = 1;
- revs.reverse = 1;
- revs.right_only = 1;
- revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
- revs.topo_order = 1;
- strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
-
- if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
- ret = error(_("unhandled options"));
- goto cleanup;
- }
-
- strvec_clear(&rev_walk_args);
-
- if (prepare_revision_walk(&revs) < 0) {
- ret = error(_("error preparing revisions"));
- goto cleanup;
- }
-
- init_merge_options(&merge_opt, the_repository);
- memset(&result, 0, sizeof(result));
- merge_opt.show_rename_progress = 1;
- merge_opt.branch1 = "HEAD";
- head_tree = repo_get_commit_tree(the_repository, 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 = repo_get_commit_tree(the_repository, commit);
- base_tree = repo_get_commit_tree(the_repository, 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-pkt-line.c b/t/helper/test-pkt-line.c
index f4d134a145..4daa82f00f 100644
--- a/t/helper/test-pkt-line.c
+++ b/t/helper/test-pkt-line.c
@@ -1,7 +1,9 @@
#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)
{
@@ -64,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;
@@ -79,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 */
@@ -97,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;
}
@@ -126,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"))
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
deleted file mode 100644
index f0bf255f5f..0000000000
--- a/t/helper/test-prio-queue.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "test-tool.h"
-#include "prio-queue.h"
-
-static int intcmp(const void *va, const void *vb, void *data UNUSED)
-{
- const int *a = va, *b = vb;
- return *a - *b;
-}
-
-static void show(int *v)
-{
- if (!v)
- printf("NULL\n");
- else
- printf("%d\n", *v);
- free(v);
-}
-
-int cmd__prio_queue(int argc UNUSED, const char **argv)
-{
- struct prio_queue pq = { intcmp };
-
- while (*++argv) {
- if (!strcmp(*argv, "get")) {
- void *peek = prio_queue_peek(&pq);
- void *get = prio_queue_get(&pq);
- if (peek != get)
- BUG("peek and get results do not match");
- show(get);
- } else if (!strcmp(*argv, "dump")) {
- void *peek;
- void *get;
- while ((peek = prio_queue_peek(&pq))) {
- get = prio_queue_get(&pq);
- if (peek != get)
- BUG("peek and get results do not match");
- show(get);
- }
- } else if (!strcmp(*argv, "stack")) {
- pq.compare = NULL;
- } else {
- int *v = xmalloc(sizeof(*v));
- *v = atoi(*argv);
- prio_queue_put(&pq, v);
- }
- }
-
- clear_prio_queue(&pq);
-
- return 0;
-}
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 3e173399a0..1e3b431e3e 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -1,11 +1,9 @@
#include "test-tool.h"
#include "commit.h"
#include "commit-reach.h"
-#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "parse-options.h"
#include "ref-filter.h"
#include "setup.h"
#include "string-list.h"
@@ -113,13 +111,16 @@ int cmd__reach(int ac, const char **av)
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],
- repo_in_merge_bases_many(the_repository, A, X_nr, X_array));
+ repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
else if (!strcmp(av[1], "is_descendant_of"))
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
else if (!strcmp(av[1], "get_merge_bases_many")) {
- struct commit_list *list = repo_get_merge_bases_many(the_repository,
- A, X_nr,
- X_array);
+ struct commit_list *list = NULL;
+ if (repo_get_merge_bases_many(the_repository,
+ A, X_nr,
+ X_array,
+ &list) < 0)
+ exit(128);
printf("%s(A,X):\n", av[1]);
print_sorted_commit_ids(list);
} else if (!strcmp(av[1], "reduce_heads")) {
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 702ec1f128..82bbf6e2e6 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -112,25 +112,6 @@ static const char **get_store(const char **argv, struct ref_store **refs)
return argv + 1;
}
-static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
- FLAG_DEF(PACK_REFS_ALL),
- { NULL, 0 } };
-
-static int cmd_pack_refs(struct ref_store *refs, const char **argv)
-{
- unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
- 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 };
-
- 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)
{
const char *refname = notnull(*argv++, "refname");
@@ -221,15 +202,21 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
return ret;
}
+static int each_reflog(const char *refname, void *cb_data UNUSED)
+{
+ printf("%s\n", refname);
+ return 0;
+}
+
static int cmd_for_each_reflog(struct ref_store *refs,
const char **argv UNUSED)
{
- return refs_for_each_reflog(refs, each_ref, NULL);
+ return refs_for_each_reflog(refs, each_reflog, NULL);
}
-static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
- const char *committer, timestamp_t timestamp,
- int tz, const char *msg, void *cb_data UNUSED)
+static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
+ const char *committer, timestamp_t timestamp,
+ int tz, const char *msg, void *cb_data UNUSED)
{
printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
oid_to_hex(new_oid), committer, timestamp, tz,
@@ -241,14 +228,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+ return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs);
}
static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
{
const char *refname = notnull(*argv++, "refname");
- return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+ return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs);
}
static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
@@ -320,7 +307,6 @@ struct command {
};
static struct command commands[] = {
- { "pack-refs", cmd_pack_refs },
{ "create-symref", cmd_create_symref },
{ "delete-refs", cmd_delete_refs },
{ "rename-ref", cmd_rename_ref },
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index bd871a735b..80042eafc2 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -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");
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 4cd8a952e5..0c7c5aa4dd 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -1,10 +1,8 @@
#include "test-tool.h"
#include "commit-graph.h"
#include "commit.h"
-#include "config.h"
#include "environment.h"
#include "hex.h"
-#include "object-store-ll.h"
#include "object.h"
#include "repository.h"
#include "setup.h"
diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c
index 941ae7e3bc..fb5927775d 100644
--- a/t/helper/test-simple-ipc.c
+++ b/t/helper/test-simple-ipc.c
@@ -4,7 +4,6 @@
#include "test-tool.h"
#include "gettext.h"
-#include "strbuf.h"
#include "simple-ipc.h"
#include "parse-options.h"
#include "thread-utils.h"
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 356e0a26c5..7197969a08 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -4,16 +4,24 @@
#include "remote.h"
#include "repository.h"
#include "setup.h"
+#include "strbuf.h"
#include "submodule-config.h"
#include "submodule.h"
#define TEST_TOOL_CHECK_NAME_USAGE \
- "test-tool submodule check-name <name>"
+ "test-tool submodule check-name"
static const char *submodule_check_name_usage[] = {
TEST_TOOL_CHECK_NAME_USAGE,
NULL
};
+#define TEST_TOOL_CHECK_URL_USAGE \
+ "test-tool submodule check-url"
+static const char *submodule_check_url_usage[] = {
+ TEST_TOOL_CHECK_URL_USAGE,
+ NULL
+};
+
#define TEST_TOOL_IS_ACTIVE_USAGE \
"test-tool submodule is-active <name>"
static const char *submodule_is_active_usage[] = {
@@ -30,31 +38,26 @@ static const char *submodule_resolve_relative_url_usage[] = {
static const char *submodule_usage[] = {
TEST_TOOL_CHECK_NAME_USAGE,
+ TEST_TOOL_CHECK_URL_USAGE,
TEST_TOOL_IS_ACTIVE_USAGE,
TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
NULL
};
+typedef int (*check_fn_t)(const char *);
+
/*
- * Exit non-zero if any of the submodule names given on the command line is
- * invalid. If no names are given, filter stdin to print only valid names
- * (which is primarily intended for testing).
+ * Apply 'check_fn' to each line of stdin, printing values that pass the check
+ * to stdout.
*/
-static int check_name(int argc, const char **argv)
+static int check_submodule(check_fn_t check_fn)
{
- if (argc > 1) {
- while (*++argv) {
- if (check_submodule_name(*argv) < 0)
- return 1;
- }
- } else {
- struct strbuf buf = STRBUF_INIT;
- while (strbuf_getline(&buf, stdin) != EOF) {
- if (!check_submodule_name(buf.buf))
- printf("%s\n", buf.buf);
- }
- strbuf_release(&buf);
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (!check_fn(buf.buf))
+ printf("%s\n", buf.buf);
}
+ strbuf_release(&buf);
return 0;
}
@@ -68,7 +71,20 @@ static int cmd__submodule_check_name(int argc, const char **argv)
if (argc)
usage_with_options(submodule_check_name_usage, options);
- return check_name(argc, argv);
+ return check_submodule(check_submodule_name);
+}
+
+static int cmd__submodule_check_url(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ argc = parse_options(argc, argv, "test-tools", options,
+ submodule_check_url_usage, 0);
+ if (argc)
+ usage_with_options(submodule_check_url_usage, options);
+
+ return check_submodule(check_submodule_url);
}
static int cmd__submodule_is_active(int argc, const char **argv)
@@ -194,6 +210,7 @@ static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
static struct test_cmd cmds[] = {
{ "check-name", cmd__submodule_check_name },
+ { "check-url", cmd__submodule_check_url },
{ "is-active", cmd__submodule_is_active },
{ "resolve-relative-url", cmd__submodule_resolve_relative_url},
{ "config-list", cmd__submodule_config_list },
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 876cd2dc31..80a946b847 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -19,8 +19,8 @@ static struct test_cmd cmds[] = {
{ "config", cmd__config },
{ "crontab", cmd__crontab },
{ "csprng", cmd__csprng },
- { "ctype", cmd__ctype },
{ "date", cmd__date },
+ { "delete-gpgsig", cmd__delete_gpgsig },
{ "delta", cmd__delta },
{ "dir-iterator", cmd__dir_iterator },
{ "drop-caches", cmd__drop_caches },
@@ -30,7 +30,6 @@ static struct test_cmd cmds[] = {
{ "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 },
@@ -57,7 +56,6 @@ static struct test_cmd cmds[] = {
{ "path-utils", cmd__path_utils },
{ "pcre2-config", cmd__pcre2_config },
{ "pkt-line", cmd__pkt_line },
- { "prio-queue", cmd__prio_queue },
{ "proc-receive", cmd__proc_receive },
{ "progress", cmd__progress },
{ "reach", cmd__reach },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 70dd4eba11..2808b92419 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -12,9 +12,9 @@ int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__crontab(int argc, const char **argv);
int cmd__csprng(int argc, const char **argv);
-int cmd__ctype(int argc, const char **argv);
int cmd__date(int argc, const char **argv);
int cmd__delta(int argc, const char **argv);
+int cmd__delete_gpgsig(int argc, const char **argv);
int cmd__dir_iterator(int argc, const char **argv);
int cmd__drop_caches(int argc, const char **argv);
int cmd__dump_cache_tree(int argc, const char **argv);
@@ -24,7 +24,6 @@ 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);
@@ -50,7 +49,6 @@ int cmd__partial_clone(int argc, const char **argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pcre2_config(int argc, const char **argv);
int cmd__pkt_line(int argc, const char **argv);
-int cmd__prio_queue(int argc, const char **argv);
int cmd__proc_receive(int argc, const char **argv);
int cmd__progress(int argc, const char **argv);
int cmd__reach(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index d5ca0046c8..1adac29a57 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -412,6 +412,56 @@ static int ut_201counter(int argc, const char **argv)
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>
@@ -438,6 +488,11 @@ static struct unit_test ut_table[] = {
{ 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/lib-credential.sh b/t/lib-credential.sh
index 15fc9a31e2..44799c0d38 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -50,6 +50,7 @@ helper_test_clean() {
reject $1 https example.com user-overwrite
reject $1 https example.com user-erase1
reject $1 https example.com user-erase2
+ reject $1 https victim.example.com user
reject $1 http path.tld user
reject $1 https timeout.tld user
reject $1 https sso.tld
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 32b3473379..57b9b2db9b 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -71,8 +71,8 @@ test_cmp_branch_tree () {
find . -type d -name .git -prune -o -type f -print
) | sort >module-git-"$1".list &&
test_cmp module-cvs-"$1".list module-git-"$1".list &&
- cat module-cvs-"$1".list | while read f
+ while read f
do
test_cmp_branch_file "$1" "$f" || return 1
- done
+ done <module-cvs-"$1".list
}
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 83b83c9abb..add11e88fc 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -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
diff --git a/t/lib-parallel-checkout.sh b/t/lib-parallel-checkout.sh
index acaee9cbb6..8324d6c96d 100644
--- a/t/lib-parallel-checkout.sh
+++ b/t/lib-parallel-checkout.sh
@@ -20,7 +20,7 @@ test_checkout_workers () {
BUG "too few arguments to test_checkout_workers"
fi &&
- local expected_workers=$1 &&
+ local expected_workers="$1" &&
shift &&
local trace_file=trace-test-checkout-workers &&
diff --git a/t/oid-info/hash-info b/t/oid-info/hash-info
index d0736dd1a0..b8a5bcb187 100644
--- a/t/oid-info/hash-info
+++ b/t/oid-info/hash-info
@@ -15,3 +15,15 @@ empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a3037218
empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321
+
+blob17_1 sha1:263
+blob17_1 sha256:34
+
+blob17_2 sha1:410
+blob17_2 sha256:174
+
+blob17_3 sha1:523
+blob17_3 sha256:313
+
+blob17_4 sha1:790
+blob17_4 sha256:481
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index b952e5024b..ab0c763411 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -15,7 +15,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# These variables must be set before the inclusion of test-lib.sh below,
# because it will change our working directory.
diff --git a/t/perf/repos/inflate-repo.sh b/t/perf/repos/inflate-repo.sh
index fcfc992b5b..412e4b450b 100755
--- a/t/perf/repos/inflate-repo.sh
+++ b/t/perf/repos/inflate-repo.sh
@@ -33,7 +33,7 @@ do
done
git ls-tree -r HEAD >GEN_src_list
-nr_src_files=$(cat GEN_src_list | wc -l)
+nr_src_files=$(wc -l <GEN_src_list)
src_branch=$(git symbolic-ref --short HEAD)
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 2b78e3be47..b131d665db 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -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 &&
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 736516cc6a..bf3bf604ab 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -40,7 +40,7 @@ test_expect_success 'final setup + check rev-parse --git-dir' '
test_expect_success 'check hash-object' '
echo "foo" >bar &&
- SHA=$(cat bar | git hash-object -w --stdin) &&
+ SHA=$(git hash-object -w --stdin <bar) &&
test_path_is_file "$REAL/objects/$(objpath $SHA)"
'
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index aee2298f01..774b52c298 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -19,6 +19,20 @@ 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" &&
@@ -558,4 +572,66 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
test_cmp expect err
'
+test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
+ >normal &&
+ attr_check_object_mode normal 100644 &&
+ mkdir dir &&
+ attr_check_object_mode dir 040000
+'
+
+test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
+ >exec &&
+ chmod +x exec &&
+ attr_check_object_mode exec 100755
+'
+
+test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
+ ln -s to_sym sym &&
+ attr_check_object_mode sym 120000
+'
+
+test_expect_success 'native object mode attributes work with --cached' '
+ >normal &&
+ git add normal &&
+ empty_blob=$(git rev-parse :normal) &&
+ git update-index --index-info <<-EOF &&
+ 100755 $empty_blob 0 exec
+ 120000 $empty_blob 0 symlink
+ EOF
+ attr_check_object_mode normal 100644 --cached &&
+ attr_check_object_mode exec 100755 --cached &&
+ attr_check_object_mode symlink 120000 --cached
+'
+
+test_expect_success 'check object mode attributes work for submodules' '
+ mkdir sub &&
+ (
+ cd sub &&
+ git init &&
+ mv .git .real &&
+ echo "gitdir: .real" >.git &&
+ test_commit first
+ ) &&
+ attr_check_object_mode sub 160000 &&
+ attr_check_object_mode sub unspecified --cached &&
+ git add sub &&
+ attr_check_object_mode sub 160000 --cached
+'
+
+test_expect_success 'we do not allow user defined builtin_* attributes' '
+ echo "foo* builtin_foo" >.gitattributes &&
+ git add .gitattributes 2>actual &&
+ echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'user defined builtin_objectmode values are ignored' '
+ echo "foo* builtin_objectmode=12345" >.gitattributes &&
+ git add .gitattributes &&
+ >foo_1 &&
+ attr_check_object_mode_basic foo_1 100644 &&
+ echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index e18b160286..3031256d14 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -46,6 +46,7 @@ check_show () {
TIME='1466000000 +0200'
check_show iso8601 "$TIME" '2016-06-15 16:13:20 +0200'
check_show iso8601-strict "$TIME" '2016-06-15T16:13:20+02:00'
+check_show iso8601-strict "$(echo "$TIME" | sed 's/+0200$/+0000/')" '2016-06-15T14:13:20Z'
check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 16:13:20 +0200'
check_show short "$TIME" '2016-06-15'
check_show default "$TIME" 'Wed Jun 15 16:13:20 2016 +0200'
@@ -69,6 +70,14 @@ check_show 'format:%s' '123456789 +1234' 123456789
check_show 'format:%s' '123456789 -1234' 123456789
check_show 'format-local:%s' '123456789 -1234' 123456789
+# negative TZ offset
+TIME='1466000000 -0200'
+check_show iso8601 "$TIME" '2016-06-15 12:13:20 -0200'
+check_show iso8601-strict "$TIME" '2016-06-15T12:13:20-02:00'
+check_show rfc2822 "$TIME" 'Wed, 15 Jun 2016 12:13:20 -0200'
+check_show default "$TIME" 'Wed Jun 15 12:13:20 2016 -0200'
+check_show raw "$TIME" '1466000000 -0200'
+
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
deleted file mode 100755
index eea99107a4..0000000000
--- a/t/t0009-prio-queue.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-
-test_description='basic tests for priority queue implementation'
-
-TEST_PASSES_SANITIZE_LEAK=true
-. ./test-lib.sh
-
-cat >expect <<'EOF'
-1
-2
-3
-4
-5
-5
-6
-7
-8
-9
-10
-EOF
-test_expect_success 'basic ordering' '
- test-tool prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-2
-3
-4
-1
-5
-6
-EOF
-test_expect_success 'mixed put and get' '
- test-tool prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-1
-2
-NULL
-1
-2
-NULL
-EOF
-test_expect_success 'notice empty queue' '
- test-tool prio-queue 1 2 get get get 1 2 get get get >actual &&
- test_cmp expect actual
-'
-
-cat >expect <<'EOF'
-3
-2
-6
-4
-5
-1
-8
-EOF
-test_expect_success 'stack order' '
- test-tool prio-queue stack 8 1 5 4 6 2 3 dump >actual &&
- test_cmp expect actual
-'
-
-test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index 837c8b7228..84172a3739 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -10,25 +10,24 @@ TEST_PASSES_SANITIZE_LEAK=true
for trial in 0 1 2 3 4
do
- rm -f .git/index
- echo frotz >infocom
- git update-index --add infocom
- echo xyzzy >infocom
-
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part A" \
- 'test "" != "$files"'
-
+ test_expect_success "Racy git trial #$trial part A" '
+ rm -f .git/index &&
+ echo frotz >infocom &&
+ git update-index --add infocom &&
+ echo xyzzy >infocom &&
+
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
sleep 1
- echo xyzzy >cornerstone
- git update-index --add cornerstone
- files=$(git diff-files -p)
- test_expect_success \
- "Racy GIT trial #$trial part B" \
- 'test "" != "$files"'
+ test_expect_success "Racy git trial #$trial part B" '
+ echo xyzzy >cornerstone &&
+ git update-index --add cornerstone &&
+ git diff-files -p >out &&
+ test_file_not_empty out
+ '
done
test_done
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 1cb6aa6824..46e74ad107 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -239,7 +239,7 @@ test_expect_success 'grow / shrink' '
echo value40 >> expect &&
echo size >> in &&
echo 64 39 >> expect &&
- cat in | test-tool hashmap > out &&
+ test-tool hashmap <in >out &&
test_cmp expect out
'
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index c13057a4ca..0dcfb760a2 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -17,7 +17,6 @@ test_expect_success 'advice should be printed when config variable is unset' '
test_expect_success 'advice should be printed when config variable is set to true' '
cat >expect <<-\EOF &&
hint: This is a piece of advice
- hint: Disable this message with "git config advice.nestedTag false"
EOF
test_config advice.nestedTag true &&
test-tool advise "This is a piece of advice" 2>actual &&
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index a34de56420..a7f4de4a43 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -9,7 +9,7 @@ test_expect_success setup '
git config core.autocrlf true &&
- printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+ printf "CRLF line ending\r\nAnd another\r\n" >sample &&
git add sample &&
test_tick &&
@@ -19,8 +19,9 @@ test_expect_success setup '
test_expect_success 'tar archive' '
- git archive --format=tar HEAD |
- ( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
+ git archive --format=tar HEAD >test.tar &&
+ mkdir untarred &&
+ "$TAR" xf test.tar -C untarred &&
test_cmp sample untarred/sample
@@ -30,7 +31,11 @@ test_expect_success UNZIP 'zip archive' '
git archive --format=zip HEAD >test.zip &&
- ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) &&
+ mkdir unzipped &&
+ (
+ cd unzipped &&
+ "$GIT_UNZIP" ../test.zip
+ ) &&
test_cmp sample unzipped/sample
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index 1b55f59c23..ad151a3467 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -131,8 +131,8 @@ do
test_when_finished "rm -f crlf.utf${i}.raw lf.utf${i}.raw" &&
test_when_finished "git reset --hard HEAD^" &&
- cat lf.utf8.raw | write_utf${i} >lf.utf${i}.raw &&
- cat crlf.utf8.raw | write_utf${i} >crlf.utf${i}.raw &&
+ write_utf${i} <lf.utf8.raw >lf.utf${i}.raw &&
+ write_utf${i} <crlf.utf8.raw >crlf.utf${i}.raw &&
cp crlf.utf${i}.raw eol.utf${i} &&
cat >expectIndexLF <<-EOF &&
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index d1b3be8725..f10f42ff1e 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -401,6 +401,21 @@ test_expect_success 'strip comments with changed comment char' '
test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)"
'
+test_expect_success 'strip comments with changed comment string' '
+ test ! -z "$(echo "// comment" | git -c core.commentchar=// stripspace)" &&
+ test -z "$(echo "// comment" | git -c core.commentchar="//" stripspace -s)"
+'
+
+test_expect_success 'newline as commentchar is forbidden' '
+ test_must_fail git -c core.commentChar="$LF" stripspace -s 2>err &&
+ grep "core.commentchar cannot contain newline" err
+'
+
+test_expect_success 'empty commentchar is forbidden' '
+ test_must_fail git -c core.commentchar= stripspace -s 2>err &&
+ grep "core.commentchar must have at least one character" err
+'
+
test_expect_success '-c with single line' '
printf "# foo\n" >expect &&
printf "foo" | git stripspace -c >actual &&
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index 038b8b788d..d3cb2a1cb9 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -29,9 +29,20 @@ expect_rejected () {
grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
}
-test_expect_success 'setup bare repo in worktree' '
+test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
git init outer-repo &&
- git init --bare outer-repo/bare-repo
+ git init --bare --initial-branch=main outer-repo/bare-repo &&
+ git -C outer-repo worktree add ../outer-secondary &&
+ test_path_is_dir outer-secondary &&
+ (
+ cd outer-repo &&
+ test_commit A &&
+ git push bare-repo +HEAD:refs/heads/main &&
+ git -c protocol.file.allow=always \
+ submodule add --name subn -- ./bare-repo subd
+ ) &&
+ test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
+ test_path_is_dir outer-repo/.git/modules/subn
'
test_expect_success 'safe.bareRepository unset' '
@@ -53,8 +64,7 @@ test_expect_success 'safe.bareRepository in the repository' '
# safe.bareRepository must not be "explicit", otherwise
# git config fails with "fatal: not in a git directory" (like
# safe.directory)
- test_config -C outer-repo/bare-repo safe.bareRepository \
- all &&
+ test_config -C outer-repo/bare-repo safe.bareRepository all &&
test_config_global safe.bareRepository explicit &&
expect_rejected -C outer-repo/bare-repo
'
@@ -78,4 +88,20 @@ test_expect_success 'no trace when GIT_DIR is explicitly provided' '
expect_accepted_explicit "$pwd/outer-repo/bare-repo"
'
+test_expect_success 'no trace when "bare repository" is .git' '
+ expect_accepted_implicit -C outer-repo/.git
+'
+
+test_expect_success 'no trace when "bare repository" is a subdir of .git' '
+ expect_accepted_implicit -C outer-repo/.git/objects
+'
+
+test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
+ expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
+'
+
+test_expect_success 'no trace in $GIT_DIR of a submodule' '
+ expect_accepted_implicit -C outer-repo/.git/modules/subn
+'
+
test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 8fdef88b65..8bb2a8b453 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -210,6 +210,22 @@ test_expect_success 'superfluous value provided: boolean' '
test_cmp expect actual
'
+test_expect_success 'superfluous value provided: boolean, abbreviated' '
+ cat >expect <<-\EOF &&
+ error: option `yes'\'' takes no value
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --ye=hi 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `no-yes'\'' takes no value
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ test-tool parse-options --no-ye=hi 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'superfluous value provided: cmdmode' '
cat >expect <<-\EOF &&
error: option `mode1'\'' takes no value
@@ -376,7 +392,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
test_must_be_empty output &&
test_grep "mode1" output.err &&
test_grep "mode2" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
@@ -384,7 +400,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "set23" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
@@ -392,7 +408,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "set23" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
@@ -401,7 +417,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
test_must_be_empty output &&
test_grep "mode2" output.err &&
test_grep "mode34.3" output.err &&
- test_grep "is incompatible with" output.err
+ test_grep "cannot be used together" output.err
'
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
index 487bc8d905..0ecec2ba71 100755
--- a/t/t0070-fundamental.sh
+++ b/t/t0070-fundamental.sh
@@ -9,10 +9,6 @@ Verify wrappers and compatibility functions.
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
-test_expect_success 'character classes (isspace, isalpha etc.)' '
- test-tool ctype
-'
-
test_expect_success 'mktemp to nonexistent directory prints filename' '
test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err &&
grep "doesnotexist/test" err
@@ -53,4 +49,62 @@ test_expect_success 'missing sideband designator is reported' '
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
index 961b54b06c..6657c114a3 100755
--- a/t/t0080-unit-test-output.sh
+++ b/t/t0080-unit-test-output.sh
@@ -2,6 +2,7 @@
test_description='Test the output of the unit test framework'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'TAP output from unit tests' '
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 8798feefe3..fca39048fe 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -39,9 +39,9 @@ test_expect_success 'sanity check "System Info" section' '
sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
- # The beginning should match "git version --build-info" verbatim,
+ # The beginning should match "git version --build-options" verbatim,
# but rather than checking bit-for-bit equality, just test some basics.
- grep "git version [0-9]." system &&
+ grep "git version " system &&
grep "shell-path: ." system &&
# After the version, there should be some more info.
diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh
index 4f2e0dcb02..310a450012 100755
--- a/t/t0204-gettext-reencode-sanity.sh
+++ b/t/t0204-gettext-reencode-sanity.sh
@@ -82,7 +82,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'gettext.c: git init UTF-8 -> ISO-8859-1'
printf "Bjó til tóma Git lind" >expect &&
LANGUAGE=is LC_ALL="$is_IS_iso_locale" git init repo >actual &&
test_when_finished "rm -rf repo" &&
- grep "^$(cat expect | iconv -f UTF-8 -t ISO8859-1) " actual
+ grep "^$(iconv -f UTF-8 -t ISO8859-1 <expect) " actual
'
test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 80e76a4695..c312657a12 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -2,7 +2,7 @@
test_description='test trace2 facility (normal target)'
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -283,4 +283,22 @@ test_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
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index cfba686132..13ef69b92f 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -2,7 +2,7 @@
test_description='test trace2 facility (perf target)'
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
@@ -268,4 +268,254 @@ test_expect_success PTHREADS 'global counter test/test2' '
have_counter_event "main" "counter" "test" "test2" 60 actual
'
+test_expect_success 'unsafe URLs are redacted by default' '
+ test_when_finished \
+ "rm -r actual trace.perf unredacted.perf clone clone2" &&
+
+ test_config_global \
+ "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+ test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+ GIT_TRACE2_PERF="$(pwd)/trace.perf" \
+ git clone https://user:pwd@example.com/ clone &&
+ ! grep user:pwd trace.perf &&
+
+ GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \
+ git clone https://user:pwd@example.com/ clone2 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual &&
+ grep "d0|main|start|.* clone https://user:pwd@example.com" actual &&
+ grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
+'
+
+# Confirm that the requested command produces a "cmd_name" and a
+# set of "def_param" events.
+#
+try_simple () {
+ test_when_finished "rm prop.perf actual" &&
+
+ cmd=$1 &&
+ cmd_name=$2 &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ $cmd &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+ grep "d0|main|cmd_name|.*|$cmd_name" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual
+}
+
+# Representative mainstream builtin Git command dispatched
+# in run_builtin() in git.c
+#
+test_expect_success 'expect def_params for normal builtin command' '
+ try_simple "git version" "version"
+'
+
+# Representative query command dispatched in handle_options()
+# in git.c
+#
+test_expect_success 'expect def_params for query command' '
+ try_simple "git --man-path" "_query_"
+'
+
+# remote-curl.c does not use the builtin setup in git.c, so confirm
+# that executables built from remote-curl.c emit def_params.
+#
+# Also tests the dashed-command handling where "git foo" silently
+# spawns "git-foo". Make sure that both commands should emit
+# def_params.
+#
+# Pass bogus arguments to remote-https and allow the command to fail
+# because we don't actually have a remote to fetch from. We just want
+# to see the run-dashed code run an executable built from
+# remote-curl.c rather than git.c. Confirm that we get def_param
+# events from both layers.
+#
+test_expect_success 'expect def_params for remote-curl and _run_dashed_' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_might_fail env \
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git remote-http x y &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ grep "d1|main|cmd_name|.*|remote-curl" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Similarly, `git-http-fetch` is not built from git.c so do a
+# trivial fetch so that the main git.c run-dashed code spawns
+# an executable built from http-fetch.c. Confirm that we get
+# def_param events from both layers.
+#
+test_expect_success 'expect def_params for http-fetch and _run_dashed_' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_might_fail env \
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git http-fetch --stdin file:/// <<-EOF &&
+ EOF
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ grep "d0|main|cmd_name|.*|_run_dashed_" actual &&
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ grep "d1|main|cmd_name|.*|http-fetch" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+# Historically, alias expansion explicitly emitted the def_param
+# events (independent of whether the command was a builtin, a Git
+# command or arbitrary shell command) so that it wasn't dependent
+# upon the unpeeling of the alias. Let's make sure that we preserve
+# the net effect.
+#
+test_expect_success 'expect def_params during git alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+ # We unpeel that and substitute "version" into "xxx" (giving
+ # "git version") and update the cmd_name event.
+ grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual &&
+
+ # These def_param events could be associated with either of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # The "git version" child sees a different cmd_name hierarchy.
+ # Also test the def_param (only for completeness).
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during shell alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "!git version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+
+ # We unpeel that and substitute "git version" for "git xxx" (as a
+ # shell command. Another cmd_name event is emitted as we unpeel.
+ grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual &&
+
+ # These def_param events could be associated with either of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # We get the following only because we used a git command for the
+ # shell command. In general, it could have been a shell script and
+ # we would see nothing.
+ #
+ # The child knows the cmd_name hierarchy so it includes it.
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
+test_expect_success 'expect def_params during nested git alias expansion' '
+ test_when_finished "rm prop.perf actual" &&
+
+ test_config_global "trace2.configParams" "cfg.prop.*" &&
+ test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" &&
+
+ test_config_global "cfg.prop.foo" "red" &&
+
+ test_config_global "alias.xxx" "yyy" &&
+ test_config_global "alias.yyy" "version" &&
+
+ ENV_PROP_FOO=blue \
+ GIT_TRACE2_PERF="$(pwd)/prop.perf" \
+ git xxx &&
+
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <prop.perf >actual &&
+
+ # "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx"
+ # and the child will fail.
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual &&
+ grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual &&
+
+ # We unpeel that and substitute "yyy" into "xxx" (giving "git yyy")
+ # and spawn "git-yyy" and the child will fail.
+ grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual &&
+ grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual &&
+ grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual &&
+
+ # We unpeel that and substitute "version" into "xxx" (giving
+ # "git version") and update the cmd_name event.
+ grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual &&
+ grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual &&
+
+ # These def_param events could be associated with any of the
+ # above cmd_name events. It does not matter.
+ grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches &&
+ grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual &&
+
+ # However, we do not want them repeated each time we unpeel.
+ test_line_count = 1 actual.matches &&
+
+ # The "git version" child sees a different cmd_name hierarchy.
+ # Also test the def_param (only for completeness).
+ grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual &&
+ grep "d1|main|def_param|.*|cfg.prop.foo:red" actual &&
+ grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual
+'
+
test_done
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 6d3374ff77..147643d582 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -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
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index 8300faadea..f2c146fa2a 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -8,6 +8,14 @@ test -z "$NO_UNIX_SOCKETS" || {
skip_all='skipping credential-cache tests, unix sockets not available'
test_done
}
+if test_have_prereq MINGW
+then
+ service_running=$(sc query afunix | grep "4 RUNNING")
+ test -z "$service_running" || {
+ skip_all='skipping credential-cache tests, unix sockets not available'
+ test_done
+ }
+fi
uname_s=$(uname -s)
case $uname_s in
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
index 095574bfc6..72ae405c3e 100755
--- a/t/t0303-credential-external.sh
+++ b/t/t0303-credential-external.sh
@@ -32,9 +32,24 @@ commands.
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-credential.sh
+# If we're not given a specific external helper to run against,
+# there isn't much to test. But we can still run through our
+# battery of tests with a fake helper and check that the
+# test themselves are self-consistent and clean up after
+# themselves.
+#
+# We'll use the "store" helper, since we can easily inspect
+# its state by looking at the on-disk file. But since it doesn't
+# implement any caching or expiry logic, we'll cheat and override
+# the "check" function to just report all results as OK.
if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
- skip_all="used to test external credential helpers"
- test_done
+ GIT_TEST_CREDENTIAL_HELPER=store
+ GIT_TEST_CREDENTIAL_HELPER_TIMEOUT=store
+ check () {
+ test "$1" = "approve" || return 0
+ git -c credential.helper=store credential approve
+ }
+ check_cleanup=t
fi
test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
@@ -59,4 +74,11 @@ fi
# might be long-term system storage
helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+if test "$check_cleanup" = "t"
+then
+ test_expect_success 'test cleanup removes everything' '
+ test_must_be_empty "$HOME/.git-credentials"
+ '
+fi
+
test_done
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index 5b7bee888d..88a66f0904 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -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 DEFAULT_REPO_FORMAT 'convert to partial clone with noop extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -60,7 +60,7 @@ test_expect_success SHA1 '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 DEFAULT_REPO_FORMAT 'converting to partial clone fails with unrecognized extension' '
rm -fr server client &&
test_create_repo server &&
test_commit -C server my_commit 1 &&
@@ -665,6 +665,21 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
git -C partial.git rev-list --objects --missing=print HEAD >out &&
grep "[?]$FILE_HASH" out &&
+ # The no-lazy-fetch mechanism prevents Git from fetching
+ test_must_fail env GIT_NO_LAZY_FETCH=1 \
+ git -C partial.git cat-file -e "$FILE_HASH" &&
+
+ # The same with command line option to "git"
+ test_must_fail git --no-lazy-fetch -C partial.git cat-file -e "$FILE_HASH" &&
+
+ # The same, forcing a subprocess via an alias
+ test_must_fail git --no-lazy-fetch -C partial.git \
+ -c alias.foo="!git cat-file" foo -e "$FILE_HASH" &&
+
+ # Sanity check that the file is still missing
+ git -C partial.git rev-list --objects --missing=print HEAD >out &&
+ grep "[?]$FILE_HASH" out &&
+
git -C full cat-file -s "$FILE_HASH" >expect &&
test-tool partial-clone object-info partial.git "$FILE_HASH" >actual &&
test_cmp expect actual &&
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index cd3969e852..69917d7b84 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -59,7 +59,9 @@ txt_to_synopsis () {
-e '/^\[verse\]$/,/^$/ {
/^$/d;
/^\[verse\]$/d;
-
+ s/_//g;
+ s/++//g;
+ s/`//g;
s/{litdd}/--/g;
s/'\''\(git[ a-z-]*\)'\''/\1/g;
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
new file mode 100755
index 0000000000..64214340e7
--- /dev/null
+++ b/t/t0600-reffiles-backend.sh
@@ -0,0 +1,475 @@
+#!/bin/sh
+
+test_description='Test reffiles backend'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+if ! test_have_prereq REFFILES
+then
+ skip_all='skipping reffiles specific tests'
+ test_done
+fi
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m Initial &&
+ C=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m Second &&
+ D=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m Third &&
+ E=$(git rev-parse HEAD)
+'
+
+test_expect_success 'empty directory should not fool rev-parse' '
+ prefix=refs/e-rev-parse &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ echo "$C" >expected &&
+ git rev-parse $prefix/foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool for-each-ref' '
+ prefix=refs/e-for-each-ref &&
+ git update-ref $prefix/foo $C &&
+ git for-each-ref $prefix >expected &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ git for-each-ref $prefix >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'empty directory should not fool create' '
+ prefix=refs/e-create &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "create %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool verify' '
+ prefix=refs/e-verify &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "verify %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg update' '
+ prefix=refs/e-update-1 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "update %s $D\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 2-arg update' '
+ prefix=refs/e-update-2 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "update %s $D $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 0-arg delete' '
+ prefix=refs/e-delete-0 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "delete %s\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'empty directory should not fool 1-arg delete' '
+ prefix=refs/e-delete-1 &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ mkdir -p .git/$prefix/foo/bar/baz &&
+ printf "delete %s $C\n" $prefix/foo |
+ git update-ref --stdin
+'
+
+test_expect_success 'non-empty directory blocks create' '
+ prefix=refs/ne-create &&
+ mkdir -p .git/$prefix/foo/bar &&
+ : >.git/$prefix/foo/bar/baz.lock &&
+ test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/foo $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/foo $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks create' '
+ prefix=refs/broken-create &&
+ mkdir -p .git/$prefix &&
+ echo "gobbledigook" >.git/$prefix/foo &&
+ test_when_finished "rm -f .git/$prefix/foo" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/foo $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/foo $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'non-empty directory blocks indirect create' '
+ prefix=refs/ne-indirect-create &&
+ git symbolic-ref $prefix/symref $prefix/foo &&
+ mkdir -p .git/$prefix/foo/bar &&
+ : >.git/$prefix/foo/bar/baz.lock &&
+ test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/symref $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
+ EOF
+ printf "%s\n" "update $prefix/symref $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'broken reference blocks indirect create' '
+ prefix=refs/broken-indirect-create &&
+ git symbolic-ref $prefix/symref $prefix/foo &&
+ echo "gobbledigook" >.git/$prefix/foo &&
+ test_when_finished "rm -f .git/$prefix/foo" &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/symref $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err &&
+ cat >expected <<-EOF &&
+ fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
+ EOF
+ printf "%s\n" "update $prefix/symref $D $C" |
+ test_must_fail git update-ref --stdin 2>output.err &&
+ test_cmp expected output.err
+'
+
+test_expect_success 'no bogus intermediate values during delete' '
+ prefix=refs/slow-transaction &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ # Now try to update the reference, but hold the `packed-refs` lock
+ # for a while to see what happens while the process is blocked:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ {
+ # Note: the following command is intentionally run in the
+ # background. We increase the timeout so that `update-ref`
+ # attempts to acquire the `packed-refs` lock for much longer
+ # than it takes for us to do the check then delete it:
+ git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
+ } &&
+ pid2=$! &&
+ # Give update-ref plenty of time to get to the point where it tries
+ # to lock packed-refs:
+ sleep 1 &&
+ # Make sure that update-ref did not complete despite the lock:
+ kill -0 $pid2 &&
+ # Verify that the reference still has its old value:
+ sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
+ case "$sha1" in
+ $D)
+ # This is what we hope for; it means that nothing
+ # user-visible has changed yet.
+ : ;;
+ undefined)
+ # This is not correct; it means the deletion has happened
+ # already even though update-ref should not have been
+ # able to acquire the lock yet.
+ echo "$prefix/foo deleted prematurely" &&
+ break
+ ;;
+ $C)
+ # This value should never be seen. Probably the loose
+ # reference has been deleted but the packed reference
+ # is still there:
+ echo "$prefix/foo incorrectly observed to be C" &&
+ break
+ ;;
+ *)
+ # WTF?
+ echo "unexpected value observed for $prefix/foo: $sha1" &&
+ break
+ ;;
+ esac >out &&
+ rm -f .git/packed-refs.lock &&
+ wait $pid2 &&
+ test_must_be_empty out &&
+ test_must_fail git rev-parse --verify --quiet $prefix/foo
+'
+
+test_expect_success 'delete fails cleanly if packed-refs file is locked' '
+ prefix=refs/locked-packed-refs &&
+ # Set up a reference with differing loose and packed versions:
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # Now try to delete it while the `packed-refs` lock is held:
+ : >.git/packed-refs.lock &&
+ test_when_finished "rm -f .git/packed-refs.lock" &&
+ test_must_fail git update-ref -d $prefix/foo >out 2>err &&
+ git for-each-ref $prefix >actual &&
+ test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
+ test_cmp unchanged actual
+'
+
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+ # Setup and expectations are similar to the test above.
+ prefix=refs/failed-packed-refs &&
+ git update-ref $prefix/foo $C &&
+ git pack-refs --all &&
+ git update-ref $prefix/foo $D &&
+ git for-each-ref $prefix >unchanged &&
+ # This should not happen in practice, but it is an easy way to get a
+ # reliable error (we open with create_tempfile(), which uses O_EXCL).
+ : >.git/packed-refs.new &&
+ test_when_finished "rm -f .git/packed-refs.new" &&
+ test_must_fail git update-ref -d $prefix/foo &&
+ git for-each-ref $prefix >actual &&
+ test_cmp unchanged actual
+'
+
+RWT="test-tool ref-store worktree:wt"
+RMAIN="test-tool ref-store worktree:main"
+
+test_expect_success 'setup worktree' '
+ test_commit first &&
+ git worktree add -b wt-main wt &&
+ (
+ cd wt &&
+ test_commit second
+ )
+'
+
+# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# only appear in the for-each-reflog output if it is called from the correct
+# worktree, which is exercised in this test. This test is poorly written for
+# mulitple reasons: 1) it creates invalidly formatted log entres. 2) it uses
+# direct FS access for creating the reflogs. 3) PSEUDO-WT and refs/bisect/random
+# do not create reflogs by default, so it is not testing a realistic scenario.
+test_expect_success 'for_each_reflog()' '
+ echo $ZERO_OID >.git/logs/PSEUDO_MAIN_HEAD &&
+ mkdir -p .git/logs/refs/bisect &&
+ echo $ZERO_OID >.git/logs/refs/bisect/random &&
+
+ echo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD &&
+ mkdir -p .git/worktrees/wt/logs/refs/bisect &&
+ echo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+ $RWT for-each-reflog >actual &&
+ cat >expected <<-\EOF &&
+ HEAD
+ PSEUDO_WT_HEAD
+ refs/bisect/wt-random
+ refs/heads/main
+ refs/heads/wt-main
+ EOF
+ test_cmp expected actual &&
+
+ $RMAIN for-each-reflog >actual &&
+ cat >expected <<-\EOF &&
+ HEAD
+ PSEUDO_MAIN_HEAD
+ refs/bisect/random
+ refs/heads/main
+ refs/heads/wt-main
+ EOF
+ test_cmp expected actual
+'
+
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
+ git checkout -b reflogskip &&
+ zf=$(test_oid zero_2) &&
+ ident="abc <xyz> 0000000001 +0000" &&
+ for i in $(test_seq 1 75); do
+ printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
+ if test $i = 75; then
+ for j in $(test_seq 1 89); do
+ printf X || return 1
+ done
+ else
+ printf X
+ fi &&
+ printf "\n" || return 1
+ done >.git/logs/refs/heads/reflogskip &&
+ git rev-parse reflogskip@{73} >actual &&
+ echo ${zf}03 >expect &&
+ test_cmp expect actual
+'
+
+# This test takes a lock on an individual ref; this is not supported in
+# reftable.
+test_expect_success 'reflog expire operates on symref not referrent' '
+ git branch --create-reflog the_symref &&
+ git branch --create-reflog referrent &&
+ git update-ref referrent HEAD &&
+ git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
+ test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
+ touch .git/refs/heads/referrent.lock &&
+ git reflog expire --expire=all the_symref
+'
+
+test_expect_success 'empty reflog' '
+ test_when_finished "rm -rf empty" &&
+ git init empty &&
+ test_commit -C empty A &&
+ >empty/.git/logs/refs/heads/foo &&
+ git -C empty reflog expire --all 2>err &&
+ test_must_be_empty err
+'
+
+test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
+ ln -s does-not-exist .git/refs/heads/broken &&
+ test_must_fail git rev-parse --verify broken
+'
+
+test_expect_success 'log diagnoses bogus HEAD hash' '
+ git init empty &&
+ test_when_finished "rm -rf empty" &&
+ echo 1234abcd >empty/.git/refs/heads/main &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_grep broken stderr
+'
+
+test_expect_success 'log diagnoses bogus HEAD symref' '
+ git init empty &&
+ test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
+ test_must_fail git -C empty log 2>stderr &&
+ test_grep broken stderr &&
+ test_must_fail git -C empty log --default totally-bogus 2>stderr &&
+ test_grep broken stderr
+'
+
+test_expect_success 'empty directory removal' '
+ git branch d1/d2/r1 HEAD &&
+ git branch d1/r2 HEAD &&
+ test_path_is_file .git/refs/heads/d1/d2/r1 &&
+ test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
+ git branch -d d1/d2/r1 &&
+ test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
+ test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
+ test_path_is_file .git/refs/heads/d1/r2 &&
+ test_path_is_file .git/logs/refs/heads/d1/r2
+'
+
+test_expect_success 'symref empty directory removal' '
+ git branch e1/e2/r1 HEAD &&
+ git branch e1/r2 HEAD &&
+ git checkout e1/e2/r1 &&
+ test_when_finished "git checkout main" &&
+ test_path_is_file .git/refs/heads/e1/e2/r1 &&
+ test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
+ git update-ref -d HEAD &&
+ test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
+ test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
+ test_path_is_file .git/refs/heads/e1/r2 &&
+ test_path_is_file .git/logs/refs/heads/e1/r2 &&
+ test_path_is_file .git/logs/HEAD
+'
+
+test_expect_success 'directory not created deleting packed ref' '
+ git branch d1/d2/r1 HEAD &&
+ git pack-refs --all &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ git update-ref -d refs/heads/d1/d2/r1 &&
+ test_path_is_missing .git/refs/heads/d1/d2 &&
+ test_path_is_missing .git/refs/heads/d1
+'
+
+test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+ git branch --create-reflog u &&
+ mv .git/logs/refs/heads/u real-u &&
+ ln -s real-u .git/logs/refs/heads/u &&
+ test_must_fail git branch -m u v
+'
+
+test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
+ test_when_finished "rm -rf subdir" &&
+ git init --bare subdir &&
+
+ rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
+ ln -s ../.git/refs subdir/refs &&
+ ln -s ../.git/objects subdir/objects &&
+ ln -s ../.git/packed-refs subdir/packed-refs &&
+
+ git -C subdir rev-parse --absolute-git-dir >subdir.dir &&
+ git rev-parse --absolute-git-dir >our.dir &&
+ ! test_cmp subdir.dir our.dir &&
+
+ git -C subdir log &&
+ git -C subdir branch rename-src &&
+ git rev-parse rename-src >expect &&
+ git -C subdir branch -m rename-src rename-dest &&
+ git rev-parse rename-dest >actual &&
+ test_cmp expect actual &&
+ git branch -D rename-dest
+'
+
+test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
+ git checkout main &&
+ mv .git/logs actual_logs &&
+ cmd //c "mklink /D .git\logs ..\actual_logs" &&
+ git rebase -f HEAD^ &&
+ test -L .git/logs &&
+ rm .git/logs &&
+ mv actual_logs .git/logs
+'
+
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
+ umask 077 &&
+ git config core.sharedRepository group &&
+ git reflog expire --all &&
+ actual="$(ls -l .git/logs/refs/heads/main)" &&
+ case "$actual" in
+ -rw-rw-*)
+ : happy
+ ;;
+ *)
+ echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
+ false
+ ;;
+ esac
+'
+
+test_done
diff --git a/t/t3210-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh
index 7f4e98db7d..7d4ab0b91a 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t0601-reffiles-pack-refs.sh
@@ -15,6 +15,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+if ! test_have_prereq REFFILES
+then
+ skip_all='skipping reffiles specific tests'
+ test_done
+fi
+
test_expect_success 'enable reflogs' '
git config core.logallrefupdates true
'
@@ -26,6 +32,19 @@ test_expect_success 'prepare a trivial repository' '
HEAD=$(git rev-parse --verify HEAD)
'
+test_expect_success 'pack-refs --prune --all' '
+ test_path_is_missing .git/packed-refs &&
+ git pack-refs --no-prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f | wc -l) &&
+ test "$N" != 0 &&
+
+ git pack-refs --prune --all &&
+ test_path_is_file .git/packed-refs &&
+ N=$(find .git/refs -type f) &&
+ test -z "$N"
+'
+
SHA1=
test_expect_success 'see if git show-ref works as expected' '
@@ -145,6 +164,13 @@ test_expect_success 'test --exclude takes precedence over --include' '
git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
test -f .git/refs/heads/dont_pack5'
+test_expect_success '--auto packs and prunes refs as usual' '
+ git branch auto &&
+ test_path_is_file .git/refs/heads/auto &&
+ git pack-refs --auto --all &&
+ test_path_is_missing .git/refs/heads/auto
+'
+
test_expect_success 'see if up-to-date packed refs are preserved' '
git branch q &&
git pack-refs --all --prune &&
@@ -294,4 +320,64 @@ test_expect_success SYMLINKS 'pack symlinked packed-refs' '
test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
'
+# The 'packed-refs' file is stored directly in .git/. This means it is global
+# to the repository, and can only contain refs that are shared across all
+# worktrees.
+test_expect_success 'refs/worktree must not be packed' '
+ test_commit initial &&
+ test_commit wt1 &&
+ test_commit wt2 &&
+ git worktree add wt1 wt1 &&
+ git worktree add wt2 wt2 &&
+ git checkout initial &&
+ git update-ref refs/worktree/foo HEAD &&
+ git -C wt1 update-ref refs/worktree/foo HEAD &&
+ git -C wt2 update-ref refs/worktree/foo HEAD &&
+ git pack-refs --all &&
+ test_path_is_missing .git/refs/tags/wt1 &&
+ test_path_is_file .git/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
+ test_path_is_file .git/worktrees/wt2/refs/worktree/foo
+'
+
+# we do not want to count on running pack-refs to
+# actually pack it, as it is perfectly reasonable to
+# skip processing a broken ref
+test_expect_success 'create packed-refs file with broken ref' '
+ test_tick && git commit --allow-empty -m one &&
+ recoverable=$(git rev-parse HEAD) &&
+ test_tick && git commit --allow-empty -m two &&
+ missing=$(git rev-parse HEAD) &&
+ rm -f .git/refs/heads/main &&
+ cat >.git/packed-refs <<-EOF &&
+ $missing refs/heads/main
+ $recoverable refs/heads/other
+ EOF
+ echo $missing >expect &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not silently delete broken packed ref' '
+ git pack-refs --all --prune &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pack-refs does not drop broken refs during deletion' '
+ git update-ref -d refs/heads/other &&
+ git rev-parse refs/heads/main >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'maintenance --auto unconditionally packs loose refs' '
+ git update-ref refs/heads/something HEAD &&
+ test_path_is_file .git/refs/heads/something &&
+ git rev-parse refs/heads/something >expect &&
+ git maintenance run --task=pack-refs --auto &&
+ test_path_is_missing .git/refs/heads/something &&
+ git rev-parse refs/heads/something >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
new file mode 100755
index 0000000000..178791e086
--- /dev/null
+++ b/t/t0610-reftable-basics.sh
@@ -0,0 +1,1032 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Google LLC
+#
+
+test_description='reftable basics'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+if ! test_have_prereq REFTABLE
+then
+ skip_all='skipping reftable tests; set GIT_TEST_DEFAULT_REF_FORMAT=reftable'
+ test_done
+fi
+
+INVALID_OID=$(test_oid 001)
+
+test_expect_success 'init: creates basic reftable structures' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_path_is_dir repo/.git/reftable &&
+ test_path_is_file repo/.git/reftable/tables.list &&
+ echo reftable >expect &&
+ git -C repo rev-parse --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via environment variable' '
+ test_when_finished "rm -rf repo" &&
+ GIT_DEFAULT_HASH=sha256 git init repo &&
+ cat >expect <<-EOF &&
+ sha256
+ reftable
+ EOF
+ git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: sha256 object format via option' '
+ test_when_finished "rm -rf repo" &&
+ git init --object-format=sha256 repo &&
+ cat >expect <<-EOF &&
+ sha256
+ reftable
+ EOF
+ git -C repo rev-parse --show-object-format --show-ref-format >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing reftable backend succeeds' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo for-each-ref >expect &&
+ git init --ref-format=reftable repo &&
+ git -C repo for-each-ref >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'init: reinitializing files with reftable backend fails' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=files repo &&
+ test_commit -C repo file &&
+
+ cp repo/.git/HEAD expect &&
+ test_must_fail git init --ref-format=reftable repo &&
+ test_cmp expect repo/.git/HEAD
+'
+
+test_expect_success 'init: reinitializing reftable with files backend fails' '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=reftable repo &&
+ test_commit -C repo file &&
+
+ cp repo/.git/HEAD expect &&
+ test_must_fail git init --ref-format=files repo &&
+ test_cmp expect repo/.git/HEAD
+'
+
+test_expect_perms () {
+ local perms="$1"
+ local file="$2"
+ local actual="$(ls -l "$file")" &&
+
+ case "$actual" in
+ $perms*)
+ : happy
+ ;;
+ *)
+ echo "$(basename $2) is not $perms but $actual"
+ false
+ ;;
+ esac
+}
+
+test_expect_reftable_perms () {
+ local umask="$1"
+ local shared="$2"
+ local expect="$3"
+
+ test_expect_success POSIXPERM "init: honors --shared=$shared with umask $umask" '
+ test_when_finished "rm -rf repo" &&
+ (
+ umask $umask &&
+ git init --shared=$shared repo
+ ) &&
+ test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+ for table in repo/.git/reftable/*.ref
+ do
+ test_expect_perms "$expect" "$table" ||
+ return 1
+ done
+ '
+
+ test_expect_success POSIXPERM "pack-refs: honors --shared=$shared with umask $umask" '
+ test_when_finished "rm -rf repo" &&
+ (
+ umask $umask &&
+ git init --shared=$shared repo &&
+ test_commit -C repo A &&
+ test_line_count = 2 repo/.git/reftable/tables.list &&
+ git -C repo pack-refs
+ ) &&
+ test_expect_perms "$expect" repo/.git/reftable/tables.list &&
+ for table in repo/.git/reftable/*.ref
+ do
+ test_expect_perms "$expect" "$table" ||
+ return 1
+ done
+ '
+}
+
+test_expect_reftable_perms 002 umask "-rw-rw-r--"
+test_expect_reftable_perms 022 umask "-rw-r--r--"
+test_expect_reftable_perms 027 umask "-rw-r-----"
+
+test_expect_reftable_perms 002 group "-rw-rw-r--"
+test_expect_reftable_perms 022 group "-rw-rw-r--"
+test_expect_reftable_perms 027 group "-rw-rw----"
+
+test_expect_reftable_perms 002 world "-rw-rw-r--"
+test_expect_reftable_perms 022 world "-rw-rw-r--"
+test_expect_reftable_perms 027 world "-rw-rw-r--"
+
+test_expect_success 'clone: can clone reftable repository' '
+ test_when_finished "rm -rf repo clone" &&
+ git init repo &&
+ test_commit -C repo message1 file1 &&
+
+ git clone repo cloned &&
+ echo reftable >expect &&
+ git -C cloned rev-parse --show-ref-format >actual &&
+ test_cmp expect actual &&
+ test_path_is_file cloned/file1
+'
+
+test_expect_success 'clone: can clone reffiles into reftable repository' '
+ test_when_finished "rm -rf reffiles reftable" &&
+ git init --ref-format=files reffiles &&
+ test_commit -C reffiles A &&
+ git clone --ref-format=reftable ./reffiles reftable &&
+
+ git -C reffiles rev-parse HEAD >expect &&
+ git -C reftable rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C reftable rev-parse --show-ref-format >actual &&
+ echo reftable >expect &&
+ test_cmp expect actual &&
+
+ git -C reffiles rev-parse --show-ref-format >actual &&
+ echo files >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'clone: can clone reftable into reffiles repository' '
+ test_when_finished "rm -rf reffiles reftable" &&
+ git init --ref-format=reftable reftable &&
+ test_commit -C reftable A &&
+ git clone --ref-format=files ./reftable reffiles &&
+
+ git -C reftable rev-parse HEAD >expect &&
+ git -C reffiles rev-parse HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C reftable rev-parse --show-ref-format >actual &&
+ echo reftable >expect &&
+ test_cmp expect actual &&
+
+ git -C reffiles rev-parse --show-ref-format >actual &&
+ echo files >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ref transaction: corrupted tables cause failure' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ for f in .git/reftable/*.ref
+ do
+ : >"$f" || return 1
+ done &&
+ test_must_fail git update-ref refs/heads/main HEAD
+ )
+'
+
+test_expect_success 'ref transaction: corrupted tables.list cause failure' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ echo garbage >.git/reftable/tables.list &&
+ test_must_fail git update-ref refs/heads/main HEAD
+ )
+'
+
+test_expect_success 'ref transaction: refuses to write ref causing F/D conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_must_fail git -C repo update-ref refs/heads/main/forbidden
+'
+
+test_expect_success 'ref transaction: deleting ref with invalid name fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_must_fail git -C repo update-ref -d ../../my-private-file
+'
+
+test_expect_success 'ref transaction: can skip object ID verification' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID 0 &&
+ test-tool -C repo ref-store main update-ref msg refs/heads/branch $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION
+'
+
+test_expect_success 'ref transaction: updating same ref multiple times fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ cat >updates <<-EOF &&
+ update refs/heads/main $A
+ update refs/heads/main $A
+ EOF
+ cat >expect <<-EOF &&
+ fatal: multiple updates for ref ${SQ}refs/heads/main${SQ} not allowed
+ EOF
+ test_must_fail git -C repo update-ref --stdin <updates 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: can delete symbolic self-reference with git-symbolic-ref(1)' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ git -C repo symbolic-ref -d refs/heads/self
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference without --no-deref fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ cat >expect <<-EOF &&
+ error: multiple updates for ${SQ}refs/heads/self${SQ} (including one via symref ${SQ}refs/heads/self${SQ}) are not allowed
+ EOF
+ test_must_fail git -C repo update-ref -d refs/heads/self 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: deleting symbolic self-reference with --no-deref succeeds' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo symbolic-ref refs/heads/self refs/heads/self &&
+ git -C repo update-ref -d --no-deref refs/heads/self
+'
+
+test_expect_success 'ref transaction: creating symbolic ref fails with F/D conflict' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ cat >expect <<-EOF &&
+ error: unable to write symref for refs/heads: file/directory conflict
+ EOF
+ test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'ref transaction: ref deletion' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ HEAD_OID=$(git show-ref -s --verify HEAD) &&
+ cat >expect <<-EOF &&
+ $HEAD_OID refs/heads/main
+ $HEAD_OID refs/tags/file
+ EOF
+ git show-ref >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git update-ref -d refs/tags/file $INVALID_OID &&
+ git show-ref >actual &&
+ test_cmp expect actual &&
+
+ git update-ref -d refs/tags/file $HEAD_OID &&
+ echo "$HEAD_OID refs/heads/main" >expect &&
+ git show-ref >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ref transaction: writes cause auto-compaction' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ test_commit -C repo --no-tag A &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ test_commit -C repo --no-tag B &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'ref transaction: env var disables compaction' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+
+ start=$(wc -l <repo/.git/reftable/tables.list) &&
+ iterations=5 &&
+ expected=$((start + iterations)) &&
+
+ for i in $(test_seq $iterations)
+ do
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo update-ref branch-$i HEAD || return 1
+ done &&
+ test_line_count = $expected repo/.git/reftable/tables.list &&
+
+ git -C repo update-ref foo HEAD &&
+ test_line_count -lt $expected repo/.git/reftable/tables.list
+'
+
+test_expect_success 'ref transaction: alternating table sizes are compacted' '
+ test_when_finished "rm -rf repo" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+ for i in $(test_seq 5)
+ do
+ git -C repo branch -f foo &&
+ git -C repo branch -d foo || return 1
+ done &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+check_fsync_events () {
+ local trace="$1" &&
+ shift &&
+
+ cat >expect &&
+ sed -n \
+ -e '/^{"event":"counter",.*"category":"fsync",/ {
+ s/.*"category":"fsync",//;
+ s/}$//;
+ p;
+ }' \
+ <"$trace" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'ref transaction: writes are synced' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo initial &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TEST_FSYNC=true \
+ git -C repo -c core.fsync=reference \
+ -c core.fsyncMethod=fsync update-ref refs/heads/branch HEAD &&
+ check_fsync_events trace2.txt <<-EOF
+ "name":"hardware-flush","count":4
+ EOF
+'
+
+test_expect_success 'ref transaction: empty transaction in empty repo' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo --no-tag A &&
+ git -C repo update-ref -d refs/heads/main &&
+ test-tool -C repo ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+ git -C repo update-ref --stdin <<-EOF
+ prepare
+ commit
+ EOF
+'
+
+test_expect_success 'ref transaction: fails gracefully when auto compaction fails' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ for i in $(test_seq 10)
+ do
+ git branch branch-$i &&
+ for table in .git/reftable/*.ref
+ do
+ touch "$table.lock" || exit 1
+ done ||
+ exit 1
+ done &&
+ test_line_count = 10 .git/reftable/tables.list
+ )
+'
+
+test_expect_success 'pack-refs: compacts tables' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+
+ test_commit -C repo A &&
+ ls -1 repo/.git/reftable >table-files &&
+ test_line_count = 3 table-files &&
+ test_line_count = 2 repo/.git/reftable/tables.list &&
+
+ git -C repo pack-refs &&
+ ls -1 repo/.git/reftable >table-files &&
+ test_line_count = 2 table-files &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'pack-refs: compaction raises locking errors' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo A &&
+ touch repo/.git/reftable/tables.list.lock &&
+ cat >expect <<-EOF &&
+ error: unable to compact stack: data is locked
+ EOF
+ test_must_fail git -C repo pack-refs 2>err &&
+ test_cmp expect err
+'
+
+for command in pack-refs gc "maintenance run --task=pack-refs"
+do
+test_expect_success "$command: auto compaction" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+
+ # We need a bit of setup to ensure that git-gc(1) actually
+ # triggers, and that it does not write anything to the refdb.
+ git config gc.auto 1 &&
+ git config gc.autoDetach 0 &&
+ git config gc.reflogExpire never &&
+ git config gc.reflogExpireUnreachable never &&
+ test_oid blob17_1 | git hash-object -w --stdin &&
+
+ # The tables should have been auto-compacted, and thus auto
+ # compaction should not have to do anything.
+ ls -1 .git/reftable >tables-expect &&
+ test_line_count = 3 tables-expect &&
+ git $command --auto &&
+ ls -1 .git/reftable >tables-actual &&
+ test_cmp tables-expect tables-actual &&
+
+ test_oid blob17_2 | git hash-object -w --stdin &&
+
+ # Lock all tables write some refs. Auto-compaction will be
+ # unable to compact tables and thus fails gracefully, leaving
+ # the stack in a sub-optimal state.
+ ls .git/reftable/*.ref |
+ while read table
+ do
+ touch "$table.lock" || exit 1
+ done &&
+ git branch B &&
+ git branch C &&
+ rm .git/reftable/*.lock &&
+ test_line_count = 4 .git/reftable/tables.list &&
+
+ git $command --auto &&
+ test_line_count = 1 .git/reftable/tables.list
+ )
+'
+done
+
+test_expect_success 'pack-refs: prunes stale tables' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ touch repo/.git/reftable/stale-table.ref &&
+ git -C repo pack-refs &&
+ test_path_is_missing repo/.git/reftable/stable-ref.ref
+'
+
+test_expect_success 'pack-refs: does not prune non-table files' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ touch repo/.git/reftable/garbage &&
+ git -C repo pack-refs &&
+ test_path_is_file repo/.git/reftable/garbage
+'
+
+test_expect_success 'packed-refs: writes are synced' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo initial &&
+ test_line_count = 2 table-files &&
+
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+ GIT_TEST_FSYNC=true \
+ git -C repo -c core.fsync=reference \
+ -c core.fsyncMethod=fsync pack-refs &&
+ check_fsync_events trace2.txt <<-EOF
+ "name":"hardware-flush","count":2
+ EOF
+'
+
+test_expect_success 'ref iterator: bogus names are flagged' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit --no-tag file &&
+ test-tool ref-store main update-ref msg "refs/heads/bogus..name" $(git rev-parse HEAD) $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+ cat >expect <<-EOF &&
+ $ZERO_OID refs/heads/bogus..name 0xc
+ $(git rev-parse HEAD) refs/heads/main 0x0
+ EOF
+ test-tool ref-store main for-each-ref "" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'ref iterator: missing object IDs are not flagged' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test-tool ref-store main update-ref msg "refs/heads/broken-hash" $INVALID_OID $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+
+ cat >expect <<-EOF &&
+ $INVALID_OID refs/heads/broken-hash 0x0
+ EOF
+ test-tool ref-store main for-each-ref "" >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'basic: commit and list refs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ test_write_lines refs/heads/main refs/tags/file >expect &&
+ git -C repo for-each-ref --format="%(refname)" >actual &&
+ test_cmp actual expect
+'
+
+test_expect_success 'basic: can write large commit message' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ perl -e "
+ print \"this is a long commit message\" x 50000
+ " >commit-msg &&
+ git -C repo commit --allow-empty --file=../commit-msg
+'
+
+test_expect_success 'basic: show-ref fails with empty repository' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_must_fail git -C repo show-ref >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'basic: can check out unborn branch' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ git -C repo checkout -b main
+'
+
+test_expect_success 'basic: peeled tags are stored' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ test_commit -C repo file &&
+ git -C repo tag -m "annotated tag" test_tag HEAD &&
+ for ref in refs/heads/main refs/tags/file refs/tags/test_tag refs/tags/test_tag^{}
+ do
+ echo "$(git -C repo rev-parse "$ref") $ref" || return 1
+ done >expect &&
+ git -C repo show-ref -d >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'basic: for-each-ref can print symrefs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ git branch &&
+ git symbolic-ref refs/heads/sym refs/heads/main &&
+ cat >expected <<-EOF &&
+ refs/heads/main
+ EOF
+ git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'basic: notes' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ write_script fake_editor <<-\EOF &&
+ echo "$MSG" >"$1"
+ echo "$MSG" >&2
+ EOF
+
+ test_commit 1st &&
+ test_commit 2nd &&
+ GIT_EDITOR=./fake_editor MSG=b4 git notes add &&
+ GIT_EDITOR=./fake_editor MSG=b3 git notes edit &&
+ echo b4 >expect &&
+ git notes --ref commits@{1} show >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'basic: stash' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file &&
+ git stash list >expect &&
+ test_line_count = 0 expect &&
+
+ echo hoi >>file.t &&
+ git stash push -m stashed &&
+ git stash list >expect &&
+ test_line_count = 1 expect &&
+
+ git stash clear &&
+ git stash list >expect &&
+ test_line_count = 0 expect
+ )
+'
+
+test_expect_success 'basic: cherry-pick' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit message1 file1 &&
+ test_commit message2 file2 &&
+ git branch source &&
+ git checkout HEAD^ &&
+ test_commit message3 file3 &&
+ git cherry-pick source &&
+ test_path_is_file file2
+ )
+'
+
+test_expect_success 'basic: rebase' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit message1 file1 &&
+ test_commit message2 file2 &&
+ git branch source &&
+ git checkout HEAD^ &&
+ test_commit message3 file3 &&
+ git rebase source &&
+ test_path_is_file file2
+ )
+'
+
+test_expect_success 'reflog: can delete separate reflog entries' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit file &&
+ test_commit file2 &&
+ test_commit file3 &&
+ test_commit file4 &&
+ git reflog >actual &&
+ grep file3 actual &&
+
+ git reflog delete HEAD@{1} &&
+ git reflog >actual &&
+ ! grep file3 actual
+ )
+'
+
+test_expect_success 'reflog: can switch to previous branch' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ git checkout -b branch1 &&
+ test_commit file2 &&
+ git checkout -b branch2 &&
+ git switch - &&
+ git rev-parse --symbolic-full-name HEAD >actual &&
+ echo refs/heads/branch1 >expect &&
+ test_cmp actual expect
+ )
+'
+
+test_expect_success 'reflog: copying branch writes reflog entry' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit file1 &&
+ test_commit file2 &&
+ oid=$(git rev-parse --short HEAD) &&
+ git branch src &&
+ cat >expect <<-EOF &&
+ ${oid} dst@{0}: Branch: copied refs/heads/src to refs/heads/dst
+ ${oid} dst@{1}: branch: Created from main
+ EOF
+ git branch -c src dst &&
+ git reflog dst >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog: renaming branch writes reflog entry' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git symbolic-ref HEAD refs/heads/before &&
+ test_commit file &&
+ git show-ref >expected.refs &&
+ sed s/before/after/g <expected.refs >expected &&
+ git branch -M after &&
+ git show-ref >actual &&
+ test_cmp expected actual &&
+ echo refs/heads/after >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success 'reflog: can store empty logs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_must_fail test-tool ref-store main reflog-exists refs/heads/branch &&
+ test-tool ref-store main create-reflog refs/heads/branch &&
+ test-tool ref-store main reflog-exists refs/heads/branch &&
+ test-tool ref-store main for-each-reflog-ent-reverse refs/heads/branch >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'reflog: expiry empties reflog' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit initial &&
+ git checkout -b branch &&
+ test_commit fileA &&
+ test_commit fileB &&
+
+ cat >expect <<-EOF &&
+ commit: fileB
+ commit: fileA
+ branch: Created from HEAD
+ EOF
+ git reflog show --format="%gs" refs/heads/branch >actual &&
+ test_cmp expect actual &&
+
+ git reflog expire branch --expire=all &&
+ git reflog show --format="%gs" refs/heads/branch >actual &&
+ test_must_be_empty actual &&
+ test-tool ref-store main reflog-exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog: can be deleted' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit initial &&
+ test-tool ref-store main reflog-exists refs/heads/main &&
+ test-tool ref-store main delete-reflog refs/heads/main &&
+ test_must_fail test-tool ref-store main reflog-exists refs/heads/main
+ )
+'
+
+test_expect_success 'reflog: garbage collection deletes reflog entries' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ for count in $(test_seq 1 10)
+ do
+ test_commit "number $count" file.t $count number-$count ||
+ return 1
+ done &&
+ git reflog refs/heads/main >actual &&
+ test_line_count = 10 actual &&
+ grep "commit (initial): number 1" actual &&
+ grep "commit: number 10" actual &&
+
+ git gc &&
+ git reflog refs/heads/main >actual &&
+ test_line_count = 0 actual
+ )
+'
+
+test_expect_success 'reflog: updates via HEAD update HEAD reflog' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit main-one &&
+ git checkout -b new-branch &&
+ test_commit new-one &&
+ test_commit new-two &&
+
+ echo new-one >expect &&
+ git log -1 --format=%s HEAD@{1} >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'worktree: adding worktree creates separate stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ test_path_is_file repo/.git/worktrees/worktree/refs/heads &&
+ echo "ref: refs/heads/.invalid" >expect &&
+ test_cmp expect repo/.git/worktrees/worktree/HEAD &&
+ test_path_is_dir repo/.git/worktrees/worktree/reftable &&
+ test_path_is_file repo/.git/worktrees/worktree/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in main repo packs main refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo worktree add ../worktree &&
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/worktree/per-worktree HEAD &&
+
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list &&
+ git -C repo pack-refs &&
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C repo worktree add ../worktree &&
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/worktree/per-worktree HEAD &&
+
+ test_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 3 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating shared ref updates main stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ GIT_TEST_REFTABLE_AUTOCOMPACTION=false \
+ git -C worktree update-ref refs/heads/shared HEAD &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref updates worktree stack' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C worktree update-ref refs/bisect/per-worktree HEAD &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from main repo' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C repo update-ref worktrees/worktree/refs/bisect/per-worktree HEAD &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: creating per-worktree ref from second worktree' '
+ test_when_finished "rm -rf repo wt1 wt2" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../wt1 &&
+ git -C repo worktree add ../wt2 &&
+ git -C repo pack-refs &&
+ git -C wt1 pack-refs &&
+ git -C wt2 pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+ test_line_count = 1 repo/.git/worktrees/wt2/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ git -C wt1 update-ref worktrees/wt2/refs/bisect/per-worktree HEAD &&
+ test_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&
+ test_line_count = 2 repo/.git/worktrees/wt2/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can create shared and per-worktree ref in one transaction' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo A &&
+
+ git -C repo worktree add ../worktree &&
+ git -C repo pack-refs &&
+ git -C worktree pack-refs &&
+ test_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 1 repo/.git/reftable/tables.list &&
+
+ cat >stdin <<-EOF &&
+ create worktrees/worktree/refs/bisect/per-worktree HEAD
+ create refs/branches/shared HEAD
+ EOF
+ git -C repo update-ref --stdin <stdin &&
+ test_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&
+ test_line_count = 2 repo/.git/reftable/tables.list
+'
+
+test_expect_success 'worktree: can access common refs' '
+ test_when_finished "rm -rf repo worktree" &&
+ git init repo &&
+ test_commit -C repo file1 &&
+ git -C repo branch branch1 &&
+ git -C repo worktree add ../worktree &&
+
+ echo refs/heads/worktree >expect &&
+ git -C worktree symbolic-ref HEAD >actual &&
+ test_cmp expect actual &&
+ git -C worktree checkout branch1
+'
+
+test_expect_success 'worktree: adds worktree with detached HEAD' '
+ test_when_finished "rm -rf repo worktree" &&
+
+ git init repo &&
+ test_commit -C repo A &&
+ git -C repo rev-parse main >expect &&
+
+ git -C repo worktree add --detach ../worktree main &&
+ git -C worktree rev-parse HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+ test_when_finished "rm -rf repo sub" &&
+
+ git init sub &&
+ test_commit -C sub two &&
+ git -C sub rev-parse HEAD >expect &&
+
+ git init repo &&
+ test_commit -C repo one &&
+ git -C repo fetch ../sub &&
+ git -C repo rev-parse FETCH_HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0611-reftable-httpd.sh b/t/t0611-reftable-httpd.sh
new file mode 100755
index 0000000000..5e05b9c1f2
--- /dev/null
+++ b/t/t0611-reftable-httpd.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='reftable HTTPD tests'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+
+test_expect_success 'serving ls-remote' '
+ git init --ref-format=reftable -b main "$REPO" &&
+ cd "$REPO" &&
+ test_commit m1 &&
+ >.git/git-daemon-export-ok &&
+ git ls-remote "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" | cut -f 2-2 -d " " >actual &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/tags/m1
+ EOF
+ test_cmp actual expect
+'
+
+test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index d73a0be1b9..e12b221972 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -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 \
@@ -112,65 +112,65 @@ strlen () {
run_tests () {
type=$1
- sha1=$2
+ oid=$2
size=$3
content=$4
pretty_content=$5
- batch_output="$sha1 $type $size
+ batch_output="$oid $type $size
$content"
test_expect_success "$type exists" '
- git cat-file -e $sha1
+ git cat-file -e $oid
'
test_expect_success "Type of $type is correct" '
echo $type >expect &&
- git cat-file -t $sha1 >actual &&
+ git cat-file -t $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct" '
echo $size >expect &&
- git cat-file -s $sha1 >actual &&
+ git cat-file -s $oid >actual &&
test_cmp expect actual
'
test_expect_success "Type of $type is correct using --allow-unknown-type" '
echo $type >expect &&
- git cat-file -t --allow-unknown-type $sha1 >actual &&
+ git cat-file -t --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of $type is correct using --allow-unknown-type" '
echo $size >expect &&
- git cat-file -s --allow-unknown-type $sha1 >actual &&
+ git cat-file -s --allow-unknown-type $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "Content of $type is correct" '
echo_without_newline "$content" >expect &&
- git cat-file $type $sha1 >actual &&
+ git cat-file $type $oid >actual &&
test_cmp expect actual
'
test_expect_success "Pretty content of $type is correct" '
echo_without_newline "$pretty_content" >expect &&
- git cat-file -p $sha1 >actual &&
+ git cat-file -p $oid >actual &&
test_cmp expect actual
'
test -z "$content" ||
test_expect_success "--batch output of $type is correct" '
echo "$batch_output" >expect &&
- echo $sha1 | git cat-file --batch >actual &&
+ echo $oid | git cat-file --batch >actual &&
test_cmp expect actual
'
test_expect_success "--batch-check output of $type is correct" '
- echo "$sha1 $type $size" >expect &&
- echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+ echo "$oid $type $size" >expect &&
+ echo_without_newline $oid | git cat-file --batch-check >actual &&
test_cmp expect actual
'
@@ -179,33 +179,33 @@ $content"
test -z "$content" ||
test_expect_success "--batch-command $opt output of $type content is correct" '
echo "$batch_output" >expect &&
- test_write_lines "contents $sha1" | git cat-file --batch-command $opt >actual &&
+ test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual &&
test_cmp expect actual
'
test_expect_success "--batch-command $opt output of $type info is correct" '
- echo "$sha1 $type $size" >expect &&
- test_write_lines "info $sha1" |
+ echo "$oid $type $size" >expect &&
+ test_write_lines "info $oid" |
git cat-file --batch-command $opt >actual &&
test_cmp expect actual
'
done
test_expect_success "custom --batch-check format" '
- echo "$type $sha1" >expect &&
- echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+ echo "$type $oid" >expect &&
+ echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
test_cmp expect actual
'
test_expect_success "custom --batch-command format" '
- echo "$type $sha1" >expect &&
- echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+ echo "$type $oid" >expect &&
+ echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
test_cmp expect actual
'
test_expect_success '--batch-check with %(rest)' '
echo "$type this is some extra content" >expect &&
- echo "$sha1 this is some extra content" |
+ echo "$oid this is some extra content" |
git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
test_cmp expect actual
'
@@ -216,7 +216,7 @@ $content"
echo "$size" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objectsize)" >actual &&
+ echo $oid | git cat-file --batch="%(objectsize)" >actual &&
test_cmp expect actual
'
@@ -226,114 +226,154 @@ $content"
echo "$type" &&
echo "$content"
} >expect &&
- echo $sha1 | git cat-file --batch="%(objecttype)" >actual &&
+ echo $oid | git cat-file --batch="%(objecttype)" >actual &&
test_cmp expect actual
'
}
hello_content="Hello World"
hello_size=$(strlen "$hello_content")
-hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+hello_oid=$(echo_without_newline "$hello_content" | git hash-object --stdin)
test_expect_success "setup" '
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $test_hash_algo &&
+ git config extensions.compatobjectformat $test_compat_hash_algo &&
echo_without_newline "$hello_content" > hello &&
git update-index --add hello
'
-run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+run_blob_tests () {
+ oid=$1
-test_expect_success '--batch-command --buffer with flush for blob info' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- test_write_lines "info $hello_sha1" "flush" |
+ run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content"
+
+ test_expect_success '--batch-command --buffer with flush for blob info' '
+ echo "$oid blob $hello_size" >expect &&
+ test_write_lines "info $oid" "flush" |
GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
git cat-file --batch-command --buffer >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch-command --buffer without flush for blob info' '
+ test_expect_success '--batch-command --buffer without flush for blob info' '
touch output &&
- test_write_lines "info $hello_sha1" |
+ test_write_lines "info $oid" |
GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
git cat-file --batch-command --buffer >>output &&
test_must_be_empty output
-'
+ '
+}
+
+hello_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $hello_oid)
+run_blob_tests $hello_oid
+run_blob_tests $hello_compat_oid
test_expect_success '--batch-check without %(rest) considers whole line' '
- echo "$hello_sha1 blob $hello_size" >expect &&
- git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
+ echo "$hello_oid blob $hello_size" >expect &&
+ git update-index --add --cacheinfo 100644 $hello_oid "white space" &&
test_when_finished "git update-index --remove \"white space\"" &&
echo ":white space" | git cat-file --batch-check >actual &&
test_cmp expect actual
'
-tree_sha1=$(git write-tree)
+tree_oid=$(git write-tree)
+tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid)
tree_size=$(($(test_oid rawsz) + 13))
-tree_pretty_content="100644 blob $hello_sha1 hello${LF}"
+tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13))
+tree_pretty_content="100644 blob $hello_oid hello${LF}"
+tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}"
-run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content"
+run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content"
commit_message="Initial commit"
-commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid)
+commit_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $commit_oid)
commit_size=$(($(test_oid hexsz) + 137))
-commit_content="tree $tree_sha1
+commit_compat_size=$(($(test_oid --hash=compat hexsz) + 137))
+commit_content="tree $tree_oid
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+$commit_message"
+
+commit_compat_content="tree $tree_compat_oid
author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
$commit_message"
-run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content"
+run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content"
-tag_header_without_timestamp="object $hello_sha1
-type blob
+tag_header_without_oid="type blob
tag hellotag
tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_header_without_timestamp="object $hello_oid
+$tag_header_without_oid"
+tag_compat_header_without_timestamp="object $hello_compat_oid
+$tag_header_without_oid"
tag_description="This is a tag"
tag_content="$tag_header_without_timestamp 0 +0000
$tag_description"
+tag_compat_content="$tag_compat_header_without_timestamp 0 +0000
-tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
+$tag_description"
+
+tag_oid=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
tag_size=$(strlen "$tag_content")
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content"
+tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid)
+tag_compat_size=$(strlen "$tag_compat_content")
+
+run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content"
+run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content"
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 &&
+ git cat-file blob $tag_oid >actual &&
test_cmp expect actual
'
-for batch in batch batch-check batch-command
+for oid in $hello_oid $hello_compat_oid
do
- for opt in t s e p
+ for batch in batch batch-check batch-command
do
+ for opt in t s e p
+ do
test_expect_success "Passing -$opt with --$batch fails" '
- test_must_fail git cat-file --$batch -$opt $hello_sha1
+ test_must_fail git cat-file --$batch -$opt $oid
'
test_expect_success "Passing --$batch with -$opt fails" '
- test_must_fail git cat-file -$opt --$batch $hello_sha1
+ test_must_fail git cat-file -$opt --$batch $oid
'
- done
+ done
- test_expect_success "Passing <type> with --$batch fails" '
- test_must_fail git cat-file --$batch blob $hello_sha1
- '
+ test_expect_success "Passing <type> with --$batch fails" '
+ test_must_fail git cat-file --$batch blob $oid
+ '
- test_expect_success "Passing --$batch with <type> fails" '
- test_must_fail git cat-file blob --$batch $hello_sha1
- '
+ test_expect_success "Passing --$batch with <type> fails" '
+ test_must_fail git cat-file blob --$batch $oid
+ '
- test_expect_success "Passing sha1 with --$batch fails" '
- test_must_fail git cat-file --$batch $hello_sha1
- '
+ test_expect_success "Passing oid with --$batch fails" '
+ test_must_fail git cat-file --$batch $oid
+ '
+ done
done
-for opt in t s e p
+for oid in $hello_oid $hello_compat_oid
do
- test_expect_success "Passing -$opt with --follow-symlinks fails" '
- test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+ for opt in t s e p
+ do
+ test_expect_success "Passing -$opt with --follow-symlinks fails" '
+ test_must_fail git cat-file --follow-symlinks -$opt $oid
'
+ done
done
test_expect_success "--batch-check for a non-existent named object" '
@@ -360,12 +400,12 @@ test_expect_success "--batch-check for a non-existent hash" '
test_expect_success "--batch for an existent and a non-existent hash" '
cat >expect <<-EOF &&
- $tag_sha1 tag $tag_size
+ $tag_oid tag $tag_size
$tag_content
0000000000000000000000000000000000000000 missing
EOF
- printf "$tag_sha1\n0000000000000000000000000000000000000000" >in &&
+ printf "$tag_oid\n0000000000000000000000000000000000000000" >in &&
git cat-file --batch <in >actual &&
test_cmp expect actual
'
@@ -386,112 +426,102 @@ test_expect_success 'empty --batch-check notices missing object' '
test_cmp expect actual
'
-batch_input="$hello_sha1
-$commit_sha1
-$tag_sha1
+batch_tests () {
+ boid=$1
+ loid=$2
+ lsize=$3
+ coid=$4
+ csize=$5
+ ccontent=$6
+ toid=$7
+ tsize=$8
+ tcontent=$9
+
+ batch_input="$boid
+$coid
+$toid
deadbeef
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ printf "%s\0" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" \
" missing" >batch_output
-test_expect_success '--batch with multiple sha1s gives correct format' '
+ test_expect_success '--batch with multiple oids gives correct format' '
tr "\0" "\n" <batch_output >expect &&
echo_without_newline "$batch_input" >in &&
git cat-file --batch <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch, -z with multiple sha1s gives correct format' '
+ test_expect_success '--batch, -z with multiple oids gives correct format' '
echo_without_newline_nul "$batch_input" >in &&
tr "\0" "\n" <batch_output >expect &&
git cat-file --batch -z <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success '--batch, -Z with multiple sha1s gives correct format' '
+ test_expect_success '--batch, -Z with multiple oids gives correct format' '
echo_without_newline_nul "$batch_input" >in &&
git cat-file --batch -Z <in >actual &&
test_cmp batch_output actual
-'
+ '
-batch_check_input="$hello_sha1
-$tree_sha1
-$commit_sha1
-$tag_sha1
+batch_check_input="$boid
+$loid
+$coid
+$toid
deadbeef
"
-printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
- "$tree_sha1 tree $tree_size" \
- "$commit_sha1 commit $commit_size" \
- "$tag_sha1 tag $tag_size" \
+ printf "%s\0" \
+ "$boid blob $hello_size" \
+ "$loid tree $lsize" \
+ "$coid commit $csize" \
+ "$toid tag $tsize" \
"deadbeef missing" \
" missing" >batch_check_output
-test_expect_success "--batch-check with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check with multiple oids gives correct format" '
tr "\0" "\n" <batch_check_output >expect &&
echo_without_newline "$batch_check_input" >in &&
git cat-file --batch-check <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success "--batch-check, -z with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check, -z with multiple oids gives correct format" '
tr "\0" "\n" <batch_check_output >expect &&
echo_without_newline_nul "$batch_check_input" >in &&
git cat-file --batch-check -z <in >actual &&
test_cmp expect actual
-'
+ '
-test_expect_success "--batch-check, -Z with multiple sha1s gives correct format" '
+ test_expect_success "--batch-check, -Z with multiple oids gives correct format" '
echo_without_newline_nul "$batch_check_input" >in &&
git cat-file --batch-check -Z <in >actual &&
test_cmp batch_check_output actual
-'
-
-test_expect_success FUNNYNAMES 'setup with newline in input' '
- touch -- "newline${LF}embedded" &&
- git add -- "newline${LF}embedded" &&
- git commit -m "file with newline embedded" &&
- test_tick &&
-
- printf "HEAD:newline${LF}embedded" >in
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
- git cat-file --batch-check -z <in >actual &&
- echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
- test_cmp expect actual
-'
-
-test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
- git cat-file --batch-check -Z <in >actual &&
- printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
- test_cmp expect actual
-'
+ '
-batch_command_multiple_info="info $hello_sha1
-info $tree_sha1
-info $commit_sha1
-info $tag_sha1
+batch_command_multiple_info="info $boid
+info $loid
+info $coid
+info $toid
info deadbeef"
-test_expect_success '--batch-command with multiple info calls gives correct format' '
+ test_expect_success '--batch-command with multiple info calls gives correct format' '
cat >expect <<-EOF &&
- $hello_sha1 blob $hello_size
- $tree_sha1 tree $tree_size
- $commit_sha1 commit $commit_size
- $tag_sha1 tag $tag_size
+ $boid blob $hello_size
+ $loid tree $lsize
+ $coid commit $csize
+ $toid tag $tsize
deadbeef missing
EOF
@@ -510,22 +540,22 @@ test_expect_success '--batch-command with multiple info calls gives correct form
git cat-file --batch-command --buffer -Z <in >actual &&
test_cmp expect_nul actual
-'
+ '
-batch_command_multiple_contents="contents $hello_sha1
-contents $commit_sha1
-contents $tag_sha1
+batch_command_multiple_contents="contents $boid
+contents $coid
+contents $toid
contents deadbeef
flush"
-test_expect_success '--batch-command with multiple command calls gives correct format' '
+ test_expect_success '--batch-command with multiple command calls gives correct format' '
printf "%s\0" \
- "$hello_sha1 blob $hello_size" \
+ "$boid blob $hello_size" \
"$hello_content" \
- "$commit_sha1 commit $commit_size" \
- "$commit_content" \
- "$tag_sha1 tag $tag_size" \
- "$tag_content" \
+ "$coid commit $csize" \
+ "$ccontent" \
+ "$toid tag $tsize" \
+ "$tcontent" \
"deadbeef missing" >expect_nul &&
tr "\0" "\n" <expect_nul >expect &&
@@ -543,6 +573,33 @@ test_expect_success '--batch-command with multiple command calls gives correct f
git cat-file --batch-command --buffer -Z <in >actual &&
test_cmp expect_nul actual
+ '
+
+}
+
+batch_tests $hello_oid $tree_oid $tree_size $commit_oid $commit_size "$commit_content" $tag_oid $tag_size "$tag_content"
+batch_tests $hello_compat_oid $tree_compat_oid $tree_compat_size $commit_compat_oid $commit_compat_size "$commit_compat_content" $tag_compat_oid $tag_compat_size "$tag_compat_content"
+
+
+test_expect_success FUNNYNAMES 'setup with newline in input' '
+ touch -- "newline${LF}embedded" &&
+ git add -- "newline${LF}embedded" &&
+ git commit -m "file with newline embedded" &&
+ test_tick &&
+
+ printf "HEAD:newline${LF}embedded" >in
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
+ git cat-file --batch-check -z <in >actual &&
+ echo "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -Z with newline in input' '
+ git cat-file --batch-check -Z <in >actual &&
+ printf "%s\0" "$(git rev-parse "HEAD:newline${LF}embedded") blob 0" >expect &&
+ test_cmp expect actual
'
test_expect_success 'setup blobs which are likely to delta' '
@@ -569,7 +626,7 @@ test_expect_success 'confirm that neither loose blob is a delta' '
# we will check only that one of the two objects is a delta
# against the other, but not the order. We can do so by just
# asking for the base of both, and checking whether either
-# sha1 appears in the output.
+# oid appears in the output.
test_expect_success '%(deltabase) reports packed delta bases' '
git repack -ad &&
git cat-file --batch-check="%(deltabase)" <blobs >actual &&
@@ -583,12 +640,12 @@ test_expect_success 'setup bogus data' '
bogus_short_type="bogus" &&
bogus_short_content="bogus" &&
bogus_short_size=$(strlen "$bogus_short_content") &&
- bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+ bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
bogus_long_content="bogus" &&
bogus_long_size=$(strlen "$bogus_long_content") &&
- bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+ bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
'
for arg1 in '' --allow-unknown-type
@@ -608,9 +665,9 @@ do
if test "$arg1" = "--allow-unknown-type"
then
- git cat-file $arg1 $arg2 $bogus_short_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
@@ -620,21 +677,21 @@ do
if test "$arg2" = "-p"
then
cat >expect <<-EOF
- error: header for $bogus_long_sha1 too long, exceeds 32 bytes
- fatal: Not a valid object name $bogus_long_sha1
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
+ fatal: Not a valid object name $bogus_long_oid
EOF
else
cat >expect <<-EOF
- error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+ error: header for $bogus_long_oid too long, exceeds 32 bytes
fatal: git cat-file: could not get object info
EOF
fi &&
if test "$arg1" = "--allow-unknown-type"
then
- git cat-file $arg1 $arg2 $bogus_short_sha1
+ git cat-file $arg1 $arg2 $bogus_short_oid
else
- test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+ test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
fi
@@ -668,28 +725,28 @@ do
done
test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
- git cat-file -e $bogus_short_sha1
+ git cat-file -e $bogus_short_oid
'
test_expect_success '-e can not be combined with --allow-unknown-type' '
- test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+ test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_oid
'
test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
- test_must_fail git cat-file -p $bogus_short_sha1 &&
- test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+ test_must_fail git cat-file -p $bogus_short_oid &&
+ test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
'
test_expect_success '<type> <hash> does not work with objects of broken types' '
cat >err.expect <<-\EOF &&
fatal: invalid object type "bogus"
EOF
- test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+ test_must_fail git cat-file $bogus_short_type $bogus_short_oid 2>err.actual &&
test_cmp err.expect err.actual
'
test_expect_success 'broken types combined with --batch and --batch-check' '
- echo $bogus_short_sha1 >bogus-oid &&
+ echo $bogus_short_oid >bogus-oid &&
cat >err.expect <<-\EOF &&
fatal: invalid object type
@@ -711,52 +768,52 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
cat >expect <<-EOF &&
$bogus_short_type
EOF
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual &&
# Create it manually, as "git replace" will die on bogus
# types.
head=$(git rev-parse --verify HEAD) &&
- test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
- test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
+ test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
+ test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
cat >expect <<-EOF &&
commit
EOF
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success "Type of broken object is correct" '
echo $bogus_short_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of broken object is correct" '
echo $bogus_short_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
+ git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'
test_expect_success 'clean up broken object' '
- rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+ rm .git/objects/$(test_oid_to_path $bogus_short_oid)
'
test_expect_success "Type of broken object is correct when type is large" '
echo $bogus_long_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
+ git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
test_expect_success "Size of large broken object is correct when type is large" '
echo $bogus_long_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
+ git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'
test_expect_success 'clean up broken object' '
- rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+ rm .git/objects/$(test_oid_to_path $bogus_long_oid)
'
test_expect_success 'cat-file -t and -s on corrupt loose object' '
@@ -853,7 +910,7 @@ test_expect_success 'prep for symlink tests' '
test_ln_s_add loop2 loop1 &&
git add morx dir/subdir/ind2 dir/ind1 &&
git commit -am "test" &&
- echo $hello_sha1 blob $hello_size >found
+ echo $hello_oid blob $hello_size >found
'
test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
@@ -941,7 +998,7 @@ test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/
echo HEAD:dirlink/morx >>expect &&
echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual &&
- echo $hello_sha1 blob $hello_size >expect &&
+ echo $hello_oid blob $hello_size >expect &&
echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
test_cmp expect actual
'
@@ -1100,6 +1157,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 &&
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index ac3d173767..64aea38486 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -124,8 +124,8 @@ test_expect_success 'check that appropriate filter is invoke when --path is used
path0_sha=$(git hash-object --path=file0 file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha" &&
- path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
- path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+ path1_sha=$(git hash-object --path=file1 --stdin <file0) &&
+ path0_sha=$(git hash-object --path=file0 --stdin <file1) &&
test "$file0_sha" = "$path0_sha" &&
test "$file1_sha" = "$path1_sha"
'
@@ -154,7 +154,7 @@ test_expect_success '--path works in a subdirectory' '
test_expect_success 'check that --no-filters option works' '
nofilters_file1=$(git hash-object --no-filters file1) &&
test "$file0_sha" = "$nofilters_file1" &&
- nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+ nofilters_file1=$(git hash-object --stdin <file1) &&
test "$file0_sha" = "$nofilters_file1"
'
diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh
new file mode 100755
index 0000000000..be3206a16f
--- /dev/null
+++ b/t/t1016-compatObjectFormat.sh
@@ -0,0 +1,281 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Eric Biederman
+#
+
+test_description='Test how well compatObjectFormat works'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+# All of the follow variables must be defined in the environment:
+# GIT_AUTHOR_NAME
+# GIT_AUTHOR_EMAIL
+# GIT_AUTHOR_DATE
+# GIT_COMMITTER_NAME
+# GIT_COMMITTER_EMAIL
+# GIT_COMMITTER_DATE
+#
+# The test relies on these variables being set so that the two
+# different commits in two different repositories encoded with two
+# different hash functions result in the same content in the commits.
+# This means that when the commit is translated between hash functions
+# the commit is identical to the commit in the other repository.
+
+compat_hash () {
+ case "$1" in
+ "sha1")
+ echo "sha256"
+ ;;
+ "sha256")
+ echo "sha1"
+ ;;
+ esac
+}
+
+hello_oid () {
+ case "$1" in
+ "sha1")
+ echo "$hello_sha1_oid"
+ ;;
+ "sha256")
+ echo "$hello_sha256_oid"
+ ;;
+ esac
+}
+
+tree_oid () {
+ case "$1" in
+ "sha1")
+ echo "$tree_sha1_oid"
+ ;;
+ "sha256")
+ echo "$tree_sha256_oid"
+ ;;
+ esac
+}
+
+commit_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit_sha256_oid"
+ ;;
+ esac
+}
+
+commit2_oid () {
+ case "$1" in
+ "sha1")
+ echo "$commit2_sha1_oid"
+ ;;
+ "sha256")
+ echo "$commit2_sha256_oid"
+ ;;
+ esac
+}
+
+del_sigcommit () {
+ local delete="$1"
+
+ if test "$delete" = "sha256" ; then
+ local pattern="gpgsig-sha256"
+ else
+ local pattern="gpgsig"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+
+del_sigtag () {
+ local storage="$1"
+ local delete="$2"
+
+ if test "$storage" = "$delete" ; then
+ local pattern="trailer"
+ elif test "$storage" = "sha256" ; then
+ local pattern="gpgsig"
+ else
+ local pattern="gpgsig-sha256"
+ fi
+ test-tool delete-gpgsig "$pattern"
+}
+
+base=$(pwd)
+for hash in sha1 sha256
+do
+ cd "$base"
+ mkdir -p repo-$hash
+ cd repo-$hash
+
+ test_expect_success "setup $hash repository" '
+ git init --object-format=$hash &&
+ git config core.repositoryformatversion 1 &&
+ git config extensions.objectformat $hash &&
+ git config extensions.compatobjectformat $(compat_hash $hash) &&
+ git config gpg.program $TEST_DIRECTORY/t1016/gpg &&
+ echo "Hellow World!" > hello &&
+ eval hello_${hash}_oid=$(git hash-object hello) &&
+ git update-index --add hello &&
+ git commit -m "Initial commit" &&
+ eval commit_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success "create a $hash tagged blob" '
+ git tag --no-sign -m "This is a tag" hellotag $(hello_oid $hash) &&
+ eval hellotag_${hash}_oid=$(git rev-parse hellotag)
+ '
+ test_expect_success "create a $hash tagged tree" '
+ git tag --no-sign -m "This is a tag" treetag $(tree_oid $hash) &&
+ eval treetag_${hash}_oid=$(git rev-parse treetag)
+ '
+ test_expect_success "create a $hash tagged commit" '
+ git tag --no-sign -m "This is a tag" committag $(commit_oid $hash) &&
+ eval committag_${hash}_oid=$(git rev-parse committag)
+ '
+ test_expect_success GPG2 "create a $hash signed commit" '
+ git commit --gpg-sign --allow-empty -m "This is a signed commit" &&
+ eval signedcommit_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create a $hash signed tag" '
+ git tag -s -m "This is a signed tag" signedtag HEAD &&
+ eval signedtag_${hash}_oid=$(git rev-parse signedtag)
+ '
+ test_expect_success "create a $hash branch" '
+ git checkout -b branch $(commit_oid $hash) &&
+ echo "More more more give me more!" > more &&
+ eval more_${hash}_oid=$(git hash-object more) &&
+ echo "Another and another and another" > another &&
+ eval another_${hash}_oid=$(git hash-object another) &&
+ git update-index --add more another &&
+ git commit -m "Add more files!" &&
+ eval commit2_${hash}_oid=$(git rev-parse HEAD) &&
+ eval tree2_${hash}_oid=$(git rev-parse HEAD^{tree})
+ '
+ test_expect_success GPG2 "create another $hash signed tag" '
+ git tag -s -m "This is another signed tag" signedtag2 $(commit2_oid $hash) &&
+ eval signedtag2_${hash}_oid=$(git rev-parse signedtag2)
+ '
+ test_expect_success GPG2 "merge the $hash branches together" '
+ git merge -S -m "merge some signed tags together" signedtag signedtag2 &&
+ eval signedcommit2_${hash}_oid=$(git rev-parse HEAD)
+ '
+ test_expect_success GPG2 "create additional $hash signed commits" '
+ git commit --gpg-sign --allow-empty -m "This is an additional signed commit" &&
+ git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" &&
+ git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" &&
+ eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) &&
+ eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4)
+ '
+ test_expect_success GPG2 "create additional $hash signed tags" '
+ git tag -s -m "This is an additional signed tag" signedtag34 HEAD &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 &&
+ git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 &&
+ eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) &&
+ eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4)
+ '
+done
+cd "$base"
+
+compare_oids () {
+ test "$#" = 5 && { local PREREQ="$1"; shift; } || PREREQ=
+ local type="$1"
+ local name="$2"
+ local sha1_oid="$3"
+ local sha256_oid="$4"
+
+ echo ${sha1_oid} > ${name}_sha1_expected
+ echo ${sha256_oid} > ${name}_sha256_expected
+ echo ${type} > ${name}_type_expected
+
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found
+ local sha1_sha256_oid="$(cat ${name}_sha1_sha256_found)"
+ local sha256_sha1_oid="$(cat ${name}_sha256_sha1_found)"
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" '
+ git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 &&
+ test_cmp ${name}_sha1 ${name}_sha1_expected
+'
+
+ test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" '
+ git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 &&
+ test_cmp ${name}_sha256 ${name}_sha256_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 type" '
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 &&
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type2 &&
+ test_cmp ${name}_type1 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 type" '
+ git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 &&
+ git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type4 &&
+ test_cmp ${name}_type3 ${name}_type_expected
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 size" '
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 &&
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 &&
+ test_cmp ${name}_size1 ${name}_size2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 size" '
+ git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 &&
+ git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 &&
+ test_cmp ${name}_size3 ${name}_size4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" '
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 &&
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 &&
+ test_cmp ${name}_content1 ${name}_content2
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" '
+ git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 &&
+ git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 &&
+ test_cmp ${name}_content3 ${name}_content4
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha1 content" '
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 &&
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 &&
+ test_cmp ${name}_content5 ${name}_content6
+'
+
+ test_expect_success $PREREQ "Verify ${name}'s sha256 content" '
+ git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 &&
+ git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 &&
+ test_cmp ${name}_content7 ${name}_content8
+'
+
+}
+
+compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid"
+compare_oids 'tree' tree "$tree_sha1_oid" "$tree_sha256_oid"
+compare_oids 'commit' commit "$commit_sha1_oid" "$commit_sha256_oid"
+compare_oids GPG2 'commit' signedcommit "$signedcommit_sha1_oid" "$signedcommit_sha256_oid"
+compare_oids 'tag' hellotag "$hellotag_sha1_oid" "$hellotag_sha256_oid"
+compare_oids 'tag' treetag "$treetag_sha1_oid" "$treetag_sha256_oid"
+compare_oids 'tag' committag "$committag_sha1_oid" "$committag_sha256_oid"
+compare_oids GPG2 'tag' signedtag "$signedtag_sha1_oid" "$signedtag_sha256_oid"
+
+compare_oids 'blob' more "$more_sha1_oid" "$more_sha256_oid"
+compare_oids 'blob' another "$another_sha1_oid" "$another_sha256_oid"
+compare_oids 'tree' tree2 "$tree2_sha1_oid" "$tree2_sha256_oid"
+compare_oids 'commit' commit2 "$commit2_sha1_oid" "$commit2_sha256_oid"
+compare_oids GPG2 'tag' signedtag2 "$signedtag2_sha1_oid" "$signedtag2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit2 "$signedcommit2_sha1_oid" "$signedcommit2_sha256_oid"
+compare_oids GPG2 'commit' signedcommit3 "$signedcommit3_sha1_oid" "$signedcommit3_sha256_oid"
+compare_oids GPG2 'commit' signedcommit4 "$signedcommit4_sha1_oid" "$signedcommit4_sha256_oid"
+compare_oids GPG2 'tag' signedtag3 "$signedtag3_sha1_oid" "$signedtag3_sha256_oid"
+compare_oids GPG2 'tag' signedtag4 "$signedtag4_sha1_oid" "$signedtag4_sha256_oid"
+
+test_done
diff --git a/t/t1016/gpg b/t/t1016/gpg
new file mode 100755
index 0000000000..2601cb18a5
--- /dev/null
+++ b/t/t1016/gpg
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec gpg --faked-system-time "20230918T154812" "$@"
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index f67611da28..ab3a105fff 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -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 &&
/*
!/*/
@@ -886,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 &&
@@ -962,7 +968,7 @@ test_expect_success 'check-rules non-cone mode' '
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 sparse-checkout set --no-cone --stdin <rules &&
git -C repo ls-files -t >out &&
sed -n "/^S /!s/^. //p" out >ls-files &&
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index f4e2752134..9b65d9eaf5 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,98 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+test_expect_success 'setup whitespace config' '
+ sed -e "s/^|//" \
+ -e "s/[$]$//" \
+ -e "s/X/ /g" >.git/config <<-\EOF
+ [section]
+ | solid = rock
+ | sparse = big XX blue
+ | sparseAndTail = big XX blue $
+ | sparseAndTailQuoted = "big XX blue "
+ | sparseAndBiggerTail = big XX blue X X
+ | sparseAndBiggerTailQuoted = "big XX blue X X"
+ | sparseAndBiggerTailQuotedPlus = "big XX blue X X"X $
+ | headAndTail = Xbig blue $
+ | headAndTailQuoted = "Xbig blue "
+ | headAndTailQuotedPlus = "Xbig blue " $
+ | annotated = big blueX# to be discarded
+ | annotatedQuoted = "big blue"X# to be discarded
+ EOF
+'
+
+test_expect_success 'no internal whitespace' '
+ echo "rock" >expect &&
+ git config --get section.solid >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparse >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and trailing whitespace, all quoted' '
+ echo "big QQ blue " | q_to_tab >expect &&
+ git config --get section.sparseAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace' '
+ echo "big QQ blue" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'internal and more trailing whitespace, not all quoted' '
+ echo "big QQ blue Q Q" | q_to_tab >expect &&
+ git config --get section.sparseAndBiggerTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace' '
+ echo "big blue" >expect &&
+ git config --get section.headAndTail >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuoted >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'leading and trailing whitespace, not all quoted' '
+ echo "Qbig blue " | q_to_tab >expect &&
+ git config --get section.headAndTailQuotedPlus >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment' '
+ echo "big blue" >expect &&
+ git config --get section.annotated >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inline comment, quoted' '
+ echo "big blue" >expect &&
+ git config --get section.annotatedQuoted >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'clear default config' '
rm -f .git/config
'
@@ -69,14 +161,32 @@ test_expect_success 'replace with non-match (actually matching)' '
cat > expect << EOF
[section]
- penguin = very blue
Movie = BadPhysics
UPPERCASE = true
- penguin = kingpin
+ penguin = gentoo # Pygoscelis papua
+ disposition = peckish # find fish
+ foo = bar #abc
+ spsp = value # and comment
+ htsp = value # and comment
[Sections]
WhatEver = Second
EOF
+test_expect_success 'append comments' '
+ git config --replace-all --comment="Pygoscelis papua" section.penguin gentoo &&
+ git config --comment="find fish" section.disposition peckish &&
+ git config --comment="#abc" section.foo bar &&
+
+ git config --comment="and comment" section.spsp value &&
+ git config --comment=" # and comment" section.htsp value &&
+
+ test_cmp expect .git/config
+'
+
+test_expect_success 'Prohibited LF in comment' '
+ test_must_fail git config --comment="a${LF}b" section.k v
+'
+
test_expect_success 'non-match result' 'test_cmp expect .git/config'
test_expect_success 'find mixed-case key by canonical name' '
@@ -1066,9 +1176,25 @@ test_expect_success '--null --get-regexp' '
test_cmp expect result
'
-test_expect_success 'inner whitespace kept verbatim' '
- git config section.val "foo bar" &&
- test_cmp_config "foo bar" section.val
+test_expect_success 'inner whitespace kept verbatim, spaces only' '
+ echo "foo bar" >expect &&
+ git config section.val "foo bar" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs only' '
+ echo "fooQQbar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'inner whitespace kept verbatim, horizontal tabs and spaces' '
+ echo "foo Q bar" | q_to_tab >expect &&
+ git config section.val "$(cat expect)" &&
+ git config --get section.val >actual &&
+ test_cmp expect actual
'
test_expect_success SYMLINKS 'symlinked configuration' '
@@ -1098,15 +1224,20 @@ test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
test_must_fail git config --file=linktolinktonada --list
'
-test_expect_success 'check split_cmdline return' "
- git config alias.split-cmdline-fix 'echo \"' &&
- test_must_fail git split-cmdline-fix &&
- echo foo > foo &&
- git add foo &&
- git commit -m 'initial commit' &&
- git config branch.main.mergeoptions 'echo \"' &&
- test_must_fail git merge main
-"
+test_expect_success 'check split_cmdline return' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git config alias.split-cmdline-fix "echo \"" &&
+ test_must_fail git split-cmdline-fix &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -m "initial commit" &&
+ git config branch.main.mergeoptions "echo \"" &&
+ test_must_fail git merge main
+ )
+'
test_expect_success 'git -c "key=value" support' '
cat >expect <<-\EOF &&
@@ -1157,10 +1288,16 @@ test_expect_success 'git -c works with aliases of builtins' '
'
test_expect_success 'aliases can be CamelCased' '
- test_config alias.CamelCased "rev-parse HEAD" &&
- git CamelCased >out &&
- git rev-parse HEAD >expect &&
- test_cmp expect out
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git config alias.CamelCased "rev-parse HEAD" &&
+ git CamelCased >out &&
+ git rev-parse HEAD >expect &&
+ test_cmp expect out
+ )
'
test_expect_success 'git -c does not split values on equals' '
@@ -2009,11 +2146,11 @@ test_expect_success '--show-origin getting a single key' '
'
test_expect_success 'set up custom config file' '
- CUSTOM_CONFIG_FILE="custom.conf" &&
- cat >"$CUSTOM_CONFIG_FILE" <<-\EOF
+ cat >"custom.conf" <<-\EOF &&
[user]
custom = true
EOF
+ CUSTOM_CONFIG_FILE="$(test-tool path-utils real_path custom.conf)"
'
test_expect_success !MINGW 'set up custom config file with special name characters' '
@@ -2052,22 +2189,33 @@ test_expect_success '--show-origin stdin with file include' '
'
test_expect_success '--show-origin blob' '
- blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
- cat >expect <<-EOF &&
- blob:$blob user.custom=true
- EOF
- git config --blob=$blob --show-origin --list >output &&
- test_cmp expect output
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ blob=$(git hash-object -w "$CUSTOM_CONFIG_FILE") &&
+ cat >expect <<-EOF &&
+ blob:$blob user.custom=true
+ EOF
+ git config --blob=$blob --show-origin --list >output &&
+ test_cmp expect output
+ )
'
test_expect_success '--show-origin blob ref' '
- cat >expect <<-\EOF &&
- blob:main:custom.conf user.custom=true
- EOF
- git add "$CUSTOM_CONFIG_FILE" &&
- git commit -m "new config file" &&
- git config --blob=main:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
- test_cmp expect output
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ cat >expect <<-\EOF &&
+ blob:main:custom.conf user.custom=true
+ EOF
+ cp "$CUSTOM_CONFIG_FILE" custom.conf &&
+ git add custom.conf &&
+ git commit -m "new config file" &&
+ git config --blob=main:custom.conf --show-origin --list >output &&
+ test_cmp expect output
+ )
'
test_expect_success '--show-origin with --default' '
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index e5a0d65caa..29cf8a9661 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -52,7 +52,7 @@ test_expect_success 'shared=all' '
test 2 = $(git config core.sharedrepository)
'
-test_expect_failure 'template can set core.bare' '
+test_expect_success 'template cannot set core.bare' '
test_when_finished "rm -rf subdir" &&
test_when_finished "rm -rf templates" &&
test_config core.bare true &&
@@ -60,18 +60,7 @@ test_expect_failure 'template can set core.bare' '
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_path_is_missing subdir/HEAD
'
test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
@@ -137,22 +126,6 @@ test_expect_success POSIXPERM 'info/refs respects umask in unshared repo' '
test_cmp expect actual
'
-test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
- umask 077 &&
- git config core.sharedRepository group &&
- git reflog expire --all &&
- actual="$(ls -l .git/logs/refs/heads/main)" &&
- case "$actual" in
- -rw-rw-*)
- : happy
- ;;
- *)
- echo Ooops, .git/logs/refs/heads/main is not 066x [$actual]
- false
- ;;
- esac
-'
-
test_expect_success POSIXPERM 'forced modes' '
test_when_finished "rm -rf new" &&
mkdir -p templates/hooks &&
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 179474fa65..42caa0d297 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -9,10 +9,6 @@ TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
- test_oid_cache <<-\EOF &&
- version sha1:0
- version sha256:1
- EOF
cat >test.patch <<-\EOF &&
diff --git a/test.txt b/test.txt
new file mode 100644
@@ -28,7 +24,12 @@ test_expect_success 'setup' '
'
test_expect_success 'gitdir selection on normal repos' '
- test_oid version >expect &&
+ if test_have_prereq DEFAULT_REPO_FORMAT
+ then
+ echo 0
+ else
+ echo 1
+ fi >expect &&
git config core.repositoryformatversion >actual &&
git -C test config core.repositoryformatversion >actual2 &&
test_cmp expect actual &&
@@ -79,8 +80,13 @@ mkconfig () {
while read outcome version extensions; do
test_expect_success "$outcome version=$version $extensions" "
- mkconfig $version $extensions >.git/config &&
- check_${outcome}
+ test_when_finished 'rm -rf extensions' &&
+ git init extensions &&
+ (
+ cd extensions &&
+ mkconfig $version $extensions >.git/config &&
+ check_${outcome}
+ )
"
done <<\EOF
allow 0
@@ -94,7 +100,8 @@ allow 1 noop-v1
EOF
test_expect_success 'precious-objects allowed' '
- mkconfig 1 preciousObjects >.git/config &&
+ git config core.repositoryFormatVersion 1 &&
+ git config extensions.preciousObjects 1 &&
check_allow
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 6d1810c358..ec3443cc87 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -288,33 +288,6 @@ test_expect_success "set $m (logged by touch)" '
test $A = $(git show-ref -s --verify $m)
'
-test_expect_success REFFILES 'empty directory removal' '
- git branch d1/d2/r1 HEAD &&
- git branch d1/r2 HEAD &&
- test_path_is_file .git/refs/heads/d1/d2/r1 &&
- test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
- git branch -d d1/d2/r1 &&
- test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
- test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
- test_path_is_file .git/refs/heads/d1/r2 &&
- test_path_is_file .git/logs/refs/heads/d1/r2
-'
-
-test_expect_success REFFILES 'symref empty directory removal' '
- git branch e1/e2/r1 HEAD &&
- git branch e1/r2 HEAD &&
- git checkout e1/e2/r1 &&
- test_when_finished "git checkout main" &&
- test_path_is_file .git/refs/heads/e1/e2/r1 &&
- test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
- git update-ref -d HEAD &&
- test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
- test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
- test_path_is_file .git/refs/heads/e1/r2 &&
- test_path_is_file .git/logs/refs/heads/e1/r2 &&
- test_path_is_file .git/logs/HEAD
-'
-
cat >expect <<EOF
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
@@ -357,14 +330,28 @@ test_expect_success "verifying $m's log (logged by config)" '
'
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"
@@ -412,13 +399,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_grep -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
'
@@ -440,6 +426,22 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
rm -f expect
git update-ref -d $m
+test_expect_success 'query reflog with gap' '
+ test_when_finished "git update-ref -d $m" &&
+
+ GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $A &&
+ GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+ GIT_COMMITTER_DATE="1117150480 -0500" git update-ref $m $C &&
+ GIT_COMMITTER_DATE="1117150580 -0500" git update-ref $m $D &&
+ GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+ git reflog delete $m@{2} &&
+
+ git rev-parse --verify "main@{2005-05-26 23:33:01}" >actual 2>stderr &&
+ echo "$B" >expect &&
+ test_cmp expect actual &&
+ test_grep -F "warning: log for ref $m has gap after $gd" stderr
+'
+
test_expect_success 'creating initial files' '
test_when_finished rm -f M &&
echo TEST >F &&
@@ -495,51 +497,51 @@ test_expect_success 'given old value for missing pseudoref, do not create' '
test_expect_success 'create pseudoref' '
git update-ref PSEUDOREF $A &&
- test $A = $(git rev-parse PSEUDOREF)
+ test $A = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with no old value given' '
git update-ref PSEUDOREF $B &&
- test $B = $(git rev-parse PSEUDOREF)
+ test $B = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'overwrite pseudoref with correct old value' '
git update-ref PSEUDOREF $C $B &&
- test $C = $(git rev-parse PSEUDOREF)
+ test $C = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'do not overwrite pseudoref with wrong old value' '
test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
- test $C = $(git rev-parse PSEUDOREF) &&
+ test $C = $(git show-ref -s --verify PSEUDOREF) &&
test_grep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref' '
git update-ref -d PSEUDOREF &&
- test_must_fail git rev-parse PSEUDOREF
+ test_must_fail git show-ref -s --verify PSEUDOREF
'
test_expect_success 'do not delete pseudoref with wrong old value' '
git update-ref PSEUDOREF $A &&
test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
- test $A = $(git rev-parse PSEUDOREF) &&
+ test $A = $(git show-ref -s --verify PSEUDOREF) &&
test_grep "cannot lock ref.*expected" err
'
test_expect_success 'delete pseudoref with correct old value' '
git update-ref -d PSEUDOREF $A &&
- test_must_fail git rev-parse PSEUDOREF
+ test_must_fail git show-ref -s --verify PSEUDOREF
'
test_expect_success 'create pseudoref with old OID zero' '
git update-ref PSEUDOREF $A $Z &&
- test $A = $(git rev-parse PSEUDOREF)
+ test $A = $(git show-ref -s --verify PSEUDOREF)
'
test_expect_success 'do not overwrite pseudoref with old OID zero' '
test_when_finished git update-ref -d PSEUDOREF &&
test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
- test $A = $(git rev-parse PSEUDOREF) &&
+ test $A = $(git show-ref -s --verify PSEUDOREF) &&
test_grep "already exists" err
'
@@ -620,7 +622,7 @@ test_expect_success 'stdin fails create with no ref' '
test_expect_success 'stdin fails create with no new value' '
echo "create $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $a: missing <newvalue>" err
+ grep "fatal: create $a: missing <new-oid>" err
'
test_expect_success 'stdin fails create with too many arguments' '
@@ -638,7 +640,7 @@ test_expect_success 'stdin fails update with no ref' '
test_expect_success 'stdin fails update with no new value' '
echo "update $a" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: update $a: missing <newvalue>" err
+ grep "fatal: update $a: missing <new-oid>" err
'
test_expect_success 'stdin fails update with too many arguments' '
@@ -763,21 +765,21 @@ test_expect_success 'stdin update ref fails with wrong old value' '
test_expect_success 'stdin update ref fails with bad old value' '
echo "update $c $m does-not-exist" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin create ref fails with bad new value' '
echo "create $c does-not-exist" >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin create ref fails with zero new value' '
echo "create $c " >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: create $c: zero <newvalue>" err &&
+ grep "fatal: create $c: zero <new-oid>" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -801,7 +803,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
test_expect_success 'stdin delete ref fails with zero old value' '
echo "delete $a " >stdin &&
test_must_fail git update-ref --stdin <stdin 2>err &&
- grep "fatal: delete $a: zero <oldvalue>" err &&
+ grep "fatal: delete $a: zero <old-oid>" err &&
git rev-parse $m >expect &&
git rev-parse $a >actual &&
test_cmp expect actual
@@ -1025,7 +1027,7 @@ test_expect_success 'stdin -z fails create with no ref' '
test_expect_success 'stdin -z fails create with no new value' '
printf $F "create $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
+ grep "fatal: create $a: unexpected end of input when reading <new-oid>" err
'
test_expect_success 'stdin -z fails create with too many arguments' '
@@ -1043,27 +1045,27 @@ test_expect_success 'stdin -z fails update with no ref' '
test_expect_success 'stdin -z fails update with too few args' '
printf $F "update $a" "$m" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z emits warning with empty new value' '
git update-ref $a $m &&
printf $F "update $a" "" "" >stdin &&
git update-ref -z --stdin <stdin 2>err &&
- grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+ grep "warning: update $a: missing <new-oid>, treating as zero" err &&
test_must_fail git rev-parse --verify -q $a
'
test_expect_success 'stdin -z fails update with no new value' '
printf $F "update $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <new-oid>" err
'
test_expect_success 'stdin -z fails update with no old value' '
printf $F "update $a" "$m" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: update $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails update with too many arguments' '
@@ -1081,7 +1083,7 @@ test_expect_success 'stdin -z fails delete with no ref' '
test_expect_success 'stdin -z fails delete with no old value' '
printf $F "delete $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: delete $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -1099,7 +1101,7 @@ test_expect_success 'stdin -z fails verify with too many arguments' '
test_expect_success 'stdin -z fails verify with no old value' '
printf $F "verify $a" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
+ grep "fatal: verify $a: unexpected end of input when reading <old-oid>" err
'
test_expect_success 'stdin -z fails option with unknown name' '
@@ -1158,7 +1160,7 @@ test_expect_success 'stdin -z update ref fails with wrong old value' '
test_expect_success 'stdin -z update ref fails with bad old value' '
printf $F "update $c" "$m" "does-not-exist" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
+ grep "fatal: update $c: invalid <old-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -1176,14 +1178,14 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
git update-ref -d "$c" &&
printf $F "create $c" "does-not-exist" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
+ grep "fatal: create $c: invalid <new-oid>: does-not-exist" err &&
test_must_fail git rev-parse --verify -q $c
'
test_expect_success 'stdin -z create ref fails with empty new value' '
printf $F "create $c" "" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: create $c: missing <newvalue>" err &&
+ grep "fatal: create $c: missing <new-oid>" err &&
test_must_fail git rev-parse --verify -q $c
'
@@ -1207,7 +1209,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
test_expect_success 'stdin -z delete ref fails with zero old value' '
printf $F "delete $a" "$Z" >stdin &&
test_must_fail git update-ref -z --stdin <stdin 2>err &&
- grep "fatal: delete $a: zero <oldvalue>" err &&
+ grep "fatal: delete $a: zero <old-oid>" err &&
git rev-parse $m >expect &&
git rev-parse $a >actual &&
test_cmp expect actual
@@ -1639,13 +1641,4 @@ test_expect_success PIPE 'transaction flushes status updates' '
test_cmp expected actual
'
-test_expect_success REFFILES 'directory not created deleting packed ref' '
- git branch d1/d2/r1 HEAD &&
- git pack-refs --all &&
- test_path_is_missing .git/refs/heads/d1/d2 &&
- git update-ref -d refs/heads/d1/d2/r1 &&
- test_path_is_missing .git/refs/heads/d1/d2 &&
- test_path_is_missing .git/refs/heads/d1
-'
-
test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index c7745e1bf6..5c60d6f812 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -106,9 +106,8 @@ test_expect_success LONG_REF 'we can parse long symbolic ref' '
'
test_expect_success 'symbolic-ref reports failure in exit code' '
- test_when_finished "rm -f .git/HEAD.lock" &&
- >.git/HEAD.lock &&
- test_must_fail git symbolic-ref HEAD refs/heads/whatever
+ # Create d/f conflict to simulate failure.
+ test_must_fail git symbolic-ref refs/heads refs/heads/foo
'
test_expect_success 'symbolic-ref writes reflog entry' '
@@ -171,8 +170,8 @@ 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
'
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index b50ae6fcf1..33fb7a38ff 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -174,6 +174,14 @@ test_expect_success 'show-ref --verify HEAD' '
test_must_be_empty actual
'
+test_expect_success 'show-ref --verify pseudorefs' '
+ git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
+ test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
+ git show-ref -s --verify HEAD >actual &&
+ git show-ref -s --verify CHERRY_PICK_HEAD >expect &&
+ test_cmp actual expect
+'
+
test_expect_success 'show-ref --verify with dangling ref' '
sha1_file() {
echo "$*" | sed "s#..#.git/objects/&/#"
@@ -197,18 +205,20 @@ test_expect_success 'show-ref --verify with dangling ref' '
'
test_expect_success 'show-ref sub-modes are mutually exclusive' '
- cat >expect <<-EOF &&
- fatal: only one of ${SQ}--exclude-existing${SQ}, ${SQ}--verify${SQ} or ${SQ}--exists${SQ} can be given
- EOF
-
test_must_fail git show-ref --verify --exclude-existing 2>err &&
- test_cmp expect err &&
+ grep "verify" err &&
+ grep "exclude-existing" err &&
+ grep "cannot be used together" err &&
test_must_fail git show-ref --verify --exists 2>err &&
- test_cmp expect err &&
+ grep "verify" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err &&
test_must_fail git show-ref --exclude-existing --exists 2>err &&
- test_cmp expect err
+ grep "exclude-existing" err &&
+ grep "exists" err &&
+ grep "cannot be used together" err
'
test_expect_success '--exists with existing reference' '
@@ -260,10 +270,20 @@ test_expect_success '--exists with non-commit object' '
test_expect_success '--exists with directory fails with generic error' '
cat >expect <<-EOF &&
- error: failed to look up reference: Is a directory
+ error: reference does not exist
EOF
- test_expect_code 1 git show-ref --exists refs/heads 2>err &&
+ test_expect_code 2 git show-ref --exists refs/heads 2>err &&
test_cmp expect err
'
+test_expect_success '--exists with non-existent special ref' '
+ test_expect_code 2 git show-ref --exists FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+ test_when_finished "rm .git/FETCH_HEAD" &&
+ git rev-parse HEAD >.git/FETCH_HEAD &&
+ git show-ref --exists FETCH_HEAD
+'
+
test_done
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 0369beea33..98e9158bd2 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -92,9 +92,6 @@ df_test() {
else
delname="$delref"
fi &&
- cat >expected-err <<-EOF &&
- fatal: cannot lock ref $SQ$addname$SQ: $SQ$delref$SQ exists; cannot create $SQ$addref$SQ
- EOF
$pack &&
if $add_del
then
@@ -103,7 +100,7 @@ df_test() {
printf "%s\n" "delete $delname" "create $addname $D"
fi >commands &&
test_must_fail git update-ref --stdin <commands 2>output.err &&
- test_cmp expected-err output.err &&
+ grep "fatal:\( cannot lock ref $SQ$addname$SQ:\)\? $SQ$delref$SQ exists; cannot create $SQ$addref$SQ" output.err &&
printf "%s\n" "$C $delref" >expected-refs &&
git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
test_cmp expected-refs actual-refs
@@ -191,141 +188,69 @@ test_expect_success 'one new ref is a simple prefix of another' '
'
-test_expect_success REFFILES 'empty directory should not fool rev-parse' '
- prefix=refs/e-rev-parse &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- echo "$C" >expected &&
- git rev-parse $prefix/foo >actual &&
- test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool for-each-ref' '
- prefix=refs/e-for-each-ref &&
- git update-ref $prefix/foo $C &&
- git for-each-ref $prefix >expected &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- git for-each-ref $prefix >actual &&
- test_cmp expected actual
-'
-
-test_expect_success REFFILES 'empty directory should not fool create' '
- prefix=refs/e-create &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "create %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool verify' '
- prefix=refs/e-verify &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "verify %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg update' '
- prefix=refs/e-update-1 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "update %s $D\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 2-arg update' '
- prefix=refs/e-update-2 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "update %s $D $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 0-arg delete' '
- prefix=refs/e-delete-0 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "delete %s\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'empty directory should not fool 1-arg delete' '
- prefix=refs/e-delete-1 &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- mkdir -p .git/$prefix/foo/bar/baz &&
- printf "delete %s $C\n" $prefix/foo |
- git update-ref --stdin
-'
-
-test_expect_success REFFILES 'D/F conflict prevents add long + delete short' '
+test_expect_success 'D/F conflict prevents add long + delete short' '
df_test refs/df-al-ds --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long' '
+test_expect_success 'D/F conflict prevents add short + delete long' '
df_test refs/df-as-dl --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents delete long + add short' '
+test_expect_success 'D/F conflict prevents delete long + add short' '
df_test refs/df-dl-as --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents delete short + add long' '
+test_expect_success 'D/F conflict prevents delete short + add long' '
df_test refs/df-ds-al --del-add foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents add long + delete short packed' '
+test_expect_success 'D/F conflict prevents add long + delete short packed' '
df_test refs/df-al-dsp --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add short + delete long packed' '
+test_expect_success 'D/F conflict prevents add short + delete long packed' '
df_test refs/df-as-dlp --pack --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents delete long packed + add short' '
+test_expect_success 'D/F conflict prevents delete long packed + add short' '
df_test refs/df-dlp-as --pack --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents delete short packed + add long' '
+test_expect_success 'D/F conflict prevents delete short packed + add long' '
df_test refs/df-dsp-al --pack --del-add foo foo/bar
'
# Try some combinations involving symbolic refs...
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short' '
df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add short + indirect delete long' '
+test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
'
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents add long + indirect delete short packed' '
+test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
'
-test_expect_success REFFILES 'D/F conflict prevents indirect delete long packed + indirect add short' '
+test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
'
@@ -468,169 +393,4 @@ test_expect_success 'incorrect old value blocks indirect no-deref delete' '
test_cmp expected output.err
'
-test_expect_success REFFILES 'non-empty directory blocks create' '
- prefix=refs/ne-create &&
- mkdir -p .git/$prefix/foo/bar &&
- : >.git/$prefix/foo/bar/baz.lock &&
- test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/foo $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/foo $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks create' '
- prefix=refs/broken-create &&
- mkdir -p .git/$prefix &&
- echo "gobbledigook" >.git/$prefix/foo &&
- test_when_finished "rm -f .git/$prefix/foo" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/foo $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/foo$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/foo $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'non-empty directory blocks indirect create' '
- prefix=refs/ne-indirect-create &&
- git symbolic-ref $prefix/symref $prefix/foo &&
- mkdir -p .git/$prefix/foo/bar &&
- : >.git/$prefix/foo/bar/baz.lock &&
- test_when_finished "rm -f .git/$prefix/foo/bar/baz.lock" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: there is a non-empty directory $SQ.git/$prefix/foo$SQ blocking reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/symref $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ
- EOF
- printf "%s\n" "update $prefix/symref $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'broken reference blocks indirect create' '
- prefix=refs/broken-indirect-create &&
- git symbolic-ref $prefix/symref $prefix/foo &&
- echo "gobbledigook" >.git/$prefix/foo &&
- test_when_finished "rm -f .git/$prefix/foo" &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/symref $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err &&
- cat >expected <<-EOF &&
- fatal: cannot lock ref $SQ$prefix/symref$SQ: unable to resolve reference $SQ$prefix/foo$SQ: reference broken
- EOF
- printf "%s\n" "update $prefix/symref $D $C" |
- test_must_fail git update-ref --stdin 2>output.err &&
- test_cmp expected output.err
-'
-
-test_expect_success REFFILES 'no bogus intermediate values during delete' '
- prefix=refs/slow-transaction &&
- # Set up a reference with differing loose and packed versions:
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- # Now try to update the reference, but hold the `packed-refs` lock
- # for a while to see what happens while the process is blocked:
- : >.git/packed-refs.lock &&
- test_when_finished "rm -f .git/packed-refs.lock" &&
- {
- # Note: the following command is intentionally run in the
- # background. We increase the timeout so that `update-ref`
- # attempts to acquire the `packed-refs` lock for much longer
- # than it takes for us to do the check then delete it:
- git -c core.packedrefstimeout=30000 update-ref -d $prefix/foo &
- } &&
- pid2=$! &&
- # Give update-ref plenty of time to get to the point where it tries
- # to lock packed-refs:
- sleep 1 &&
- # Make sure that update-ref did not complete despite the lock:
- kill -0 $pid2 &&
- # Verify that the reference still has its old value:
- sha1=$(git rev-parse --verify --quiet $prefix/foo || echo undefined) &&
- case "$sha1" in
- $D)
- # This is what we hope for; it means that nothing
- # user-visible has changed yet.
- : ;;
- undefined)
- # This is not correct; it means the deletion has happened
- # already even though update-ref should not have been
- # able to acquire the lock yet.
- echo "$prefix/foo deleted prematurely" &&
- break
- ;;
- $C)
- # This value should never be seen. Probably the loose
- # reference has been deleted but the packed reference
- # is still there:
- echo "$prefix/foo incorrectly observed to be C" &&
- break
- ;;
- *)
- # WTF?
- echo "unexpected value observed for $prefix/foo: $sha1" &&
- break
- ;;
- esac >out &&
- rm -f .git/packed-refs.lock &&
- wait $pid2 &&
- test_must_be_empty out &&
- test_must_fail git rev-parse --verify --quiet $prefix/foo
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs file is locked' '
- prefix=refs/locked-packed-refs &&
- # Set up a reference with differing loose and packed versions:
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
- # Now try to delete it while the `packed-refs` lock is held:
- : >.git/packed-refs.lock &&
- test_when_finished "rm -f .git/packed-refs.lock" &&
- test_must_fail git update-ref -d $prefix/foo >out 2>err &&
- git for-each-ref $prefix >actual &&
- test_grep "Unable to create $SQ.*packed-refs.lock$SQ: " err &&
- test_cmp unchanged actual
-'
-
-test_expect_success REFFILES 'delete fails cleanly if packed-refs.new write fails' '
- # Setup and expectations are similar to the test above.
- prefix=refs/failed-packed-refs &&
- git update-ref $prefix/foo $C &&
- git pack-refs --all &&
- git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
- # This should not happen in practice, but it is an easy way to get a
- # reliable error (we open with create_tempfile(), which uses O_EXCL).
- : >.git/packed-refs.new &&
- test_when_finished "rm -f .git/packed-refs.new" &&
- test_must_fail git update-ref -d $prefix/foo &&
- git for-each-ref $prefix >actual &&
- test_cmp unchanged actual
-'
-
test_done
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index e4627cf1b6..a6bcd62ab6 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -15,14 +15,6 @@ test_expect_success 'setup' '
test_commit one
'
-test_expect_success REFFILES 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
- N=`find .git/refs -type f | wc -l` &&
- test "$N" != 0 &&
- $RUN pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
- N=`find .git/refs -type f` &&
- test -z "$N"
-'
-
test_expect_success 'create_symref(FOO, refs/heads/main)' '
$RUN create-symref FOO refs/heads/main nothing &&
echo refs/heads/main >expected &&
@@ -41,12 +33,6 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
test_must_fail git rev-parse refs/tags/new-tag --
'
-# In reftable, we keep the reflogs around for deleted refs.
-test_expect_success !REFFILES 'delete-reflog(FOO, refs/tags/new-tag)' '
- $RUN delete-reflog FOO &&
- $RUN delete-reflog refs/tags/new-tag
-'
-
test_expect_success 'rename_refs(main, new-main)' '
git rev-parse main >expected &&
$RUN rename-ref refs/heads/main refs/heads/new-main &&
@@ -82,11 +68,11 @@ test_expect_success 'verify_ref(new-main)' '
'
test_expect_success 'for_each_reflog()' '
- $RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
+ $RUN for-each-reflog >actual &&
cat >expected <<-\EOF &&
- HEAD 0x1
- refs/heads/main 0x0
- refs/heads/new-main 0x0
+ HEAD
+ refs/heads/main
+ refs/heads/new-main
EOF
test_cmp expected actual
'
@@ -112,7 +98,7 @@ test_expect_success 'delete_reflog(HEAD)' '
test_must_fail git reflog exists HEAD
'
-test_expect_success REFFILES 'create-reflog(HEAD)' '
+test_expect_success 'create-reflog(HEAD)' '
$RUN create-reflog HEAD &&
git reflog exists HEAD
'
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index e6a7f7334b..c01f0f14a1 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -63,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' '
'
test_expect_success 'for_each_reflog()' '
- $RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
+ $RUN for-each-reflog >actual &&
cat >expected <<-\EOF &&
- HEAD 0x1
- refs/heads/main 0x0
- refs/heads/new-main 0x0
+ HEAD
+ refs/heads/main
+ refs/heads/new-main
EOF
test_cmp expected actual
'
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index 05b1881c59..48b1c92a41 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -53,41 +53,4 @@ test_expect_success 'create_symref(FOO, refs/heads/main)' '
test_cmp expected actual
'
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
-# only appear in the for-each-reflog output if it is called from the correct
-# worktree, which is exercised in this test. This test is poorly written (and
-# therefore marked REFFILES) for mulitple reasons: 1) it creates invalidly
-# formatted log entres. 2) it uses direct FS access for creating the reflogs. 3)
-# PSEUDO-WT and refs/bisect/random do not create reflogs by default, so it is
-# not testing a realistic scenario.
-test_expect_success REFFILES 'for_each_reflog()' '
- echo $ZERO_OID > .git/logs/PSEUDO-MAIN &&
- mkdir -p .git/logs/refs/bisect &&
- echo $ZERO_OID > .git/logs/refs/bisect/random &&
-
- echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT &&
- mkdir -p .git/worktrees/wt/logs/refs/bisect &&
- echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random &&
-
- $RWT for-each-reflog | cut -d" " -f 2- | sort >actual &&
- cat >expected <<-\EOF &&
- HEAD 0x1
- PSEUDO-WT 0x0
- refs/bisect/wt-random 0x0
- refs/heads/main 0x0
- refs/heads/wt-main 0x0
- EOF
- test_cmp expected actual &&
-
- $RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual &&
- cat >expected <<-\EOF &&
- HEAD 0x1
- PSEUDO-MAIN 0x0
- refs/bisect/random 0x0
- refs/heads/main 0x0
- refs/heads/wt-main 0x0
- EOF
- test_cmp expected actual
-'
-
test_done
diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh
index f23c0152a8..7748973733 100755
--- a/t/t1409-avoid-packing-refs.sh
+++ b/t/t1409-avoid-packing-refs.sh
@@ -5,6 +5,12 @@ test_description='avoid rewriting packed-refs unnecessarily'
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping files-backend specific pack-refs tests'
+ test_done
+fi
+
# Add an identifying mark to the packed-refs file header line. This
# shouldn't upset readers, and it should be omitted if the file is
# ever rewritten.
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index aeddc2fb3f..5bf883f1e3 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -354,36 +354,6 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
test_must_be_empty actual
'
-# Triggering the bug detected by this test requires a newline to fall
-# exactly BUFSIZ-1 bytes from the end of the file. We don't know
-# what that value is, since it's platform dependent. However, if
-# we choose some value N, we also catch any D which divides N evenly
-# (since we will read backwards in chunks of D). So we choose 8K,
-# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
-#
-# Each line is 114 characters, so we need 75 to still have a few before the
-# last 8K. The 89-character padding on the final entry lines up our
-# newline exactly.
-test_expect_success REFFILES,SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
- git checkout -b reflogskip &&
- zf=$(test_oid zero_2) &&
- ident="abc <xyz> 0000000001 +0000" &&
- for i in $(test_seq 1 75); do
- printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
- if test $i = 75; then
- for j in $(test_seq 1 89); do
- printf X || return 1
- done
- else
- printf X
- fi &&
- printf "\n" || return 1
- done >.git/logs/refs/heads/reflogskip &&
- git rev-parse reflogskip@{73} >actual &&
- echo ${zf}03 >expect &&
- test_cmp expect actual
-'
-
test_expect_success 'no segfaults for reflog containing non-commit sha1s' '
git update-ref --create-reflog -m "Creating ref" \
refs/tests/tree-in-reflog HEAD &&
@@ -397,18 +367,6 @@ test_expect_failure 'reflog with non-commit entries displays all entries' '
test_line_count = 3 actual
'
-# This test takes a lock on an individual ref; this is not supported in
-# reftable.
-test_expect_success REFFILES 'reflog expire operates on symref not referrent' '
- git branch --create-reflog the_symref &&
- git branch --create-reflog referrent &&
- git update-ref referrent HEAD &&
- git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
- test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
- touch .git/refs/heads/referrent.lock &&
- git reflog expire --expire=all the_symref
-'
-
test_expect_success 'continue walking past root commits' '
git init orphanage &&
(
@@ -469,13 +427,121 @@ test_expect_success 'expire one of multiple worktrees' '
)
'
-test_expect_success REFFILES 'empty reflog' '
+test_expect_success 'empty reflog' '
test_when_finished "rm -rf empty" &&
git init empty &&
test_commit -C empty A &&
- >empty/.git/logs/refs/heads/foo &&
+ test-tool ref-store main create-reflog refs/heads/foo &&
git -C empty reflog expire --all 2>err &&
test_must_be_empty err
'
+test_expect_success 'list reflogs' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ git reflog list >actual &&
+ test_must_be_empty actual &&
+
+ test_commit A &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual &&
+
+ git branch b &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/b
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'list reflogs with worktree' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ test_commit A &&
+ git worktree add wt &&
+ git -c core.logAllRefUpdates=always \
+ update-ref refs/worktree/main HEAD &&
+ git -c core.logAllRefUpdates=always \
+ update-ref refs/worktree/per-worktree HEAD &&
+ git -c core.logAllRefUpdates=always -C wt \
+ update-ref refs/worktree/per-worktree HEAD &&
+ git -c core.logAllRefUpdates=always -C wt \
+ update-ref refs/worktree/worktree HEAD &&
+
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/wt
+ refs/worktree/main
+ refs/worktree/per-worktree
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/wt
+ refs/worktree/per-worktree
+ refs/worktree/worktree
+ EOF
+ git -C wt reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog list returns error with additional args' '
+ cat >expect <<-EOF &&
+ error: list does not accept arguments: ${SQ}bogus${SQ}
+ EOF
+ test_must_fail git reflog list bogus 2>err &&
+ test_cmp expect err
+'
+
+test_expect_success 'reflog for symref with unborn target can be listed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git symbolic-ref HEAD refs/heads/unborn &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'reflog with invalid object ID can be listed' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test-tool ref-store main update-ref msg refs/heads/missing \
+ $(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+ cat >expect <<-EOF &&
+ HEAD
+ refs/heads/main
+ refs/heads/missing
+ EOF
+ git reflog list >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
index ea64cecf47..be6c3f472c 100755
--- a/t/t1414-reflog-walk.sh
+++ b/t/t1414-reflog-walk.sh
@@ -121,13 +121,12 @@ test_expect_success 'min/max age uses entry date to limit' '
# Create a situation where the reflog and ref database disagree about the latest
# state of HEAD.
-test_expect_success REFFILES 'walk prefers reflog to ref tip' '
+test_expect_success 'walk prefers reflog to ref tip' '
+ test_commit A &&
+ test_commit B &&
+ git reflog delete HEAD@{0} &&
head=$(git rev-parse HEAD) &&
- one=$(git rev-parse one) &&
- ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
- echo "$head $one $ident broken reflog entry" >>.git/logs/HEAD &&
-
- echo $one >expect &&
+ git rev-parse A >expect &&
git log -g --format=%H -1 >actual &&
test_cmp expect actual
'
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index 3b531842dd..eb4eec8bec 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -17,17 +17,6 @@ test_expect_success 'setup' '
git -C wt2 update-ref refs/worktree/foo HEAD
'
-# The 'packed-refs' file is stored directly in .git/. This means it is global
-# to the repository, and can only contain refs that are shared across all
-# worktrees.
-test_expect_success REFFILES 'refs/worktree must not be packed' '
- git pack-refs --all &&
- test_path_is_missing .git/refs/tags/wt1 &&
- test_path_is_file .git/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
- test_path_is_file .git/worktrees/wt2/refs/worktree/foo
-'
-
test_expect_success 'refs/worktree are per-worktree' '
test_cmp_rev worktree/foo initial &&
( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&
diff --git a/t/t1417-reflog-updateref.sh b/t/t1417-reflog-updateref.sh
index 14f13b57c6..0eb5e674bc 100755
--- a/t/t1417-reflog-updateref.sh
+++ b/t/t1417-reflog-updateref.sh
@@ -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
index 5d8c86b657..1359574419 100755
--- a/t/t1419-exclude-refs.sh
+++ b/t/t1419-exclude-refs.sh
@@ -8,6 +8,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
+if test_have_prereq !REFFILES
+then
+ skip_all='skipping `git for-each-ref --exclude` tests; need files backend'
+ test_done
+fi
+
for_each_ref__exclude () {
GIT_TRACE2_PERF=1 test-tool ref-store main \
for-each-ref--exclude "$@" >actual.raw
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 3f9e7f62e4..a669e592f1 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -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 &&
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index f0737593c3..b754b9fd74 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -322,4 +322,15 @@ check_invalid_long_option optionspec-neg --no-positive-only
check_invalid_long_option optionspec-neg --negative
check_invalid_long_option optionspec-neg --no-no-negative
+test_expect_success 'ambiguous: --no matches both --noble and --no-noble' '
+ cat >spec <<-\EOF &&
+ some-command [options]
+ --
+ noble The feudal switch.
+ EOF
+ test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+ git rev-parse --parseopt -- <spec 2>err --no &&
+ grep "error: ambiguous option: no (could be --noble or --no-noble)" err
+'
+
test_done
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index bc136833c1..79df65ec7f 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -144,11 +144,6 @@ test_expect_success 'main@{n} for various n' '
test_must_fail git rev-parse --verify main@{$Np1}
'
-test_expect_success SYMLINKS,REFFILES 'ref resolution not confused by broken symlinks' '
- ln -s does-not-exist .git/refs/heads/broken &&
- test_must_fail git rev-parse --verify broken
-'
-
test_expect_success 'options can appear after --verify' '
git rev-parse --verify HEAD >expect &&
git rev-parse --verify -q HEAD >actual &&
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
index 6d47e2c725..dc997e0a64 100755
--- a/t/t1509/prepare-chroot.sh
+++ b/t/t1509/prepare-chroot.sh
@@ -43,7 +43,7 @@ rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
# env might slip through, see test-lib.sh, unset.*PERL_PATH
sed 's|^PERL_PATH=.*|PERL_PATH=/bin/true|' GIT-BUILD-OPTIONS > "$R$(pwd)/GIT-BUILD-OPTIONS"
for cmd in git $BB;do
- ldd $cmd | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+ ldd $cmd | sed -n '/\//s,.*\s\(/[^ ]*\).*,\1,p' | while read i; do
mkdir -p "$R$(dirname $i)"
cp "$i" "$R/$i"
done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index 3c8135831b..04f53b1ea1 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -29,36 +29,33 @@ test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
test_must_fail git checkout -b other
'
-test_expect_success REFFILES 'create ref directory/file conflict scenario' '
+test_expect_success 'create ref directory/file conflict scenario' '
git update-ref refs/heads/outer/inner main &&
-
- # do not rely on symbolic-ref to get a known state,
- # as it may use the same code we are testing
reset_to_df () {
- echo "ref: refs/heads/outer" >.git/HEAD
+ git symbolic-ref HEAD refs/heads/outer
}
'
-test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '
+test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
reset_to_df &&
git checkout main
'
-test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '
+test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
reset_to_df &&
git checkout --detach main
'
-test_expect_success REFFILES 'pack refs' '
+test_expect_success 'pack refs' '
git pack-refs --all --prune
'
-test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '
+test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
reset_to_df &&
git checkout main
'
-test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '
+test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
reset_to_df &&
git checkout --detach main
'
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index 747eb5563e..c4f9bf09aa 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -38,26 +38,32 @@ test_expect_success 'git checkout -p with staged changes' '
verify_state dir/foo index index
'
-test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
- set_and_save_state dir/foo work head &&
- test_write_lines n y n | git checkout -p HEAD &&
- verify_saved_state bar &&
- verify_saved_state dir/foo
-'
-
-test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
- test_write_lines n y y | git checkout -p HEAD &&
- verify_saved_state bar &&
- verify_state dir/foo head head
-'
-
-test_expect_success 'git checkout -p HEAD with change already staged' '
- set_state dir/foo index index &&
- # the third n is to get out in case it mistakenly does not apply
- test_write_lines n y n | git checkout -p HEAD &&
- verify_saved_state bar &&
- verify_state dir/foo head head
-'
+for opt in "HEAD" "@"
+do
+ test_expect_success "git checkout -p $opt with NO staged changes: abort" '
+ set_and_save_state dir/foo work head &&
+ test_write_lines n y n | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo &&
+ test_grep "Discard" output
+ '
+
+ test_expect_success "git checkout -p $opt with NO staged changes: apply" '
+ test_write_lines n y y | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head &&
+ test_grep "Discard" output
+ '
+
+ test_expect_success "git checkout -p $opt with change already staged" '
+ set_state dir/foo index index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git checkout -p $opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head &&
+ test_grep "Discard" output
+ '
+done
test_expect_success 'git checkout -p HEAD^...' '
# the third n is to get out in case it mistakenly does not apply
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 947d1587ac..a5c7358eea 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -86,7 +86,7 @@ test_expect_success '--orphan makes reflog by default' '
git rev-parse --verify delta@{0}
'
-test_expect_success REFFILES '--orphan does not make reflog when core.logAllRefUpdates = false' '
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
git checkout main &&
git config core.logAllRefUpdates false &&
git checkout --orphan epsilon &&
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index 8202ef8c74..8d90d02850 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -45,6 +45,18 @@ test_expect_success 'checkout branch does not detach' '
check_not_detached
'
+for opt in "HEAD" "@"
+do
+ test_expect_success "checkout $opt no-op/don't detach" '
+ reset &&
+ cat .git/HEAD >expect &&
+ git checkout $opt &&
+ cat .git/HEAD >actual &&
+ check_not_detached &&
+ test_cmp expect actual
+ '
+done
+
test_expect_success 'checkout tag detaches' '
reset &&
git checkout tag &&
@@ -164,7 +176,10 @@ test_expect_success 'tracking count is accurate after orphan check' '
git config branch.child.merge refs/heads/main &&
git checkout child^ &&
git checkout child >stdout &&
- test_cmp expect stdout
+ test_cmp expect stdout &&
+
+ git checkout --detach child >stdout &&
+ test_grep ! "can be fast-forwarded\." stdout
'
test_expect_success 'no advice given for explicit detached head state' '
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index a97416ce65..a3b1449ef1 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -113,7 +113,7 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
test_grep ! "^hint: " stderr
'
-test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
+test_expect_success 'checkout -p with multiple remotes does not print advice' '
git checkout -B main &&
test_might_fail git branch -D foo &&
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index e247a4735b..c91c4db936 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -170,8 +170,10 @@ test_expect_success 'switch back when temporarily detached and checked out elsew
# 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
'
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index 16d6348b69..ac404945d4 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -5,6 +5,7 @@ test_description='restore basic functionality'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2071-restore-patch.sh b/t/t2071-restore-patch.sh
index b5c5c0ff7e..42d5522119 100755
--- a/t/t2071-restore-patch.sh
+++ b/t/t2071-restore-patch.sh
@@ -2,9 +2,10 @@
test_description='git restore --patch'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
mkdir dir &&
echo parent >dir/foo &&
echo dummy >bar &&
@@ -16,43 +17,47 @@ test_expect_success PERL 'setup' '
save_head
'
-test_expect_success PERL 'restore -p without pathspec is fine' '
+test_expect_success 'restore -p without pathspec is fine' '
echo q >cmd &&
git restore -p <cmd
'
# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
set_and_save_state dir/foo work head &&
test_write_lines n n | git restore -p &&
verify_saved_state bar &&
verify_saved_state dir/foo
'
-test_expect_success PERL 'git restore -p' '
+test_expect_success 'git restore -p' '
set_and_save_state dir/foo work head &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'git restore -p with staged changes' '
+test_expect_success 'git restore -p with staged changes' '
set_state dir/foo work index &&
test_write_lines n y | git restore -p &&
verify_saved_state bar &&
verify_state dir/foo index index
'
-test_expect_success PERL 'git restore -p --source=HEAD' '
- set_state dir/foo work index &&
- # the third n is to get out in case it mistakenly does not apply
- test_write_lines n y n | git restore -p --source=HEAD &&
- verify_saved_state bar &&
- verify_state dir/foo head index
-'
-
-test_expect_success PERL 'git restore -p --source=HEAD^' '
+for opt in "HEAD" "@"
+do
+ test_expect_success "git restore -p --source=$opt" '
+ set_state dir/foo work index &&
+ # the third n is to get out in case it mistakenly does not apply
+ test_write_lines n y n | git restore -p --source=$opt >output &&
+ verify_saved_state bar &&
+ verify_state dir/foo head index &&
+ test_grep "Discard" output
+ '
+done
+
+test_expect_success 'git restore -p --source=HEAD^' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD^ &&
@@ -60,7 +65,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^' '
verify_state dir/foo parent index
'
-test_expect_success PERL 'git restore -p --source=HEAD^...' '
+test_expect_success 'git restore -p --source=HEAD^...' '
set_state dir/foo work index &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines n y n | git restore -p --source=HEAD^... &&
@@ -68,7 +73,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^...' '
verify_state dir/foo parent index
'
-test_expect_success PERL 'git restore -p handles deletion' '
+test_expect_success 'git restore -p handles deletion' '
set_state dir/foo work index &&
rm dir/foo &&
test_write_lines n y | git restore -p &&
@@ -81,21 +86,21 @@ test_expect_success PERL 'git restore -p handles deletion' '
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
-test_expect_success PERL 'path limiting works: dir' '
+test_expect_success 'path limiting works: dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'path limiting works: -- dir' '
+test_expect_success 'path limiting works: -- dir' '
set_state dir/foo work head &&
test_write_lines y n | git restore -p -- dir &&
verify_saved_state bar &&
verify_state dir/foo head head
'
-test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
+test_expect_success 'path limiting works: HEAD^ -- dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | git restore -p --source=HEAD^ -- dir &&
@@ -103,7 +108,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
verify_state dir/foo parent head
'
-test_expect_success PERL 'path limiting works: foo inside dir' '
+test_expect_success 'path limiting works: foo inside dir' '
set_state dir/foo work head &&
# the third n is to get out in case it mistakenly does not apply
test_write_lines y n n | (cd dir && git restore -p foo) &&
@@ -111,7 +116,7 @@ test_expect_success PERL 'path limiting works: foo inside dir' '
verify_state dir/foo head head
'
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
index 8198a1e578..86c9c88788 100755
--- a/t/t2072-restore-pathspec-file.sh
+++ b/t/t2072-restore-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='restore --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 0bab134d71..7ec7f30b44 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -11,27 +11,27 @@ TEST_PASSES_SANITIZE_LEAK=true
sane_unset GIT_TEST_SPLIT_INDEX
test_set_index_version () {
- GIT_INDEX_VERSION="$1"
- export GIT_INDEX_VERSION
+ GIT_INDEX_VERSION="$1"
+ export GIT_INDEX_VERSION
}
test_set_index_version 3
-cat >expect.full <<EOF
-H 1
-H 2
-H sub/1
-H sub/2
-EOF
+test_expect_success 'setup' '
+ cat >expect.full <<-\EOF &&
+ H 1
+ H 2
+ H sub/1
+ H sub/2
+ EOF
-cat >expect.skip <<EOF
-S 1
-H 2
-S sub/1
-H sub/2
-EOF
+ cat >expect.skip <<-\EOF &&
+ S 1
+ H 2
+ S sub/1
+ H sub/2
+ EOF
-test_expect_success 'setup' '
mkdir sub &&
touch ./1 ./2 sub/1 sub/2 &&
git add 1 2 sub/1 sub/2 &&
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index c01492f33f..df235ac306 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -65,6 +65,16 @@ test_expect_success 'update did not touch untracked files' '
test_must_be_empty out
'
+test_expect_success 'error out when passing untracked path' '
+ git reset --hard &&
+ echo content >>baz &&
+ echo content >>top &&
+ test_must_fail git add -u baz top 2>err &&
+ test_grep -e "error: pathspec .baz. did not match any file(s) known to git" err &&
+ git diff --cached --name-only >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'cache tree has not been corrupted' '
git ls-files -s |
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index df4aff7825..ba320dc417 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -126,6 +126,28 @@ test_expect_success 'die the same branch is already checked out' '
)
'
+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
+ )
+'
+
test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
head=$(git -C there rev-parse --git-path HEAD) &&
ref=$(git -C there symbolic-ref HEAD) &&
@@ -405,7 +427,7 @@ test_expect_success '"add" worktree with orphan branch, lock, and reason' '
# Note: Quoted arguments containing spaces are not supported.
test_wt_add_orphan_hint () {
local context="$1" &&
- local use_branch=$2 &&
+ local use_branch="$2" &&
shift 2 &&
local opts="$*" &&
test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
@@ -415,7 +437,7 @@ test_wt_add_orphan_hint () {
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 orphan branch" 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
@@ -436,7 +458,7 @@ test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD
(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 orphan branch" actual
+ ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
'
test_expect_success 'local clone from linked checkout' '
@@ -468,7 +490,8 @@ test_expect_success 'put a worktree under rebase' '
cd under-rebase &&
set_fake_editor &&
FAKE_LINES="edit 1" git rebase -i HEAD^ &&
- git worktree list | grep "under-rebase.*detached HEAD"
+ git worktree list >actual &&
+ grep "under-rebase.*detached HEAD" actual
)
'
@@ -509,7 +532,8 @@ test_expect_success 'checkout a branch under bisect' '
git bisect start &&
git bisect bad &&
git bisect good HEAD~2 &&
- git worktree list | grep "under-bisect.*detached HEAD" &&
+ git worktree list >actual &&
+ grep "under-bisect.*detached HEAD" actual &&
test_must_fail git worktree add new-bisect under-bisect &&
! test -d new-bisect
)
@@ -709,9 +733,9 @@ 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 orphan branch" &&
+ 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: '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+ 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" &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 6a316f081e..ccfa6a720d 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -75,13 +75,13 @@ test_expect_success 'git branch HEAD should fail' '
test_must_fail git branch HEAD
'
-cat >expect <<EOF
-$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_ref_exists refs/heads/d/e/f &&
+ cat >expect <<-EOF &&
+ $HEAD refs/heads/d/e/f@{0}: branch: Created from main
+ EOF
git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
test_cmp expect actual
'
@@ -440,10 +440,10 @@ test_expect_success 'git branch --list -v with --abbrev' '
test_expect_success 'git branch --column' '
COLUMNS=81 git branch --column=column >actual &&
- cat >expect <<\EOF &&
- a/b/c bam foo l * main n o/p r
- abc bar j/k m/m mb o/o q topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c bam foo l * main n o/p r
+ abc bar j/k m/m mb o/o q topic
+ EOF
test_cmp expect actual
'
@@ -453,25 +453,25 @@ test_expect_success 'git branch --column with an extremely long branch name' '
test_when_finished "git branch -d $long" &&
git branch $long &&
COLUMNS=80 git branch --column=column >actual &&
- cat >expect <<EOF &&
- a/b/c
- abc
- bam
- bar
- foo
- j/k
- l
- m/m
-* main
- mb
- n
- o/o
- o/p
- q
- r
- topic
- $long
-EOF
+ cat >expect <<-EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+ * main
+ mb
+ n
+ o/o
+ o/p
+ q
+ r
+ topic
+ $long
+ EOF
test_cmp expect actual
'
@@ -481,10 +481,10 @@ test_expect_success 'git branch with column.*' '
COLUMNS=80 git branch >actual &&
git config --unset column.branch &&
git config --unset column.ui &&
- cat >expect <<\EOF &&
- a/b/c bam foo l * main n o/p r
- abc bar j/k m/m mb o/o q topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c bam foo l * main n o/p r
+ abc bar j/k m/m mb o/o q topic
+ EOF
test_cmp expect actual
'
@@ -496,39 +496,36 @@ test_expect_success 'git branch -v with column.ui ignored' '
git config column.ui column &&
COLUMNS=80 git branch -v | cut -c -8 | sed "s/ *$//" >actual &&
git config --unset column.ui &&
- cat >expect <<\EOF &&
- a/b/c
- abc
- bam
- bar
- foo
- j/k
- l
- m/m
-* main
- mb
- n
- o/o
- o/p
- q
- r
- topic
-EOF
+ cat >expect <<-\EOF &&
+ a/b/c
+ abc
+ bam
+ bar
+ foo
+ j/k
+ l
+ m/m
+ * main
+ mb
+ n
+ o/o
+ o/p
+ q
+ r
+ topic
+ EOF
test_cmp expect actual
'
-mv .git/config .git/config-saved
-
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
+ test_when_finished mv .git/config-saved .git/config &&
+ mv .git/config .git/config-saved &&
git branch -m q q2 &&
git branch -m q2 q
'
-mv .git/config-saved .git/config
-
-git config branch.s/s.dummy Hello
-
test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
+ git config branch.s/s.dummy Hello &&
git branch --create-reflog s/s &&
git reflog exists refs/heads/s/s &&
git branch --create-reflog s/t &&
@@ -579,7 +576,7 @@ EOF
# ...and that the comments for those sections are also
# preserved.
- cat config.branch | sed "s/\"source\"/\"dest\"/" >expect &&
+ sed "s/\"source\"/\"dest\"/" config.branch >expect &&
sed -n -e "/Note the lack/,\$p" .git/config >actual &&
test_cmp expect actual
'
@@ -836,35 +833,6 @@ test_expect_success 'renaming a symref is not allowed' '
test_ref_missing refs/heads/new-topic
'
-test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '
- git branch --create-reflog u &&
- mv .git/logs/refs/heads/u real-u &&
- ln -s real-u .git/logs/refs/heads/u &&
- test_must_fail git branch -m u v
-'
-
-test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '
- test_when_finished "rm -rf subdir" &&
- git init --bare subdir &&
-
- rm -rfv subdir/refs subdir/objects subdir/packed-refs &&
- ln -s ../.git/refs subdir/refs &&
- ln -s ../.git/objects subdir/objects &&
- ln -s ../.git/packed-refs subdir/packed-refs &&
-
- git -C subdir rev-parse --absolute-git-dir >subdir.dir &&
- git rev-parse --absolute-git-dir >our.dir &&
- ! test_cmp subdir.dir our.dir &&
-
- git -C subdir log &&
- git -C subdir branch rename-src &&
- git rev-parse rename-src >expect &&
- git -C subdir branch -m rename-src rename-dest &&
- git rev-parse rename-dest >actual &&
- test_cmp expect actual &&
- git branch -D rename-dest
-'
-
test_expect_success 'test tracking setup via --track' '
git config remote.local.url . &&
git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
@@ -1141,14 +1109,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
test_cmp expect actual
"
-# Keep this test last, as it changes the current branch
-cat >expect <<EOF
-$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' '
+ test_when_finished git checkout main &&
GIT_COMMITTER_DATE="2005-05-26 23:30" \
git checkout -b g/h/i -l main &&
test_ref_exists refs/heads/g/h/i &&
+ cat >expect <<-EOF &&
+ $HEAD refs/heads/g/h/i@{0}: branch: Created from main
+ EOF
git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
test_cmp expect actual
'
@@ -1186,9 +1154,9 @@ test_expect_success 'avoid ambiguous track and advise' '
hint: tracking ref '\''refs/heads/main'\'':
hint: ambi1
hint: ambi2
- hint: ''
+ hint:
hint: This is typically a configuration error.
- hint: ''
+ hint:
hint: To support setting up tracking branches, ensure that
hint: different remotes'\'' fetch refspecs map into different
hint: tracking namespaces.
@@ -1725,4 +1693,14 @@ test_expect_success '--track overrides branch.autoSetupMerge' '
test_cmp_config "" --default "" branch.foo5.merge
'
+test_expect_success 'errors if given a bad branch name' '
+ cat <<-\EOF >expect &&
+ fatal: '\''foo..bar'\'' is not a valid branch name
+ hint: See `man git check-ref-format`
+ hint: Disable this message with "git config advice.refSyntax false"
+ EOF
+ test_must_fail git branch foo..bar >actual 2>&1 &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh
index 6a98b2df76..a1139f79e2 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -4,9 +4,6 @@ test_description='test show-branch'
. ./test-lib.sh
-# arbitrary reference time: 2009-08-30 19:20:00
-GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
-
test_expect_success 'error descriptions on empty repository' '
current=$(git branch --show-current) &&
cat >expect <<-EOF &&
@@ -187,18 +184,6 @@ test_expect_success 'show branch --merge-base with N arguments' '
test_cmp expect actual
'
-test_expect_success 'show branch --reflog=2' '
- sed "s/^> //" >expect <<-\EOF &&
- > ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
- > ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
- > --
- > + [refs/heads/branch10@{0}] branch10
- > ++ [refs/heads/branch10@{1}] initial
- EOF
- git show-branch --reflog=2 >actual &&
- test_cmp actual expect
-'
-
# incompatible options
while read combo
do
@@ -264,4 +249,38 @@ test_expect_success 'error descriptions on orphan branch' '
test_branch_op_in_wt -c new-branch
'
+test_expect_success 'setup reflogs' '
+ test_commit base &&
+ git checkout -b branch &&
+ test_commit one &&
+ git reset --hard HEAD^ &&
+ test_commit two &&
+ test_commit three
+'
+
+test_expect_success '--reflog shows reflog entries' '
+ cat >expect <<-\EOF &&
+ ! [branch@{0}] (0 seconds ago) commit: three
+ ! [branch@{1}] (60 seconds ago) commit: two
+ ! [branch@{2}] (2 minutes ago) reset: moving to HEAD^
+ ! [branch@{3}] (2 minutes ago) commit: one
+ ----
+ + [branch@{0}] three
+ ++ [branch@{1}] two
+ + [branch@{3}] one
+ ++++ [branch@{2}] base
+ EOF
+ # the output always contains relative timestamps; use
+ # a known time to get deterministic results
+ GIT_TEST_DATE_NOW=$test_tick \
+ git show-branch --reflog branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reflog handles missing reflog' '
+ git reflog expire --expire=now branch &&
+ git show-branch --reflog branch >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh
index 60d6ed2dc8..597df5ebc0 100755
--- a/t/t3310-notes-merge-manual-resolve.sh
+++ b/t/t3310-notes-merge-manual-resolve.sh
@@ -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 &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
index 088a852dd4..beca346056 100755
--- a/t/t3321-notes-stripspace.sh
+++ b/t/t3321-notes-stripspace.sh
@@ -442,7 +442,7 @@ test_expect_success 'add note by specifying "-C", "--no-stripspace" is the defau
${LF}
EOF
- cat expect | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <expect >blob &&
git notes add -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual &&
@@ -468,7 +468,7 @@ test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add --stripspace -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
@@ -492,7 +492,7 @@ test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspac
third-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -C $(cat blob) -m "third-line" &&
git notes show >actual &&
test_cmp expect actual
@@ -511,7 +511,7 @@ test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not
second-line
EOF
- cat data | git hash-object -w --stdin >blob &&
+ git hash-object -w --stdin <data >blob &&
git notes add -m "first-line" -C $(cat blob) &&
git notes show >actual &&
test_cmp expect actual
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 57f1392926..e1c8c5f701 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -424,16 +424,6 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '
test_grep "already used by worktree at" err
'
-test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
- git checkout main &&
- mv .git/logs actual_logs &&
- cmd //c "mklink /D .git\logs ..\actual_logs" &&
- git rebase -f HEAD^ &&
- test -L .git/logs &&
- rm .git/logs &&
- mv actual_logs .git/logs
-'
-
test_expect_success 'rebase when inside worktree subdirectory' '
git init main-wt &&
(
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 64b641002e..d1bead61fa 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -153,6 +153,18 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git rebase --continue
'
+test_expect_success 'cherry-pick works with rebase --exec' '
+ test_when_finished "git cherry-pick --abort; \
+ git rebase --abort; \
+ git checkout primary" &&
+ echo "exec git cherry-pick G" >todo &&
+ (
+ set_replace_editor todo &&
+ test_must_fail git rebase -i D D
+ ) &&
+ test_cmp_rev G CHERRY_PICK_HEAD
+'
+
test_expect_success 'rebase -x with empty command fails' '
test_when_finished "git rebase --abort ||:" &&
test_must_fail env git rebase -x "" @ 2>actual &&
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index ebbaed147a..9f49c4228b 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -40,9 +40,24 @@ testrebase() {
test_path_is_missing "$state_dir"
'
+ test_expect_success "pre rebase$type head is marked as reachable" '
+ # Clean up the state from the previous one
+ git checkout -f --detach pre-rebase &&
+ test_tick &&
+ git commit --amend --only -m "reworded" &&
+ orig_head=$(git rev-parse HEAD) &&
+ test_must_fail git rebase$type main &&
+ # Stop ORIG_HEAD marking $state_dir/orig-head as reachable
+ git update-ref -d ORIG_HEAD &&
+ git reflog expire --expire="$GIT_COMMITTER_DATE" --all &&
+ git prune --expire=now &&
+ git rebase --abort &&
+ test_cmp_rev $orig_head HEAD
+ '
+
test_expect_success "rebase$type --abort after --skip" '
# Clean up the state from the previous one
- git reset --hard pre-rebase &&
+ git checkout -B to-rebase pre-rebase &&
test_must_fail git rebase$type main &&
test_path_is_dir "$state_dir" &&
test_must_fail git rebase --skip &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 693934ee8b..1a820f1481 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -333,4 +333,14 @@ test_expect_success 'never change active branch' '
test_cmp_rev not-the-feature-branch unrelated-onto-branch
'
+test_expect_success 'autostash commit is marked as reachable' '
+ echo changed >file0 &&
+ git rebase --autostash --exec "git prune --expire=now" \
+ feature-branch^ feature-branch &&
+ # git rebase succeeds if the stash cannot be applied so we need to check
+ # the contents of file0
+ echo changed >expect &&
+ test_cmp expect file0
+'
+
test_done
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 5e1045a0af..1ee6b00fd5 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -72,6 +72,17 @@ test_expect_success 'rebase --merge --empty=keep' '
test_cmp expect actual
'
+test_expect_success 'rebase --merge --empty=stop' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=stop upstream &&
+
+ git rebase --skip &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'rebase --merge --empty=ask' '
git checkout -B testing localmods &&
test_must_fail git rebase --merge --empty=ask upstream &&
@@ -101,9 +112,9 @@ test_expect_success 'rebase --interactive --empty=keep' '
test_cmp expect actual
'
-test_expect_success 'rebase --interactive --empty=ask' '
+test_expect_success 'rebase --interactive --empty=stop' '
git checkout -B testing localmods &&
- test_must_fail git rebase --interactive --empty=ask upstream &&
+ test_must_fail git rebase --interactive --empty=stop upstream &&
git rebase --skip &&
@@ -112,7 +123,7 @@ test_expect_success 'rebase --interactive --empty=ask' '
test_cmp expect actual
'
-test_expect_success 'rebase --interactive uses default of --empty=ask' '
+test_expect_success 'rebase --interactive uses default of --empty=stop' '
git checkout -B testing localmods &&
test_must_fail git rebase --interactive upstream &&
@@ -167,4 +178,42 @@ test_expect_success 'rebase --merge does not leave state laying around' '
test_path_is_missing .git/MERGE_MSG
'
+test_expect_success 'rebase --exec --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" --empty=drop upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec uses default of --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --exec "true" upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --exec --empty=stop' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --exec "true" --empty=stop upstream &&
+
+ git rebase --skip &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
index e1b1e94764..1bebd1ce74 100755
--- a/t/t3428-rebase-signoff.sh
+++ b/t/t3428-rebase-signoff.sh
@@ -8,47 +8,45 @@ 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
-cat >file <<EOF
-a
-EOF
+test_expect_success 'setup' '
+ git commit --allow-empty -m "Initial empty commit" &&
+ test_commit first file a &&
+
+ ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
-# Expected commit message for initial commit after rebase --signoff
-cat >expected-initial-signed <<EOF
-Initial empty commit
+ # Expected commit message for initial commit after rebase --signoff
+ cat >expected-initial-signed <<-EOF &&
+ Initial empty commit
-Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
-EOF
+ Signed-off-by: $ident
+ EOF
-# Expected commit message after rebase --signoff
-cat >expected-signed <<EOF
-first
+ # Expected commit message after rebase --signoff
+ cat >expected-signed <<-EOF &&
+ first
-Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
-EOF
+ Signed-off-by: $ident
+ EOF
-# Expected commit message after rebase without --signoff (or with --no-signoff)
-cat >expected-unsigned <<EOF
-first
-EOF
+ # Expected commit message after rebase without --signoff (or with --no-signoff)
+ cat >expected-unsigned <<-EOF &&
+ first
+ EOF
+ git config alias.rbs "rebase --signoff"
+'
# We configure an alias to do the rebase --signoff so that
# on the next subtest we can show that --no-signoff overrides the alias
-test_expect_success 'rebase --signoff adds a sign-off line' '
- git commit --allow-empty -m "Initial empty commit" &&
- git add file && git commit -m first &&
- git config alias.rbs "rebase --signoff" &&
- git rbs HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
- test_cmp expected-signed actual
+test_expect_success 'rebase --apply --signoff adds a sign-off line' '
+ git rbs --apply HEAD^ &&
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase --no-signoff does not add a sign-off line' '
git commit --amend -m "first" &&
git rbs --no-signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
- test_cmp expected-unsigned actual
+ test_commit_message HEAD expected-unsigned
'
test_expect_success 'rebase --exec --signoff adds a sign-off line' '
@@ -56,30 +54,25 @@ test_expect_success 'rebase --exec --signoff adds a sign-off line' '
git commit --amend -m "first" &&
git rebase --exec "touch exec" --signoff HEAD^ &&
test_path_is_file exec &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase --root --signoff adds a sign-off line' '
git commit --amend -m "first" &&
git rebase --root --keep-empty --signoff &&
- git cat-file commit HEAD^ | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-initial-signed actual &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD^ expected-initial-signed &&
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase -i --signoff fails' '
git commit --amend -m "first" &&
git rebase -i --signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_expect_success 'rebase -m --signoff fails' '
git commit --amend -m "first" &&
git rebase -m --signoff HEAD^ &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- test_cmp expected-signed actual
+ test_commit_message HEAD expected-signed
'
test_done
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
index c614c4f2e4..821f08e5af 100755
--- a/t/t3438-rebase-broken-files.sh
+++ b/t/t3438-rebase-broken-files.sh
@@ -58,4 +58,13 @@ test_expect_success 'unknown key in author-script' '
check_resolve_fails
'
+test_expect_success POSIXPERM,SANITY 'unwritable rebased-patches does not leak' '
+ >.git/rebased-patches &&
+ chmod a-w .git/rebased-patches &&
+
+ git checkout -b side HEAD^ &&
+ test_commit unrelated &&
+ test_must_fail git rebase --apply --onto tmp HEAD^
+'
+
test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index aeab689a98..411027fb58 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -104,11 +104,19 @@ test_expect_success 'revert forbidden on dirty working tree' '
'
test_expect_success 'cherry-pick on unborn branch' '
- git checkout --orphan unborn &&
+ git switch --orphan unborn &&
git rm --cached -r . &&
- rm -rf * &&
git cherry-pick initial &&
- git diff --quiet initial &&
+ git diff --exit-code initial &&
+ test_cmp_rev ! initial HEAD
+'
+
+test_expect_success 'cherry-pick on unborn branch with --allow-empty' '
+ git checkout --detach &&
+ git branch -D unborn &&
+ git switch --orphan unborn &&
+ git cherry-pick initial --allow-empty &&
+ git diff --exit-code initial &&
test_cmp_rev ! initial HEAD
'
@@ -170,6 +178,7 @@ test_expect_success 'advice from failed revert' '
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_commit --append --no-tag "double-add dream" dream dream &&
test_must_fail git revert HEAD^ 2>actual &&
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index eba3c38d5a..9748443530 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -84,7 +84,7 @@ test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
git commit -m "add file2 on the side"
'
-test_expect_success 'cherry-pick a no-op without --keep-redundant' '
+test_expect_success 'cherry-pick a no-op with neither --keep-redundant nor --empty' '
git reset --hard &&
git checkout fork^0 &&
test_must_fail git cherry-pick main
@@ -99,4 +99,53 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
test_cmp expect actual
'
+test_expect_success '--keep-redundant-commits is incompatible with operations' '
+ test_must_fail git cherry-pick HEAD 2>output &&
+ test_grep "The previous cherry-pick is now empty" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --continue 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --continue" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --skip 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --skip" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --abort 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --abort" output &&
+ test_must_fail git cherry-pick --keep-redundant-commits --quit 2>output &&
+ test_grep "fatal: cherry-pick: --keep-redundant-commits cannot be used with --quit" output &&
+ git cherry-pick --abort
+'
+
+test_expect_success '--empty is incompatible with operations' '
+ test_must_fail git cherry-pick HEAD 2>output &&
+ test_grep "The previous cherry-pick is now empty" output &&
+ test_must_fail git cherry-pick --empty=stop --continue 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --continue" output &&
+ test_must_fail git cherry-pick --empty=stop --skip 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --skip" output &&
+ test_must_fail git cherry-pick --empty=stop --abort 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --abort" output &&
+ test_must_fail git cherry-pick --empty=stop --quit 2>output &&
+ test_grep "fatal: cherry-pick: --empty cannot be used with --quit" output &&
+ git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=stop' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ test_must_fail git cherry-pick --empty=stop main 2>output &&
+ test_grep "The previous cherry-pick is now empty" output
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=drop' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ git cherry-pick --empty=drop main &&
+ test_commit_message HEAD -m "add file2 on the side"
+'
+
+test_expect_success 'cherry-pick a no-op with --empty=keep' '
+ git reset --hard &&
+ git checkout fork^0 &&
+ git cherry-pick --empty=keep main &&
+ test_commit_message HEAD -m "add file2 on main"
+'
+
test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index c88d597b12..f3947b400a 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -60,6 +60,7 @@ test_expect_success 'advice from failed cherry-pick' '
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
+ hint: Disable this message with "git config advice.mergeConflict false"
EOF
test_must_fail git cherry-pick picked 2>actual &&
@@ -74,6 +75,7 @@ test_expect_success 'advice from failed cherry-pick --no-commit' "
error: could not apply \$picked... picked
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
+ hint: Disable this message with \"git config advice.mergeConflict false\"
EOF
test_must_fail git cherry-pick --no-commit picked 2>actual &&
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 72020a51c4..7eb52b12ed 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -90,6 +90,38 @@ test_expect_success 'cherry-pick persists opts correctly' '
test_cmp expect actual
'
+test_expect_success 'cherry-pick persists --empty=stop correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=stop anotherpick yetanotherpick &&
+ test_must_fail git cherry-pick --skip 2>msg &&
+ test_grep "The previous cherry-pick is now empty" msg &&
+ rm msg &&
+ git cherry-pick --abort
+'
+
+test_expect_success 'cherry-pick persists --empty=drop correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=drop anotherpick yetanotherpick &&
+ git cherry-pick --skip &&
+ test_cmp_rev yetanotherpick HEAD
+'
+
+test_expect_success 'cherry-pick persists --empty=keep correctly' '
+ pristine_detach yetanotherpick &&
+ # Picking `anotherpick` forces a conflict so that we stop. That
+ # commit is then skipped, after which we pick `yetanotherpick`
+ # while already on `yetanotherpick` to cause an empty commit
+ test_must_fail git cherry-pick --empty=keep anotherpick yetanotherpick &&
+ git cherry-pick --skip &&
+ test_cmp_rev yetanotherpick HEAD^
+'
+
test_expect_success 'revert persists opts correctly' '
pristine_detach initial &&
# to make sure that the session to revert a sequence
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
new file mode 100755
index 0000000000..389670262e
--- /dev/null
+++ b/t/t3650-replay-basics.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='basic git replay tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+
+ git switch -c topic1 &&
+ test_commit C &&
+ git switch -c topic2 &&
+ test_commit D &&
+ test_commit E &&
+ git switch topic1 &&
+ test_commit F &&
+ git switch -c topic3 &&
+ test_commit G &&
+ test_commit H &&
+ git switch -c topic4 main &&
+ test_commit I &&
+ test_commit J &&
+
+ git switch -c next main &&
+ test_commit K &&
+ git merge -m "Merge topic1" topic1 &&
+ git merge -m "Merge topic2" topic2 &&
+ git merge -m "Merge topic3" topic3 &&
+ >evil &&
+ git add evil &&
+ git commit --amend &&
+ git merge -m "Merge topic4" topic4 &&
+
+ git switch main &&
+ test_commit L &&
+ test_commit M &&
+
+ git switch -c conflict B &&
+ test_commit C.conflict C.t conflict
+'
+
+test_expect_success 'setup bare' '
+ git clone --bare . bare
+'
+
+test_expect_success 'using replay to rebase two branches, one on top of other' '
+ git replay --onto main topic1..topic2 >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic2 " >expect &&
+ printf "%s " $(cut -f 3 -d " " result) >>expect &&
+ git rev-parse topic2 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
+ git -C bare replay --onto main topic1..topic2 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase with a conflict' '
+ test_expect_code 1 git replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay on bare repo to rebase with a conflict' '
+ test_expect_code 1 git -C bare replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay to perform basic cherry-pick' '
+ # The differences between this test and previous ones are:
+ # --advance vs --onto
+ # 2nd field of result is refs/heads/main vs. refs/heads/topic2
+ # 4th field of result is hash for main instead of hash for topic2
+
+ git replay --advance main topic1..topic2 >result &&
+
+ test_line_count = 1 result &&
+
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/main " >expect &&
+ printf "%s " $(cut -f 3 -d " " result) >>expect &&
+ git rev-parse main >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
+ git -C bare replay --advance main topic1..topic2 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'replay on bare repo fails with both --advance and --onto' '
+ test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
+'
+
+test_expect_success 'replay fails when both --advance and --onto are omitted' '
+ test_must_fail git replay topic1..topic2 >result
+'
+
+test_expect_success 'using replay to also rebase a contained branch' '
+ git replay --contained --onto main main..topic3 >result &&
+
+ test_line_count = 2 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+ test_write_lines F C M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+ test_write_lines H G F C M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic1 " >expect &&
+ printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic1 >>expect &&
+ printf "update refs/heads/topic3 " >>expect &&
+ printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic3 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+ git -C bare replay --contained --onto main main..topic3 >result-bare &&
+ test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase multiple divergent branches' '
+ git replay --onto main ^topic1 topic2 topic4 >result &&
+
+ test_line_count = 2 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+ test_write_lines E D M L B A >expect &&
+ test_cmp expect actual &&
+
+ git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+ test_write_lines J I M L B A >expect &&
+ test_cmp expect actual &&
+
+ printf "update refs/heads/topic2 " >expect &&
+ printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic2 >>expect &&
+ printf "update refs/heads/topic4 " >>expect &&
+ printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+ git rev-parse topic4 >>expect &&
+
+ test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' '
+ git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result &&
+
+ test_line_count = 4 result &&
+ cut -f 3 -d " " result >new-branch-tips &&
+
+ >expect &&
+ for i in 2 1 3 4
+ do
+ printf "update refs/heads/topic$i " >>expect &&
+ printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+ git -C bare rev-parse topic$i >>expect || return 1
+ done &&
+
+ test_cmp expect result &&
+
+ test_write_lines F C M L B A >expect1 &&
+ test_write_lines E D C M L B A >expect2 &&
+ test_write_lines H G F C M L B A >expect3 &&
+ test_write_lines J I M L B A >expect4 &&
+
+ for i in 1 2 3 4
+ do
+ git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+ test_cmp expect$i actual || return 1
+ done
+'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index f23d39f0d5..839c904745 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -28,6 +28,16 @@ test_expect_success 'Test of git add' '
touch foo && git add foo
'
+test_expect_success 'Test with no pathspecs' '
+ cat >expect <<-EOF &&
+ Nothing specified, nothing added.
+ hint: Maybe you wanted to say ${SQ}git add .${SQ}?
+ hint: Disable this message with "git config advice.addEmptyPathspec false"
+ EOF
+ git add 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'Post-check that foo is in the index' '
git ls-files foo | grep foo
'
@@ -339,6 +349,40 @@ test_expect_success '"git add ." in empty repo' '
)
'
+test_expect_success '"git add" a embedded repository' '
+ rm -fr outer && git init outer &&
+ (
+ cd outer &&
+ for i in 1 2
+ do
+ name=inner$i &&
+ git init $name &&
+ git -C $name commit --allow-empty -m $name ||
+ return 1
+ done &&
+ git add . 2>actual &&
+ cat >expect <<-EOF &&
+ warning: adding embedded git repository: inner1
+ hint: You${SQ}ve added another git repository inside your current repository.
+ hint: Clones of the outer repository will not contain the contents of
+ hint: the embedded repository and will not know how to obtain it.
+ hint: If you meant to add a submodule, use:
+ hint:
+ hint: git submodule add <url> inner1
+ hint:
+ hint: If you added this path by mistake, you can remove it from the
+ hint: index with:
+ hint:
+ hint: git rm --cached inner1
+ hint:
+ hint: See "git help submodule" for more information.
+ hint: Disable this message with "git config advice.addEmbeddedRepo false"
+ warning: adding embedded git repository: inner2
+ EOF
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'error on a repository with no commits' '
rm -fr empty &&
git init empty &&
@@ -370,8 +414,7 @@ cat >expect.err <<\EOF
The following paths are ignored by one of your .gitignore files:
ignored-file
hint: Use -f if you really want to add them.
-hint: Turn this message off by running
-hint: "git config advice.addIgnoredFile false"
+hint: Disable this message with "git config advice.addIgnoredFile false"
EOF
cat >expect.out <<\EOF
add 'track-this'
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 0b5339ac6c..bc55255b0a 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -325,9 +325,9 @@ test_expect_success 'different prompts for mode change/deleted' '
git -c core.filemode=true add -p >actual &&
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
cat >expect <<-\EOF &&
- (1/1) Stage deletion [y,n,q,a,d,?]?
- (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
+ (1/1) Stage deletion [y,n,q,a,d,p,?]?
+ (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,p,?]?
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]?
EOF
test_cmp expect actual.filtered
'
@@ -514,13 +514,13 @@ test_expect_success 'split hunk setup' '
test_expect_success 'goto hunk' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1: -1,2 +1,3 +15
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? + 1: -1,2 +1,3 +15
_ 2: -2,4 +3,8 +21
go to which hunk? @@ -1,2 +1,3 @@
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y g 1 | git add -p >actual &&
tail -n 7 <actual >actual.trimmed &&
@@ -530,11 +530,11 @@ test_expect_success 'goto hunk' '
test_expect_success 'navigate to hunk via regex' '
test_when_finished "git reset" &&
tr _ " " >expect <<-EOF &&
- (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
+ (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? @@ -1,2 +1,3 @@
_10
+15
_20
- (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
+ (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_
EOF
test_write_lines s y /1,2 | git add -p >actual &&
tail -n 5 <actual >actual.trimmed &&
@@ -715,21 +715,21 @@ test_expect_success 'colors can be overridden' '
<BLUE>+<RESET><BLUE>new<RESET>
<CYAN> more-context<RESET>
<BLUE>+<RESET><BLUE>another-one<RESET>
- <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
+ <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,p,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
<MAGENTA>@@ -1,3 +1,3 @@<RESET>
<CYAN> context<RESET>
<BOLD>-old<RESET>
<BLUE>+<RESET><BLUE>new<RESET>
<CYAN> more-context<RESET>
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
<CYAN> more-context<RESET>
<BLUE>+<RESET><BLUE>another-one<RESET>
- <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
+ <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,p,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
<CYAN> context<RESET>
<BOLD>-old<RESET>
<BLUE>+new<RESET>
<CYAN> more-context<RESET>
- <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET>
+ <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>
EOF
test_cmp expect actual
'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 34faeac3f1..00db82fb24 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -200,7 +200,7 @@ test_expect_success 'drop stash reflog updates refs/stash' '
test_cmp expect actual
'
-test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+test_expect_success 'drop stash reflog updates refs/stash with rewrite' '
git init repo &&
(
cd repo &&
@@ -213,16 +213,16 @@ test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite'
new_oid="$(git -C repo rev-parse stash@{0})" &&
cat >expect <<-EOF &&
- $(test_oid zero) $old_oid
- $old_oid $new_oid
+ $new_oid
+ $old_oid
EOF
- cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+ git -C repo reflog show refs/stash --format=%H >actual &&
test_cmp expect actual &&
git -C repo stash drop stash@{1} &&
- cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+ git -C repo reflog show refs/stash --format=%H >actual &&
cat >expect <<-EOF &&
- $(test_oid zero) $new_oid
+ $new_oid
EOF
test_cmp expect actual
'
@@ -1516,4 +1516,56 @@ test_expect_success 'restore untracked files even when we hit conflicts' '
)
'
+test_expect_success 'stash create reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash create 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'stash push reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash push 2>err &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'stash apply reports a locked index' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A A.file &&
+ echo change >A.file &&
+ git stash push &&
+ touch .git/index.lock &&
+
+ cat >expect <<-EOF &&
+ error: could not write index
+ EOF
+ test_must_fail git stash apply 2>err &&
+ test_cmp expect err
+ )
+'
+
test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index accfe3845c..368fc2a6cc 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -3,12 +3,6 @@
test_description='stash -p'
. ./lib-patch-mode.sh
-if ! test_have_prereq PERL
-then
- skip_all='skipping stash -p tests, perl not available'
- test_done
-fi
-
test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
index 67fd2345af..50ae222f08 100755
--- a/t/t3920-crlf-messages.sh
+++ b/t/t3920-crlf-messages.sh
@@ -10,7 +10,7 @@ LIB_CRLF_BRANCHES=""
create_crlf_ref () {
branch="$1" &&
cat >.crlf-orig-$branch.txt &&
- cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+ append_cr <.crlf-orig-$branch.txt >.crlf-message-$branch.txt &&
grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
grep 'Body' .crlf-orig-$branch.txt | append_cr >.crlf-body-$branch.txt &&
LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
@@ -97,7 +97,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
git branch -v >tmp &&
# Remove first two columns, and the line for the currently checked out branch
current=$(git branch --show-current) &&
- grep -v $current <tmp | awk "{\$1=\$2=\"\"}1" >actual &&
+ awk "/$current/ { next } { \$1 = \$2 = \"\" } 1" <tmp >actual &&
test_cmp expect actual
'
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index 85be1367de..49c042a38a 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -286,4 +286,28 @@ test_expect_success 'basename similarity vs best similarity' '
test_cmp expected actual
'
+test_expect_success 'last line matters too' '
+ {
+ test_write_lines a 0 1 2 3 4 5 6 7 8 9 &&
+ printf "git ignores final up to 63 characters if not newline terminated"
+ } >no-final-lf &&
+ git add no-final-lf &&
+ git commit -m "original version of file with no final newline" &&
+
+ # Change ONLY the first character of the whole file
+ {
+ test_write_lines b 0 1 2 3 4 5 6 7 8 9 &&
+ printf "git ignores final up to 63 characters if not newline terminated"
+ } >no-final-lf &&
+ git add no-final-lf &&
+ git mv no-final-lf still-absent-final-lf &&
+ git commit -a -m "rename no-final-lf -> still-absent-final-lf" &&
+ git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
+ sed -e "s/^R[0-9]* /R /" actual >actual.munged &&
+ cat >expected <<-\EOF &&
+ R no-final-lf still-absent-final-lf
+ EOF
+ test_cmp expected actual.munged
+'
+
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 7afc883ec3..cb3307010c 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -405,7 +405,7 @@ test_expect_success 'diff-tree -r B A == diff-tree -r -R A 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 &&
+ sed "/^index/d; s#/-#/NN#" >.test-a &&
test_must_fail git diff --no-index -- MN NN |
grep -v "^index" >.test-b &&
test_cmp .test-a .test-b
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index d7a5f7ae78..bc8ba88719 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -13,13 +13,13 @@ TEST_PASSES_SANITIZE_LEAK=true
# Print the short OID of a symlink with the given name.
symlink_oid () {
- local oid=$(printf "%s" "$1" | git hash-object --stdin) &&
+ local oid="$(printf "%s" "$1" | git hash-object --stdin)" &&
git rev-parse --short "$oid"
}
# Print the short OID of the given file.
short_oid () {
- local oid=$(git hash-object "$1") &&
+ local oid="$(git hash-object "$1")" &&
git rev-parse --short "$oid"
}
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index c4d508e00a..3855d68dbc 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -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
@@ -636,8 +633,8 @@ check_prefix () {
test_cmp expect actual.paths
}
-test_expect_success 'diff-files does not respect diff.noprefix' '
- git -c diff.noprefix diff-files -p >actual &&
+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
'
@@ -646,24 +643,65 @@ test_expect_success 'diff-files respects --no-prefix' '
check_prefix actual file0 file0
'
-test_expect_success 'diff respects diff.noprefix' '
- git -c diff.noprefix diff >actual &&
+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 &&
+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 &&
+test_expect_success 'diff respects diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff >actual &&
+ check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.mnemonicPrefix' '
+ git -c diff.mnemonicPrefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.srcPrefix' '
+ git -c diff.srcPrefix=x/ diff >actual &&
+ check_prefix actual x/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff >actual &&
+ check_prefix actual a/file0 y/file0
+'
+
+test_expect_success 'diff --src-prefix overrides diff.srcPrefix' '
+ git -c diff.srcPrefix=y/ diff --src-prefix=z/ >actual &&
+ check_prefix actual z/file0 b/file0
+'
+
+test_expect_success 'diff --dst-prefix overrides diff.dstPrefix' '
+ git -c diff.dstPrefix=y/ diff --dst-prefix=z/ >actual &&
+ check_prefix actual a/file0 z/file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.noPrefix' '
+ git -c diff.dstPrefix=y/ -c diff.srcPrefix=x/ -c diff.noPrefix diff >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff.{src,dst}Prefix ignored with diff.mnemonicPrefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ -c diff.mnemonicPrefix diff >actual &&
check_prefix actual i/file0 w/file0
'
-test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
- git -c diff.mnemonicprefix diff --default-prefix >actual &&
+test_expect_success 'diff.{src,dst}Prefix ignored with --default-prefix' '
+ git -c diff.dstPrefix=x/ -c diff.srcPrefix=y/ diff --default-prefix >actual &&
check_prefix actual a/file0 b/file0
'
+test_expect_success 'diff --no-renames cannot be abbreviated' '
+ test_expect_code 129 git diff --no-rename >actual 2>error &&
+ test_must_be_empty actual &&
+ grep "invalid option: --no-rename" error
+'
+
test_done
diff --git a/t/t4018/csharp-exclude-assignments b/t/t4018/csharp-exclude-assignments
new file mode 100644
index 0000000000..239f312963
--- /dev/null
+++ b/t/t4018/csharp-exclude-assignments
@@ -0,0 +1,20 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ var constantAssignment = "test";
+ var methodAssignment = MethodCall();
+ var multiLineMethodAssignment = MethodCall(
+ );
+ var multiLine = "first"
+ + MethodCall()
+ +
+ ( MethodCall()
+ )
+ + MethodCall();
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-control-statements b/t/t4018/csharp-exclude-control-statements
new file mode 100644
index 0000000000..3a0f404ee1
--- /dev/null
+++ b/t/t4018/csharp-exclude-control-statements
@@ -0,0 +1,34 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ if (false)
+ {
+ return "out";
+ }
+ else { }
+ if (true) MethodCall(
+ );
+ else MethodCall(
+ );
+ switch ("test")
+ {
+ case "one":
+ return MethodCall(
+ );
+ case "two":
+ break;
+ }
+ (int, int) tuple = (1, 4);
+ switch (tuple)
+ {
+ case (1, 4):
+ MethodCall();
+ break;
+ }
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-exceptions b/t/t4018/csharp-exclude-exceptions
new file mode 100644
index 0000000000..b1e64256cf
--- /dev/null
+++ b/t/t4018/csharp-exclude-exceptions
@@ -0,0 +1,29 @@
+using System;
+
+class Example
+{
+ string Method(int RIGHT)
+ {
+ try
+ {
+ throw new Exception("fail");
+ }
+ catch (Exception)
+ {
+ }
+ finally
+ {
+ }
+ try { } catch (Exception) {}
+ try
+ {
+ throw GetException(
+ );
+ }
+ catch (Exception) { }
+
+ return "ChangeMe";
+ }
+
+ Exception GetException() => new Exception("fail");
+}
diff --git a/t/t4018/csharp-exclude-generic-method-calls b/t/t4018/csharp-exclude-generic-method-calls
new file mode 100644
index 0000000000..31af546665
--- /dev/null
+++ b/t/t4018/csharp-exclude-generic-method-calls
@@ -0,0 +1,12 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ GenericMethodCall<int, int>(
+ );
+
+ return "ChangeMe";
+ }
+
+ string GenericMethodCall<T, T2>() => "test";
+}
diff --git a/t/t4018/csharp-exclude-init-dispose b/t/t4018/csharp-exclude-init-dispose
new file mode 100644
index 0000000000..2bc8e194e2
--- /dev/null
+++ b/t/t4018/csharp-exclude-init-dispose
@@ -0,0 +1,22 @@
+using System;
+
+class Example : IDisposable
+{
+ string Method(int RIGHT)
+ {
+ new Example();
+ new Example(
+ );
+ new Example { };
+ using (this)
+ {
+ }
+ var def =
+ this is default(
+ Example);
+
+ return "ChangeMe";
+ }
+
+ public void Dispose() {}
+}
diff --git a/t/t4018/csharp-exclude-iterations b/t/t4018/csharp-exclude-iterations
new file mode 100644
index 0000000000..960aa182ae
--- /dev/null
+++ b/t/t4018/csharp-exclude-iterations
@@ -0,0 +1,26 @@
+using System.Linq;
+
+class Example
+{
+ string Method(int RIGHT)
+ {
+ do { } while (true);
+ do MethodCall(
+ ); while (true);
+ while (true);
+ while (true) {
+ break;
+ }
+ for (int i = 0; i < 10; ++i)
+ {
+ }
+ foreach (int i in Enumerable.Range(0, 10))
+ {
+ }
+ int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
+
+ return "ChangeMe";
+ }
+
+ string MethodCall(int a = 0, int b = 0) => "test";
+}
diff --git a/t/t4018/csharp-exclude-method-calls b/t/t4018/csharp-exclude-method-calls
new file mode 100644
index 0000000000..51e2dc2040
--- /dev/null
+++ b/t/t4018/csharp-exclude-method-calls
@@ -0,0 +1,20 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ MethodCall();
+ MethodCall(1, 2);
+ MethodCall(
+ 1, 2);
+ MethodCall(
+ 1, 2,
+ 3);
+ MethodCall(
+ 1, MethodCall(),
+ 2);
+
+ return "ChangeMe";
+ }
+
+ int MethodCall(int a = 0, int b = 0, int c = 0) => 42;
+}
diff --git a/t/t4018/csharp-exclude-other b/t/t4018/csharp-exclude-other
new file mode 100644
index 0000000000..4d5581cf3e
--- /dev/null
+++ b/t/t4018/csharp-exclude-other
@@ -0,0 +1,18 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ lock (this)
+ {
+ }
+ unsafe
+ {
+ byte[] bytes = [1, 2, 3];
+ fixed (byte* pointerToFirst = bytes)
+ {
+ }
+ }
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method b/t/t4018/csharp-method
new file mode 100644
index 0000000000..16b367aca2
--- /dev/null
+++ b/t/t4018/csharp-method
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-array b/t/t4018/csharp-method-array
new file mode 100644
index 0000000000..1126de8201
--- /dev/null
+++ b/t/t4018/csharp-method-array
@@ -0,0 +1,10 @@
+class Example
+{
+ string[] Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ return ["ChangeMe"];
+ }
+}
diff --git a/t/t4018/csharp-method-explicit b/t/t4018/csharp-method-explicit
new file mode 100644
index 0000000000..5a710116cc
--- /dev/null
+++ b/t/t4018/csharp-method-explicit
@@ -0,0 +1,12 @@
+using System;
+
+class Example : IDisposable
+{
+ void IDisposable.Dispose() // RIGHT
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ }
+}
diff --git a/t/t4018/csharp-method-generics b/t/t4018/csharp-method-generics
new file mode 100644
index 0000000000..b3216bfb2a
--- /dev/null
+++ b/t/t4018/csharp-method-generics
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+ Example<int, string> Method<TA, TB>(TA RIGHT, TB b)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return null;
+ }
+}
diff --git a/t/t4018/csharp-method-generics-alternate-spaces b/t/t4018/csharp-method-generics-alternate-spaces
new file mode 100644
index 0000000000..9583621743
--- /dev/null
+++ b/t/t4018/csharp-method-generics-alternate-spaces
@@ -0,0 +1,11 @@
+class Example<T1, T2>
+{
+ Example<int,string> Method<TA ,TB>(TA RIGHT, TB b)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return null;
+ }
+}
diff --git a/t/t4018/csharp-method-modifiers b/t/t4018/csharp-method-modifiers
new file mode 100644
index 0000000000..caefa8ee99
--- /dev/null
+++ b/t/t4018/csharp-method-modifiers
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+class Example
+{
+ static internal async Task Method(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ await Task.Delay(1);
+ }
+}
diff --git a/t/t4018/csharp-method-multiline b/t/t4018/csharp-method-multiline
new file mode 100644
index 0000000000..3983ff42f5
--- /dev/null
+++ b/t/t4018/csharp-method-multiline
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method_RIGHT(
+ int a,
+ int b,
+ int c)
+ {
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-params b/t/t4018/csharp-method-params
new file mode 100644
index 0000000000..3f00410ba1
--- /dev/null
+++ b/t/t4018/csharp-method-params
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method(int RIGHT, int b, int c = 42)
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-method-special-chars b/t/t4018/csharp-method-special-chars
new file mode 100644
index 0000000000..e6c7bc01a1
--- /dev/null
+++ b/t/t4018/csharp-method-special-chars
@@ -0,0 +1,11 @@
+class @Some_Type
+{
+ @Some_Type @Method_With_Underscore(int RIGHT)
+ {
+ // Filler
+ // Filler
+
+ // ChangeMe
+ return new @Some_Type();
+ }
+}
diff --git a/t/t4018/csharp-method-with-spacing b/t/t4018/csharp-method-with-spacing
new file mode 100644
index 0000000000..233bb976cc
--- /dev/null
+++ b/t/t4018/csharp-method-with-spacing
@@ -0,0 +1,10 @@
+class Example
+{
+ string Method ( int RIGHT )
+ {
+ // Filler
+ // Filler
+
+ return "ChangeMe";
+ }
+}
diff --git a/t/t4018/csharp-property b/t/t4018/csharp-property
new file mode 100644
index 0000000000..e56dfce34c
--- /dev/null
+++ b/t/t4018/csharp-property
@@ -0,0 +1,11 @@
+class Example
+{
+ public bool RIGHT
+ {
+ get { return true; }
+ set
+ {
+ // ChangeMe
+ }
+ }
+}
diff --git a/t/t4018/csharp-property-braces-same-line b/t/t4018/csharp-property-braces-same-line
new file mode 100644
index 0000000000..608131d3d3
--- /dev/null
+++ b/t/t4018/csharp-property-braces-same-line
@@ -0,0 +1,10 @@
+class Example
+{
+ public bool RIGHT {
+ get { return true; }
+ set
+ {
+ // ChangeMe
+ }
+ }
+}
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index c1ac09ecc7..fdd865f7c3 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -232,7 +232,7 @@ keep_only_cr () {
test_expect_success 'external diff with autocrlf = true' '
test_config core.autocrlf true &&
GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
- test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+ test $(wc -l <crlfed.txt) = $(keep_only_cr <crlfed.txt | wc -c)
'
test_expect_success 'diff --cached' '
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
index bf33aedf4b..8ebfa3c1be 100755
--- a/t/t4042-diff-textconv-caching.sh
+++ b/t/t4042-diff-textconv-caching.sh
@@ -118,4 +118,26 @@ test_expect_success 'log notes cache and still use cache for -p' '
git log --no-walk -p refs/notes/textconv/magic HEAD
'
+test_expect_success 'caching is silently ignored outside repo' '
+ mkdir -p non-repo &&
+ echo one >non-repo/one &&
+ echo two >non-repo/two &&
+ echo "* diff=test" >attr &&
+ test_expect_code 1 \
+ nongit git -c core.attributesFile="$PWD/attr" \
+ -c diff.test.textconv="tr a-z A-Z <" \
+ -c diff.test.cachetextconv=true \
+ diff --no-index one two >actual &&
+ cat >expect <<-\EOF &&
+ diff --git a/one b/two
+ index 5626abf..f719efd 100644
+ --- a/one
+ +++ b/two
+ @@ -1 +1 @@
+ -ONE
+ +TWO
+ EOF
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 5ce345d309..651ec77660 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -205,6 +205,18 @@ test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not lik
test_cmp expected actual
'
+test_expect_success POSIXPERM 'external diff with mode-only change' '
+ echo content >not-executable &&
+ echo content >executable &&
+ chmod +x executable &&
+ echo executable executable $(test_oid zero) 100755 \
+ not-executable $(test_oid zero) 100644 not-executable \
+ >expect &&
+ test_expect_code 1 git -c diff.external=echo diff \
+ --no-index executable not-executable >actual &&
+ test_cmp expect actual
+'
+
test_expect_success "diff --no-index treats '-' as stdin" '
cat >expect <<-EOF &&
diff --git a/- b/a/1
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index ece9fae207..56210b5609 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -66,4 +66,28 @@ test_expect_success 'apply --index create' '
git diff --exit-code
'
+test_expect_success !MINGW 'apply with no-contents and a funny pathname' '
+ test_when_finished "rm -fr \"funny \"; git reset --hard" &&
+
+ mkdir "funny " &&
+ >"funny /empty" &&
+ git add "funny /empty" &&
+ git diff HEAD -- "funny /" >sample.patch &&
+ git diff -R HEAD -- "funny /" >elpmas.patch &&
+
+ git reset --hard &&
+
+ git apply --stat --check --apply sample.patch &&
+ test_must_be_empty "funny /empty" &&
+
+ git apply --stat --check --apply elpmas.patch &&
+ test_path_is_missing "funny /empty" &&
+
+ git apply -R --stat --check --apply elpmas.patch &&
+ test_must_be_empty "funny /empty" &&
+
+ git apply -R --stat --check --apply sample.patch &&
+ test_path_is_missing "funny /empty"
+'
+
test_done
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
index e7a7295f1b..4eb8444029 100755
--- a/t/t4129-apply-samemode.sh
+++ b/t/t4129-apply-samemode.sh
@@ -41,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
chmod +x file &&
git add file &&
git apply --cached patch-0.txt &&
- git ls-files -s file | grep "^100755"
+ git ls-files -s file >ls-files-output &&
+ test_grep "^100755" ls-files-output
'
test_expect_success FILEMODE 'mode update (no index)' '
@@ -60,7 +61,8 @@ test_expect_success FILEMODE 'mode update (with index)' '
test_expect_success FILEMODE 'mode update (index only)' '
git reset --hard &&
git apply --cached patch-1.txt &&
- git ls-files -s file | grep "^100755"
+ git ls-files -s file >ls-files-output &&
+ test_grep "^100755" ls-files-output
'
test_expect_success FILEMODE 'empty mode is rejected' '
@@ -101,4 +103,31 @@ test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree
)
'
+test_expect_success 'git apply respects core.fileMode' '
+ test_config core.fileMode false &&
+ echo true >script.sh &&
+ git add --chmod=+x script.sh &&
+ git ls-files -s script.sh >ls-files-output &&
+ test_grep "^100755" ls-files-output &&
+ test_tick && git commit -m "Add script" &&
+ git ls-tree -r HEAD script.sh >ls-tree-output &&
+ test_grep "^100755" ls-tree-output &&
+
+ echo true >>script.sh &&
+ test_tick && git commit -m "Modify script" script.sh &&
+ git format-patch -1 --stdout >patch &&
+ test_grep "^index.*100755$" patch &&
+
+ git switch -c branch HEAD^ &&
+ git apply --index patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err &&
+ git reset --hard &&
+
+ git apply patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err &&
+
+ git apply --cached patch 2>err &&
+ test_grep ! "has type 100644, expected 100755" err
+'
+
test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 3b12576269..5e2b6c80ea 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1224,8 +1224,8 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
test_expect_success 'skip an empty patch in the middle of an am session' '
git checkout empty-commit^ &&
- test_must_fail git am empty-commit.patch >err &&
- grep "Patch is empty." err &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
git am --skip &&
test_path_is_missing .git/rebase-apply &&
@@ -1236,8 +1236,8 @@ test_expect_success 'skip an empty patch in the middle of an am session' '
test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
git checkout empty-commit^ &&
- test_must_fail git am empty-commit.patch >err &&
- grep "Patch is empty." err &&
+ test_must_fail git am empty-commit.patch >out 2>err &&
+ grep "Patch is empty." out &&
grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
git am --allow-empty >output &&
grep "No changes - recorded it as an empty commit." output &&
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index d7382709fc..f698d0c9ad 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -312,6 +312,38 @@ test_expect_success 'shortlog de-duplicates trailers in a single commit' '
test_cmp expect actual
'
+# Trailers that have unfolded (single line) and folded (multiline) values which
+# are otherwise identical are treated as the same trailer for de-duplication.
+test_expect_success 'shortlog de-duplicates trailers in a single commit (folded/unfolded values)' '
+ git commit --allow-empty -F - <<-\EOF &&
+ subject one
+
+ this message has two distinct values, plus a repeat (folded)
+
+ Repeated-trailer: Foo foo foo
+ Repeated-trailer: Bar
+ Repeated-trailer: Foo
+ foo foo
+ EOF
+
+ git commit --allow-empty -F - <<-\EOF &&
+ subject two
+
+ similar to the previous, but without the second distinct value
+
+ Repeated-trailer: Foo foo foo
+ Repeated-trailer: Foo
+ foo foo
+ EOF
+
+ cat >expect <<-\EOF &&
+ 2 Foo foo foo
+ 1 Bar
+ EOF
+ git shortlog -ns --group=trailer:repeated-trailer -2 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'shortlog can match multiple groups' '
git commit --allow-empty -F - <<-\EOF &&
subject one
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index ddd205f98a..60fe60d761 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -2255,23 +2255,6 @@ test_expect_success 'log on empty repo fails' '
test_grep does.not.have.any.commits stderr
'
-test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
- git init empty &&
- test_when_finished "rm -rf empty" &&
- echo 1234abcd >empty/.git/refs/heads/main &&
- test_must_fail git -C empty log 2>stderr &&
- test_grep broken stderr
-'
-
-test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
- git init empty &&
- test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
- test_must_fail git -C empty log 2>stderr &&
- test_grep broken stderr &&
- test_must_fail git -C empty log --default totally-bogus 2>stderr &&
- test_grep broken stderr
-'
-
test_expect_success 'log does not default to HEAD when rev input is given' '
git log --branches=does-not-exist >actual &&
test_must_be_empty actual
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index e3d655e6b8..158b49d4b6 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -30,40 +30,46 @@ test_expect_success 'set up basic repos' '
>bar &&
git add foo &&
test_tick &&
- git config i18n.commitEncoding $test_encoding &&
+ test_config i18n.commitEncoding $test_encoding &&
commit_msg $test_encoding | git commit -F - &&
git add bar &&
test_tick &&
- git commit -m "add bar" &&
- git config --unset i18n.commitEncoding
+ git commit -m "add bar"
'
test_expect_success 'alias builtin format' '
git log --pretty=oneline >expected &&
- git config pretty.test-alias oneline &&
+ test_config pretty.test-alias oneline &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
test_expect_success 'alias masking builtin format' '
git log --pretty=oneline >expected &&
- git config pretty.oneline "%H" &&
+ test_config pretty.oneline "%H" &&
git log --pretty=oneline >actual &&
test_cmp expected actual
'
test_expect_success 'alias user-defined format' '
git log --pretty="format:%h" >expected &&
- git config pretty.test-alias "format:%h" &&
+ test_config pretty.test-alias "format:%h" &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
+test_expect_success 'alias user-defined format is matched case-insensitively' '
+ git log --pretty="format:%h" >expected &&
+ test_config pretty.testone "format:%h" &&
+ test_config pretty.testtwo testOne &&
+ git log --pretty=testTwo >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'alias user-defined tformat with %s (ISO8859-1 encoding)' '
- git config i18n.logOutputEncoding $test_encoding &&
+ test_config i18n.logOutputEncoding $test_encoding &&
git log --oneline >expected-s &&
git log --pretty="tformat:%h %s" >actual-s &&
- git config --unset i18n.logOutputEncoding &&
test_cmp expected-s actual-s
'
@@ -75,34 +81,34 @@ test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
test_expect_success 'alias user-defined tformat' '
git log --pretty="tformat:%h" >expected &&
- git config pretty.test-alias "tformat:%h" &&
+ test_config pretty.test-alias "tformat:%h" &&
git log --pretty=test-alias >actual &&
test_cmp expected actual
'
test_expect_success 'alias non-existent format' '
- git config pretty.test-alias format-that-will-never-exist &&
+ test_config pretty.test-alias format-that-will-never-exist &&
test_must_fail git log --pretty=test-alias
'
test_expect_success 'alias of an alias' '
git log --pretty="tformat:%h" >expected &&
- git config pretty.test-foo "tformat:%h" &&
- git config pretty.test-bar test-foo &&
+ test_config pretty.test-foo "tformat:%h" &&
+ test_config pretty.test-bar test-foo &&
git log --pretty=test-bar >actual && test_cmp expected actual
'
test_expect_success 'alias masking an alias' '
git log --pretty=format:"Two %H" >expected &&
- git config pretty.duplicate "format:One %H" &&
- git config --add pretty.duplicate "format:Two %H" &&
+ test_config pretty.duplicate "format:One %H" &&
+ test_config pretty.duplicate "format:Two %H" --add &&
git log --pretty=duplicate >actual &&
test_cmp expected actual
'
test_expect_success 'alias loop' '
- git config pretty.test-foo test-bar &&
- git config pretty.test-bar test-foo &&
+ test_config pretty.test-foo test-bar &&
+ test_config pretty.test-bar test-foo &&
test_must_fail git log --pretty=test-foo
'
@@ -156,7 +162,7 @@ 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 || return 1
+ lf_to_nul <raw || return 1
done >expect &&
# the trailing NUL is already produced so we do not need to
# output another one
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index d2dfcf164e..75216f19ce 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -64,7 +64,7 @@ test_expect_success 'log --grep does not find non-reencoded values (latin1)' '
'
triggers_undefined_behaviour () {
- local engine=$1
+ local engine="$1"
case $engine in
fixed)
@@ -85,7 +85,7 @@ triggers_undefined_behaviour () {
}
mismatched_git_log () {
- local pattern=$1
+ local pattern="$1"
LC_ALL=$is_IS_locale git log --encoding=ISO-8859-1 --format=%s \
--grep=$pattern
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index 45f1d4f95e..661feb6070 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -59,7 +59,7 @@ test_expect_success setup '
# Also, it had the unwanted side-effect of deleting f.
test_expect_success 'try to apply corrupted patch' '
test_when_finished "git am --abort" &&
- test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
+ test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual &&
echo "error: git diff header lacks filename information (line 4)" >expected &&
test_path_is_file f &&
test_cmp expected actual
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index b2c8a43fce..eea19907b5 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -313,7 +313,7 @@ test_expect_success 'rename/add handling' '
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash all_blobs &&
@@ -380,7 +380,7 @@ test_expect_success SYMLINKS 'rename/add, where add is a mode conflict' '
# First, check that the bar that appears at stage 3 does not
# correspond to an individual blob anywhere in history
#
- hash=$(cat out | tr "\0" "\n" | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
+ hash=$(tr "\0" "\n" <out | head -n 3 | grep 3.bar | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash all_blobs &&
@@ -630,8 +630,8 @@ test_expect_success 'mod6: chains of rename/rename(1to2) and add/add via collidi
# conflict entries do not appear as individual blobs anywhere
# in history.
#
- hash1=$(cat out | tr "\0" "\n" | head | grep 2.four | cut -f 2 -d " ") &&
- hash2=$(cat out | tr "\0" "\n" | head | grep 3.two | cut -f 2 -d " ") &&
+ hash1=$(tr "\0" "\n" <out | head | grep 2.four | cut -f 2 -d " ") &&
+ hash2=$(tr "\0" "\n" <out | head | grep 3.two | cut -f 2 -d " ") &&
git rev-list --objects --all >all_blobs &&
! grep $hash1 all_blobs &&
! grep $hash2 all_blobs &&
@@ -887,7 +887,7 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
test_expect_success '--merge-base is incompatible with --stdin' '
test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
- grep "^fatal: --merge-base is incompatible with --stdin" expect
+ grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
'
# specify merge-base as parent of branch2
@@ -945,4 +945,49 @@ test_expect_success 'check the input format when --stdin is passed' '
test_cmp expect actual
'
+test_expect_success '--merge-base with tree OIDs' '
+ git merge-tree --merge-base=side1^ side1 side3 >with-commits &&
+ git merge-tree --merge-base=side1^^{tree} side1^{tree} side3^{tree} >with-trees &&
+ test_cmp with-commits with-trees
+'
+
+test_expect_success 'error out on missing tree objects' '
+ git init --bare missing-tree.git &&
+ git rev-list side3 >list &&
+ git rev-parse side3^: >>list &&
+ git pack-objects missing-tree.git/objects/pack/side3-tree-is-missing <list &&
+ side3=$(git rev-parse side3) &&
+ test_must_fail git --git-dir=missing-tree.git merge-tree $side3^ $side3 >actual 2>err &&
+ test_grep "Could not read $(git rev-parse $side3:)" err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing blob objects' '
+ echo 1 | git hash-object -w --stdin >blob1 &&
+ echo 2 | git hash-object -w --stdin >blob2 &&
+ echo 3 | git hash-object -w --stdin >blob3 &&
+ printf "100644 blob $(cat blob1)\tblob\n" | git mktree >tree1 &&
+ printf "100644 blob $(cat blob2)\tblob\n" | git mktree >tree2 &&
+ printf "100644 blob $(cat blob3)\tblob\n" | git mktree >tree3 &&
+ git init --bare missing-blob.git &&
+ cat blob1 blob3 tree1 tree2 tree3 |
+ git pack-objects missing-blob.git/objects/pack/side1-whatever-is-missing &&
+ test_must_fail git --git-dir=missing-blob.git >actual 2>err \
+ merge-tree --merge-base=$(cat tree1) $(cat tree2) $(cat tree3) &&
+ test_grep "unable to read blob object $(cat blob2)" err &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'error out on missing commits as well' '
+ git init --bare missing-commit.git &&
+ git rev-list --objects side1 side3 >list-including-initial &&
+ grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
+ git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
+ side1=$(git rev-parse side1) &&
+ side3=$(git rev-parse side3) &&
+ test_must_fail git --git-dir=missing-commit.git \
+ merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 4b4c3315d8..72b8d0ff02 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -124,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 &&
diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh
index fc499cdff0..961c6aac25 100755
--- a/t/t5003-archive-zip.sh
+++ b/t/t5003-archive-zip.sh
@@ -239,4 +239,38 @@ check_zip with_untracked2
check_added with_untracked2 untracked one/untracked
check_added with_untracked2 untracked two/untracked
+# Test remote archive over HTTP protocol.
+#
+# Note: this should be the last part of this test suite, because
+# by including lib-httpd.sh, the test may end early if httpd tests
+# should not be run.
+#
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success "setup for HTTP protocol" '
+ cp -R bare.git "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/bare.git" \
+ config http.uploadpack true &&
+ set_askpass user@host pass@host
+'
+
+setup_askpass_helper
+
+test_expect_success 'remote archive does not work with protocol v1' '
+ test_must_fail git -c protocol.version=1 archive \
+ --remote="$HTTPD_URL/auth/smart/bare.git" \
+ --output=remote-http.zip HEAD >actual 2>&1 &&
+ cat >expect <<-EOF &&
+ fatal: can${SQ}t connect to subservice git-upload-archive
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'archive remote http repository' '
+ git archive --remote="$HTTPD_URL/auth/smart/bare.git" \
+ --output=remote-http.zip HEAD &&
+ test_cmp_bin d.zip remote-http.zip
+'
+
test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index db11cababd..c8d0655454 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -70,7 +70,7 @@ test_expect_success 'respect NULs' '
git mailsplit -d3 -o. "$DATA/nul-plain" &&
test_cmp "$DATA/nul-plain" 001 &&
- (cat 001 | git mailinfo msg patch) &&
+ git mailinfo msg patch <001 &&
test_line_count = 4 patch
'
@@ -268,4 +268,26 @@ test_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
diff --git a/t/t5100/comment.expect b/t/t5100/comment.expect
index 7228177984..bd71956a47 100644
--- a/t/t5100/comment.expect
+++ b/t/t5100/comment.expect
@@ -1,4 +1,4 @@
-Author: A U Thor (this is (really) a comment (honestly))
+Author: (this is (really) a "comment" (honestly)) A U Thor
Email: somebody@example.com
Subject: testing comments
Date: Sun, 25 May 2008 00:38:18 -0700
diff --git a/t/t5100/comment.in b/t/t5100/comment.in
index c53a192dfe..0b7e903b06 100644
--- a/t/t5100/comment.in
+++ b/t/t5100/comment.in
@@ -1,5 +1,5 @@
From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
-From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+From: (this is \(really\) a "comment" (honestly)) "A U Thor" <somebody@example.com>
Date: Sun, 25 May 2008 00:38:18 -0700
Subject: [PATCH] testing comments
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index d402ec18b7..61e2be2903 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -441,6 +441,47 @@ test_expect_success 'index-pack with --strict' '
)
'
+test_expect_success 'setup for --strict and --fsck-objects downgrading fsck msgs' '
+ git init strict &&
+ (
+ cd strict &&
+ test_commit first hello &&
+ cat >commit <<-EOF &&
+ tree $(git rev-parse HEAD^{tree})
+ parent $(git rev-parse HEAD)
+ author A U Thor
+ committer A U Thor
+
+ commit: this is a commit with bad emails
+
+ EOF
+ git hash-object --literally -t commit -w --stdin <commit >commit_list &&
+ git pack-objects test <commit_list >pack-name
+ )
+'
+
+test_with_bad_commit () {
+ must_fail_arg="$1" &&
+ must_pass_arg="$2" &&
+ (
+ cd strict &&
+ test_must_fail git index-pack "$must_fail_arg" "test-$(cat pack-name).pack" &&
+ git index-pack "$must_pass_arg" "test-$(cat pack-name).pack"
+ )
+}
+
+test_expect_success 'index-pack with --strict downgrading fsck msgs' '
+ test_with_bad_commit --strict --strict="missingEmail=ignore"
+'
+
+test_expect_success 'index-pack with --fsck-objects downgrading fsck msgs' '
+ test_with_bad_commit --fsck-objects --fsck-objects="missingEmail=ignore"
+'
+
+test_expect_success 'cleanup for --strict and --fsck-objects downgrading fsck msgs' '
+ rm -rf strict
+'
+
test_expect_success 'honor pack.packSizeLimit' '
git config pack.packSizeLimit 3m &&
packname_10=$(git pack-objects test-10 <obj-list) &&
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 230cb38712..d8d2e30468 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -111,30 +111,4 @@ test_expect_success 'pack-refs does not silently delete broken loose ref' '
test_cmp expect actual
'
-# we do not want to count on running pack-refs to
-# actually pack it, as it is perfectly reasonable to
-# skip processing a broken ref
-test_expect_success REFFILES 'create packed-refs file with broken ref' '
- rm -f .git/refs/heads/main &&
- cat >.git/packed-refs <<-EOF &&
- $missing refs/heads/main
- $recoverable refs/heads/other
- EOF
- echo $missing >expect &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success REFFILES 'pack-refs does not silently delete broken packed ref' '
- git pack-refs --all --prune &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
-test_expect_success REFFILES 'pack-refs does not drop broken refs during deletion' '
- git update-ref -d refs/heads/other &&
- git rev-parse refs/heads/main >actual &&
- test_cmp expect actual
-'
-
test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 2ff3eef9a3..79552d6ef7 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -455,7 +455,7 @@ test_expect_success 'setup r1 - delete loose blobs' '
test_parse_ls_files_stage_oids <ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
rm r1/.git/objects/$id || return 1
done
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 7fe7c72a87..a2b4442660 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -911,10 +911,10 @@ test_expect_success 'stale commit cannot be parsed when given directly' '
# Verify that it is possible to read the commit from the
# commit graph when not being paranoid, ...
- GIT_COMMIT_GRAPH_PARANOIA=false git rev-list B &&
+ git rev-list B &&
# ... but parsing the commit when double checking that
# it actually exists in the object database should fail.
- test_must_fail git rev-list -1 B
+ test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
)
'
@@ -938,9 +938,9 @@ test_expect_success 'stale commit cannot be parsed when traversing graph' '
# Again, we should be able to parse the commit when not
# being paranoid about commit graph staleness...
- GIT_COMMIT_GRAPH_PARANOIA=false git rev-parse HEAD~2 &&
+ git rev-parse HEAD~2 &&
# ... but fail when we are paranoid.
- test_must_fail git rev-parse HEAD~2 2>error &&
+ 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
)
'
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
index 2ba788b042..3c20738bce 100755
--- a/t/t5332-multi-pack-reuse.sh
+++ b/t/t5332-multi-pack-reuse.sh
@@ -2,6 +2,7 @@
test_description='pack-objects multi-pack reuse'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bitmap.sh
@@ -23,6 +24,27 @@ pack_position () {
grep "$1" objects | cut -d" " -f1
}
+# test_pack_objects_reused_all <pack-reused> <packs-reused>
+test_pack_objects_reused_all () {
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+ git pack-objects --stdout --revs --all --delta-base-offset \
+ >/dev/null &&
+
+ test_pack_reused "$1" <trace2.txt &&
+ test_packs_reused "$2" <trace2.txt
+}
+
+# test_pack_objects_reused <pack-reused> <packs-reused>
+test_pack_objects_reused () {
+ : >trace2.txt &&
+ GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+ git pack-objects --stdout --revs >/dev/null &&
+
+ test_pack_reused "$1" <trace2.txt &&
+ test_packs_reused "$2" <trace2.txt
+}
+
test_expect_success 'preferred pack is reused for single-pack reuse' '
test_config pack.allowPackReuse single &&
@@ -34,12 +56,24 @@ test_expect_success 'preferred pack is reused for single-pack reuse' '
git multi-pack-index write --bitmap &&
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --revs --all >/dev/null &&
+ test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'multi-pack reuse is disabled by default' '
+ test_pack_objects_reused_all 3 1
+'
+
+test_expect_success 'feature.experimental implies multi-pack reuse' '
+ test_config feature.experimental true &&
+
+ test_pack_objects_reused_all 6 2
+'
+
+test_expect_success 'multi-pack reuse can be disabled with feature.experimental' '
+ test_config feature.experimental true &&
+ test_config pack.allowPackReuse single &&
- test_pack_reused 3 <trace2.txt &&
- test_packs_reused 1 <trace2.txt
+ test_pack_objects_reused_all 3 1
'
test_expect_success 'enable multi-pack reuse' '
@@ -57,21 +91,11 @@ test_expect_success 'reuse all objects from subset of bitmapped packs' '
^$(git rev-parse A)
EOF
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --revs <in >/dev/null &&
-
- test_pack_reused 6 <trace2.txt &&
- test_packs_reused 2 <trace2.txt
+ test_pack_objects_reused 6 2 <in
'
test_expect_success 'reuse all objects from all packs' '
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --revs --all >/dev/null &&
-
- test_pack_reused 9 <trace2.txt &&
- test_packs_reused 3 <trace2.txt
+ test_pack_objects_reused_all 9 3
'
test_expect_success 'reuse objects from first pack with middle gap' '
@@ -104,12 +128,7 @@ test_expect_success 'reuse objects from first pack with middle gap' '
^$(git rev-parse D)
EOF
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
- test_pack_reused 3 <trace2.txt &&
- test_packs_reused 1 <trace2.txt
+ test_pack_objects_reused 3 1 <in
'
test_expect_success 'reuse objects from middle pack with middle gap' '
@@ -125,12 +144,7 @@ test_expect_success 'reuse objects from middle pack with middle gap' '
^$(git rev-parse D)
EOF
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
- test_pack_reused 3 <trace2.txt &&
- test_packs_reused 1 <trace2.txt
+ test_pack_objects_reused 3 1 <in
'
test_expect_success 'omit delta with uninteresting base (same pack)' '
@@ -160,10 +174,6 @@ test_expect_success 'omit delta with uninteresting base (same pack)' '
^$base
EOF
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
# We can only reuse the 3 objects corresponding to "other" from
# the latest pack.
#
@@ -175,8 +185,7 @@ test_expect_success 'omit delta with uninteresting base (same pack)' '
# The remaining objects from the other pack are similarly not
# reused because their objects are on the uninteresting side of
# the query.
- test_pack_reused 3 <trace2.txt &&
- test_packs_reused 1 <trace2.txt
+ test_pack_objects_reused 3 1 <in
'
test_expect_success 'omit delta from uninteresting base (cross pack)' '
@@ -189,15 +198,10 @@ test_expect_success 'omit delta from uninteresting base (cross pack)' '
git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
- : >trace2.txt &&
- GIT_TRACE2_EVENT="$PWD/trace2.txt" \
- git pack-objects --stdout --delta-base-offset --all >/dev/null &&
-
packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
objects_nr="$(git rev-list --count --all --objects)" &&
- test_pack_reused $(($objects_nr - 1)) <trace2.txt &&
- test_packs_reused $packs_nr <trace2.txt
+ test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
'
test_done
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 001b7a17ad..d8cadeec73 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -123,7 +123,7 @@ remote: STDOUT post-update
remote: STDERR post-update
EOF
test_expect_success 'send-pack stderr contains hook messages' '
- grep ^remote: send.err | sed "s/ *\$//" >actual &&
+ sed -n "/^remote:/s/ *\$//p" send.err >actual &&
test_cmp expect actual
'
@@ -133,10 +133,8 @@ 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/*"
'
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index dcadd56d3a..33d34d5ae9 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -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 &&
@@ -802,7 +803,8 @@ 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
)
'
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index a95841dc36..25772c85c5 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -24,6 +24,15 @@ setup_repository () {
)
}
+setup_test_clone () {
+ test_dir="$1" &&
+ git clone one "$test_dir" &&
+ for r in one two three
+ do
+ git -C "$test_dir" remote add "$r" "../$r" || return 1
+ done
+}
+
test_expect_success setup '
setup_repository one &&
setup_repository two &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
git fetch --multiple --jobs=0)
'
+create_fetch_all_expect () {
+ cat >expect <<-\EOF
+ one/main
+ one/side
+ origin/HEAD -> origin/main
+ origin/main
+ origin/side
+ three/another
+ three/main
+ three/side
+ two/another
+ two/main
+ two/side
+ EOF
+}
+
+for fetch_all in true false
+do
+ test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+ test_dir="test_fetch_all_$fetch_all" &&
+ setup_test_clone "$test_dir" &&
+ (
+ cd "$test_dir" &&
+ git config fetch.all $fetch_all &&
+ git fetch --all &&
+ create_fetch_all_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+ '
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+ setup_test_clone test9 &&
+ (
+ cd test9 &&
+ git config fetch.all true &&
+ git fetch &&
+ git branch -r >actual &&
+ create_fetch_all_expect &&
+ test_cmp expect actual
+ )
+'
+
+create_fetch_one_expect () {
+ cat >expect <<-\EOF
+ one/main
+ one/side
+ origin/HEAD -> origin/main
+ origin/main
+ origin/side
+ EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+ setup_test_clone test10 &&
+ (
+ cd test10 &&
+ git config fetch.all true &&
+ git fetch one &&
+ create_fetch_one_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+create_fetch_two_as_origin_expect () {
+ cat >expect <<-\EOF
+ origin/HEAD -> origin/main
+ origin/another
+ origin/main
+ origin/side
+ EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+ setup_test_clone test11 &&
+ (
+ cd test11 &&
+ git config fetch.all false &&
+ git remote set-url origin ../two &&
+ git fetch &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+for fetch_all in true false
+do
+ test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+ test_dir="test_no_all_fetch_all_$fetch_all" &&
+ setup_test_clone "$test_dir" &&
+ (
+ cd "$test_dir" &&
+ git config fetch.all $fetch_all &&
+ git remote set-url origin ../two &&
+ git fetch --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+ '
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+ setup_test_clone test12 &&
+ (
+ cd test12 &&
+ git config --unset-all fetch.all || true &&
+ git remote set-url origin ../two &&
+ git fetch --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+ setup_test_clone test13 &&
+ (
+ cd test13 &&
+ git remote set-url origin ../two &&
+ git fetch --all --no-all &&
+ create_fetch_two_as_origin_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+ setup_test_clone test14 &&
+ (
+ cd test14 &&
+ git fetch --no-all one &&
+ create_fetch_one_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+ setup_test_clone test15 &&
+ (
+ cd test15 &&
+ git fetch --no-all --all &&
+ create_fetch_all_expect &&
+ git branch -r >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 7ab220fa31..5e566205ba 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '
git -C dst fetch --recurse-submodules &&
# Break the receiving submodule
- test-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+ rm -r dst/sub/.git/objects &&
# NOTE: without the fix the following tests will recurse forever!
# They should terminate with an error.
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index b4bc24691c..c91a62b77a 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -303,7 +303,7 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
EOF
sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
) >expect.in &&
- key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+ key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
noop=$(git rev-parse noop) &&
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index df758e187d..71428f3d5c 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -232,8 +232,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
test_config -C "$d" http.receivepack true &&
up="$HTTPD_URL"/smart/atomic-branches.git &&
- # break ref updates for other on the remote site
- mkdir "$d/refs/heads/other.lock" &&
+ # Create d/f conflict to break ref updates for other on the remote site.
+ git -C "$d" update-ref -d refs/heads/other &&
+ git -C "$d" update-ref refs/heads/other/conflict HEAD &&
# add the new commit to other
git branch -f other collateral &&
@@ -241,18 +242,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
# --atomic should cause entire push to be rejected
test_must_fail git push --atomic "$up" atomic other 2>output &&
- # the new branch should not have been created upstream
- test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
-
- # upstream should still reflect atomic2, the last thing we pushed
- # successfully
- git rev-parse atomic2 >expected &&
- # ...to other.
- git -C "$d" rev-parse refs/heads/other >actual &&
- test_cmp expected actual &&
-
- # the new branch should not have been created upstream
+ # The atomic and other branches should not be created upstream.
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+ test_must_fail git -C "$d" show-ref --verify refs/heads/other &&
# the failed refs should be indicated to the user
grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index e444b30bf6..4c3b32785d 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -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
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 8a41adf1e1..a623a1058c 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -359,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' '
@@ -731,4 +733,22 @@ test_expect_success 'no empty path components' '
! grep "//" log
'
+test_expect_success 'tag following always works over v0 http' '
+ upstream=$HTTPD_DOCUMENT_ROOT_PATH/tags &&
+ git init "$upstream" &&
+ (
+ cd "$upstream" &&
+ git commit --allow-empty -m base &&
+ git tag not-annotated &&
+ git tag -m foo annotated
+ ) &&
+ git init tags &&
+ git -C tags -c protocol.version=0 \
+ fetch --depth 1 $HTTPD_URL/smart/tags \
+ refs/tags/annotated:refs/tags/annotated &&
+ git -C "$upstream" for-each-ref refs/tags >expect &&
+ git -C tags for-each-ref >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5555-http-smart-common.sh b/t/t5555-http-smart-common.sh
index b1cfe8b7db..3dcb3340a3 100755
--- a/t/t5555-http-smart-common.sh
+++ b/t/t5555-http-smart-common.sh
@@ -131,7 +131,6 @@ test_expect_success 'git upload-pack --advertise-refs: v2' '
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
- object-info
0000
EOF
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index 996a08e90c..1ca5f745e7 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -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 &&
@@ -284,6 +293,15 @@ 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 &&
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
index a9b06b2251..5883839a04 100755
--- a/t/t5574-fetch-output.sh
+++ b/t/t5574-fetch-output.sh
@@ -61,11 +61,10 @@ test_expect_success 'fetch compact output' '
test_cmp expect actual
'
-test_expect_success 'fetch porcelain output' '
- test_when_finished "rm -rf porcelain" &&
-
+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" &&
@@ -74,15 +73,10 @@ test_expect_success 'fetch porcelain output' '
FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
git checkout main &&
- # 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 . porcelain &&
- git -C porcelain fetch origin $refspecs &&
+ # Backup to preseed.git
+ git clone --mirror . preseed.git &&
- # Now that we have set up the client repositories we can change our
- # local references.
+ # Continue changing our local references.
git branch new-branch &&
git branch -d deleted-branch &&
git checkout fast-forward &&
@@ -91,36 +85,53 @@ test_expect_success 'fetch porcelain output' '
git checkout force-updated &&
git reset --hard HEAD~ &&
test_commit --no-tag force-update-new &&
- FORCE_UPDATED_NEW=$(git rev-parse HEAD) &&
-
- 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
-
- # 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 \
- --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 \
- --porcelain --prune origin $refspecs >actual 2>stderr &&
- test_cmp expect actual &&
- test_must_be_empty stderr
+ 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" &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 47eae641f0..ca43185681 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -157,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/ &&
@@ -759,6 +776,18 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
+test_expect_success 'clone with includeIf' '
+ test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
+ git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+
+ test_when_finished "rm \"$HOME\"/.gitconfig" &&
+ cat >"$HOME"/.gitconfig <<-EOF &&
+ [includeIf "onbranch:something"]
+ path = /does/not/exist.inc
+ EOF
+ git clone $HTTPD_URL/smart/repo.git repo
+'
+
test_expect_success 'partial clone using HTTP' '
partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
'
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index fc4bbd9daf..e93e0d0cc3 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -64,7 +64,7 @@ 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
'
@@ -120,14 +120,14 @@ test_expect_success 'prefers -c config over --template config' '
'
-test_expect_failure 'prefers --template config even for core.bare' '
+test_expect_success 'ignore --template config for core.bare' '
template="$TRASH_DIRECTORY/template-with-bare-config" &&
mkdir "$template" &&
git config --file "$template/config" core.bare true &&
git clone "--template=$template" parent clone-bare-config &&
- test "$(git -C clone-bare-config config --local core.bare)" = "true" &&
- test_path_is_file clone-bare-config/HEAD
+ test "$(git -C clone-bare-config config --local core.bare)" = "false" &&
+ test_path_is_missing clone-bare-config/HEAD
'
test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 3591bc2417..c48830de8f 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -20,7 +20,6 @@ test_expect_success 'test capability advertisement' '
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
- object-info
EOF
cat >expect.trailer <<-EOF &&
0000
@@ -323,6 +322,8 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
# Test the basics of object-info
#
test_expect_success 'basics of object-info' '
+ test_config transfer.advertiseObjectInfo true &&
+
test-tool pkt-line pack >in <<-EOF &&
command=object-info
object-format=$(test_oid algo)
@@ -380,4 +381,25 @@ test_expect_success 'basics of bundle-uri: dies if not enabled' '
test_must_be_empty out
'
+test_expect_success 'object-info missing from capabilities when disabled' '
+ test_config transfer.advertiseObjectInfo false &&
+
+ GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+ --advertise-capabilities >out &&
+ test-tool pkt-line unpack <out >actual &&
+
+ ! grep object.info actual
+'
+
+test_expect_success 'object-info commands rejected when disabled' '
+ test_config transfer.advertiseObjectInfo false &&
+
+ test-tool pkt-line pack >in <<-EOF &&
+ command=object-info
+ EOF
+
+ test_must_fail test-tool serve-v2 --stateless-rpc <in 2>err &&
+ grep invalid.command err
+'
+
test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 6ef4971845..1ef540f73d 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -778,6 +778,25 @@ test_expect_success 'archive with custom path does not request v2' '
! grep ^GIT_PROTOCOL env.trace
'
+test_expect_success 'reject client packfile-uris if not advertised' '
+ {
+ packetize command=fetch &&
+ packetize object-format=$(test_oid algo) &&
+ printf 0001 &&
+ packetize packfile-uris https &&
+ packetize done &&
+ printf 0000
+ } >input &&
+ test_must_fail env GIT_PROTOCOL=version=2 \
+ git upload-pack client <input &&
+ test_must_fail env GIT_PROTOCOL=version=2 \
+ git -c uploadpack.blobpackfileuri \
+ upload-pack client <input &&
+ GIT_PROTOCOL=version=2 \
+ git -c uploadpack.blobpackfileuri=anything \
+ upload-pack client <input
+'
+
# Test protocol v2 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t5801/git-remote-testgit b/t/t5801/git-remote-testgit
index 1544d6dc6b..c5b10f5775 100755
--- a/t/t5801/git-remote-testgit
+++ b/t/t5801/git-remote-testgit
@@ -12,6 +12,11 @@ url=$2
dir="$GIT_DIR/testgit/$alias"
+if ! git rev-parse --is-inside-git-dir
+then
+ exit 1
+fi
+
h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
@@ -25,6 +30,7 @@ GIT_DIR="$url/.git"
export GIT_DIR
force=
+object_format=
mkdir -p "$dir"
@@ -56,7 +62,8 @@ do
echo
;;
list)
- echo ":object-format $(git rev-parse --show-object-format=storage)"
+ test -n "$object_format" &&
+ echo ":object-format $(git rev-parse --show-object-format=storage)"
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
head=$(git symbolic-ref HEAD)
echo "@$head HEAD"
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index 0729f800c3..ee0306aeec 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -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 &&
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index ced40157ed..91db8fafe8 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -63,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
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 67d523d405..3b181f771c 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -214,15 +214,13 @@ do
for pseudoopt in branches tags remotes
do
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
- echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
- test_cmp expected err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
- echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
- test_cmp expected err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
done
done
diff --git a/t/t6021-rev-list-exclude-hidden.sh b/t/t6021-rev-list-exclude-hidden.sh
index cdf7aa9427..51df02105d 100755
--- a/t/t6021-rev-list-exclude-hidden.sh
+++ b/t/t6021-rev-list-exclude-hidden.sh
@@ -151,12 +151,12 @@ do
do
test_expect_success "$section: fails with --$pseudoopt" '
test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
- test_grep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
test_expect_success "$section: fails with --$pseudoopt=pattern" '
test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
- test_grep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+ test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
'
done
done
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
index 40265a4f66..127180e1c9 100755
--- a/t/t6022-rev-list-missing.sh
+++ b/t/t6022-rev-list-missing.sh
@@ -10,9 +10,18 @@ TEST_PASSES_SANITIZE_LEAK=true
test_expect_success 'create repository and alternate directory' '
test_commit 1 &&
test_commit 2 &&
- test_commit 3
+ test_commit 3 &&
+ git tag -m "tag message" annot_tag HEAD~1 &&
+ git tag regul_tag HEAD~1 &&
+ git branch a_branch HEAD~1
'
+# We manually corrupt the repository, which means that the commit-graph may
+# 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" '
@@ -40,9 +49,10 @@ do
git rev-list --objects --no-object-names \
HEAD ^$obj >expect.raw &&
- # Blobs are shared by all commits, so evethough a commit/tree
+ # Blobs are shared by all commits, so even though a commit/tree
# might be skipped, its blob must be accounted for.
- if [ $obj != "HEAD:1.t" ]; then
+ if test $obj != "HEAD:1.t"
+ then
echo $(git rev-parse HEAD:1.t) >>expect.raw &&
echo $(git rev-parse HEAD:2.t) >>expect.raw
fi &&
@@ -71,4 +81,69 @@ do
done
done
+for missing_tip in "annot_tag" "regul_tag" "a_branch" "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
+do
+ # We want to check that things work when both
+ # - all the tips passed are missing (case existing_tip = ""), and
+ # - there is one missing tip and one existing tip (case existing_tip = "HEAD")
+ for existing_tip in "" "HEAD"
+ do
+ for action in "allow-any" "print"
+ do
+ test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" '
+ # Before the object is made missing, we use rev-list to
+ # get the expected oids.
+ if test "$existing_tip" = "HEAD"
+ then
+ git rev-list --objects --no-object-names \
+ HEAD ^$missing_tip >expect.raw
+ else
+ >expect.raw
+ fi &&
+
+ # Blobs are shared by all commits, so even though a commit/tree
+ # might be skipped, its blob must be accounted for.
+ if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t"
+ then
+ echo $(git rev-parse HEAD:1.t) >>expect.raw &&
+ echo $(git rev-parse HEAD:2.t) >>expect.raw
+ fi &&
+
+ missing_oid="$(git rev-parse $missing_tip)" &&
+
+ if test "$missing_tip" = "annot_tag"
+ then
+ oid="$(git rev-parse $missing_tip^{commit})" &&
+ echo "$missing_oid" >>expect.raw
+ else
+ oid="$missing_oid"
+ fi &&
+
+ path=".git/objects/$(test_oid_to_path $oid)" &&
+
+ mv "$path" "$path.hidden" &&
+ test_when_finished "mv $path.hidden $path" &&
+
+ git rev-list --missing=$action --objects --no-object-names \
+ $missing_oid $existing_tip >actual.raw &&
+
+ # When the action is to print, we should also add the missing
+ # oid to the expect list.
+ case $action in
+ allow-any)
+ ;;
+ print)
+ grep ?$oid actual.raw &&
+ echo ?$oid >>expect.raw
+ ;;
+ esac &&
+
+ sort actual.raw >actual &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ '
+ done
+ done
+done
+
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 2a5b7d8379..cdc0270640 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -170,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 &&
@@ -872,7 +878,7 @@ test_expect_success 'broken branch creation' '
echo "" > expected.ok
cat > expected.missing-tree.default <<EOF
-fatal: unable to read tree $deleted
+fatal: unable to read tree ($deleted)
EOF
test_expect_success 'bisect fails if tree is broken on start commit' '
@@ -1176,7 +1182,7 @@ 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" &&
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index 52822b9461..43e1afd44c 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -670,7 +670,7 @@ test_expect_success 'rev-list W/ --missing=print' '
awk -f print_2.awk ls_files_result |
sort >expected &&
- for id in `cat expected | sed "s|..|&/|"`
+ for id in `sed "s|..|&/|" expected`
do
rm r1/.git/objects/$id || return 1
done &&
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 459f0d7412..a9656a1ec8 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -1,10 +1,11 @@
#!/bin/sh
test_description='rev-list combining bitmaps and filters'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bitmap.sh
-TEST_PASSES_SANITIZE_LEAK=true
test_expect_success 'set up bitmapped repo' '
# one commit will have bitmaps, the other will not
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
index 00b84ecba6..120dcd74a5 100755
--- a/t/t6135-pathspec-with-attrs.sh
+++ b/t/t6135-pathspec-with-attrs.sh
@@ -393,4 +393,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
test_cmp expect actual
'
+test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
+ >mode_exec_file_1 &&
+
+ git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
+ echo ?? mode_exec_file_1 >expect &&
+ test_cmp expect actual &&
+
+ git add mode_exec_file_1 &&
+ chmod +x mode_exec_file_1 &&
+ git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
+ echo AM mode_exec_file_1 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
+ >mode_1_regular &&
+ >mode_1_exec &&
+ chmod +x mode_1_exec &&
+ git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
+ echo ?? mode_1_exec >expect &&
+ test_cmp expect actual &&
+
+ git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
+ echo ?? mode_1_regular >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 54e2281259..eb6c8204e8 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -20,12 +20,13 @@ setdate_and_increment () {
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
-test_expect_success setup '
- test_oid_cache <<-EOF &&
- disklen sha1:138
- disklen sha256:154
- EOF
+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 '
# setup .mailmap
cat >.mailmap <<-EOF &&
A Thor <athor@example.com> A U Thor <author@example.com>
@@ -94,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
@@ -129,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)
@@ -203,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)
@@ -1356,6 +1356,52 @@ test_expect_success '--no-sort without subsequent --sort prints expected refs' '
test_cmp expected actual
'
+test_expect_success 'set up custom date sorting' '
+ # Dates:
+ # - Wed Feb 07 2024 21:34:20 +0000
+ # - Tue Dec 14 1999 00:05:22 +0000
+ # - Fri Jun 04 2021 11:26:51 +0000
+ # - Mon Jan 22 2007 16:44:01 GMT+0000
+ i=1 &&
+ for when in 1707341660 945129922 1622806011 1169484241
+ do
+ GIT_COMMITTER_DATE="@$when +0000" \
+ GIT_COMMITTER_EMAIL="user@example.com" \
+ git tag -m "tag $when" custom-dates-$i &&
+ i=$(($i+1)) || return 1
+ done
+'
+
+test_expect_success 'sort by date defaults to full timestamp' '
+ cat >expected <<-\EOF &&
+ 945129922 refs/tags/custom-dates-2
+ 1169484241 refs/tags/custom-dates-4
+ 1622806011 refs/tags/custom-dates-3
+ 1707341660 refs/tags/custom-dates-1
+ EOF
+
+ git for-each-ref \
+ --format="%(creatordate:unix) %(refname)" \
+ --sort=creatordate \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'sort by custom date format' '
+ cat >expected <<-\EOF &&
+ 00:05:22 refs/tags/custom-dates-2
+ 11:26:51 refs/tags/custom-dates-3
+ 16:44:01 refs/tags/custom-dates-4
+ 21:34:20 refs/tags/custom-dates-1
+ EOF
+
+ git for-each-ref \
+ --format="%(creatordate:format:%H:%M:%S) %(refname)" \
+ --sort="creatordate:format:%H:%M:%S" \
+ "refs/tags/custom-dates-*" >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
test_when_finished "git checkout main" &&
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 2667dd13fe..83b8a19d94 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -15,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" &&
@@ -25,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" &&
@@ -39,15 +39,14 @@ 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 &&
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 82f3d1ea0f..948f1bb5f4 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,6 +31,37 @@ test_expect_success 'setup some history and refs' '
git update-ref refs/odd/spot main
'
+test_expect_success '--include-root-refs pattern prints pseudorefs' '
+ cat >expect <<-\EOF &&
+ HEAD
+ ORIG_HEAD
+ refs/heads/main
+ refs/heads/side
+ refs/odd/spot
+ refs/tags/annotated-tag
+ refs/tags/doubly-annotated-tag
+ refs/tags/doubly-signed-tag
+ refs/tags/four
+ refs/tags/one
+ refs/tags/signed-tag
+ refs/tags/three
+ refs/tags/two
+ EOF
+ git update-ref ORIG_HEAD main &&
+ git for-each-ref --format="%(refname)" --include-root-refs >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--include-root-refs with other patterns' '
+ cat >expect <<-\EOF &&
+ HEAD
+ ORIG_HEAD
+ EOF
+ git update-ref ORIG_HEAD main &&
+ git for-each-ref --format="%(refname)" --include-root-refs "*HEAD" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'filtering with --points-at' '
cat >expect <<-\EOF &&
refs/heads/main
diff --git a/t/t6403-merge-file.sh b/t/t6403-merge-file.sh
index 2c92209eca..fb872c5a11 100755
--- a/t/t6403-merge-file.sh
+++ b/t/t6403-merge-file.sh
@@ -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' '
@@ -447,4 +507,66 @@ test_expect_success '--object-id fails without repository' '
grep "not a git repository" err
'
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
+ cat >expect.c <<-\EOF &&
+ int g(size_t u)
+ {
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+ }
+
+ int h(int x, int y, int z)
+ {
+ <<<<<<< ours.c
+ if (z == 0)
+ ||||||| base.c
+ while (u < 30)
+ =======
+ while (u > 34)
+ >>>>>>> theirs.c
+ {
+ <<<<<<< ours.c
+ return x;
+ ||||||| base.c
+ u++;
+ =======
+ u--;
+ >>>>>>> theirs.c
+ }
+ return y;
+ }
+ EOF
+
+ test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
+ test_cmp expect.c myers_output.c
+'
+
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
+ cat >expect.c <<-\EOF &&
+ int g(size_t u)
+ {
+ while (u > 34)
+ {
+ u--;
+ }
+ return u;
+ }
+
+ int h(int x, int y, int z)
+ {
+ if (z == 0)
+ {
+ return x;
+ }
+ return y;
+ }
+ EOF
+
+ git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
+ test_cmp expect.c histogram_output.c
+'
+
test_done
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 72f8c1722f..156a1efacf 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -42,11 +42,15 @@ test_expect_success setup '
#!/bin/sh
orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
+ orig_name="$6" our_name="$7" their_name="$8"
(
echo "orig is $orig"
echo "ours is $ours"
echo "theirs is $theirs"
echo "path is $path"
+ echo "orig_name is $orig_name"
+ echo "our_name is $our_name"
+ echo "their_name is $their_name"
echo "=== orig ==="
cat "$orig"
echo "=== ours ==="
@@ -121,7 +125,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -132,7 +136,8 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file main^:text) &&
a=$(git unpack-file side^:text) &&
b=$(git unpack-file main:text) &&
- sh -c "./custom-merge $o $a $b 0 text" &&
+ base_revid=$(git rev-parse --short main^) &&
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
rm -f $o $a $b
@@ -142,7 +147,7 @@ test_expect_success 'custom merge backend' '
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
+ merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
@@ -159,7 +164,8 @@ test_expect_success 'custom merge backend' '
o=$(git unpack-file main^:text) &&
a=$(git unpack-file anchor:text) &&
b=$(git unpack-file main:text) &&
- sh -c "./custom-merge $o $a $b 0 text" &&
+ base_revid=$(git rev-parse --short main^) &&
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
sed -e 1,3d $a >check-2 &&
cmp check-1 check-2 &&
sed -e 1,3d -e 4q $a >check-3 &&
@@ -173,7 +179,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
git reset --hard anchor &&
git config --replace-all \
- merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index b4f4a313f4..647ea1e838 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -34,14 +34,14 @@ test_expect_success setup '
test_expect_success 'Check "ours" is CRLF' '
git reset --hard initial &&
git merge side -s ours &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
test_expect_success 'Check that conflict file is CRLF' '
git reset --hard a &&
test_must_fail git merge side &&
- cat file | remove_cr | append_cr >file.temp &&
+ remove_cr <file | append_cr >file.temp &&
test_cmp file file.temp
'
diff --git a/t/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 41288a60ce..48a62cb855 100755
--- a/t/t6418-merge-text-auto.sh
+++ b/t/t6418-merge-text-auto.sh
@@ -15,6 +15,7 @@ test_description='CRLF merge conflict across text=auto change
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index d02fa16614..0f39ed0d08 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -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 &&
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 70650521b0..7a3f1cb27c 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -113,7 +113,7 @@ test_expect_success 'merging should conflict for non fast-forward' '
git checkout -b test-nonforward-a b &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual &&
+ test_must_fail git merge c 2>actual &&
sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
grep "$sub_expect" actual
else
@@ -154,9 +154,9 @@ test_expect_success 'merging should conflict for non fast-forward (resolution ex
git rev-parse --short sub-d > ../expect) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual &&
+ test_must_fail git merge c >actual 2>sub-actual &&
sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
- grep "$sub_expect" actual
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -181,9 +181,9 @@ test_expect_success 'merging should fail for ambiguous common parent' '
) &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
- test_must_fail git merge c >actual &&
+ test_must_fail git merge c >actual 2>sub-actual &&
sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
- grep "$sub_expect" actual
+ grep "$sub_expect" sub-actual
else
test_must_fail git merge c 2> actual
fi &&
@@ -227,7 +227,7 @@ test_expect_success 'merging should fail for changes that are backwards' '
git commit -a -m "f" &&
git checkout -b test-backward e &&
- test_must_fail git merge f >actual &&
+ test_must_fail git merge f 2>actual &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
@@ -535,7 +535,7 @@ test_expect_success 'merging should fail with no merge base' '
git checkout -b b init &&
git add sub &&
git commit -m "b" &&
- test_must_fail git merge a >actual &&
+ test_must_fail git merge a 2>actual &&
if test "$GIT_TEST_MERGE_ALGORITHM" = ort
then
sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 18fe1c25e6..43d40175f8 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -11,23 +11,7 @@ test_expect_success 'setup' '
# behavior, make sure we always pack everything to one pack by
# default
git config gc.bigPackThreshold 2g &&
-
- # These are simply values which, when hashed as a blob with a newline,
- # produce a hash where the first byte is 0x17 in their respective
- # algorithms.
- test_oid_cache <<-EOF
- obj1 sha1:263
- obj1 sha256:34
-
- obj2 sha1:410
- obj2 sha256:174
-
- obj3 sha1:523
- obj3 sha256:313
-
- obj4 sha1:790
- obj4 sha256:481
- EOF
+ test_oid_init
'
test_expect_success 'gc empty repository' '
@@ -114,8 +98,8 @@ test_expect_success 'pre-auto-gc hook can stop auto gc' '
# We need to create two object whose sha1s start with 17
# since this is what git gc counts. As it happens, these
# two blobs will do so.
- test_commit "$(test_oid obj1)" &&
- test_commit "$(test_oid obj2)" &&
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
git gc --auto >../out.actual 2>../err.actual
) &&
@@ -146,13 +130,13 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
# We need to create two object whose sha1s start with 17
# since this is what git gc counts. As it happens, these
# two blobs will do so.
- test_commit "$(test_oid obj1)" &&
- test_commit "$(test_oid obj2)" &&
+ test_commit "$(test_oid blob17_1)" &&
+ test_commit "$(test_oid blob17_2)" &&
# Our first gc will create a pack; our second will create a second pack
git gc --auto &&
ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
- test_commit "$(test_oid obj3)" &&
- test_commit "$(test_oid obj4)" &&
+ test_commit "$(test_oid blob17_3)" &&
+ test_commit "$(test_oid blob17_4)" &&
git gc --auto 2>err &&
test_grep ! "^warning:" err &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index f6aebe92ff..5ab4d41ee7 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -396,10 +396,7 @@ test_expect_success '--prune-empty is able to prune entire branch' '
git branch prune-entire B &&
git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
test_must_fail git rev-parse refs/heads/prune-entire &&
- if test_have_prereq REFFILES
- then
- test_must_fail git reflog exists refs/heads/prune-entire
- fi
+ test_must_fail git reflog exists refs/heads/prune-entire
'
test_expect_success '--remap-to-ancestor with filename filters' '
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index b41a47eb94..696866d779 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1777,10 +1777,10 @@ test_expect_success '--points-at finds annotated tags of tags' '
'
test_expect_success 'recursive tagging should give advice' '
- sed -e "s/|$//" <<-EOF >expect &&
+ cat >expect <<-EOF &&
hint: You have created a nested tag. The object referred to by your new tag is
hint: already a tag. If you meant to tag the object that it points to, use:
- hint: |
+ hint:
hint: git tag -f nested annotated-v4.0^{}
hint: Disable this message with "git config advice.nestedTag false"
EOF
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 4287863ae6..62d9f846ce 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -616,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
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
index 05079c7246..f4f3b7a677 100755
--- a/t/t7105-reset-patch.sh
+++ b/t/t7105-reset-patch.sh
@@ -5,7 +5,7 @@ test_description='git reset --patch'
TEST_PASSES_SANITIZE_LEAK=true
. ./lib-patch-mode.sh
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
mkdir dir &&
echo parent > dir/foo &&
echo dummy > bar &&
@@ -19,42 +19,46 @@ test_expect_success PERL 'setup' '
# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
set_and_save_state dir/foo work work &&
test_write_lines n n | git reset -p &&
verify_saved_state dir/foo &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p' '
- test_write_lines n y | git reset -p >output &&
- verify_state dir/foo work head &&
- verify_saved_state bar &&
- test_grep "Unstage" output
-'
-
-test_expect_success PERL 'git reset -p HEAD^' '
+for opt in "HEAD" "@" ""
+do
+ test_expect_success "git reset -p $opt" '
+ set_and_save_state dir/foo work work &&
+ test_write_lines n y | git reset -p $opt >output &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar &&
+ test_grep "Unstage" output
+ '
+done
+
+test_expect_success 'git reset -p HEAD^' '
test_write_lines n y | git reset -p HEAD^ >output &&
verify_state dir/foo work parent &&
verify_saved_state bar &&
test_grep "Apply" output
'
-test_expect_success PERL 'git reset -p HEAD^^{tree}' '
+test_expect_success 'git reset -p HEAD^^{tree}' '
test_write_lines n y | git reset -p HEAD^^{tree} >output &&
verify_state dir/foo work parent &&
verify_saved_state bar &&
test_grep "Apply" output
'
-test_expect_success PERL 'git reset -p HEAD^:dir/foo (blob fails)' '
+test_expect_success 'git reset -p HEAD^:dir/foo (blob fails)' '
set_and_save_state dir/foo work work &&
test_must_fail git reset -p HEAD^:dir/foo &&
verify_saved_state dir/foo &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
+test_expect_success 'git reset -p aaaaaaaa (unknown fails)' '
set_and_save_state dir/foo work work &&
test_must_fail git reset -p aaaaaaaa &&
verify_saved_state dir/foo &&
@@ -66,27 +70,27 @@ test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
# the failure case (and thus get out of the loop).
-test_expect_success PERL 'git reset -p dir' '
+test_expect_success 'git reset -p dir' '
set_state dir/foo work work &&
test_write_lines y n | git reset -p dir &&
verify_state dir/foo work head &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p -- foo (inside dir)' '
+test_expect_success 'git reset -p -- foo (inside dir)' '
set_state dir/foo work work &&
test_write_lines y n | (cd dir && git reset -p -- foo) &&
verify_state dir/foo work head &&
verify_saved_state bar
'
-test_expect_success PERL 'git reset -p HEAD^ -- dir' '
+test_expect_success 'git reset -p HEAD^ -- dir' '
test_write_lines y n | git reset -p HEAD^ -- dir &&
verify_state dir/foo work parent &&
verify_saved_state bar
'
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
verify_saved_head
'
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index d20e5709f9..88d1c8adf4 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -34,7 +34,7 @@ test_expect_success 'reset $file' '
test_cmp expect actual
'
-test_expect_success PERL 'reset -p' '
+test_expect_success 'reset -p' '
rm .git/index &&
git add a &&
echo y >yes &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 10cc6c4605..42352dc0db 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -631,6 +631,72 @@ test_expect_success 'checkout --conflict=diff3' '
test_cmp merged file
'
+test_expect_success 'checkout --conflict=diff3 --no-conflict does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-conflict -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --conflict=diff3 --no-merge does not merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >expect &&
+ cat expect >fild &&
+ cat expect >file &&
+ test_must_fail git checkout --conflict=diff3 --no-merge -- fild file 2>err &&
+ test_cmp expect file &&
+ test_cmp expect fild &&
+ echo "error: path ${SQ}file${SQ} is unmerged" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'checkout --no-merge --conflict=diff3 does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --no-merge --conflict=diff3 -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ ||||||| base
+ original
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 --no-conflict does merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >fild &&
+ echo "none of the above" >file &&
+ git checkout --merge --conflict=diff3 --no-conflict -- fild file &&
+ echo "ourside" >expect &&
+ test_cmp expect fild &&
+ cat >expect <<-\EOF &&
+ <<<<<<< ours
+ ourside
+ =======
+ theirside
+ >>>>>>> theirs
+ EOF
+ test_cmp expect file
+'
+
+test_expect_success 'checkout with invalid conflict style' '
+ test_must_fail git checkout --conflict=bad 2>actual -- file &&
+ echo "error: unknown conflict style ${SQ}bad${SQ}" >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'failing checkout -b should not break working tree' '
git clean -fd && # Remove untracked files in the way
git reset --hard main &&
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 611b3dd3ae..0aae0dee67 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -5,6 +5,7 @@
test_description='git clean basic tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
git config clean.requireForce no
@@ -407,6 +408,12 @@ test_expect_success 'clean.requireForce and -f' '
'
+test_expect_success 'clean.requireForce and --interactive' '
+ git clean --interactive </dev/null >output 2>error &&
+ test_grep ! "requireForce is true and" error &&
+ test_grep "\*\*\* Commands \*\*\*" output
+'
+
test_expect_success 'core.excludesfile' '
echo excludes >excludes &&
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
index d82a3210a1..4afe53c66a 100755
--- a/t/t7301-clean-interactive.sh
+++ b/t/t7301-clean-interactive.sh
@@ -25,18 +25,18 @@ test_expect_success 'git clean -i (c: clean hotkey)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo c | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -46,18 +46,18 @@ test_expect_success 'git clean -i (cl: clean prefix)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo cl | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -67,18 +67,18 @@ test_expect_success 'git clean -i (quit)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo quit | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -88,18 +88,18 @@ test_expect_success 'git clean -i (Ctrl+D)' '
touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
docs/manual.txt obj.o build/lib.so &&
echo "\04" | git clean -i &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -110,18 +110,18 @@ test_expect_success 'git clean -id (filter all)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "*" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -132,18 +132,18 @@ test_expect_success 'git clean -id (filter patterns)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "part3.* *.out" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -154,18 +154,18 @@ test_expect_success 'git clean -id (filter patterns 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines f "* !*.out" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -176,18 +176,18 @@ test_expect_success 'git clean -id (select - all)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "*" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -198,18 +198,18 @@ test_expect_success 'git clean -id (select - none)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -220,18 +220,18 @@ test_expect_success 'git clean -id (select - number)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s 3 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -242,18 +242,18 @@ test_expect_success 'git clean -id (select - number 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "2 3" 5 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -264,18 +264,18 @@ test_expect_success 'git clean -id (select - number 3)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "3,4 5" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -285,11 +285,11 @@ test_expect_success 'git clean -id (select - filenames)' '
touch a.out foo.txt bar.txt baz.txt &&
test_write_lines s "a.out fo ba bar" "" c |
git clean -id &&
- test -f Makefile &&
- test ! -f a.out &&
- test ! -f foo.txt &&
- test ! -f bar.txt &&
- test -f baz.txt &&
+ test_path_is_file Makefile &&
+ test_path_is_missing a.out &&
+ test_path_is_missing foo.txt &&
+ test_path_is_missing bar.txt &&
+ test_path_is_file baz.txt &&
rm baz.txt
'
@@ -301,18 +301,18 @@ test_expect_success 'git clean -id (select - range)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "1,3-4" 2 "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test ! -f docs/manual.txt &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -323,18 +323,18 @@ test_expect_success 'git clean -id (select - range 2)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "4- 1" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test ! -f src/part4.c &&
- test ! -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_missing src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -345,18 +345,18 @@ test_expect_success 'git clean -id (inverse select)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines s "*" "-5- 1 -2" "" c |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -367,18 +367,18 @@ test_expect_success 'git clean -id (ask)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines a Y y no yes bad "" |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -389,18 +389,18 @@ test_expect_success 'git clean -id (ask - Ctrl+D)' '
docs/manual.txt obj.o build/lib.so &&
test_write_lines a Y no yes "\04" |
git clean -id &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -412,18 +412,18 @@ test_expect_success 'git clean -id with prefix and path (filter)' '
(cd build/ &&
test_write_lines f docs "*.h" "" c |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_file docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -435,18 +435,18 @@ test_expect_success 'git clean -id with prefix and path (select by name)' '
(cd build/ &&
test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test -f a.out &&
- test ! -f docs/manual.txt &&
- test ! -f src/part3.c &&
- test -f src/part3.h &&
- test ! -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_file a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_missing src/part3.c &&
+ test_path_is_file src/part3.h &&
+ test_path_is_missing src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
@@ -458,18 +458,18 @@ test_expect_success 'git clean -id with prefix and path (ask)' '
(cd build/ &&
test_write_lines a Y y no yes bad "" |
git clean -id ..) &&
- test -f Makefile &&
- test -f README &&
- test -f src/part1.c &&
- test -f src/part2.c &&
- test ! -f a.out &&
- test ! -f docs/manual.txt &&
- test -f src/part3.c &&
- test ! -f src/part3.h &&
- test -f src/part4.c &&
- test -f src/part4.h &&
- test -f obj.o &&
- test -f build/lib.so
+ test_path_is_file Makefile &&
+ test_path_is_file README &&
+ test_path_is_file src/part1.c &&
+ test_path_is_file src/part2.c &&
+ test_path_is_missing a.out &&
+ test_path_is_missing docs/manual.txt &&
+ test_path_is_file src/part3.c &&
+ test_path_is_missing src/part3.h &&
+ test_path_is_file src/part4.c &&
+ test_path_is_file src/part4.h &&
+ test_path_is_file obj.o &&
+ test_path_is_file build/lib.so
'
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 00c1f1aab1..5c4a89df5c 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -212,8 +212,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
The following paths are ignored by one of your .gitignore files:
submod
hint: Use -f if you really want to add them.
- hint: Turn this message off by running
- hint: "git config advice.addIgnoredFile false"
+ hint: Disable this message with "git config advice.addIgnoredFile false"
EOF
# Does not use test_commit due to the ignore
echo "*" > .gitignore &&
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index 2b3c363078..aa2fdc31d1 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -116,7 +116,7 @@ test_expect_success 'rebasing submodule that should conflict' '
test_tick &&
git commit -m fourth &&
- test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 >actual_output &&
+ test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 2>actual_output &&
git ls-files -s submodule >actual &&
(
cd submodule &&
diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh
index 35a31acd4d..46d4fb0354 100755
--- a/t/t7450-bad-git-dotfiles.sh
+++ b/t/t7450-bad-git-dotfiles.sh
@@ -45,6 +45,32 @@ test_expect_success 'check names' '
test_cmp expect actual
'
+test_expect_success 'check urls' '
+ cat >expect <<-\EOF &&
+ ./bar/baz/foo.git
+ https://example.com/foo.git
+ http://example.com:80/deeper/foo.git
+ EOF
+
+ test-tool submodule check-url >actual <<-\EOF &&
+ ./bar/baz/foo.git
+ https://example.com/foo.git
+ http://example.com:80/deeper/foo.git
+ -a./foo
+ ../../..//test/foo.git
+ ../../../../../:localhost:8080/foo.git
+ ..\../.\../:example.com/foo.git
+ ./%0ahost=example.com/foo.git
+ https://one.example.com/evil?%0ahost=two.example.com
+ https:///example.com/foo.git
+ http://example.com:test/foo.git
+ https::example.com/foo.git
+ http:::example.com/foo.git
+ EOF
+
+ test_cmp expect actual
+'
+
test_expect_success 'create innocent subrepo' '
git init innocent &&
git -C innocent commit --allow-empty -m foo
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index 3d8500a52e..cc12f99f11 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -3,8 +3,7 @@
# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
#
-# FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff
+# FIXME: Test the various index usages, test reflog
test_description='git commit'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
@@ -92,6 +91,20 @@ test_expect_success '--long fails with nothing to commit' '
test_must_fail git commit -m initial --long
'
+test_expect_success 'fail to commit untracked file (even with --include/--only)' '
+ echo content >baz &&
+ error="error: pathspec .baz. did not match any file(s) known to git" &&
+
+ test_must_fail git commit -m "baz" baz 2>err &&
+ test_grep -e "$error" err &&
+
+ test_must_fail git commit --only -m "baz" baz 2>err &&
+ test_grep -e "$error" err &&
+
+ test_must_fail git commit --include -m "baz" baz 2>err &&
+ test_grep -e "$error" err
+'
+
test_expect_success 'setup: non-initial commit' '
echo bongo bongo bongo >file &&
git commit -m next -a
@@ -117,6 +130,51 @@ test_expect_success '--long with stuff to commit returns ok' '
git commit -m next -a --long
'
+for opt in "" "-o" "--only"
+do
+ test_expect_success 'exclude additional staged changes when given pathspec' '
+ echo content >>file &&
+ echo content >>baz &&
+ git add baz &&
+ git commit $opt -m "file" file &&
+
+ git diff --name-only >actual &&
+ test_must_be_empty actual &&
+
+ test_write_lines baz >expect &&
+ git diff --name-only --cached >actual &&
+ test_cmp expect actual &&
+
+ test_write_lines file >expect &&
+ git diff --name-only HEAD^ HEAD >actual &&
+ test_cmp expect actual
+ '
+done
+
+test_expect_success '-i/--include includes staged changes' '
+ echo content >>file &&
+ echo content >>baz &&
+ git add file &&
+
+ # baz is in the index, therefore, it will be committed
+ git commit --include -m "file and baz" baz &&
+
+ git diff --name-only HEAD >remaining &&
+ test_must_be_empty remaining &&
+
+ test_write_lines baz file >expect &&
+ git diff --name-only HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--include and --only do not mix' '
+ test_when_finished "git reset --hard" &&
+ echo content >>file &&
+ echo content >>baz &&
+ test_must_fail git commit --include --only -m "file baz" file baz 2>actual &&
+ test_grep -e "fatal: options .-i/--include. and .-o/--only. cannot be used together" actual
+'
+
test_expect_success 'commit message from non-existing file' '
echo more bongo: bongo bongo bongo bongo >file &&
test_must_fail git commit -F gah -a
@@ -389,6 +447,28 @@ test_expect_success 'amend commit to fix date' '
'
+test_expect_success 'amend commit to add signoff' '
+
+ test_commit "msg" file content &&
+ git commit --amend --signoff &&
+ test_commit_message HEAD <<-EOF
+ msg
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+'
+
+test_expect_success 'amend does not add signoff if it already exists' '
+
+ test_commit --signoff "tenor" file newcontent &&
+ git commit --amend --signoff &&
+ test_commit_message HEAD <<-EOF
+ tenor
+
+ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+ EOF
+'
+
test_expect_success 'commit mentions forced date in output' '
git commit --amend --date=2010-01-02T03:04:05 >output &&
grep "Date: *Sat Jan 2 03:04:05 2010" output
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 61c8e810cc..b37e2018a7 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -485,6 +485,24 @@ test_expect_success 'commit --trailer not confused by --- separator' '
test_cmp expected actual
'
+test_expect_success 'commit --trailer with --verbose' '
+ cat >msg <<-\EOF &&
+ subject
+
+ body
+ EOF
+ GIT_EDITOR=: git commit --edit -F msg --allow-empty \
+ --trailer="my-trailer: value" --verbose &&
+ {
+ cat msg &&
+ echo &&
+ echo "my-trailer: value"
+ } >expected &&
+ git cat-file commit HEAD >commit.msg &&
+ sed -e "1,/^\$/d" commit.msg >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'multiple -m' '
>negative &&
@@ -718,6 +736,11 @@ test_expect_success 'message shows date when it is explicitly set' '
.git/COMMIT_EDITMSG
'
+test_expect_success 'message does not have multiple scissors lines' '
+ git commit --cleanup=scissors -v --allow-empty -e -m foo &&
+ test $(grep -c -e "--- >8 ---" .git/COMMIT_EDITMSG) -eq 1
+'
+
test_expect_success AUTOIDENT 'message shows committer when it is automatic' '
echo >>negative &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index c3281b192e..4c7db19ce7 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -101,6 +101,16 @@ test_expect_success 'verbose diff is stripped out with set core.commentChar' '
test_grep "Aborting commit due to empty commit message." err
'
+test_expect_success 'verbose diff is stripped with multi-byte comment char' '
+ (
+ GIT_EDITOR=cat &&
+ export GIT_EDITOR &&
+ test_must_fail git -c core.commentchar="foo>" commit -a -v >out 2>err
+ ) &&
+ grep "^foo> " out &&
+ test_grep "Aborting commit due to empty commit message." err
+'
+
test_expect_success 'status does not verbose without --verbose' '
git status >actual &&
! grep "^diff --git" actual
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a3c18a4fc2..773383fefb 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -419,14 +419,19 @@ Changes not staged for commit:
Untracked files not listed (use -u option to show untracked files)
EOF
git status -uno >output &&
+ test_cmp expect output &&
+ git status -ufalse >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles no)' '
- test_config status.showuntrackedfiles no &&
- git status >output &&
- test_cmp expect output
-'
+for no in no false 0
+do
+ test_expect_success "status (status.showUntrackedFiles $no)" '
+ test_config status.showuntrackedfiles "$no" &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
test_expect_success 'status -uno (advice.statusHints false)' '
cat >expect <<EOF &&
@@ -488,14 +493,21 @@ Untracked files:
EOF
git status -unormal >output &&
+ test_cmp expect output &&
+ git status -utrue >output &&
+ test_cmp expect output &&
+ git status -uyes >output &&
test_cmp expect output
'
-test_expect_success 'status (status.showUntrackedFiles normal)' '
- test_config status.showuntrackedfiles normal &&
- git status >output &&
- test_cmp expect output
-'
+for normal in normal true 1
+do
+ test_expect_success "status (status.showUntrackedFiles $normal)" '
+ test_config status.showuntrackedfiles $normal &&
+ git status >output &&
+ test_cmp expect output
+ '
+done
cat >expect <<EOF
M dir1/modified
@@ -1403,7 +1415,9 @@ test_expect_success "status (core.commentchar with submodule summary)" '
test_expect_success "status (core.commentchar with two chars with submodule summary)" '
test_config core.commentchar ";;" &&
- test_must_fail git -c status.displayCommentPrefix=true status
+ sed "s/^/;/" <expect >expect.double &&
+ git -c status.displayCommentPrefix=true status >output &&
+ test_cmp expect.double output
'
test_expect_success "--ignore-submodules=all suppresses submodule summary" '
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index c2ab8a444a..802f8f704c 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -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 &&
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 832aff0616..3d3e13ccf8 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -1916,4 +1916,37 @@ test_expect_success 'suppress --- handling' '
test_cmp expected actual
'
+test_expect_success 'suppressing --- does not disable cut-line handling' '
+ echo "real-trailer: before the cut" >expected &&
+
+ git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+ subject
+
+ This input has a cut-line in it; we should stop parsing when we see it
+ and consider only trailers before that line.
+
+ real-trailer: before the cut
+
+ # ------------------------ >8 ------------------------
+ # Nothing below this line counts as part of the commit message.
+ not-a-trailer: too late
+ EOF
+
+ test_cmp expected actual
+'
+
+test_expect_success 'handling of --- lines in conjunction with cut-lines' '
+ echo "my-trailer: here" >expected &&
+
+ git interpret-trailers --parse >actual <<-\EOF &&
+ subject
+
+ my-trailer: here
+ ---
+ # ------------------------ >8 ------------------------
+ EOF
+
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t7514-commit-patch.sh b/t/t7514-commit-patch.sh
index 998a2103c7..b4de10a5dd 100755
--- a/t/t7514-commit-patch.sh
+++ b/t/t7514-commit-patch.sh
@@ -3,12 +3,6 @@
test_description='hunk edit with "commit -p -m"'
. ./test-lib.sh
-if ! test_have_prereq PERL
-then
- skip_all="skipping '$test_description' tests, perl not available"
- test_done
-fi
-
test_expect_success 'setup (initial)' '
echo line1 >file &&
git add file &&
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 78503158fd..730f3c7f81 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -978,7 +978,7 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
mkdir test_unicode/nfd &&
mkdir test_unicode/nfd/d_${utf8_nfd} &&
- git -C test_unicode fsmonitor--daemon stop &&
+ test-tool -C test_unicode fsmonitor-client query --token 0 &&
if test_have_prereq UNICODE_NFC_PRESERVED
then
@@ -1037,4 +1037,227 @@ test_expect_success 'split-index and FSMonitor work well together' '
)
'
+# The FSMonitor daemon reports the OBSERVED pathname of modified files
+# and thus contains the OBSERVED spelling on case-insensitive file
+# systems. The daemon does not (and should not) load the .git/index
+# file and therefore does not know the expected case-spelling. Since
+# it is possible for the user to create files/subdirectories with the
+# incorrect case, a modified file event for a tracked will not have
+# the EXPECTED case. This can cause `index_name_pos()` to incorrectly
+# report that the file is untracked. This causes the client to fail to
+# mark the file as possibly dirty (keeping the CE_FSMONITOR_VALID bit
+# set) so that `git status` will avoid inspecting it and thus not
+# present in the status output.
+#
+# The setup is a little contrived.
+#
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor subdir case wrong on disk' '
+ test_when_finished "stop_daemon_delete_repo subdir_case_wrong" &&
+
+ git init subdir_case_wrong &&
+ (
+ cd subdir_case_wrong &&
+ echo x >AAA &&
+ echo x >BBB &&
+
+ mkdir dir1 &&
+ echo x >dir1/file1 &&
+ mkdir dir1/dir2 &&
+ echo x >dir1/dir2/file2 &&
+ mkdir dir1/dir2/dir3 &&
+ echo x >dir1/dir2/dir3/file3 &&
+
+ echo x >yyy &&
+ echo x >zzz &&
+ git add . &&
+ git commit -m "data" &&
+
+ # This will cause "dir1/" and everything under it
+ # to be deleted.
+ git sparse-checkout set --cone --sparse-index &&
+
+ # Create dir2 with the wrong case and then let Git
+ # repopulate dir3 -- it will not correct the spelling
+ # of dir2.
+ mkdir dir1 &&
+ mkdir dir1/DIR2 &&
+ git sparse-checkout add dir1/dir2/dir3
+ ) &&
+
+ start_daemon -C subdir_case_wrong --tf "$PWD/subdir_case_wrong.trace" &&
+
+ # Enable FSMonitor in the client. Run enough commands for
+ # the .git/index to sync up with the daemon with everything
+ # marked clean.
+ git -C subdir_case_wrong config core.fsmonitor true &&
+ git -C subdir_case_wrong update-index --fsmonitor &&
+ git -C subdir_case_wrong status &&
+
+ # Make some files dirty so that FSMonitor gets FSEvents for
+ # each of them.
+ echo xx >>subdir_case_wrong/AAA &&
+ echo xx >>subdir_case_wrong/dir1/DIR2/dir3/file3 &&
+ echo xx >>subdir_case_wrong/zzz &&
+
+ GIT_TRACE_FSMONITOR="$PWD/subdir_case_wrong.log" \
+ git -C subdir_case_wrong --no-optional-locks status --short \
+ >"$PWD/subdir_case_wrong.out" &&
+
+ # "git status" should have gotten file events for each of
+ # the 3 files.
+ #
+ # "dir2" should be in the observed case on disk.
+ grep "fsmonitor_refresh_callback" \
+ <"$PWD/subdir_case_wrong.log" \
+ >"$PWD/subdir_case_wrong.log1" &&
+
+ grep -q "AAA.*pos 0" "$PWD/subdir_case_wrong.log1" &&
+ grep -q "zzz.*pos 6" "$PWD/subdir_case_wrong.log1" &&
+
+ grep -q "dir1/DIR2/dir3/file3.*pos -3" "$PWD/subdir_case_wrong.log1" &&
+
+ # Verify that we get a mapping event to correct the case.
+ grep -q "MAP:.*dir1/DIR2/dir3/file3.*dir1/dir2/dir3/file3" \
+ "$PWD/subdir_case_wrong.log1" &&
+
+ # The refresh-callbacks should have caused "git status" to clear
+ # the CE_FSMONITOR_VALID bit on each of those files and caused
+ # the worktree scan to visit them and mark them as modified.
+ grep -q " M AAA" "$PWD/subdir_case_wrong.out" &&
+ grep -q " M zzz" "$PWD/subdir_case_wrong.out" &&
+ grep -q " M dir1/dir2/dir3/file3" "$PWD/subdir_case_wrong.out"
+'
+
+test_expect_success CASE_INSENSITIVE_FS 'fsmonitor file case wrong on disk' '
+ test_when_finished "stop_daemon_delete_repo file_case_wrong" &&
+
+ git init file_case_wrong &&
+ (
+ cd file_case_wrong &&
+ echo x >AAA &&
+ echo x >BBB &&
+
+ mkdir dir1 &&
+ mkdir dir1/dir2 &&
+ mkdir dir1/dir2/dir3 &&
+ echo x >dir1/dir2/dir3/FILE-3-B &&
+ echo x >dir1/dir2/dir3/XXXX-3-X &&
+ echo x >dir1/dir2/dir3/file-3-a &&
+ echo x >dir1/dir2/dir3/yyyy-3-y &&
+ mkdir dir1/dir2/dir4 &&
+ echo x >dir1/dir2/dir4/FILE-4-A &&
+ echo x >dir1/dir2/dir4/XXXX-4-X &&
+ echo x >dir1/dir2/dir4/file-4-b &&
+ echo x >dir1/dir2/dir4/yyyy-4-y &&
+
+ echo x >yyy &&
+ echo x >zzz &&
+ git add . &&
+ git commit -m "data"
+ ) &&
+
+ start_daemon -C file_case_wrong --tf "$PWD/file_case_wrong.trace" &&
+
+ # Enable FSMonitor in the client. Run enough commands for
+ # the .git/index to sync up with the daemon with everything
+ # marked clean.
+ git -C file_case_wrong config core.fsmonitor true &&
+ git -C file_case_wrong update-index --fsmonitor &&
+ git -C file_case_wrong status &&
+
+ # Make some files dirty so that FSMonitor gets FSEvents for
+ # each of them.
+ echo xx >>file_case_wrong/AAA &&
+ echo xx >>file_case_wrong/zzz &&
+
+ # Rename some files so that FSMonitor sees a create and delete
+ # FSEvent for each. (A simple "mv foo FOO" is not portable
+ # between macOS and Windows. It works on both platforms, but makes
+ # the test messy, since (1) one platform updates "ctime" on the
+ # moved file and one does not and (2) it causes a directory event
+ # on one platform and not on the other which causes additional
+ # scanning during "git status" which causes a "H" vs "h" discrepancy
+ # in "git ls-files -f".) So old-school it and move it out of the
+ # way and copy it to the case-incorrect name so that we get fresh
+ # "ctime" and "mtime" values.
+
+ mv file_case_wrong/dir1/dir2/dir3/file-3-a file_case_wrong/dir1/dir2/dir3/ORIG &&
+ cp file_case_wrong/dir1/dir2/dir3/ORIG file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+ rm file_case_wrong/dir1/dir2/dir3/ORIG &&
+ mv file_case_wrong/dir1/dir2/dir4/FILE-4-A file_case_wrong/dir1/dir2/dir4/ORIG &&
+ cp file_case_wrong/dir1/dir2/dir4/ORIG file_case_wrong/dir1/dir2/dir4/file-4-a &&
+ rm file_case_wrong/dir1/dir2/dir4/ORIG &&
+
+ # Run status enough times to fully sync.
+ #
+ # The first instance should get the create and delete FSEvents
+ # for each pair. Status should update the index with a new FSM
+ # token (so the next invocation will not see data for these
+ # events).
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try1.log" \
+ git -C file_case_wrong status --short \
+ >"$PWD/file_case_wrong-try1.out" &&
+ grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-3-a.*pos 4" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos 6" "$PWD/file_case_wrong-try1.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try1.log" &&
+
+ # FSM refresh will have invalidated the FSM bit and cause a regular
+ # (real) scan of these tracked files, so they should have "H" status.
+ # (We will not see a "h" status until the next refresh (on the next
+ # command).)
+
+ git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf1.out" &&
+ grep -q "H dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf1.out" &&
+ grep -q "H dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf1.out" &&
+
+
+ # Try the status again. We assume that the above status command
+ # advanced the token so that the next one will not see those events.
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try2.log" \
+ git -C file_case_wrong status --short \
+ >"$PWD/file_case_wrong-try2.out" &&
+ ! grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*file-3-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*FILE-4-A.*pos" "$PWD/file_case_wrong-try2.log" &&
+ ! grep -q "fsmonitor_refresh_callback.*file-4-a.*pos" "$PWD/file_case_wrong-try2.log" &&
+
+ # FSM refresh saw nothing, so it will mark all files as valid,
+ # so they should now have "h" status.
+
+ git -C file_case_wrong ls-files -f >"$PWD/file_case_wrong-lsf2.out" &&
+ grep -q "h dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-lsf2.out" &&
+ grep -q "h dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-lsf2.out" &&
+
+
+ # We now have files with clean content, but with case-incorrect
+ # file names. Modify them to see if status properly reports
+ # them.
+
+ echo xx >>file_case_wrong/dir1/dir2/dir3/FILE-3-A &&
+ echo xx >>file_case_wrong/dir1/dir2/dir4/file-4-a &&
+
+ GIT_TRACE_FSMONITOR="$PWD/file_case_wrong-try3.log" \
+ git -C file_case_wrong --no-optional-locks status --short \
+ >"$PWD/file_case_wrong-try3.out" &&
+
+ # Verify that we get a mapping event to correct the case.
+ grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir3/FILE-3-A.*dir1/dir2/dir3/file-3-a" \
+ "$PWD/file_case_wrong-try3.log" &&
+ grep -q "fsmonitor_refresh_callback MAP:.*dir1/dir2/dir4/file-4-a.*dir1/dir2/dir4/FILE-4-A" \
+ "$PWD/file_case_wrong-try3.log" &&
+
+ # FSEvents are in observed case.
+ grep -q "fsmonitor_refresh_callback.*FILE-3-A.*pos -3" "$PWD/file_case_wrong-try3.log" &&
+ grep -q "fsmonitor_refresh_callback.*file-4-a.*pos -9" "$PWD/file_case_wrong-try3.log" &&
+
+ # The refresh-callbacks should have caused "git status" to clear
+ # the CE_FSMONITOR_VALID bit on each of those files and caused
+ # the worktree scan to visit them and mark them as modified.
+ grep -q " M dir1/dir2/dir3/file-3-a" "$PWD/file_case_wrong-try3.out" &&
+ grep -q " M dir1/dir2/dir4/FILE-4-A" "$PWD/file_case_wrong-try3.out"
+'
+
test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index d2975e6c93..127efe99f8 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -271,7 +271,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o
ls .git/objects/pack/*.pack >before-pack-dir &&
test_must_fail git fsck &&
- test_must_fail git repack --cruft -d 2>err &&
+ 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
@@ -629,6 +629,7 @@ test_expect_success '--write-midx with preferred bitmap tips' '
git log --format="create refs/tags/%s/%s %H" HEAD >refs &&
git update-ref --stdin <refs &&
+ GIT_TEST_MULTI_PACK_INDEX=0 \
git repack --write-midx --write-bitmap-index &&
test_path_is_file $midx &&
test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
@@ -749,6 +750,7 @@ test_expect_success '--write-midx with --pack-kept-objects' '
keep="$objdir/pack/pack-$one.keep" &&
touch "$keep" &&
+ GIT_TEST_MULTI_PACK_INDEX=0 \
git repack --write-midx --write-bitmap-index --geometric=2 -d \
--pack-kept-objects &&
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index be3735dff0..71e1ef3a10 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -48,7 +48,7 @@ test_expect_success '--expire-to stores pruned objects (now)' '
# ...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 &&
+ sort expired.objects remaining.objects >actual &&
test_cmp expect actual &&
# The "moved" objects (i.e., those in expired.git)
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 6a36be1e63..cc917b257e 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -91,58 +91,67 @@ test_expect_success 'difftool forwards arguments to diff' '
rm for-diff
'
-test_expect_success 'difftool ignores exit code' '
- test_config difftool.error.cmd false &&
- git difftool -y -t error branch
-'
-
-test_expect_success 'difftool forwards exit code with --trust-exit-code' '
- test_config difftool.error.cmd false &&
- test_must_fail git difftool -y --trust-exit-code -t error branch
-'
-
-test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' '
- test_config difftool.vimdiff.path false &&
- test_must_fail git difftool -y --trust-exit-code -t vimdiff branch
-'
-
-test_expect_success 'difftool honors difftool.trustExitCode = true' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode true &&
- test_must_fail git difftool -y -t error branch
-'
-
-test_expect_success 'difftool honors difftool.trustExitCode = false' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode false &&
- git difftool -y -t error branch
-'
-
-test_expect_success 'difftool ignores exit code with --no-trust-exit-code' '
- test_config difftool.error.cmd false &&
- test_config difftool.trustExitCode true &&
- git difftool -y --no-trust-exit-code -t error branch
-'
-
-test_expect_success 'difftool stops on error with --trust-exit-code' '
- test_when_finished "rm -f for-diff .git/fail-right-file" &&
- test_when_finished "git reset -- for-diff" &&
- write_script .git/fail-right-file <<-\EOF &&
- echo failed
- exit 1
- EOF
- >for-diff &&
- git add for-diff &&
- test_must_fail git difftool -y --trust-exit-code \
- --extcmd .git/fail-right-file branch >actual &&
- test_line_count = 1 actual
-'
-
-test_expect_success 'difftool honors exit status if command not found' '
- test_config difftool.nonexistent.cmd i-dont-exist &&
- test_config difftool.trustExitCode false &&
- test_must_fail git difftool -y -t nonexistent branch
-'
+for opt in '' '--dir-diff'
+do
+ test_expect_success "difftool ${opt:-without options} ignores exit code" '
+ test_config difftool.error.cmd false &&
+ git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code" '
+ test_config difftool.error.cmd false &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} forwards exit code with --trust-exit-code for built-ins" '
+ test_config difftool.vimdiff.path false &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code -t vimdiff branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = true" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ test_must_fail git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors difftool.trustExitCode = false" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode false &&
+ git difftool ${opt} -y -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} ignores exit code with --no-trust-exit-code" '
+ test_config difftool.error.cmd false &&
+ test_config difftool.trustExitCode true &&
+ git difftool ${opt} -y --no-trust-exit-code -t error branch
+ '
+
+ test_expect_success "difftool ${opt:-without options} stops on error with --trust-exit-code" '
+ test_when_finished "rm -f for-diff .git/fail-right-file" &&
+ test_when_finished "git reset -- for-diff" &&
+ write_script .git/fail-right-file <<-\EOF &&
+ echo failed
+ exit 1
+ EOF
+ >for-diff &&
+ git add for-diff &&
+ test_must_fail git difftool ${opt} -y --trust-exit-code \
+ --extcmd .git/fail-right-file branch >actual &&
+ test_line_count = 1 actual
+ '
+
+ test_expect_success "difftool ${opt:-without options} honors exit status if command not found" '
+ test_config difftool.nonexistent.cmd i-dont-exist &&
+ test_config difftool.trustExitCode false &&
+ if test "${opt}" = --dir-diff
+ then
+ expected_code=127
+ else
+ expected_code=128
+ fi &&
+ test_expect_code ${expected_code} git difftool ${opt} -y -t nonexistent branch
+ '
+done
test_expect_success 'difftool honors --gui' '
difftool_test_setup &&
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 00d29871e6..0943dfa18a 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -67,6 +67,51 @@ test_expect_success 'maintenance.auto config option' '
test_subcommand ! git maintenance run --auto --quiet <false
'
+test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
+ test_when_finished rm -r .config/git/config &&
+ (
+ XDG_CONFIG_HOME=.config &&
+ export XDG_CONFIG_HOME &&
+ mkdir -p $XDG_CONFIG_HOME/git &&
+ >$XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+ pwd >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
+ test_when_finished git maintenance unregister &&
+ test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git config --global --get maintenance.repo >actual &&
+ pwd >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
+ test_when_finished rm -r .config/git/config &&
+ (
+ XDG_CONFIG_HOME=.config &&
+ export XDG_CONFIG_HOME &&
+ mkdir -p $XDG_CONFIG_HOME/git &&
+ >$XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git maintenance unregister &&
+ test_must_fail git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+ test_must_be_empty actual
+ )
+'
+
+test_expect_success 'unregister does not need XDG_CONFIG_HOME config to exist' '
+ test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+ git maintenance register &&
+ git maintenance unregister &&
+ test_must_fail git config --global --get maintenance.repo >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'maintenance.<task>.enabled' '
git config maintenance.gc.enabled false &&
git config maintenance.commit-graph.enabled true &&
diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh
index ca04242ca0..eb64b766bd 100755
--- a/t/t8010-cat-file-filters.sh
+++ b/t/t8010-cat-file-filters.sh
@@ -43,7 +43,7 @@ test_expect_success 'cat-file --textconv --path=<path> works' '
sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
git cat-file --textconv --path=hello.txt $sha1 >rot13 &&
- test uryyb = "$(cat rot13 | remove_cr)"
+ test uryyb = "$(remove_cr <rot13)"
'
test_expect_success '--path=<path> complains without --textconv/--filters' '
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index 9a03b0f361..dbfbd86e83 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -25,11 +25,11 @@ test_expect_success setup '
git blame --line-porcelain file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse X >expect &&
test_cmp expect actual
'
@@ -53,11 +53,11 @@ do
test_expect_success "ignore_rev_changing_lines ($I)" '
git blame --line-porcelain --ignore-rev $I file >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -79,10 +79,10 @@ test_expect_success ignore_rev_adding_unblamable_lines '
git rev-parse Y >expect &&
git blame --line-porcelain file --ignore-rev Y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 3/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 4/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
@@ -92,11 +92,11 @@ test_expect_success ignore_revs_from_files '
git rev-parse Y >ignore_y &&
git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -106,11 +106,11 @@ test_expect_success ignore_revs_from_configs_and_files '
git config --add blame.ignoreRevsFile ignore_x &&
git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse A >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual
'
@@ -121,10 +121,10 @@ test_expect_success override_ignore_revs_file '
git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
git rev-parse X >expect &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 2/s/ .*//p" blame_raw >actual &&
test_cmp expect actual
'
test_expect_success bad_files_and_revs '
@@ -279,11 +279,11 @@ test_expect_success ignore_merge '
test_merge M B &&
git blame --line-porcelain file --ignore-rev M >blame_raw &&
- grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 1/s/ .*//p" blame_raw >actual &&
git rev-parse B >expect &&
test_cmp expect actual &&
- grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
+ sed -ne "/^[0-9a-f][0-9a-f]* [0-9][0-9]* 9/s/ .*//p" blame_raw >actual &&
git rev-parse C >expect &&
test_cmp expect actual
'
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index 6d3dbde3fe..d5b98e615b 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='git column'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
@@ -195,4 +196,15 @@ EOF
test_cmp expected actual
'
+test_expect_success 'padding must be non-negative' '
+ cat >input <<\EOF &&
+1 2 3 4 5 6
+EOF
+ cat >expected <<\EOF &&
+fatal: --padding must be non-negative
+EOF
+ test_must_fail git column --mode=column --padding=-1 <input >actual 2>&1 &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
index 32317d6bca..e06538b1c8 100755
--- a/t/t9114-git-svn-dcommit-merge.sh
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -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
}
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
index 62de819a44..3b038c338f 100755
--- a/t/t9117-git-svn-init-clone.sh
+++ b/t/t9117-git-svn-init-clone.sh
@@ -17,32 +17,32 @@ test_expect_success 'setup svnrepo' '
test_expect_success 'basic clone' '
test ! -d trunk &&
git svn clone "$svnrepo"/project/trunk &&
- test -d trunk/.git/svn &&
- test -e trunk/foo &&
+ test_path_is_dir trunk/.git/svn &&
+ test_path_exists trunk/foo &&
rm -rf trunk
'
test_expect_success 'clone to target directory' '
test ! -d target &&
git svn clone "$svnrepo"/project/trunk target &&
- test -d target/.git/svn &&
- test -e target/foo &&
+ test_path_is_dir target/.git/svn &&
+ test_path_exists target/foo &&
rm -rf target
'
test_expect_success 'clone with --stdlayout' '
test ! -d project &&
git svn clone -s "$svnrepo"/project &&
- test -d project/.git/svn &&
- test -e project/foo &&
+ test_path_is_dir project/.git/svn &&
+ test_path_exists project/foo &&
rm -rf project
'
test_expect_success 'clone to target directory with --stdlayout' '
test ! -d target &&
git svn clone -s "$svnrepo"/project target &&
- test -d target/.git/svn &&
- test -e target/foo &&
+ test_path_is_dir target/.git/svn &&
+ test_path_exists target/foo &&
rm -rf target
'
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index a159ff96b7..d3261e35b8 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -38,7 +38,7 @@ test_expect_success 'setup svnrepo' '
# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
# Look at what SVN wound up naming the branch and use that.
# Be sure to escape the @ if it shows up.
-non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/')
+non_reflog=$(svn_cmd ls "$svnrepo/pr ject/branches" | sed -ne '/not-a/ { s/\///; s/@/%40/; p }')
test_expect_success 'test clone with funky branch names' '
git svn clone -s "$svnrepo/pr ject" project &&
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 09606f1b3c..926ac81439 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -20,11 +20,7 @@ test_expect_success 'empty directories exist' '
cd cloned &&
for i in a b c d d/e d/e/f "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -37,11 +33,7 @@ test_expect_success 'option automkdirs set to false' '
git svn fetch &&
for i in a b c d d/e d/e/f "weird file name"
do
- if test -d "$i"
- then
- echo >&2 "$i exists" &&
- exit 1
- fi
+ test_path_is_missing "$i" || exit 1
done
)
'
@@ -52,7 +44,7 @@ test_expect_success 'more emptiness' '
test_expect_success 'git svn rebase creates empty directory' '
( cd cloned && git svn rebase ) &&
- test -d cloned/"! !"
+ test_path_is_dir cloned/"! !"
'
test_expect_success 'git svn mkdirs recreates empty directories' '
@@ -62,11 +54,7 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
git svn mkdirs &&
for i in a b c d d/e d/e/f "weird file name" "! !"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -78,25 +66,13 @@ test_expect_success 'git svn mkdirs -r works' '
git svn mkdirs -r7 &&
for i in a b c d d/e d/e/f "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done &&
- if test -d "! !"
- then
- echo >&2 "$i should not exist" &&
- exit 1
- fi &&
+ test_path_is_missing "! !" || exit 1 &&
git svn mkdirs -r8 &&
- if ! test -d "! !"
- then
- echo >&2 "$i not exist" &&
- exit 1
- fi
+ test_path_is_dir "! !" || exit 1
)
'
@@ -114,11 +90,7 @@ test_expect_success 'empty directories in trunk exist' '
cd trunk &&
for i in a "weird file name"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
)
'
@@ -129,7 +101,7 @@ test_expect_success 'remove a top-level directory from svn' '
test_expect_success 'removed top-level directory does not exist' '
git svn clone "$svnrepo" removed &&
- test ! -e removed/d
+ test_path_is_missing removed/d
'
unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
@@ -143,15 +115,11 @@ test_expect_success 'git svn gc-ed files work' '
svn_cmd mkdir -m gz "$svnrepo"/gz &&
git reset --hard $(git rev-list HEAD | tail -1) &&
git svn rebase &&
- test -f "$unhandled".gz &&
- test -f "$unhandled" &&
+ test_path_is_file "$unhandled".gz &&
+ test_path_is_file "$unhandled" &&
for i in a b c "weird file name" gz "! !"
do
- if ! test -d "$i"
- then
- echo >&2 "$i does not exist" &&
- exit 1
- fi
+ test_path_is_dir "$i" || exit 1
done
fi
)
diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh
index 4432a30d10..428339e342 100755
--- a/t/t9210-scalar.sh
+++ b/t/t9210-scalar.sh
@@ -154,7 +154,14 @@ test_expect_success 'scalar clone' '
test_cmp expect actual &&
test_path_is_missing 1/2 &&
- test_must_fail git rev-list --missing=print $second &&
+
+ # This relies on the fact that the presence of "--missing"
+ # on the command line forces lazy fetching off before
+ # "$second^{blob}" gets parsed. Without "^{blob}", a
+ # bare object name "$second" is taken into the queue and
+ # the command may not fail with a fixed "rev-list --missing".
+ test_must_fail git rev-list --missing=print "$second^{blob}" -- &&
+
git rev-list $second &&
git cat-file blob $second >actual &&
echo "second" >expect &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index dbb5042b0b..1e68426852 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -986,7 +986,7 @@ test_expect_success 'L: nested tree copy does not corrupt deltas' '
test_when_finished "git update-ref -d refs/heads/L2" &&
git fast-import <input &&
git ls-tree L2 g/b/ >tmp &&
- cat tmp | cut -f 2 >actual &&
+ cut -f 2 <tmp >actual &&
test_cmp expect actual &&
git fsck $(git rev-parse L2)
'
@@ -1059,30 +1059,33 @@ test_expect_success 'M: rename subdirectory to new subdirectory' '
compare_diff_raw expect actual
'
-test_expect_success 'M: rename root to subdirectory' '
- cat >input <<-INPUT_END &&
- commit refs/heads/M4
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- rename root
- COMMIT
+for root in '""' ''
+do
+ test_expect_success "M: rename root ($root) to subdirectory" '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/M4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ rename root
+ COMMIT
- from refs/heads/M2^0
- R "" sub
+ from refs/heads/M2^0
+ R $root sub
- INPUT_END
+ INPUT_END
- cat >expect <<-EOF &&
- :100644 100644 $oldf $oldf R100 file2/oldf sub/file2/oldf
- :100755 100755 $f4id $f4id R100 file4 sub/file4
- :100755 100755 $newf $newf R100 i/am/new/to/you sub/i/am/new/to/you
- :100755 100755 $f6id $f6id R100 newdir/exec.sh sub/newdir/exec.sh
- :100644 100644 $f5id $f5id R100 newdir/interesting sub/newdir/interesting
- EOF
- git fast-import <input &&
- git diff-tree -M -r M4^ M4 >actual &&
- compare_diff_raw expect actual
-'
+ cat >expect <<-EOF &&
+ :100644 100644 $oldf $oldf R100 file2/oldf sub/file2/oldf
+ :100755 100755 $f4id $f4id R100 file4 sub/file4
+ :100755 100755 $newf $newf R100 i/am/new/to/you sub/i/am/new/to/you
+ :100755 100755 $f6id $f6id R100 newdir/exec.sh sub/newdir/exec.sh
+ :100644 100644 $f5id $f5id R100 newdir/interesting sub/newdir/interesting
+ EOF
+ git fast-import <input &&
+ git diff-tree -M -r M4^ M4 >actual &&
+ compare_diff_raw expect actual
+ '
+done
###
### series N
@@ -1259,49 +1262,52 @@ test_expect_success PIPE 'N: empty directory reads as missing' '
test_cmp expect actual
'
-test_expect_success 'N: copy root directory by tree hash' '
- cat >expect <<-EOF &&
- :100755 000000 $newf $zero D file3/newf
- :100644 000000 $oldf $zero D file3/oldf
- EOF
- root=$(git rev-parse refs/heads/branch^0^{tree}) &&
- cat >input <<-INPUT_END &&
- commit refs/heads/N6
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy root directory by tree hash
- COMMIT
-
- from refs/heads/branch^0
- M 040000 $root ""
- INPUT_END
- git fast-import <input &&
- git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
- compare_diff_raw expect actual
-'
+for root in '""' ''
+do
+ test_expect_success "N: copy root ($root) by tree hash" '
+ cat >expect <<-EOF &&
+ :100755 000000 $newf $zero D file3/newf
+ :100644 000000 $oldf $zero D file3/oldf
+ EOF
+ root_tree=$(git rev-parse refs/heads/branch^0^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N6
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by tree hash
+ COMMIT
-test_expect_success 'N: copy root by path' '
- cat >expect <<-EOF &&
- :100755 100755 $newf $newf C100 file2/newf oldroot/file2/newf
- :100644 100644 $oldf $oldf C100 file2/oldf oldroot/file2/oldf
- :100755 100755 $f4id $f4id C100 file4 oldroot/file4
- :100755 100755 $f6id $f6id C100 newdir/exec.sh oldroot/newdir/exec.sh
- :100644 100644 $f5id $f5id C100 newdir/interesting oldroot/newdir/interesting
- EOF
- cat >input <<-INPUT_END &&
- commit refs/heads/N-copy-root-path
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy root directory by (empty) path
- COMMIT
+ from refs/heads/branch^0
+ M 040000 $root_tree $root
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+ compare_diff_raw expect actual
+ '
+
+ test_expect_success "N: copy root ($root) by path" '
+ cat >expect <<-EOF &&
+ :100755 100755 $newf $newf C100 file2/newf oldroot/file2/newf
+ :100644 100644 $oldf $oldf C100 file2/oldf oldroot/file2/oldf
+ :100755 100755 $f4id $f4id C100 file4 oldroot/file4
+ :100755 100755 $f6id $f6id C100 newdir/exec.sh oldroot/newdir/exec.sh
+ :100644 100644 $f5id $f5id C100 newdir/interesting oldroot/newdir/interesting
+ EOF
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N-copy-root-path
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by (empty) path
+ COMMIT
- from refs/heads/branch^0
- C "" oldroot
- INPUT_END
- git fast-import <input &&
- git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
- compare_diff_raw expect actual
-'
+ from refs/heads/branch^0
+ C $root oldroot
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
+ compare_diff_raw expect actual
+ '
+done
test_expect_success 'N: delete directory by copying' '
cat >expect <<-\EOF &&
@@ -1431,98 +1437,102 @@ test_expect_success 'N: reject foo/ syntax in ls argument' '
INPUT_END
'
-test_expect_success 'N: copy to root by id and modify' '
- echo "hello, world" >expect.foo &&
- echo hello >expect.bar &&
- git fast-import <<-SETUP_END &&
- commit refs/heads/N7
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- hello, tree
- COMMIT
-
- deleteall
- M 644 inline foo/bar
- data <<EOF
- hello
- EOF
- SETUP_END
-
- tree=$(git rev-parse --verify N7:) &&
- git fast-import <<-INPUT_END &&
- commit refs/heads/N8
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy to root by id and modify
- COMMIT
+for root in '""' ''
+do
+ test_expect_success "N: copy to root ($root) by id and modify" '
+ echo "hello, world" >expect.foo &&
+ echo hello >expect.bar &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N7
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
- M 040000 $tree ""
- M 644 inline foo/foo
- data <<EOF
- hello, world
- EOF
- INPUT_END
- git show N8:foo/foo >actual.foo &&
- git show N8:foo/bar >actual.bar &&
- test_cmp expect.foo actual.foo &&
- test_cmp expect.bar actual.bar
-'
+ deleteall
+ M 644 inline foo/bar
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
-test_expect_success 'N: extract subtree' '
- branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
- cat >input <<-INPUT_END &&
- commit refs/heads/N9
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- extract subtree branch:newdir
- COMMIT
+ tree=$(git rev-parse --verify N7:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N8
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
- M 040000 $branch ""
- C "newdir" ""
- INPUT_END
- git fast-import <input &&
- git diff --exit-code branch:newdir N9
-'
+ M 040000 $tree $root
+ M 644 inline foo/foo
+ data <<EOF
+ hello, world
+ EOF
+ INPUT_END
+ git show N8:foo/foo >actual.foo &&
+ git show N8:foo/bar >actual.bar &&
+ test_cmp expect.foo actual.foo &&
+ test_cmp expect.bar actual.bar
+ '
+
+ test_expect_success "N: extract subtree to the root ($root)" '
+ branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N9
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ extract subtree branch:newdir
+ COMMIT
-test_expect_success 'N: modify subtree, extract it, and modify again' '
- echo hello >expect.baz &&
- echo hello, world >expect.qux &&
- git fast-import <<-SETUP_END &&
- commit refs/heads/N10
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- hello, tree
- COMMIT
+ M 040000 $branch $root
+ C "newdir" $root
+ INPUT_END
+ git fast-import <input &&
+ git diff --exit-code branch:newdir N9
+ '
+
+ test_expect_success "N: modify subtree, extract it to the root ($root), and modify again" '
+ echo hello >expect.baz &&
+ echo hello, world >expect.qux &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N10
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
- deleteall
- M 644 inline foo/bar/baz
- data <<EOF
- hello
- EOF
- SETUP_END
+ deleteall
+ M 644 inline foo/bar/baz
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
- tree=$(git rev-parse --verify N10:) &&
- git fast-import <<-INPUT_END &&
- commit refs/heads/N11
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- copy to root by id and modify
- COMMIT
+ tree=$(git rev-parse --verify N10:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N11
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
- M 040000 $tree ""
- M 100644 inline foo/bar/qux
- data <<EOF
- hello, world
- EOF
- R "foo" ""
- C "bar/qux" "bar/quux"
- INPUT_END
- git show N11:bar/baz >actual.baz &&
- git show N11:bar/qux >actual.qux &&
- git show N11:bar/quux >actual.quux &&
- test_cmp expect.baz actual.baz &&
- test_cmp expect.qux actual.qux &&
- test_cmp expect.qux actual.quux'
+ M 040000 $tree $root
+ M 100644 inline foo/bar/qux
+ data <<EOF
+ hello, world
+ EOF
+ R "foo" $root
+ C "bar/qux" "bar/quux"
+ INPUT_END
+ git show N11:bar/baz >actual.baz &&
+ git show N11:bar/qux >actual.qux &&
+ git show N11:bar/quux >actual.quux &&
+ test_cmp expect.baz actual.baz &&
+ test_cmp expect.qux actual.qux &&
+ test_cmp expect.qux actual.quux
+ '
+done
###
### series O
@@ -2007,12 +2017,11 @@ test_expect_success 'Q: verify first notes commit' '
'
test_expect_success 'Q: verify first notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2048,12 +2057,11 @@ test_expect_success 'Q: verify second notes commit' '
'
test_expect_success 'Q: verify second notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
100644 blob $commit2
100644 blob $commit3
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2088,10 +2096,9 @@ test_expect_success 'Q: verify third notes commit' '
'
test_expect_success 'Q: verify third notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit1
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2115,10 +2122,9 @@ test_expect_success 'Q: verify fourth notes commit' '
'
test_expect_success 'Q: verify fourth notes tree' '
- cat >expect.unsorted <<-EOF &&
+ sort >expect <<-EOF &&
100644 blob $commit2
EOF
- cat expect.unsorted | sort >expect &&
git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
test_cmp expect actual
'
@@ -2146,6 +2152,7 @@ test_expect_success 'Q: deny note on empty branch' '
EOF
test_must_fail git fast-import <input
'
+
###
### series R (feature and option)
###
@@ -2794,7 +2801,7 @@ test_expect_success 'R: blob appears only once' '
'
###
-### series S
+### series S (mark and path parsing)
###
#
# Make sure missing spaces and EOLs after mark references
@@ -3064,21 +3071,283 @@ test_expect_success 'S: ls with garbage after sha1 must fail' '
test_grep "space after tree-ish" err
'
+#
+# Path parsing
+#
+# There are two sorts of ways a path can be parsed, depending on whether it is
+# the last field on the line. Additionally, ls without a <dataref> has a special
+# case. Test every occurrence of <path> in the grammar against every error case.
+# Paths for the root (empty strings) are tested elsewhere.
+#
+
+#
+# Valid paths at the end of a line: filemodify, filedelete, filecopy (dest),
+# filerename (dest), and ls.
+#
+# commit :301 from root -- modify hello.c (for setup)
+# commit :302 from :301 -- modify $path
+# commit :303 from :302 -- delete $path
+# commit :304 from :301 -- copy hello.c $path
+# commit :305 from :301 -- rename hello.c $path
+# ls :305 $path
+#
+test_path_eol_success () {
+ local test="$1" path="$2" unquoted_path="$3"
+ test_expect_success "S: paths at EOL with $test must work" '
+ test_when_finished "git branch -D S-path-eol" &&
+
+ git fast-import --export-marks=marks.out <<-EOF >out 2>err &&
+ blob
+ mark :401
+ data <<BLOB
+ hello world
+ BLOB
+
+ blob
+ mark :402
+ data <<BLOB
+ hallo welt
+ BLOB
+
+ commit refs/heads/S-path-eol
+ mark :301
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ initial commit
+ COMMIT
+ M 100644 :401 hello.c
+
+ commit refs/heads/S-path-eol
+ mark :302
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filemodify
+ COMMIT
+ from :301
+ M 100644 :402 $path
+
+ commit refs/heads/S-path-eol
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filedelete
+ COMMIT
+ from :302
+ D $path
+
+ commit refs/heads/S-path-eol
+ mark :304
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filecopy dest
+ COMMIT
+ from :301
+ C hello.c $path
+
+ commit refs/heads/S-path-eol
+ mark :305
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filerename dest
+ COMMIT
+ from :301
+ R hello.c $path
+
+ ls :305 $path
+ EOF
+
+ commit_m=$(grep :302 marks.out | cut -d\ -f2) &&
+ commit_d=$(grep :303 marks.out | cut -d\ -f2) &&
+ commit_c=$(grep :304 marks.out | cut -d\ -f2) &&
+ commit_r=$(grep :305 marks.out | cut -d\ -f2) &&
+ blob1=$(grep :401 marks.out | cut -d\ -f2) &&
+ blob2=$(grep :402 marks.out | cut -d\ -f2) &&
+
+ (
+ printf "100644 blob $blob2\t$unquoted_path\n" &&
+ printf "100644 blob $blob1\thello.c\n"
+ ) | sort >tree_m.exp &&
+ git ls-tree $commit_m | sort >tree_m.out &&
+ test_cmp tree_m.exp tree_m.out &&
+
+ printf "100644 blob $blob1\thello.c\n" >tree_d.exp &&
+ git ls-tree $commit_d >tree_d.out &&
+ test_cmp tree_d.exp tree_d.out &&
+
+ (
+ printf "100644 blob $blob1\t$unquoted_path\n" &&
+ printf "100644 blob $blob1\thello.c\n"
+ ) | sort >tree_c.exp &&
+ git ls-tree $commit_c | sort >tree_c.out &&
+ test_cmp tree_c.exp tree_c.out &&
+
+ printf "100644 blob $blob1\t$unquoted_path\n" >tree_r.exp &&
+ git ls-tree $commit_r >tree_r.out &&
+ test_cmp tree_r.exp tree_r.out &&
+
+ test_cmp out tree_r.exp
+ '
+}
+
+test_path_eol_success 'quoted spaces' '" hello world.c "' ' hello world.c '
+test_path_eol_success 'unquoted spaces' ' hello world.c ' ' hello world.c '
+test_path_eol_success 'octal escapes' '"\150\151\056\143"' 'hi.c'
+
+#
+# Valid paths before a space: filecopy (source) and filerename (source).
+#
+# commit :301 from root -- modify $path (for setup)
+# commit :302 from :301 -- copy $path hello2.c
+# commit :303 from :301 -- rename $path hello2.c
+#
+test_path_space_success () {
+ local test="$1" path="$2" unquoted_path="$3"
+ test_expect_success "S: paths before space with $test must work" '
+ test_when_finished "git branch -D S-path-space" &&
+
+ git fast-import --export-marks=marks.out <<-EOF 2>err &&
+ blob
+ mark :401
+ data <<BLOB
+ hello world
+ BLOB
+
+ commit refs/heads/S-path-space
+ mark :301
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ initial commit
+ COMMIT
+ M 100644 :401 $path
+
+ commit refs/heads/S-path-space
+ mark :302
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filecopy source
+ COMMIT
+ from :301
+ C $path hello2.c
+
+ commit refs/heads/S-path-space
+ mark :303
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit filerename source
+ COMMIT
+ from :301
+ R $path hello2.c
+
+ EOF
+
+ commit_c=$(grep :302 marks.out | cut -d\ -f2) &&
+ commit_r=$(grep :303 marks.out | cut -d\ -f2) &&
+ blob=$(grep :401 marks.out | cut -d\ -f2) &&
+
+ (
+ printf "100644 blob $blob\t$unquoted_path\n" &&
+ printf "100644 blob $blob\thello2.c\n"
+ ) | sort >tree_c.exp &&
+ git ls-tree $commit_c | sort >tree_c.out &&
+ test_cmp tree_c.exp tree_c.out &&
+
+ printf "100644 blob $blob\thello2.c\n" >tree_r.exp &&
+ git ls-tree $commit_r >tree_r.out &&
+ test_cmp tree_r.exp tree_r.out
+ '
+}
+
+test_path_space_success 'quoted spaces' '" hello world.c "' ' hello world.c '
+test_path_space_success 'no unquoted spaces' 'hello_world.c' 'hello_world.c'
+test_path_space_success 'octal escapes' '"\150\151\056\143"' 'hi.c'
+
+#
+# Test a single commit change with an invalid path. Run it with all occurrences
+# of <path> in the grammar against all error kinds.
+#
+test_path_fail () {
+ local change="$1" what="$2" prefix="$3" path="$4" suffix="$5" err_grep="$6"
+ test_expect_success "S: $change with $what must fail" '
+ test_must_fail git fast-import <<-EOF 2>err &&
+ blob
+ mark :1
+ data <<BLOB
+ hello world
+ BLOB
+
+ commit refs/heads/S-path-fail
+ mark :2
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit setup
+ COMMIT
+ M 100644 :1 hello.c
+
+ commit refs/heads/S-path-fail
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ commit with bad path
+ COMMIT
+ from :2
+ $prefix$path$suffix
+ EOF
+
+ test_grep "$err_grep" err
+ '
+}
+
+test_path_base_fail () {
+ local change="$1" prefix="$2" field="$3" suffix="$4"
+ test_path_fail "$change" 'unclosed " in '"$field" "$prefix" '"hello.c' "$suffix" "Invalid $field"
+ test_path_fail "$change" "invalid escape in quoted $field" "$prefix" '"hello\xff"' "$suffix" "Invalid $field"
+ test_path_fail "$change" "escaped NUL in quoted $field" "$prefix" '"hello\000"' "$suffix" "NUL in $field"
+}
+test_path_eol_quoted_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_base_fail "$change" "$prefix" "$field" ''
+ test_path_fail "$change" "garbage after quoted $field" "$prefix" '"hello.c"' 'x' "Garbage after $field"
+ test_path_fail "$change" "space after quoted $field" "$prefix" '"hello.c"' ' ' "Garbage after $field"
+}
+test_path_eol_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_eol_quoted_fail "$change" "$prefix" "$field"
+}
+test_path_space_fail () {
+ local change="$1" prefix="$2" field="$3"
+ test_path_base_fail "$change" "$prefix" "$field" ' world.c'
+ test_path_fail "$change" "missing space after quoted $field" "$prefix" '"hello.c"' 'x world.c' "Missing space after $field"
+ test_path_fail "$change" "missing space after unquoted $field" "$prefix" 'hello.c' '' "Missing space after $field"
+}
+
+test_path_eol_fail filemodify 'M 100644 :1 ' path
+test_path_eol_fail filedelete 'D ' path
+test_path_space_fail filecopy 'C ' source
+test_path_eol_fail filecopy 'C hello.c ' dest
+test_path_space_fail filerename 'R ' source
+test_path_eol_fail filerename 'R hello.c ' dest
+test_path_eol_fail 'ls (in commit)' 'ls :2 ' path
+
+# When 'ls' has no <dataref>, the <path> must be quoted.
+test_path_eol_quoted_fail 'ls (without dataref in commit)' 'ls ' path
+
###
### series T (ls)
###
# Setup is carried over from series S.
-test_expect_success 'T: ls root tree' '
- sed -e "s/Z\$//" >expect <<-EOF &&
- 040000 tree $(git rev-parse S^{tree}) Z
- EOF
- sha1=$(git rev-parse --verify S) &&
- git fast-import --import-marks=marks <<-EOF >actual &&
- ls $sha1 ""
- EOF
- test_cmp expect actual
-'
+for root in '""' ''
+do
+ test_expect_success "T: ls root ($root) tree" '
+ sed -e "s/Z\$//" >expect <<-EOF &&
+ 040000 tree $(git rev-parse S^{tree}) Z
+ EOF
+ sha1=$(git rev-parse --verify S) &&
+ git fast-import --import-marks=marks <<-EOF >actual &&
+ ls $sha1 $root
+ EOF
+ test_cmp expect actual
+ '
+done
test_expect_success 'T: delete branch' '
git branch to-delete &&
@@ -3180,30 +3449,33 @@ test_expect_success 'U: validate directory delete result' '
compare_diff_raw expect actual
'
-test_expect_success 'U: filedelete root succeeds' '
- cat >input <<-INPUT_END &&
- commit refs/heads/U
- committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
- data <<COMMIT
- must succeed
- COMMIT
- from refs/heads/U^0
- D ""
+for root in '""' ''
+do
+ test_expect_success "U: filedelete root ($root) succeeds" '
+ cat >input <<-INPUT_END &&
+ commit refs/heads/U-delete-root
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ must succeed
+ COMMIT
+ from refs/heads/U^0
+ D $root
- INPUT_END
+ INPUT_END
- git fast-import <input
-'
+ git fast-import <input
+ '
-test_expect_success 'U: validate root delete result' '
- cat >expect <<-EOF &&
- :100644 000000 $f7id $ZERO_OID D hello.c
- EOF
+ test_expect_success "U: validate root ($root) delete result" '
+ cat >expect <<-EOF &&
+ :100644 000000 $f7id $ZERO_OID D hello.c
+ EOF
- git diff-tree -M -r U^1 U >actual &&
+ git diff-tree -M -r U U-delete-root >actual &&
- compare_diff_raw expect actual
-'
+ compare_diff_raw expect actual
+ '
+done
###
### series V (checkpoint)
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 26c25c0eb2..1eb035ee4c 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -236,7 +236,7 @@ EOF
test_expect_success 'set up faked signed tag' '
- cat signed-tag-import | git fast-import
+ git fast-import <signed-tag-import
'
@@ -537,7 +537,7 @@ test_expect_success 'full-tree re-shows unmodified files' '
test_expect_success 'set-up a few more tags for tag export tests' '
git checkout -f main &&
- HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
+ HEAD_TREE=$(git show -s --pretty=raw HEAD | sed -n "/tree/s/tree //p") &&
git tag tree_tag -m "tagging a tree" $HEAD_TREE &&
git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE &&
git tag tag-obj_tag -m "tagging a tag" tree_tag-obj &&
@@ -791,4 +791,14 @@ test_expect_success 'fast-export --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
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 003c0b61d0..e499c7f955 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -117,12 +117,12 @@ END VERIFICATION REQUEST
EOF
test_expect_success 'pserver authentication' '
- cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication failure (non-anonymous user)' '
- if cat request-git | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <request-git >log 2>&1
then
false
else
@@ -132,17 +132,17 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' '
'
test_expect_success 'pserver authentication success (non-anonymous user with password)' '
- cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <login-git-ok >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication (login)' '
- cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <login-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'pserver authentication failure (login/non-anonymous user)' '
- if cat login-git | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <login-git >log 2>&1
then
false
else
@@ -172,7 +172,7 @@ Root $WORKDIR
EOF
test_expect_success 'req_Root failure (relative pathname)' '
- if cat request-relative | git-cvsserver pserver >log 2>&1
+ if git-cvsserver pserver <request-relative >log 2>&1
then
echo unexpected success
false
@@ -183,28 +183,26 @@ test_expect_success 'req_Root failure (relative pathname)' '
'
test_expect_success 'req_Root failure (conflicting roots)' '
- cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+ git-cvsserver pserver <request-conflict >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 &&
+ git-cvsserver --strict-paths pserver "$SERVERDIR" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+ ! git-cvsserver --strict-paths pserver "$WORKDIR" <request-anonymous >log 2>&1
'
test_expect_success 'req_Root (w/o strict-paths)' '
- cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+ git-cvsserver pserver "$WORKDIR/" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (w/o strict-paths)' '
- ! cat request-anonymous |
- git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+ ! git-cvsserver pserver "$WORKDIR/gitcvs" <request-anonymous >log 2>&1
'
cat >request-base <<EOF
@@ -217,27 +215,26 @@ 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 &&
+ git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (base-path)' '
- ! cat request-anonymous |
- git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+ ! git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" <request-anonymous >log 2>&1
'
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
test_expect_success 'req_Root (export-all)' '
- cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+ git-cvsserver --export-all pserver "$WORKDIR" <request-anonymous >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
test_expect_success 'req_Root failure (export-all w/o directory list)' '
- ! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+ ! (git-cvsserver --export-all pserver <request-anonymous >log 2>&1 || false)'
test_expect_success 'req_Root (everything together)' '
- cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+ git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" <request-base >log 2>&1 &&
sed -ne \$p log | grep "^I LOVE YOU\$"
'
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 0333065d4d..7679780fb8 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -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
'
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
index 2ff4aa932d..2d03259729 100755
--- a/t/t9604-cvsimport-timestamps.sh
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -3,11 +3,28 @@
test_description='git cvsimport timestamps'
. ./lib-cvs.sh
+test_lazy_prereq POSIX_TIMEZONE '
+ local tz=XST-1XDT,M3.5.0,M11.1.0
+ echo "1711846799 -> 2024-03-31 01:59:59 +0100" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1711846799 >actual &&
+ test_cmp expected actual &&
+ echo "1711846800 -> 2024-03-31 03:00:00 +0200" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1711846800 >actual &&
+ test_cmp expected actual &&
+ echo "1730591999 -> 2024-11-03 01:59:59 +0200" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1730591999 >actual &&
+ test_cmp expected actual &&
+ echo "1730592000 -> 2024-11-03 01:00:00 +0100" >expected &&
+ TZ="$tz" test-tool date show:iso-local 1730592000 >actual &&
+ test_cmp expected actual
+'
+
setup_cvs_test_repository t9604
-test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
+test_expect_success PERL,POSIX_TIMEZONE 'check timestamps are UTC' '
- TZ=CST6CDT git cvsimport -p"-x" -C module-1 module &&
+ TZ=CST6CDT,M4.1.0,M10.5.0 \
+ git cvsimport -p"-x" -C module-1 module &&
git cvsimport -p"-x" -C module-1 module &&
(
cd module-1 &&
@@ -34,13 +51,13 @@ test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
test_cmp expect-1 actual-1
'
-test_expect_success PERL 'check timestamps with author-specific timezones' '
+test_expect_success PERL,POSIX_TIMEZONE 'check timestamps with author-specific timezones' '
cat >cvs-authors <<-EOF &&
user1=User One <user1@domain.org>
- user2=User Two <user2@domain.org> CST6CDT
- user3=User Three <user3@domain.org> EST5EDT
- user4=User Four <user4@domain.org> MST7MDT
+ user2=User Two <user2@domain.org> CST6CDT,M4.1.0,M10.5.0
+ user3=User Three <user3@domain.org> EST5EDT,M4.1.0,M10.5.0
+ user4=User Four <user4@domain.org> MST7MDT,M4.1.0,M10.5.0
EOF
git cvsimport -p"-x" -A cvs-authors -C module-2 module &&
(
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 73cca0d143..c598011635 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -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' '
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 2a6ee2a467..bb236cd2b5 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -175,7 +175,7 @@ test_expect_success 'keyword file create' '
cp k-text-k k-text-ko &&
p4 add -t text+ko k-text-ko &&
- cat k-text-k | iconv -f ascii -t utf-16 >k-utf16-k &&
+ iconv -f ascii -t utf-16 <k-text-k >k-utf16-k &&
p4 add -t utf16+k k-utf16-k &&
cp k-utf16-k k-utf16-ko &&
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index af4b286f9d..6ae7ced51b 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -418,7 +418,7 @@ test_expect_success 'description with Jobs and values on separate lines' '
marshal_dump job0 <change &&
marshal_dump job1 <change
) | sort >jobs &&
- cat jobname1 jobname2 | sort >expected &&
+ sort jobname1 jobname2 >expected &&
test_cmp expected jobs
)
'
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
index 932841003c..5e904ac80d 100755
--- a/t/t9816-git-p4-locked.sh
+++ b/t/t9816-git-p4-locked.sh
@@ -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' '
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
index a28dbbdd56..80c8c31e32 100755
--- a/t/t9824-git-p4-git-lfs.sh
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -17,8 +17,8 @@ test_file_in_lfs () {
sed -n '2,2 p' "$FILE" | grep "^oid " &&
sed -n '3,3 p' "$FILE" | grep "^size " &&
test_line_count = 3 "$FILE" &&
- cat "$FILE" | grep "size $SIZE" &&
- HASH=$(cat "$FILE" | grep "oid sha256:" | sed -e "s/oid sha256://g") &&
+ grep "size $SIZE" "$FILE" &&
+ HASH=$(sed -ne "/oid sha256:/s/oid sha256://gp" "$FILE") &&
LFS_FILE=".git/lfs/objects/$(echo "$HASH" | cut -c1-2)/$(echo "$HASH" | cut -c3-4)/$HASH" &&
echo $EXPECTED_CONTENT >expect &&
test_path_is_file "$FILE" &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a7c3b4eb63..569cf23104 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -5,6 +5,17 @@
test_description='test bash completion'
+# The Bash completion scripts must not print anything to either stdout or
+# stderr, which we try to verify. When tracing is enabled without support for
+# BASH_XTRACEFD this assertion will fail, so we have to mark the test as
+# untraceable with such ancient Bash versions.
+test_untraceable=UnfortunatelyYes
+
+# Override environment and always use master for the default initial branch
+# name for these tests, so that rev completion candidates are as expected.
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
. ./lib-bash.sh
complete ()
@@ -87,9 +98,11 @@ test_completion ()
else
sed -e 's/Z$//' |sort >expected
fi &&
- run_completion "$1" &&
+ run_completion "$1" >"$TRASH_DIRECTORY"/bash-completion-output 2>&1 &&
sort out >out_sorted &&
- test_cmp expected out_sorted
+ test_cmp expected out_sorted &&
+ test_must_be_empty "$TRASH_DIRECTORY"/bash-completion-output &&
+ rm "$TRASH_DIRECTORY"/bash-completion-output
}
# Test __gitcomp.
@@ -1250,6 +1263,29 @@ test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
test_cmp expected out
'
+test_expect_success '__git_complete_worktree_paths' '
+ test_when_finished "git worktree remove other_wt" &&
+ git worktree add --orphan other_wt &&
+ run_completion "git worktree remove " &&
+ grep other_wt out
+'
+
+test_expect_success '__git_complete_worktree_paths - not a git repository' '
+ (
+ cd non-repo &&
+ GIT_CEILING_DIRECTORIES="$ROOT" &&
+ export GIT_CEILING_DIRECTORIES &&
+ test_completion "git worktree remove " ""
+ )
+'
+
+test_expect_success '__git_complete_worktree_paths with -C' '
+ test_when_finished "git -C otherrepo worktree remove otherrepo_wt" &&
+ git -C otherrepo worktree add --orphan otherrepo_wt &&
+ run_completion "git -C otherrepo worktree remove " &&
+ grep otherrepo_wt out
+'
+
test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
test_completion "git switch " <<-\EOF
branch-in-other Z
@@ -1259,6 +1295,142 @@ test_expect_success 'git switch - with no options, complete local branches and u
EOF
'
+test_expect_success 'git bisect - when not bisecting, complete only replay and start subcommands' '
+ test_completion "git bisect " <<-\EOF
+ replay Z
+ start Z
+ EOF
+'
+
+test_expect_success 'git bisect - complete options to start subcommand' '
+ test_completion "git bisect start --" <<-\EOF
+ --term-new Z
+ --term-bad Z
+ --term-old Z
+ --term-good Z
+ --no-checkout Z
+ --first-parent Z
+ EOF
+'
+
+test_expect_success 'setup for git-bisect tests requiring a repo' '
+ git init git-bisect &&
+ (
+ cd git-bisect &&
+ echo "initial contents" >file &&
+ git add file &&
+ git commit -am "Initial commit" &&
+ git tag initial &&
+ echo "new line" >>file &&
+ git commit -am "First change" &&
+ echo "another new line" >>file &&
+ git commit -am "Second change" &&
+ git tag final
+ )
+'
+
+test_expect_success 'git bisect - start subcommand arguments before double-dash are completed as revs' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect start " <<-\EOF
+ HEAD Z
+ final Z
+ initial Z
+ master Z
+ EOF
+ )
+'
+
+# Note that these arguments are <pathspec>s, which in practice the fallback
+# completion (not the git completion) later ends up completing as paths.
+test_expect_success 'git bisect - start subcommand arguments after double-dash are not completed' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect start final initial -- " ""
+ )
+'
+
+test_expect_success 'setup for git-bisect tests requiring ongoing bisection' '
+ (
+ cd git-bisect &&
+ git bisect start --term-new=custom_new --term-old=custom_old final initial
+ )
+'
+
+test_expect_success 'git-bisect - when bisecting all subcommands are candidates' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect " <<-\EOF
+ start Z
+ bad Z
+ custom_new Z
+ custom_old Z
+ new Z
+ good Z
+ old Z
+ terms Z
+ skip Z
+ reset Z
+ visualize Z
+ replay Z
+ log Z
+ run Z
+ help Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - options to terms subcommand are candidates' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect terms --" <<-\EOF
+ --term-bad Z
+ --term-good Z
+ --term-new Z
+ --term-old Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - git-log options to visualize subcommand are candidates' '
+ (
+ cd git-bisect &&
+ # The completion used for git-log and here does not complete
+ # every git-log option, so rather than hope to stay in sync
+ # with exactly what it does we will just spot-test here.
+ test_completion "git bisect visualize --sta" <<-\EOF &&
+ --stat Z
+ EOF
+ test_completion "git bisect visualize --summar" <<-\EOF
+ --summary Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - view subcommand is not a candidate' '
+ (
+ cd git-bisect &&
+ test_completion "git bisect vi" <<-\EOF
+ visualize Z
+ EOF
+ )
+'
+
+test_expect_success 'git-bisect - existing view subcommand is recognized and enables completion of git-log options' '
+ (
+ cd git-bisect &&
+ # The completion used for git-log and here does not complete
+ # every git-log option, so rather than hope to stay in sync
+ # with exactly what it does we will just spot-test here.
+ test_completion "git bisect view --sta" <<-\EOF &&
+ --stat Z
+ EOF
+ test_completion "git bisect view --summar" <<-\EOF
+ --summary Z
+ EOF
+ )
+'
+
test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
test_completion "git checkout " <<-\EOF
HEAD Z
@@ -1571,7 +1743,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 +1753,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
)
'
@@ -1920,6 +2097,14 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
EOF
'
+test_expect_success 'git restore completes modified files' '
+ test_commit A a.file &&
+ echo B >a.file &&
+ test_completion "git restore a." <<-\EOF
+ a.file
+ EOF
+'
+
test_expect_success 'teardown after ref completion' '
git branch -d matching-branch &&
git tag -d matching-tag &&
@@ -2562,6 +2747,35 @@ test_expect_success 'git config - variable name include' '
EOF
'
+test_expect_success 'setup for git config submodule tests' '
+ test_create_repo sub &&
+ test_commit -C sub initial &&
+ git submodule add ./sub
+'
+
+test_expect_success 'git config - variable name - submodule and __git_compute_first_level_config_vars_for_section' '
+ test_completion "git config submodule." <<-\EOF
+ submodule.active Z
+ submodule.alternateErrorStrategy Z
+ submodule.alternateLocation Z
+ submodule.fetchJobs Z
+ submodule.propagateBranches Z
+ submodule.recurse Z
+ submodule.sub.Z
+ EOF
+'
+
+test_expect_success 'git config - variable name - __git_compute_second_level_config_vars_for_section' '
+ test_completion "git config submodule.sub." <<-\EOF
+ submodule.sub.url Z
+ submodule.sub.update Z
+ submodule.sub.branch Z
+ submodule.sub.fetchRecurseSubmodules Z
+ submodule.sub.ignore Z
+ submodule.sub.active Z
+ EOF
+'
+
test_expect_success 'git config - value' '
test_completion "git config color.pager " <<-\EOF
false Z
@@ -2613,6 +2827,20 @@ test_expect_success 'git clone --config= - value' '
EOF
'
+test_expect_success 'git reflog show' '
+ test_when_finished "git checkout - && git branch -d shown" &&
+ git checkout -b shown &&
+ test_completion "git reflog sho" <<-\EOF &&
+ show Z
+ shown Z
+ EOF
+ test_completion "git reflog show sho" "shown " &&
+ test_completion "git reflog shown sho" "shown " &&
+ test_completion "git reflog --unt" "--until=" &&
+ test_completion "git reflog show --unt" "--until=" &&
+ test_completion "git reflog shown --unt" "--until="
+'
+
test_expect_success 'options with value' '
test_completion "git merge -X diff-algorithm=" <<-\EOF
@@ -2715,4 +2943,31 @@ test_expect_success '__git_complete' '
test_must_fail __git_complete ga missing
'
+test_expect_success '__git_pseudoref_exists' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ sane_unset __git_repo_path &&
+
+ # HEAD should exist, even if it points to an unborn branch.
+ __git_pseudoref_exists HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # HEAD points to an existing branch, so it should exist.
+ test_commit A &&
+ __git_pseudoref_exists HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # CHERRY_PICK_HEAD does not exist, so the existence check should fail.
+ ! __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+ test_must_be_empty output &&
+
+ # CHERRY_PICK_HEAD points to a commit, so it should exist.
+ git update-ref CHERRY_PICK_HEAD A &&
+ __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+ test_must_be_empty output
+ )
+'
+
test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 93fe819b0a..862d80c974 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -14,7 +14,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# The semantics of the editor variables are that of invoking
# sh -c "$EDITOR \"$@\"" files ...
@@ -385,7 +385,7 @@ test_commit () {
shift
done &&
indir=${indir:+"$indir"/} &&
- local file=${2:-"$1.t"} &&
+ local file="${2:-"$1.t"}" &&
if test -n "$append"
then
$echo "${3-$1}" >>"$indir$file"
@@ -1263,9 +1263,8 @@ test_cmp_bin () {
cmp "$@"
}
-# Deprecated - do not use this in new code
test_i18ngrep () {
- test_grep "$@"
+ BUG "do not use test_i18ngrep---use test_grep instead"
}
test_grep () {
@@ -1277,7 +1276,7 @@ test_grep () {
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"
@@ -1656,7 +1655,21 @@ test_set_hash () {
# Detect the hash algorithm in use.
test_detect_hash () {
- test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
+ case "$GIT_TEST_DEFAULT_HASH" in
+ "sha256")
+ test_hash_algo=sha256
+ test_compat_hash_algo=sha1
+ ;;
+ *)
+ test_hash_algo=sha1
+ test_compat_hash_algo=sha256
+ ;;
+ esac
+}
+
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+ echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
}
# Load common hash metadata and common placeholder object IDs for use with
@@ -1708,6 +1721,12 @@ test_oid () {
local algo="${test_hash_algo}" &&
case "$1" in
+ --hash=storage)
+ algo="$test_hash_algo" &&
+ shift;;
+ --hash=compat)
+ algo="$test_compat_hash_algo" &&
+ shift;;
--hash=*)
algo="${1#--hash=}" &&
shift;;
@@ -1729,7 +1748,7 @@ test_oid () {
# Insert a slash into an object ID so it can be used to reference a location
# under ".git/objects". For example, "deadbeef..." becomes "de/adbeef..".
test_oid_to_path () {
- local basename=${1#??}
+ local basename="${1#??}"
echo "${1%$basename}/$basename"
}
@@ -1746,7 +1765,7 @@ test_parse_ls_tree_oids () {
# Choose a port number based on the test script's number and store it in
# the given variable name, unless that variable already contains a number.
test_set_port () {
- local var=$1 port
+ local var="$1" port
if test $# -ne 1 || test -z "$var"
then
@@ -1821,7 +1840,7 @@ test_subcommand () {
shift
fi
- local expr=$(printf '"%s",' "$@")
+ local expr="$(printf '"%s",' "$@")"
expr="${expr%,}"
if test -n "$negate"
@@ -1911,7 +1930,7 @@ test_readlink () {
# An optional increment to the magic timestamp may be specified as second
# argument.
test_set_magic_mtime () {
- local inc=${2:-0} &&
+ local inc="${2:-0}" &&
local mtime=$((1234567890 + $inc)) &&
test-tool chmtime =$mtime "$1" &&
test_is_magic_mtime "$1" $inc
@@ -1924,7 +1943,7 @@ test_set_magic_mtime () {
# argument. Usually, this should be the same increment which was used for
# the associated test_set_magic_mtime.
test_is_magic_mtime () {
- local inc=${2:-0} &&
+ local inc="${2:-0}" &&
local mtime=$((1234567890 + $inc)) &&
echo $mtime >.git/test-mtime-expect &&
test-tool chmtime --get "$1" >.git/test-mtime-actual &&
diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh
index 9c5339c577..33405c90d7 100644
--- a/t/test-lib-github-workflow-markup.sh
+++ b/t/test-lib-github-workflow-markup.sh
@@ -14,7 +14,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
#
# The idea is for `test-lib.sh` to source this file when run in GitHub
# workflows; these functions will then override (empty) functions
@@ -42,8 +42,8 @@ finalize_test_case_output () {
fixed)
echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
;;
- ok)
- # Exit without printing the "ok" tests
+ ok|broken)
+ # Exit without printing the "ok" or ""broken" tests
return
;;
esac
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
index 79c31c788b..76cbbd3299 100644
--- a/t/test-lib-junit.sh
+++ b/t/test-lib-junit.sh
@@ -15,7 +15,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
#
# The idea is for `test-lib.sh` to source this file when the user asks
# for JUnit XML; these functions will then override (empty) functions
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1656c9eed0..79d3e0e7d9 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -13,7 +13,7 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
# Test the binaries we have just built. The tests are kept in
# t/ subdirectory and are run in 'trash directory' subdirectory.
@@ -542,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
@@ -1295,6 +1297,11 @@ test_done () {
EOF
fi
+ if test -z "$passes_sanitize_leak" && test_bool_env TEST_PASSES_SANITIZE_LEAK false
+ then
+ BAIL_OUT "Please, set TEST_PASSES_SANITIZE_LEAK before sourcing test-lib.sh"
+ fi
+
if test "$test_fixed" != 0
then
say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
@@ -1745,7 +1752,16 @@ parisc* | hppa*)
;;
esac
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+ test_set_prereq REFFILES;;
+reftable)
+ test_set_prereq REFTABLE;;
+*)
+ echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+ exit 1
+ ;;
+esac
( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
test -z "$NO_CURL" && test_set_prereq LIBCURL
@@ -1936,12 +1952,17 @@ test_lazy_prereq SHA1 '
esac
'
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+ test_have_prereq SHA1,REFFILES
+'
+
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
# system permanently.
# Tests that verify the scheduler integration must set this locally
# to avoid errors.
GIT_TEST_MAINT_SCHEDULER="none:exit 1"
+export GIT_TEST_MAINT_SCHEDULER
# Does this platform support `git fsmonitor--daemon`
#
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644
index 0000000000..d6ac1fe678
--- /dev/null
+++ b/t/unit-tests/t-ctype.c
@@ -0,0 +1,53 @@
+#include "test-lib.h"
+
+#define TEST_CHAR_CLASS(class, string) do { \
+ size_t len = ARRAY_SIZE(string) - 1 + \
+ BUILD_ASSERT_OR_ZERO(ARRAY_SIZE(string) > 0) + \
+ BUILD_ASSERT_OR_ZERO(sizeof(string[0]) == sizeof(char)); \
+ int skip = test__run_begin(); \
+ if (!skip) { \
+ for (int i = 0; i < 256; i++) { \
+ if (!check_int(class(i), ==, !!memchr(string, i, len)))\
+ test_msg(" i: 0x%02x", i); \
+ } \
+ check(!class(EOF)); \
+ } \
+ test__run_end(!skip, TEST_LOCATION(), #class " works"); \
+} while (0)
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+ "\x7f"
+
+int cmd_main(int argc, const char **argv) {
+ TEST_CHAR_CLASS(isspace, " \n\r\t");
+ TEST_CHAR_CLASS(isdigit, DIGIT);
+ TEST_CHAR_CLASS(isalpha, LOWER UPPER);
+ TEST_CHAR_CLASS(isalnum, LOWER UPPER DIGIT);
+ TEST_CHAR_CLASS(is_glob_special, "*?[\\");
+ TEST_CHAR_CLASS(is_regex_special, "$()*+.?[\\^{|");
+ TEST_CHAR_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
+ TEST_CHAR_CLASS(isascii, ASCII);
+ TEST_CHAR_CLASS(islower, LOWER);
+ TEST_CHAR_CLASS(isupper, UPPER);
+ TEST_CHAR_CLASS(iscntrl, CNTRL);
+ TEST_CHAR_CLASS(ispunct, PUNCT);
+ TEST_CHAR_CLASS(isxdigit, DIGIT "abcdefABCDEF");
+ TEST_CHAR_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644
index 0000000000..a0d57df761
--- /dev/null
+++ b/t/unit-tests/t-mem-pool.c
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+ struct mem_pool pool = { .block_alloc = block_alloc };
+ f(&pool);
+ mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+ size_t size = 100;
+ char *buffer = mem_pool_calloc(pool, 1, size);
+ for (size_t i = 0; i < size; i++)
+ check_int(buffer[i], ==, 0);
+ if (!check(pool->mp_block != NULL))
+ return;
+ check(pool->mp_block->next_free != NULL);
+ check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(setup_static(t_calloc_100, 1024 * 1024),
+ "mem_pool_calloc returns 100 zeroed bytes with big block");
+ TEST(setup_static(t_calloc_100, 1),
+ "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+ return test_done();
+}
diff --git a/t/unit-tests/t-prio-queue.c b/t/unit-tests/t-prio-queue.c
new file mode 100644
index 0000000000..7a4e5780e1
--- /dev/null
+++ b/t/unit-tests/t-prio-queue.c
@@ -0,0 +1,91 @@
+#include "test-lib.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data UNUSED)
+{
+ const int *a = va, *b = vb;
+ return *a - *b;
+}
+
+
+#define MISSING -1
+#define DUMP -2
+#define STACK -3
+#define GET -4
+#define REVERSE -5
+
+static int show(int *v)
+{
+ return v ? *v : MISSING;
+}
+
+static void test_prio_queue(int *input, size_t input_size,
+ int *result, size_t result_size)
+{
+ struct prio_queue pq = { intcmp };
+ int j = 0;
+
+ for (int i = 0; i < input_size; i++) {
+ void *peek, *get;
+ switch(input[i]) {
+ case GET:
+ peek = prio_queue_peek(&pq);
+ get = prio_queue_get(&pq);
+ if (!check(peek == get))
+ return;
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
+ break;
+ case DUMP:
+ while ((peek = prio_queue_peek(&pq))) {
+ get = prio_queue_get(&pq);
+ if (!check(peek == get))
+ return;
+ if (!check_uint(j, <, result_size))
+ break;
+ if (!check_int(result[j], ==, show(get)))
+ test_msg(" j: %d", j);
+ j++;
+ }
+ break;
+ case STACK:
+ pq.compare = NULL;
+ break;
+ case REVERSE:
+ prio_queue_reverse(&pq);
+ break;
+ default:
+ prio_queue_put(&pq, &input[i]);
+ break;
+ }
+ }
+ check_uint(j, ==, result_size);
+ clear_prio_queue(&pq);
+}
+
+#define TEST_INPUT(input, result) \
+ test_prio_queue(input, ARRAY_SIZE(input), result, ARRAY_SIZE(result))
+
+int cmd_main(int argc, const char **argv)
+{
+ TEST(TEST_INPUT(((int []){ 2, 6, 3, 10, 9, 5, 7, 4, 5, 8, 1, DUMP }),
+ ((int []){ 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10 })),
+ "prio-queue works for basic input");
+ TEST(TEST_INPUT(((int []){ 6, 2, 4, GET, 5, 3, GET, GET, 1, DUMP }),
+ ((int []){ 2, 3, 4, 1, 5, 6 })),
+ "prio-queue works for mixed put & get commands");
+ TEST(TEST_INPUT(((int []){ 1, 2, GET, GET, GET, 1, 2, GET, GET, GET }),
+ ((int []){ 1, 2, MISSING, 1, 2, MISSING })),
+ "prio-queue works when queue is empty");
+ TEST(TEST_INPUT(((int []){ STACK, 8, 1, 5, 4, 6, 2, 3, DUMP }),
+ ((int []){ 3, 2, 6, 4, 5, 1, 8 })),
+ "prio-queue works when used as a LIFO stack");
+ TEST(TEST_INPUT(((int []){ STACK, 1, 2, 3, 4, 5, 6, REVERSE, DUMP }),
+ ((int []){ 1, 2, 3, 4, 5, 6 })),
+ "prio-queue works when LIFO stack is reversed");
+
+ return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
index 7bf9dfdb95..66d6980ffb 100644
--- a/t/unit-tests/test-lib.c
+++ b/t/unit-tests/test-lib.c
@@ -21,12 +21,11 @@ static struct {
.result = RESULT_NONE,
};
-#ifndef _MSC_VER
-#define make_relative(location) location
-#else
/*
* Visual C interpolates the absolute Windows path for `__FILE__`,
* but we want to see relative paths, as verified by t0080.
+ * There are other compilers that do the same, and are not for
+ * Windows.
*/
#include "dir.h"
@@ -34,32 +33,66 @@ static const char *make_relative(const char *location)
{
static char prefix[] = __FILE__, buf[PATH_MAX], *p;
static size_t prefix_len;
+ static int need_bs_to_fs = -1;
- if (!prefix_len) {
+ /* one-time preparation */
+ if (need_bs_to_fs < 0) {
size_t len = strlen(prefix);
- const char *needle = "\\t\\unit-tests\\test-lib.c";
+ char needle[] = "t\\unit-tests\\test-lib.c";
size_t needle_len = strlen(needle);
- if (len < needle_len || strcmp(needle, prefix + len - needle_len))
- die("unexpected suffix of '%s'", prefix);
+ if (len < needle_len)
+ die("unexpected prefix '%s'", prefix);
+
+ /*
+ * The path could be relative (t/unit-tests/test-lib.c)
+ * or full (/home/user/git/t/unit-tests/test-lib.c).
+ * Check the slash between "t" and "unit-tests".
+ */
+ prefix_len = len - needle_len;
+ if (prefix[prefix_len + 1] == '/') {
+ /* Oh, we're not Windows */
+ for (size_t i = 0; i < needle_len; i++)
+ if (needle[i] == '\\')
+ needle[i] = '/';
+ need_bs_to_fs = 0;
+ } else {
+ need_bs_to_fs = 1;
+ }
- /* let it end in a directory separator */
- prefix_len = len - needle_len + 1;
+ /*
+ * prefix_len == 0 if the compiler gives paths relative
+ * to the root of the working tree. Otherwise, we want
+ * to see that we did find the needle[] at a directory
+ * boundary. Again we rely on that needle[] begins with
+ * "t" followed by the directory separator.
+ */
+ if (fspathcmp(needle, prefix + prefix_len) ||
+ (prefix_len && prefix[prefix_len - 1] != needle[1]))
+ die("unexpected suffix of '%s'", prefix);
}
- /* Does it not start with the expected prefix? */
- if (fspathncmp(location, prefix, prefix_len))
+ /*
+ * Does it not start with the expected prefix?
+ * Return it as-is without making it worse.
+ */
+ if (prefix_len && fspathncmp(location, prefix, prefix_len))
return location;
- strlcpy(buf, location + prefix_len, sizeof(buf));
+ /*
+ * If we do not need to munge directory separator, we can return
+ * the substring at the tail of the location.
+ */
+ if (!need_bs_to_fs)
+ return location + prefix_len;
+
/* convert backslashes to forward slashes */
+ strlcpy(buf, location + prefix_len, sizeof(buf));
for (p = buf; *p; p++)
if (*p == '\\')
*p = '/';
-
return buf;
}
-#endif
static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
{
diff --git a/tempfile.c b/tempfile.c
index ecdebf1afb..ed88cf8431 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -50,15 +50,17 @@
static VOLATILE_LIST_HEAD(tempfile_list);
-static void remove_template_directory(struct tempfile *tempfile,
+static int remove_template_directory(struct tempfile *tempfile,
int in_signal_handler)
{
if (tempfile->directory) {
if (in_signal_handler)
- rmdir(tempfile->directory);
+ return rmdir(tempfile->directory);
else
- rmdir_or_warn(tempfile->directory);
+ return rmdir_or_warn(tempfile->directory);
}
+
+ return 0;
}
static void remove_tempfiles(int in_signal_handler)
@@ -353,16 +355,19 @@ int rename_tempfile(struct tempfile **tempfile_p, const char *path)
return 0;
}
-void delete_tempfile(struct tempfile **tempfile_p)
+int delete_tempfile(struct tempfile **tempfile_p)
{
struct tempfile *tempfile = *tempfile_p;
+ int err = 0;
if (!is_tempfile_active(tempfile))
- return;
+ return 0;
- close_tempfile_gently(tempfile);
- unlink_or_warn(tempfile->filename.buf);
- remove_template_directory(tempfile, 0);
+ err |= close_tempfile_gently(tempfile);
+ err |= unlink_or_warn(tempfile->filename.buf);
+ err |= remove_template_directory(tempfile, 0);
deactivate_tempfile(tempfile);
*tempfile_p = NULL;
+
+ return err ? -1 : 0;
}
diff --git a/tempfile.h b/tempfile.h
index d0413af733..2d2ae5b657 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -269,7 +269,7 @@ int reopen_tempfile(struct tempfile *tempfile);
* `delete_tempfile()` for a `tempfile` object that has already been
* deleted or renamed.
*/
-void delete_tempfile(struct tempfile **tempfile_p);
+int delete_tempfile(struct tempfile **tempfile_p);
/*
* Close the file descriptor and/or file pointer if they are still
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index e144712c85..29ed5ee486 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -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/tmp-objdir.c b/tmp-objdir.c
index 5f9074ad1c..3509258be5 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -6,7 +6,6 @@
#include "environment.h"
#include "object-file.h"
#include "path.h"
-#include "sigchain.h"
#include "string-list.h"
#include "strbuf.h"
#include "strvec.h"
diff --git a/trace.c b/trace.c
index 971a68abe8..8669ddfca2 100644
--- a/trace.c
+++ b/trace.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/>.
*/
#include "git-compat-util.h"
diff --git a/trace2.c b/trace2.c
index 6dc74dff4c..f894532d05 100644
--- a/trace2.c
+++ b/trace2.c
@@ -1,12 +1,9 @@
#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"
@@ -20,6 +17,7 @@
#include "trace2/tr2_tmr.h"
static int trace2_enabled;
+static int trace2_redact = 1;
static int tr2_next_child_id; /* modify under lock */
static int tr2_next_exec_id; /* modify under lock */
@@ -227,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();
@@ -247,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;
@@ -260,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)
@@ -348,6 +433,9 @@ void trace2_cmd_name_fl(const char *file, int line, const char *name)
for_each_wanted_builtin (j, tgt_j)
if (tgt_j->pfn_command_name_fl)
tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
+
+ trace2_cmd_list_config();
+ trace2_cmd_list_env_vars();
}
void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
@@ -379,17 +467,29 @@ void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
void trace2_cmd_list_config_fl(const char *file, int line)
{
+ static int emitted = 0;
+
if (!trace2_enabled)
return;
+ if (emitted)
+ return;
+ emitted = 1;
+
tr2_cfg_list_config_fl(file, line);
}
void trace2_cmd_list_env_vars_fl(const char *file, int line)
{
+ static int emitted = 0;
+
if (!trace2_enabled)
return;
+ if (emitted)
+ return;
+ emitted = 1;
+
tr2_list_env_vars_fl(file, line);
}
@@ -409,6 +509,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;
@@ -419,10 +520,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,
@@ -493,6 +608,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;
@@ -502,10 +618,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;
}
@@ -637,13 +757,19 @@ void trace2_def_param_fl(const char *file, int line, const char *param,
{
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, kvi);
+ tgt_j->pfn_param_fl(file, line, param, redacted, kvi);
+
+ if (redacted != value)
+ free((void *)redacted);
}
void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
diff --git a/trace2.h b/trace2.h
index 40d8c2e02a..1f0669bbd2 100644
--- a/trace2.h
+++ b/trace2.h
@@ -337,8 +337,8 @@ struct key_value_info;
void trace2_def_param_fl(const char *file, int line, const char *param,
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
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c
index 87cf9034fb..d3a33715c1 100644
--- a/trace2/tr2_ctr.c
+++ b/trace2/tr2_ctr.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "thread-utils.h"
#include "trace2/tr2_tgt.h"
#include "trace2/tr2_tls.h"
#include "trace2/tr2_ctr.h"
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
index d3ecac2772..048cdd5438 100644
--- a/trace2/tr2_sysenv.c
+++ b/trace2/tr2_sysenv.c
@@ -68,6 +68,8 @@ static int tr2_sysenv_cb(const char *key, const char *value,
for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+ if (!value)
+ return config_error_nonbool(key);
free(tr2_sysenv_settings[k].value);
tr2_sysenv_settings[k].value = xstrdup(value);
return 0;
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index 38d5ebddf6..baef48aa69 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -2,6 +2,7 @@
#include "config.h"
#include "repository.h"
#include "run-command.h"
+#include "strbuf.h"
#include "quote.h"
#include "version.h"
#include "trace2/tr2_dst.h"
diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c
index 601c9e5036..4f75392952 100644
--- a/trace2/tr2_tls.c
+++ b/trace2/tr2_tls.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "strbuf.h"
#include "thread-utils.h"
#include "trace.h"
#include "trace2/tr2_tls.h"
diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h
index f9049805d4..3dfe6557fc 100644
--- a/trace2/tr2_tls.h
+++ b/trace2/tr2_tls.h
@@ -1,7 +1,6 @@
#ifndef TR2_TLS_H
#define TR2_TLS_H
-#include "strbuf.h"
#include "trace2/tr2_ctr.h"
#include "trace2/tr2_tmr.h"
diff --git a/trace2/tr2_tmr.c b/trace2/tr2_tmr.c
index 31d0e4d1bd..51f564b07a 100644
--- a/trace2/tr2_tmr.c
+++ b/trace2/tr2_tmr.c
@@ -1,5 +1,4 @@
#include "git-compat-util.h"
-#include "thread-utils.h"
#include "trace2/tr2_tgt.h"
#include "trace2/tr2_tls.h"
#include "trace2/tr2_tmr.h"
diff --git a/trailer.c b/trailer.c
index b6de5d9cb2..c72ae68709 100644
--- a/trailer.c
+++ b/trailer.c
@@ -5,7 +5,6 @@
#include "string-list.h"
#include "run-command.h"
#include "commit.h"
-#include "tempfile.h"
#include "trailer.h"
#include "list.h"
/*
@@ -145,37 +144,6 @@ static char last_non_space_char(const char *s)
return '\0';
}
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
-{
- char c;
-
- if (!tok) {
- fprintf(outfile, "%s\n", val);
- return;
- }
-
- c = last_non_space_char(tok);
- if (!c)
- return;
- if (strchr(separators, c))
- fprintf(outfile, "%s%s\n", tok, val);
- else
- fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
-}
-
-static void print_all(FILE *outfile, struct list_head *head,
- const struct process_trailer_options *opts)
-{
- struct list_head *pos;
- struct trailer_item *item;
- list_for_each(pos, head) {
- item = list_entry(pos, struct trailer_item, list);
- if ((!opts->trim_empty || strlen(item->value) > 0) &&
- (!opts->only_trailers || item->token))
- print_tok_val(outfile, item->token, item->value);
- }
-}
-
static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
{
struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
@@ -366,8 +334,8 @@ static int find_same_and_apply_arg(struct list_head *head,
return 0;
}
-static void process_trailers_lists(struct list_head *head,
- struct list_head *arg_head)
+void process_trailers_lists(struct list_head *head,
+ struct list_head *arg_head)
{
struct list_head *pos, *p;
struct arg_item *arg_tok;
@@ -507,6 +475,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);
}
}
@@ -551,16 +521,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:
@@ -581,7 +557,7 @@ static int git_trailer_config(const char *conf_key, const char *value,
return 0;
}
-static void ensure_configured(void)
+void trailer_config_init(void)
{
if (configured)
return;
@@ -711,7 +687,7 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
list_add_tail(&new_item->list, arg_head);
}
-static void parse_trailers_from_config(struct list_head *config_head)
+void parse_trailers_from_config(struct list_head *config_head)
{
struct arg_item *item;
struct list_head *pos;
@@ -727,8 +703,8 @@ static void parse_trailers_from_config(struct list_head *config_head)
}
}
-static void parse_trailers_from_command_line_args(struct list_head *arg_head,
- struct list_head *new_trailer_head)
+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;
@@ -767,17 +743,6 @@ static void parse_trailers_from_command_line_args(struct list_head *arg_head,
free(cl_separators);
}
-static void read_input_file(struct strbuf *sb, const char *file)
-{
- if (file) {
- if (strbuf_read_file(sb, file, 0) < 0)
- die_errno(_("could not read input file '%s'"), file);
- } else {
- if (strbuf_read(sb, fileno(stdin), 0) < 0)
- die_errno(_("could not read from stdin"));
- }
-}
-
static const char *next_line(const char *str)
{
const char *nl = strchrnul(str, '\n');
@@ -809,28 +774,55 @@ 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)) {
- const char *v;
+ /* Assume the naive end of the input is already what we want. */
+ end = strlen(input);
+
+ /* Optionally skip over any patch part ("---" line and below). */
+ if (!no_divider) {
+ for (s = input; *s; s = next_line(s)) {
+ const char *v;
- if (skip_prefix(s, "---", &v) && isspace(*v))
- 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;
@@ -847,7 +839,7 @@ static size_t find_trailer_start(const char *buf, size_t len)
/* The first paragraph is the title and cannot be trailers */
for (s = buf; s < buf + len; s = next_line(s)) {
- if (s[0] == comment_line_char)
+ if (starts_with_mem(s, buf + len - s, comment_line_str))
continue;
if (is_blank_line(s))
break;
@@ -867,7 +859,7 @@ static size_t find_trailer_start(const char *buf, size_t len)
const char **p;
ssize_t separator_pos;
- if (bol[0] == comment_line_char) {
+ if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
non_trailer_lines += possible_continuation_lines;
possible_continuation_lines = 0;
continue;
@@ -925,12 +917,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);
@@ -970,21 +956,21 @@ static void unfold_value(struct strbuf *val)
* 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)
+void parse_trailers(const struct process_trailer_options *opts,
+ struct trailer_info *info,
+ const char *str,
+ struct list_head *head)
{
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
size_t i;
- trailer_info_get(info, str, opts);
+ trailer_info_get(opts, str, info);
for (i = 0; i < info->trailer_nr; i++) {
int separator_pos;
char *trailer = info->trailers[i];
- if (trailer[0] == comment_line_char)
+ if (starts_with(trailer, comment_line_str))
continue;
separator_pos = find_separator(trailer, separators);
if (separator_pos >= 1) {
@@ -1005,120 +991,32 @@ static void parse_trailers(struct trailer_info *info,
}
}
-static void free_all(struct list_head *head)
+void free_trailers(struct list_head *trailers)
{
struct list_head *pos, *p;
- list_for_each_safe(pos, p, head) {
+ list_for_each_safe(pos, p, trailers) {
list_del(pos);
free_trailer_item(list_entry(pos, struct trailer_item, list));
}
}
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
+void trailer_info_get(const struct process_trailer_options *opts,
+ const char *str,
+ struct trailer_info *info)
{
- struct stat st;
- struct strbuf filename_template = STRBUF_INIT;
- const char *tail;
- FILE *outfile;
-
- if (stat(file, &st))
- die_errno(_("could not stat %s"), file);
- if (!S_ISREG(st.st_mode))
- die(_("file %s is not a regular file"), file);
- if (!(st.st_mode & S_IWUSR))
- die(_("file %s is not writable by user"), file);
-
- /* Create temporary file in the same directory as the original */
- tail = strrchr(file, '/');
- if (tail)
- strbuf_add(&filename_template, file, tail - file + 1);
- strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
- trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
- strbuf_release(&filename_template);
- outfile = fdopen_tempfile(trailers_tempfile, "w");
- if (!outfile)
- die_errno(_("could not open temporary file"));
-
- return outfile;
-}
-
-void process_trailers(const char *file,
- const struct process_trailer_options *opts,
- struct list_head *new_trailer_head)
-{
- LIST_HEAD(head);
- struct strbuf sb = STRBUF_INIT;
- struct trailer_info info;
- size_t trailer_end;
- FILE *outfile = stdout;
-
- ensure_configured();
-
- read_input_file(&sb, file);
-
- if (opts->in_place)
- outfile = create_in_place_tempfile(file);
-
- parse_trailers(&info, sb.buf, &head, opts);
- trailer_end = info.trailer_end - sb.buf;
-
- /* Print the lines before the trailers */
- if (!opts->only_trailers)
- fwrite(sb.buf, 1, info.trailer_start - sb.buf, outfile);
-
- if (!opts->only_trailers && !info.blank_line_before_trailer)
- fprintf(outfile, "\n");
-
-
- if (!opts->only_input) {
- LIST_HEAD(config_head);
- LIST_HEAD(arg_head);
- parse_trailers_from_config(&config_head);
- parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
- list_splice(&config_head, &arg_head);
- process_trailers_lists(&head, &arg_head);
- }
-
- print_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);
-
- if (opts->in_place)
- if (rename_tempfile(&trailers_tempfile, file))
- die_errno(_("could not rename temporary file to %s"), file);
-
- strbuf_release(&sb);
-}
-
-void trailer_info_get(struct trailer_info *info, const char *str,
- const struct process_trailer_options *opts)
-{
- int patch_start, trailer_end, trailer_start;
+ size_t end_of_log_message = 0, trailer_block_start = 0;
struct strbuf **trailer_lines, **ptr;
char **trailer_strings = NULL;
size_t nr = 0, alloc = 0;
char **last = NULL;
- ensure_configured();
+ trailer_config_init();
- if (opts->no_divider)
- patch_start = strlen(str);
- else
- patch_start = find_patch_start(str);
+ end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+ trailer_block_start = find_trailer_block_start(str, end_of_log_message);
- trailer_end = find_trailer_end(str, patch_start);
- trailer_start = find_trailer_start(str, trailer_end);
-
- trailer_lines = strbuf_split_buf(str + trailer_start,
- trailer_end - trailer_start,
+ trailer_lines = strbuf_split_buf(str + trailer_block_start,
+ end_of_log_message - trailer_block_start,
'\n',
0);
for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1139,9 +1037,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;
}
@@ -1154,35 +1052,32 @@ void trailer_info_release(struct trailer_info *info)
free(info->trailers);
}
-static void format_trailer_info(struct strbuf *out,
- const struct trailer_info *info,
- const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+ struct list_head *trailers,
+ struct strbuf *out)
{
size_t origlen = out->len;
- size_t i;
-
- /* If we want the whole block untouched, we can take the fast path. */
- if (!opts->only_trailers && !opts->unfold && !opts->filter &&
- !opts->separator && !opts->key_only && !opts->value_only &&
- !opts->key_value_separator) {
- strbuf_add(out, info->trailer_start,
- info->trailer_end - info->trailer_start);
- return;
- }
-
- for (i = 0; i < info->trailer_nr; i++) {
- char *trailer = info->trailers[i];
- ssize_t separator_pos = find_separator(trailer, separators);
+ struct list_head *pos;
+ struct trailer_item *item;
- if (separator_pos >= 1) {
+ list_for_each(pos, trailers) {
+ item = list_entry(pos, struct trailer_item, list);
+ if (item->token) {
struct strbuf tok = STRBUF_INIT;
struct strbuf val = STRBUF_INIT;
+ strbuf_addstr(&tok, item->token);
+ strbuf_addstr(&val, item->value);
+
+ /*
+ * Skip key/value pairs where the value was empty. This
+ * can happen from trailers specified without a
+ * separator, like `--trailer "Reviewed-by"` (no
+ * corresponding value).
+ */
+ if (opts->trim_empty && !strlen(item->value))
+ continue;
- parse_trailer(&tok, &val, NULL, trailer, separator_pos);
if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
- if (opts->unfold)
- unfold_value(&val);
-
if (opts->separator && out->len != origlen)
strbuf_addbuf(out, opts->separator);
if (!opts->value_only)
@@ -1190,8 +1085,11 @@ static void format_trailer_info(struct strbuf *out,
if (!opts->key_only && !opts->value_only) {
if (opts->key_value_separator)
strbuf_addbuf(out, opts->key_value_separator);
- else
- strbuf_addstr(out, ": ");
+ else {
+ char c = last_non_space_char(tok.buf);
+ if (c && !strchr(separators, c))
+ strbuf_addf(out, "%c ", separators[0]);
+ }
}
if (!opts->key_only)
strbuf_addbuf(out, &val);
@@ -1205,22 +1103,34 @@ static void format_trailer_info(struct strbuf *out,
if (opts->separator && out->len != origlen) {
strbuf_addbuf(out, opts->separator);
}
- strbuf_addstr(out, trailer);
- if (opts->separator) {
+ strbuf_addstr(out, item->value);
+ if (opts->separator)
strbuf_rtrim(out);
- }
+ else
+ strbuf_addch(out, '\n');
}
}
-
}
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
- const struct process_trailer_options *opts)
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+ const char *msg,
+ struct strbuf *out)
{
+ LIST_HEAD(trailer_objects);
struct trailer_info info;
- trailer_info_get(&info, msg, opts);
- format_trailer_info(out, &info, opts);
+ parse_trailers(opts, &info, msg, &trailer_objects);
+
+ /* If we want the whole block untouched, we can take the fast path. */
+ if (!opts->only_trailers && !opts->unfold && !opts->filter &&
+ !opts->separator && !opts->key_only && !opts->value_only &&
+ !opts->key_value_separator) {
+ strbuf_add(out, msg + info.trailer_block_start,
+ info.trailer_block_end - info.trailer_block_start);
+ } else
+ format_trailers(opts, &trailer_objects, out);
+
+ free_trailers(&trailer_objects);
trailer_info_release(&info);
}
@@ -1230,7 +1140,7 @@ 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->internal.info, msg, &opts);
+ trailer_info_get(&opts, msg, &iter->internal.info);
iter->internal.cur = 0;
}
@@ -1247,6 +1157,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
strbuf_reset(&iter->val);
parse_trailer(&iter->key, &iter->val, NULL,
trailer, separator_pos);
+ /* Always unfold values during iteration. */
unfold_value(&iter->val);
return 1;
}
diff --git a/trailer.h b/trailer.h
index ab2cd01756..9f42aa7599 100644
--- 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.
@@ -81,28 +81,38 @@ struct process_trailer_options {
#define PROCESS_TRAILER_OPTIONS_INIT {0}
-void process_trailers(const char *file,
- const struct process_trailer_options *opts,
- struct list_head *new_trailer_head);
+void parse_trailers_from_config(struct list_head *config_head);
-void trailer_info_get(struct trailer_info *info, const char *str,
- const struct process_trailer_options *opts);
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+ struct list_head *new_trailer_head);
+
+void process_trailers_lists(struct list_head *head,
+ struct list_head *arg_head);
+
+void parse_trailers(const struct process_trailer_options *,
+ struct trailer_info *,
+ const char *str,
+ struct list_head *head);
+
+void trailer_info_get(const struct process_trailer_options *,
+ const char *str,
+ struct trailer_info *);
void trailer_info_release(struct trailer_info *info);
+void trailer_config_init(void);
+void format_trailers(const struct process_trailer_options *,
+ struct list_head *trailers,
+ struct strbuf *out);
+void free_trailers(struct list_head *);
+
/*
- * Format the trailers from the commit msg "msg" into the strbuf "out".
- * Note two caveats about "opts":
- *
- * - this is primarily a helper for pretty.c, and not
- * all of the flags are supported.
- *
- * - this differs from process_trailers slightly in that we always format
- * only the trailer block itself, even if the "only_trailers" option is not
- * set.
+ * Convenience function to format the trailers from the commit msg "msg" into
+ * the strbuf "out". Reuses format_trailers() internally.
*/
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
- const struct process_trailer_options *opts);
+void format_trailers_from_commit(const struct process_trailer_options *,
+ const char *msg,
+ struct strbuf *out);
/*
* An interface for iterating over the trailers found in a particular commit
diff --git a/transport-helper.c b/transport-helper.c
index 49811ef176..8d284b24d5 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -3,13 +3,11 @@
#include "quote.h"
#include "run-command.h"
#include "commit.h"
-#include "diff.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
#include "repository.h"
-#include "revision.h"
#include "remote.h"
#include "string-list.h"
#include "thread-utils.h"
@@ -19,6 +17,7 @@
#include "refspec.h"
#include "transport-internal.h"
#include "protocol.h"
+#include "packfile.h"
static int debug;
@@ -434,6 +433,8 @@ static int fetch_with_fetch(struct transport *transport,
warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf);
}
strbuf_release(&buf);
+
+ reprepare_packed_git(the_repository);
return 0;
}
@@ -628,7 +629,8 @@ static int process_connect_service(struct transport *transport,
ret = run_connect(transport, &cmdbuf);
} else if (data->stateless_connect &&
(get_protocol_version_config() == protocol_v2) &&
- !strcmp("git-upload-pack", name)) {
+ (!strcmp("git-upload-pack", name) ||
+ !strcmp("git-upload-archive", name))) {
strbuf_addf(&cmdbuf, "stateless-connect %s\n", name);
ret = run_connect(transport, &cmdbuf);
if (ret)
@@ -645,6 +647,7 @@ static int process_connect(struct transport *transport,
struct helper_data *data = transport->data;
const char *name;
const char *exec;
+ int ret;
name = for_push ? "git-receive-pack" : "git-upload-pack";
if (for_push)
@@ -652,7 +655,10 @@ static int process_connect(struct transport *transport,
else
exec = data->transport_options.uploadpack;
- return process_connect_service(transport, name, exec);
+ ret = process_connect_service(transport, name, exec);
+ if (ret)
+ do_take_over(transport);
+ return ret;
}
static int connect_helper(struct transport *transport, const char *name,
@@ -662,14 +668,14 @@ static int connect_helper(struct transport *transport, const char *name,
/* Get_helper so connect is inited. */
get_helper(transport);
- if (!data->connect)
- die(_("operation not supported by protocol"));
if (!process_connect_service(transport, name, exec))
die(_("can't connect to subservice %s"), name);
fd[0] = data->helper->out;
fd[1] = data->helper->in;
+
+ do_take_over(transport);
return 0;
}
@@ -684,10 +690,8 @@ static int fetch_refs(struct transport *transport,
get_helper(transport);
- if (process_connect(transport, 0)) {
- do_take_over(transport);
+ if (process_connect(transport, 0))
return transport->vtable->fetch_refs(transport, nr_heads, to_fetch);
- }
/*
* If we reach here, then the server, the client, and/or the transport
@@ -1074,7 +1078,7 @@ static int push_refs_with_export(struct transport *transport,
set_common_push_options(transport, data->name, flags);
if (flags & TRANSPORT_PUSH_FORCE) {
if (set_helper_option(transport, "force", "true") != 0)
- warning(_("helper %s does not support 'force'"), data->name);
+ warning(_("helper %s does not support '--force'"), data->name);
}
helper = get_helper(transport);
@@ -1144,10 +1148,8 @@ static int push_refs(struct transport *transport,
{
struct helper_data *data = transport->data;
- if (process_connect(transport, 1)) {
- do_take_over(transport);
+ if (process_connect(transport, 1))
return transport->vtable->push_refs(transport, remote_refs, flags);
- }
if (!remote_refs) {
fprintf(stderr,
@@ -1188,11 +1190,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
{
get_helper(transport);
- if (process_connect(transport, for_push)) {
- do_take_over(transport);
+ if (process_connect(transport, for_push))
return transport->vtable->get_refs_list(transport, for_push,
transport_options);
- }
return get_refs_list_using_list(transport, for_push);
}
@@ -1210,16 +1210,13 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
data->get_refs_list_called = 1;
helper = get_helper(transport);
- if (data->object_format) {
- write_str_in_full(helper->in, "option object-format\n");
- if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
- exit(128);
- }
+ if (data->object_format)
+ set_helper_option(transport, "object-format", "true");
if (data->push && for_push)
- write_str_in_full(helper->in, "list for-push\n");
+ write_constant(helper->in, "list for-push\n");
else
- write_str_in_full(helper->in, "list\n");
+ write_constant(helper->in, "list\n");
while (1) {
char *eov, *eon;
@@ -1276,10 +1273,8 @@ static int get_bundle_uri(struct transport *transport)
{
get_helper(transport);
- if (process_connect(transport, 0)) {
- do_take_over(transport);
+ if (process_connect(transport, 0))
return transport->vtable->get_bundle_uri(transport);
- }
return -1;
}
diff --git a/transport.c b/transport.c
index 219af8fd50..df518ead70 100644
--- a/transport.c
+++ b/transport.c
@@ -10,9 +10,7 @@
#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"
@@ -26,7 +24,6 @@
#include "transport-internal.h"
#include "protocol.h"
#include "object-name.h"
-#include "object-store-ll.h"
#include "color.h"
#include "bundle-uri.h"
@@ -1470,6 +1467,7 @@ int transport_push(struct repository *r,
if (porcelain && !push_ret)
puts("Done");
else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
+ /* stable plumbing output; do not modify or localize */
fprintf(stderr, "Everything up-to-date\n");
done:
diff --git a/tree-walk.c b/tree-walk.c
index b517792ba2..6565d9ad99 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -11,35 +11,19 @@
#include "json-writer.h"
#include "environment.h"
-static const char *get_mode(const char *str, unsigned int *modep)
-{
- unsigned char c;
- unsigned int mode = 0;
-
- if (*str == ' ')
- return NULL;
-
- while ((c = *str++) != ' ') {
- if (c < '0' || c > '7')
- return NULL;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- return str;
-}
-
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
const char *path;
- unsigned int mode, len;
- const unsigned hashsz = the_hash_algo->rawsz;
+ unsigned int len;
+ uint16_t mode;
+ const unsigned hashsz = desc->algo->rawsz;
if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
strbuf_addstr(err, _("too-short tree object"));
return -1;
}
- path = get_mode(buf, &mode);
+ path = parse_mode(buf, &mode);
if (!path) {
strbuf_addstr(err, _("malformed mode in tree entry"));
return -1;
@@ -54,15 +38,19 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
desc->entry.path = path;
desc->entry.mode = (desc->flags & TREE_DESC_RAW_MODES) ? mode : canon_mode(mode);
desc->entry.pathlen = len - 1;
- oidread(&desc->entry.oid, (const unsigned char *)path + len);
+ oidread_algop(&desc->entry.oid, (const unsigned char *)path + len,
+ desc->algo);
return 0;
}
-static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
- unsigned long size, struct strbuf *err,
+static int init_tree_desc_internal(struct tree_desc *desc,
+ const struct object_id *oid,
+ const void *buffer, unsigned long size,
+ struct strbuf *err,
enum tree_desc_flags flags)
{
+ desc->algo = (oid && oid->algo) ? &hash_algos[oid->algo] : the_hash_algo;
desc->buffer = buffer;
desc->size = size;
desc->flags = flags;
@@ -71,19 +59,21 @@ static int init_tree_desc_internal(struct tree_desc *desc, const void *buffer,
return 0;
}
-void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buffer, unsigned long size)
{
struct strbuf err = STRBUF_INIT;
- if (init_tree_desc_internal(desc, buffer, size, &err, 0))
+ if (init_tree_desc_internal(desc, tree_oid, buffer, size, &err, 0))
die("%s", err.buf);
strbuf_release(&err);
}
-int init_tree_desc_gently(struct tree_desc *desc, const void *buffer, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buffer, unsigned long size,
enum tree_desc_flags flags)
{
struct strbuf err = STRBUF_INIT;
- int result = init_tree_desc_internal(desc, buffer, size, &err, flags);
+ int result = init_tree_desc_internal(desc, oid, buffer, size, &err, flags);
if (result)
error("%s", err.buf);
strbuf_release(&err);
@@ -100,9 +90,9 @@ void *fill_tree_descriptor(struct repository *r,
if (oid) {
buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
if (!buf)
- die("unable to read tree %s", oid_to_hex(oid));
+ die(_("unable to read tree (%s)"), oid_to_hex(oid));
}
- init_tree_desc(desc, buf, size);
+ init_tree_desc(desc, oid, buf, size);
return buf;
}
@@ -119,7 +109,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a)
static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err)
{
const void *buf = desc->buffer;
- const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + the_hash_algo->rawsz;
+ const unsigned char *end = (const unsigned char *)desc->entry.path + desc->entry.pathlen + 1 + desc->algo->rawsz;
unsigned long size = desc->size;
unsigned long len = end - (const unsigned char *)buf;
@@ -633,7 +623,7 @@ int get_tree_entry(struct repository *r,
retval = -1;
} else {
struct tree_desc t;
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, tree_oid, tree, size);
retval = find_tree_entry(r, &t, name, oid, mode);
}
free(tree);
@@ -676,7 +666,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
struct tree_desc t;
int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
- init_tree_desc(&t, NULL, 0UL);
+ init_tree_desc(&t, NULL, NULL, 0UL);
strbuf_addstr(&namebuf, name);
oidcpy(&current_tree_oid, tree_oid);
@@ -712,7 +702,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
goto done;
/* descend */
- init_tree_desc(&t, tree, size);
+ init_tree_desc(&t, &current_tree_oid, tree, size);
}
/* Handle symlinks to e.g. a//b by removing leading slashes */
@@ -746,7 +736,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
free(parent->tree);
parents_nr--;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
continue;
}
@@ -826,7 +816,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
contents_start = contents;
parent = &parents[parents_nr - 1];
- init_tree_desc(&t, parent->tree, parent->size);
+ init_tree_desc(&t, &parent->oid, parent->tree, parent->size);
strbuf_splice(&namebuf, 0, len,
contents_start, link_len);
if (remainder)
diff --git a/tree-walk.h b/tree-walk.h
index a6bfa3da3a..0b1067fbc5 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -24,6 +24,7 @@ struct name_entry {
* A semi-opaque data structure used to maintain the current state of the walk.
*/
struct tree_desc {
+ const struct git_hash_algo *algo;
/*
* pointer into the memory representation of the tree. It always
* points at the current entry being visited.
@@ -83,9 +84,11 @@ int update_tree_entry_gently(struct tree_desc *);
* size parameters are assumed to be the same as the buffer and size
* members of `struct tree`.
*/
-void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
+void init_tree_desc(struct tree_desc *desc, const struct object_id *tree_oid,
+ const void *buf, unsigned long size);
-int init_tree_desc_gently(struct tree_desc *desc, const void *buf, unsigned long size,
+int init_tree_desc_gently(struct tree_desc *desc, const struct object_id *oid,
+ const void *buf, unsigned long size,
enum tree_desc_flags flags);
/*
diff --git a/tree.c b/tree.c
index 990f9c9854..7973d3f9a8 100644
--- a/tree.c
+++ b/tree.c
@@ -1,12 +1,9 @@
#include "git-compat-util.h"
-#include "cache-tree.h"
#include "hex.h"
#include "tree.h"
#include "object-name.h"
#include "object-store-ll.h"
-#include "blob.h"
#include "commit.h"
-#include "tag.h"
#include "alloc.h"
#include "tree-walk.h"
#include "repository.h"
@@ -32,7 +29,7 @@ int read_tree_at(struct repository *r,
if (parse_tree(tree))
return -1;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (retval != all_entries_interesting) {
diff --git a/upload-pack.c b/upload-pack.c
index ea234ab6a4..902144b9d3 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -9,13 +9,10 @@
#include "repository.h"
#include "object-store-ll.h"
#include "oid-array.h"
-#include "tag.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"
@@ -24,16 +21,14 @@
#include "string-list.h"
#include "strvec.h"
#include "trace2.h"
-#include "prio-queue.h"
#include "protocol.h"
-#include "quote.h"
#include "upload-pack.h"
-#include "serve.h"
#include "commit-graph.h"
#include "commit-reach.h"
#include "shallow.h"
#include "write-or-die.h"
#include "json-writer.h"
+#include "strmap.h"
/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
@@ -67,12 +62,11 @@ struct upload_pack_data {
struct string_list symref; /* v0 only */
struct object_array want_obj;
struct object_array have_obj;
- struct oid_array haves; /* v2 only */
- struct string_list wanted_refs; /* v2 only */
+ struct strmap wanted_refs; /* v2 only */
struct strvec hidden_refs;
struct object_array shallows;
- struct string_list deepen_not;
+ struct oidset deepen_not;
struct object_array extra_edge_obj;
int depth;
timestamp_t deepen_since;
@@ -119,6 +113,8 @@ struct upload_pack_data {
unsigned done : 1; /* v2 only */
unsigned allow_ref_in_want : 1; /* v2 only */
unsigned allow_sideband_all : 1; /* v2 only */
+ unsigned seen_haves : 1; /* v2 only */
+ unsigned allow_packfile_uris : 1; /* v2 only */
unsigned advertise_sid : 1;
unsigned sent_capabilities : 1;
};
@@ -126,13 +122,12 @@ struct upload_pack_data {
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 strmap wanted_refs = STRMAP_INIT;
struct strvec hidden_refs = STRVEC_INIT;
struct object_array want_obj = OBJECT_ARRAY_INIT;
struct object_array have_obj = OBJECT_ARRAY_INIT;
- struct oid_array haves = OID_ARRAY_INIT;
struct object_array shallows = OBJECT_ARRAY_INIT;
- struct string_list deepen_not = STRING_LIST_INIT_DUP;
+ struct oidset deepen_not = OID_ARRAY_INIT;
struct string_list uri_protocols = STRING_LIST_INIT_DUP;
struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
struct string_list allowed_filters = STRING_LIST_INIT_DUP;
@@ -143,7 +138,6 @@ static void upload_pack_data_init(struct upload_pack_data *data)
data->hidden_refs = hidden_refs;
data->want_obj = want_obj;
data->have_obj = have_obj;
- data->haves = haves;
data->shallows = shallows;
data->deepen_not = deepen_not;
data->uri_protocols = uri_protocols;
@@ -161,13 +155,12 @@ static void upload_pack_data_init(struct upload_pack_data *data)
static void upload_pack_data_clear(struct upload_pack_data *data)
{
string_list_clear(&data->symref, 1);
- string_list_clear(&data->wanted_refs, 1);
+ strmap_clear(&data->wanted_refs, 1);
strvec_clear(&data->hidden_refs);
object_array_clear(&data->want_obj);
object_array_clear(&data->have_obj);
- oid_array_clear(&data->haves);
object_array_clear(&data->shallows);
- string_list_clear(&data->deepen_not, 0);
+ oidset_clear(&data->deepen_not);
object_array_clear(&data->extra_edge_obj);
list_objects_filter_release(&data->filter_options);
string_list_clear(&data->allowed_filters, 0);
@@ -469,7 +462,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
fail:
free(output_state);
- send_client_data(3, abort_msg, sizeof(abort_msg),
+ send_client_data(3, abort_msg, strlen(abort_msg),
pack_data->use_sideband);
die("git upload-pack: %s", abort_msg);
}
@@ -477,7 +470,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,
static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid)
{
int we_knew_they_have = 0;
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object_with_flags(the_repository, oid,
+ PARSE_OBJECT_SKIP_HASH_CHECK |
+ PARSE_OBJECT_DISCARD_TREE);
if (!o)
die("oops (%s)", oid_to_hex(oid));
@@ -534,8 +529,6 @@ static int get_common_commits(struct upload_pack_data *data,
int got_other = 0;
int sent_ready = 0;
- save_commit_buffer = 0;
-
for (;;) {
const char *arg;
@@ -932,12 +925,13 @@ static int send_shallow_list(struct upload_pack_data *data)
strvec_push(&av, "rev-list");
if (data->deepen_since)
strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
- if (data->deepen_not.nr) {
+ if (oidset_size(&data->deepen_not)) {
+ const struct object_id *oid;
+ struct oidset_iter iter;
strvec_push(&av, "--not");
- for (i = 0; i < data->deepen_not.nr; i++) {
- struct string_list_item *s = data->deepen_not.items + i;
- strvec_push(&av, s->string);
- }
+ oidset_iter_init(&data->deepen_not, &iter);
+ while ((oid = oidset_iter_next(&iter)))
+ strvec_push(&av, oid_to_hex(oid));
strvec_push(&av, "--not");
}
for (i = 0; i < data->want_obj.nr; i++) {
@@ -1013,7 +1007,7 @@ static int process_deepen_since(const char *line, timestamp_t *deepen_since, int
return 0;
}
-static int process_deepen_not(const char *line, struct string_list *deepen_not, int *deepen_rev_list)
+static int process_deepen_not(const char *line, struct oidset *deepen_not, int *deepen_rev_list)
{
const char *arg;
if (skip_prefix(line, "deepen-not ", &arg)) {
@@ -1021,7 +1015,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
struct object_id oid;
if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
die("git upload-pack: ambiguous deepen-not: %s", line);
- string_list_append(deepen_not, ref);
+ oidset_insert(deepen_not, &oid);
free(ref);
*deepen_rev_list = 1;
return 1;
@@ -1157,7 +1151,9 @@ static void receive_needs(struct upload_pack_data *data,
free(client_sid);
}
- o = parse_object(the_repository, &oid_buf);
+ o = parse_object_with_flags(the_repository, &oid_buf,
+ PARSE_OBJECT_SKIP_HASH_CHECK |
+ PARSE_OBJECT_DISCARD_TREE);
if (!o) {
packet_writer_error(&data->writer,
"upload-pack: not our ref %s",
@@ -1368,6 +1364,9 @@ static int upload_pack_config(const char *var, const char *value,
data->allow_ref_in_want = git_config_bool(var, value);
} else if (!strcmp("uploadpack.allowsidebandall", var)) {
data->allow_sideband_all = git_config_bool(var, value);
+ } else if (!strcmp("uploadpack.blobpackfileuri", var)) {
+ if (value)
+ data->allow_packfile_uris = 1;
} else if (!strcmp("core.precomposeunicode", var)) {
precomposed_unicode = git_config_bool(var, value);
} else if (!strcmp("transfer.advertisesid", var)) {
@@ -1391,10 +1390,13 @@ static int upload_pack_protected_config(const char *var, const char *value,
return 0;
}
-static void get_upload_pack_config(struct upload_pack_data *data)
+static void get_upload_pack_config(struct repository *r,
+ struct upload_pack_data *data)
{
- git_config(upload_pack_config, data);
+ repo_config(r, upload_pack_config, data);
git_protected_config(upload_pack_protected_config, data);
+
+ data->allow_sideband_all |= git_env_bool("GIT_TEST_SIDEBAND_ALL", 0);
}
void upload_pack(const int advertise_refs, const int stateless_rpc,
@@ -1404,7 +1406,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
struct upload_pack_data data;
upload_pack_data_init(&data);
- get_upload_pack_config(&data);
+ get_upload_pack_config(the_repository, &data);
data.stateless_rpc = stateless_rpc;
data.timeout = timeout;
@@ -1474,7 +1476,8 @@ static int parse_want(struct packet_writer *writer, const char *line,
"expected to get oid, not '%s'", line);
o = parse_object_with_flags(the_repository, &oid,
- PARSE_OBJECT_SKIP_HASH_CHECK);
+ PARSE_OBJECT_SKIP_HASH_CHECK |
+ PARSE_OBJECT_DISCARD_TREE);
if (!o) {
packet_writer_error(writer,
@@ -1496,14 +1499,13 @@ 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 strmap *wanted_refs,
struct strvec *hidden_refs,
struct object_array *want_obj)
{
const char *refname_nons;
if (skip_prefix(line, "want-ref ", &refname_nons)) {
struct object_id oid;
- struct string_list_item *item;
struct object *o = NULL;
struct strbuf refname = STRBUF_INIT;
@@ -1515,8 +1517,11 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
}
strbuf_release(&refname);
- item = string_list_append(wanted_refs, refname_nons);
- item->util = oiddup(&oid);
+ if (strmap_put(wanted_refs, refname_nons, oiddup(&oid))) {
+ packet_writer_error(writer, "duplicate want-ref %s",
+ refname_nons);
+ die("duplicate want-ref %s", refname_nons);
+ }
if (!starts_with(refname_nons, "refs/tags/")) {
struct commit *commit = lookup_commit_in_graph(the_repository, &oid);
@@ -1538,15 +1543,14 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
return 0;
}
-static int parse_have(const char *line, struct oid_array *haves)
+static int parse_have(const char *line, struct upload_pack_data *data)
{
const char *arg;
if (skip_prefix(line, "have ", &arg)) {
struct object_id oid;
- if (get_oid_hex(arg, &oid))
- die("git upload-pack: expected SHA1 object, got '%s'", arg);
- oid_array_append(haves, &oid);
+ got_oid(data, arg, &oid);
+ data->seen_haves = 1;
return 1;
}
@@ -1558,13 +1562,13 @@ 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, "haves", data->have_obj.nr);
jw_object_intmax(&jw, "wants", data->want_obj.nr);
- jw_object_intmax(&jw, "want-refs", data->wanted_refs.nr);
+ jw_object_intmax(&jw, "want-refs", strmap_get_size(&data->wanted_refs));
jw_object_intmax(&jw, "depth", data->depth);
jw_object_intmax(&jw, "shallows", data->shallows.nr);
jw_object_bool(&jw, "deepen-since", data->deepen_since);
- jw_object_intmax(&jw, "deepen-not", data->deepen_not.nr);
+ jw_object_intmax(&jw, "deepen-not", oidset_size(&data->deepen_not));
jw_object_bool(&jw, "deepen-relative", data->deepen_relative);
if (data->filter_options.choice)
jw_object_string(&jw, "filter", list_object_filter_config_name(data->filter_options.choice));
@@ -1592,7 +1596,7 @@ static void process_args(struct packet_reader *request,
&data->hidden_refs, &data->want_obj))
continue;
/* process have line */
- if (parse_have(arg, &data->haves))
+ if (parse_have(arg, data))
continue;
/* process args like thin-pack */
@@ -1644,14 +1648,17 @@ static void process_args(struct packet_reader *request,
continue;
}
- if ((git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
- data->allow_sideband_all) &&
+ if (data->allow_sideband_all &&
!strcmp(arg, "sideband-all")) {
data->writer.use_sideband = 1;
continue;
}
- if (skip_prefix(arg, "packfile-uris ", &p)) {
+ if (data->allow_packfile_uris &&
+ skip_prefix(arg, "packfile-uris ", &p)) {
+ if (data->uri_protocols.nr)
+ send_err_and_die(data,
+ "multiple packfile-uris lines forbidden");
string_list_split(&data->uri_protocols, p, ',', -1);
continue;
}
@@ -1670,27 +1677,7 @@ static void process_args(struct packet_reader *request,
trace2_fetch_info(data);
}
-static int process_haves(struct upload_pack_data *data, struct oid_array *common)
-{
- int i;
-
- /* Process haves */
- for (i = 0; i < data->haves.nr; i++) {
- const struct object_id *oid = &data->haves.oid[i];
-
- if (!repo_has_object_file_with_flags(the_repository, oid,
- OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT))
- continue;
-
- oid_array_append(common, oid);
-
- do_got_oid(data, oid);
- }
-
- return 0;
-}
-
-static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
+static int send_acks(struct upload_pack_data *data, struct object_array *acks)
{
int i;
@@ -1702,7 +1689,7 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
for (i = 0; i < acks->nr; i++) {
packet_writer_write(&data->writer, "ACK %s\n",
- oid_to_hex(&acks->oid[i]));
+ oid_to_hex(&acks->objects[i].item->oid));
}
if (!data->wait_for_done && ok_to_give_up(data)) {
@@ -1716,13 +1703,11 @@ static int send_acks(struct upload_pack_data *data, struct oid_array *acks)
static int process_haves_and_send_acks(struct upload_pack_data *data)
{
- struct oid_array common = OID_ARRAY_INIT;
int ret = 0;
- process_haves(data, &common);
if (data->done) {
ret = 1;
- } else if (send_acks(data, &common)) {
+ } else if (send_acks(data, &data->have_obj)) {
packet_writer_delim(&data->writer);
ret = 1;
} else {
@@ -1731,24 +1716,23 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)
ret = 0;
}
- oid_array_clear(&data->haves);
- oid_array_clear(&common);
return ret;
}
static void send_wanted_ref_info(struct upload_pack_data *data)
{
- const struct string_list_item *item;
+ struct hashmap_iter iter;
+ const struct strmap_entry *e;
- if (!data->wanted_refs.nr)
+ if (strmap_empty(&data->wanted_refs))
return;
packet_writer_write(&data->writer, "wanted-refs\n");
- for_each_string_list_item(item, &data->wanted_refs) {
+ strmap_for_each_entry(&data->wanted_refs, &iter, e) {
packet_writer_write(&data->writer, "%s %s\n",
- oid_to_hex(item->util),
- item->string);
+ oid_to_hex(e->value),
+ e->key);
}
packet_writer_delim(&data->writer);
@@ -1777,7 +1761,7 @@ enum fetch_state {
FETCH_DONE,
};
-int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
+int upload_pack_v2(struct repository *r, struct packet_reader *request)
{
enum fetch_state state = FETCH_PROCESS_ARGS;
struct upload_pack_data data;
@@ -1786,7 +1770,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
upload_pack_data_init(&data);
data.use_sideband = LARGE_PACKET_MAX;
- get_upload_pack_config(&data);
+ get_upload_pack_config(r, &data);
while (state != FETCH_DONE) {
switch (state) {
@@ -1802,7 +1786,7 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
* they didn't want anything.
*/
state = FETCH_DONE;
- } else if (data.haves.nr) {
+ } else if (data.seen_haves) {
/*
* Request had 'have' lines, so lets ACK them.
*/
@@ -1845,41 +1829,28 @@ int upload_pack_v2(struct repository *r UNUSED, struct packet_reader *request)
int upload_pack_advertise(struct repository *r,
struct strbuf *value)
{
- if (value) {
- int allow_filter_value;
- int allow_ref_in_want;
- int allow_sideband_all_value;
- char *str = NULL;
+ struct upload_pack_data data;
+ upload_pack_data_init(&data);
+ get_upload_pack_config(r, &data);
+
+ if (value) {
strbuf_addstr(value, "shallow wait-for-done");
- if (!repo_config_get_bool(r,
- "uploadpack.allowfilter",
- &allow_filter_value) &&
- allow_filter_value)
+ if (data.allow_filter)
strbuf_addstr(value, " filter");
- if (!repo_config_get_bool(r,
- "uploadpack.allowrefinwant",
- &allow_ref_in_want) &&
- allow_ref_in_want)
+ if (data.allow_ref_in_want)
strbuf_addstr(value, " ref-in-want");
- if (git_env_bool("GIT_TEST_SIDEBAND_ALL", 0) ||
- (!repo_config_get_bool(r,
- "uploadpack.allowsidebandall",
- &allow_sideband_all_value) &&
- allow_sideband_all_value))
+ if (data.allow_sideband_all)
strbuf_addstr(value, " sideband-all");
- if (!repo_config_get_string(r,
- "uploadpack.blobpackfileuri",
- &str) &&
- str) {
+ if (data.allow_packfile_uris)
strbuf_addstr(value, " packfile-uris");
- free(str);
- }
}
+ upload_pack_data_clear(&data);
+
return 1;
}
diff --git a/usage.c b/usage.c
index 09f0ed509b..7a2f7805f5 100644
--- a/usage.c
+++ b/usage.c
@@ -19,8 +19,11 @@ static void vreportf(const char *prefix, const char *err, va_list params)
}
memcpy(msg, prefix, prefix_len);
p = msg + prefix_len;
- if (vsnprintf(p, pend - p, err, params) < 0)
+ if (vsnprintf(p, pend - p, err, params) < 0) {
+ fprintf(stderr, _("error: unable to format message: %s\n"),
+ err);
*p = '\0'; /* vsnprintf() failed, clip at prefix */
+ }
for (; p != pend - 1 && *p; p++) {
if (iscntrl(*p) && *p != '\t' && *p != '\n')
diff --git a/userdiff.c b/userdiff.c
index e399543823..82bc76b910 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -3,6 +3,7 @@
#include "userdiff.h"
#include "attr.h"
#include "strbuf.h"
+#include "environment.h"
static struct userdiff_driver *drivers;
static int ndrivers;
@@ -89,12 +90,48 @@ PATTERNS("cpp",
"|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
"|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
PATTERNS("csharp",
- /* Keywords */
- "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
- /* Methods and constructors */
- "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe|async)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
- /* Properties */
- "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+ /*
+ * Jump over reserved keywords which are illegal method names, but which
+ * can be followed by parentheses without special characters in between,
+ * making them look like methods.
+ */
+ "!(^|[ \t]+)" /* Start of line or whitespace. */
+ "(do|while|for|foreach|if|else|new|default|return|switch|case|throw"
+ "|catch|using|lock|fixed)"
+ "([ \t(]+|$)\n" /* Whitespace, "(", or end of line. */
+ /*
+ * Methods/constructors:
+ * The strategy is to identify a minimum of two groups (any combination
+ * of keywords/type/name) before the opening parenthesis, and without
+ * final unexpected characters, normally only used in ordinary statements.
+ */
+ "^[ \t]*" /* Remove leading whitespace. */
+ "(" /* Start chunk header capture. */
+ "(" /* First group. */
+ "[][[:alnum:]@_.]" /* Name. */
+ "(<[][[:alnum:]@_, \t<>]+>)?" /* Optional generic parameters. */
+ ")+"
+ "([ \t]+" /* Subsequent groups, prepended with space. */
+ "([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+ ")+"
+ "[ \t]*" /* Optional space before parameters start. */
+ "\\(" /* Start of method parameters. */
+ "[^;]*" /* Allow complex parameters, but exclude statements (;). */
+ ")$\n" /* Close chunk header capture. */
+ /*
+ * Properties:
+ * As with methods, expect a minimum of two groups. But, more trivial than
+ * methods, the vast majority of properties long enough to be worth
+ * showing a chunk header for don't include "=:;,()" on the line they are
+ * defined, since they don't have a parameter list.
+ */
+ "^[ \t]*("
+ "([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+ "([ \t]+"
+ "([][[:alnum:]@_.](<[][[:alnum:]@_, \t<>]+>)?)+"
+ ")+" /* Up to here, same as methods regex. */
+ "[^;=:,()]*" /* Compared to methods, no parameter list allowed. */
+ ")$\n"
/* Type definitions */
"^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct|record)[ \t]+.*)$\n"
/* Namespace */
@@ -323,8 +360,7 @@ static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
{
struct find_by_namelen_data *cb_data = priv;
- if (!strncmp(driver->name, cb_data->name, cb_data->len) &&
- !driver->name[cb_data->len]) {
+ if (!xstrncmpz(driver->name, cb_data->name, cb_data->len)) {
cb_data->driver = driver;
return 1; /* tell the caller to stop iterating */
}
@@ -460,7 +496,8 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,
if (!driver->textconv)
return NULL;
- if (driver->textconv_want_cache && !driver->textconv_cache) {
+ if (driver->textconv_want_cache && !driver->textconv_cache &&
+ have_git_dir()) {
struct notes_cache *c = xmalloc(sizeof(*c));
struct strbuf name = STRBUF_INIT;
diff --git a/utf8.c b/utf8.c
index 6a0dd25b0f..6bfaefa28e 100644
--- a/utf8.c
+++ b/utf8.c
@@ -2,7 +2,7 @@
#include "strbuf.h"
#include "utf8.h"
-/* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
+/* This code is originally from https://www.cl.cam.ac.uk/~mgk25/ucs/ */
static const char utf16_be_bom[] = {'\xFE', '\xFF'};
static const char utf16_le_bom[] = {'\xFF', '\xFE'};
diff --git a/utf8.h b/utf8.h
index b68efef6f4..35df76086a 100644
--- 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
*/
diff --git a/walker.c b/walker.c
index 65002a7220..c0fd632d92 100644
--- a/walker.c
+++ b/walker.c
@@ -45,7 +45,7 @@ static int process_tree(struct walker *walker, struct tree *tree)
if (parse_tree(tree))
return -1;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj = NULL;
diff --git a/worktree.c b/worktree.c
index a56a6c2a3d..cf5eea8c93 100644
--- a/worktree.c
+++ b/worktree.c
@@ -12,18 +12,23 @@
#include "wt-status.h"
#include "config.h"
+void free_worktree(struct worktree *worktree)
+{
+ if (!worktree)
+ return;
+ free(worktree->path);
+ free(worktree->id);
+ free(worktree->head_ref);
+ free(worktree->lock_reason);
+ free(worktree->prune_reason);
+ free(worktree);
+}
+
void free_worktrees(struct worktree **worktrees)
{
int i = 0;
-
- for (i = 0; worktrees[i]; i++) {
- free(worktrees[i]->path);
- free(worktrees[i]->id);
- free(worktrees[i]->head_ref);
- free(worktrees[i]->lock_reason);
- free(worktrees[i]->prune_reason);
- free(worktrees[i]);
- }
+ for (i = 0; worktrees[i]; i++)
+ free_worktree(worktrees[i]);
free (worktrees);
}
@@ -51,7 +56,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;
@@ -70,11 +75,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)
+struct worktree *get_linked_worktree(const char *id,
+ int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
@@ -93,7 +100,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);
@@ -118,7 +126,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;
@@ -128,7 +143,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);
@@ -137,7 +152,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;
}
@@ -151,6 +166,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)
@@ -395,9 +415,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;
}
@@ -591,7 +611,7 @@ static void repair_noop(int iserr UNUSED,
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)
@@ -787,9 +807,9 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
static int move_config_setting(const char *key, const char *value,
const char *from_file, const char *to_file)
{
- if (git_config_set_in_file_gently(to_file, key, value))
+ if (git_config_set_in_file_gently(to_file, key, NULL, value))
return error(_("unable to set %s in '%s'"), key, to_file);
- if (git_config_set_in_file_gently(from_file, key, NULL))
+ if (git_config_set_in_file_gently(from_file, key, NULL, NULL))
return error(_("unable to unset %s in '%s'"), key, from_file);
return 0;
}
diff --git a/worktree.h b/worktree.h
index ce45b66de9..f14784a2ff 100644
--- a/worktree.h
+++ b/worktree.h
@@ -58,6 +58,13 @@ struct worktree *find_worktree(struct worktree **list,
const char *arg);
/*
+ * Look up the worktree corresponding to `id`, or NULL of no such worktree
+ * exists.
+ */
+struct worktree *get_linked_worktree(const char *id,
+ int skip_reading_head);
+
+/*
* Return the worktree corresponding to `path`, or NULL if no such worktree
* exists.
*/
@@ -135,6 +142,11 @@ void repair_worktrees(worktree_repair_fn, void *cb_data);
void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
/*
+ * Free up the memory for a worktree.
+ */
+void free_worktree(struct worktree *);
+
+/*
* Free up the memory for worktree(s)
*/
void free_worktrees(struct worktree **);
diff --git a/wrapper.c b/wrapper.c
index 7da15a56da..eeac3741cf 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -5,7 +5,6 @@
#include "abspath.h"
#include "parse.h"
#include "gettext.h"
-#include "repository.h"
#include "strbuf.h"
#include "trace2.h"
diff --git a/write-or-die.c b/write-or-die.c
index 42a2dc73cd..01a9a51fa2 100644
--- a/write-or-die.c
+++ b/write-or-die.c
@@ -18,23 +18,20 @@
*/
void maybe_flush_or_die(FILE *f, const char *desc)
{
- static int skip_stdout_flush = -1;
- struct stat st;
- char *cp;
-
if (f == stdout) {
- if (skip_stdout_flush < 0) {
- /* NEEDSWORK: make this a normal Boolean */
- cp = getenv("GIT_FLUSH");
- if (cp)
- skip_stdout_flush = (atoi(cp) == 0);
- else if ((fstat(fileno(stdout), &st) == 0) &&
- S_ISREG(st.st_mode))
- skip_stdout_flush = 1;
- else
- skip_stdout_flush = 0;
+ static int force_flush_stdout = -1;
+
+ if (force_flush_stdout < 0) {
+ force_flush_stdout = git_env_bool("GIT_FLUSH", -1);
+ if (force_flush_stdout < 0) {
+ struct stat st;
+ if (fstat(fileno(stdout), &st))
+ force_flush_stdout = 1;
+ else
+ force_flush_stdout = !S_ISREG(st.st_mode);
+ }
}
- if (skip_stdout_flush && !ferror(f))
+ if (!force_flush_stdout && !ferror(f))
return;
}
if (fflush(f)) {
diff --git a/wt-status.c b/wt-status.c
index 9f45bf6949..bdfc23e2ae 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -70,7 +70,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
strbuf_vaddf(&sb, fmt, ap);
if (!sb.len) {
if (s->display_comment_prefix) {
- strbuf_addch(&sb, comment_line_char);
+ strbuf_addstr(&sb, comment_line_str);
if (!trail)
strbuf_addch(&sb, ' ');
}
@@ -85,7 +85,7 @@ static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
strbuf_reset(&linebuf);
if (at_bol && s->display_comment_prefix) {
- strbuf_addch(&linebuf, comment_line_char);
+ strbuf_addstr(&linebuf, comment_line_str);
if (*line != '\n' && *line != '\t')
strbuf_addch(&linebuf, ' ');
}
@@ -861,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)
@@ -1027,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, comment_line_char);
+ strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str);
free(summary_content);
}
@@ -1089,11 +1090,14 @@ size_t wt_status_locate_end(const char *s, size_t len)
const char *p;
struct strbuf pattern = STRBUF_INIT;
- strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+ strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line);
if (starts_with(s, pattern.buf + 1))
len = 0;
- else if ((p = strstr(s, pattern.buf)))
- len = p - s + 1;
+ else if ((p = strstr(s, pattern.buf))) {
+ size_t newlen = p - s + 1;
+ if (newlen < len)
+ len = newlen;
+ }
strbuf_release(&pattern);
return len;
}
@@ -1102,16 +1106,19 @@ 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, comment_line_char, "%s", cut_line);
- strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
+ strbuf_commented_addf(buf, comment_line_str, "%s", cut_line);
+ strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str);
}
-void wt_status_add_cut_line(FILE *fp)
+void wt_status_add_cut_line(struct wt_status *s)
{
struct strbuf buf = STRBUF_INIT;
+ if (s->added_cut_line)
+ return;
+ s->added_cut_line = 1;
wt_status_append_cut_line(&buf);
- fputs(buf.buf, fp);
+ fputs(buf.buf, s->fp);
strbuf_release(&buf);
}
@@ -1142,11 +1149,12 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
* file (and even the "auto" setting won't work, since it
* will have checked isatty on stdout). But we then do want
* to insert the scissor line here to reliably remove the
- * diff before committing.
+ * diff before committing, if we didn't already include one
+ * before.
*/
if (s->fp != stdout) {
rev.diffopt.use_color = 0;
- wt_status_add_cut_line(s->fp);
+ wt_status_add_cut_line(s);
}
if (s->verbose > 1 && s->committable) {
/* print_updated() printed a header, so do we */
@@ -1175,8 +1183,6 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
struct strbuf sb = STRBUF_INIT;
const char *cp, *ep, *branch_name;
struct branch *branch;
- char comment_line_string[3];
- int i;
uint64_t t_begin = 0;
assert(s->branch && !s->is_initial);
@@ -1201,20 +1207,15 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
}
}
- i = 0;
- if (s->display_comment_prefix) {
- comment_line_string[i++] = comment_line_char;
- comment_line_string[i++] = ' ';
- }
- comment_line_string[i] = '\0';
-
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
- "%s%.*s", comment_line_string,
+ "%s%s%.*s",
+ s->display_comment_prefix ? comment_line_str : "",
+ s->display_comment_prefix ? " " : "",
(int)(ep - cp), cp);
if (s->display_comment_prefix)
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
- comment_line_char);
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s",
+ comment_line_str);
else
fputs("\n", s->fp);
strbuf_release(&sb);
@@ -1295,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);
@@ -1375,7 +1382,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
git_path("%s", fname));
}
while (!strbuf_getline_lf(&line, f)) {
- if (line.len && line.buf[0] == comment_line_char)
+ if (starts_with(line.buf, comment_line_str))
continue;
strbuf_trim(&line);
if (!line.len)
@@ -1569,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."));
@@ -1733,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;
diff --git a/wt-status.h b/wt-status.h
index ab9cc9d8f0..4e377ce62b 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -23,7 +23,8 @@ enum color_wt_status {
};
enum untracked_status_type {
- SHOW_NO_UNTRACKED_FILES,
+ SHOW_UNTRACKED_FILES_ERROR = -1,
+ SHOW_NO_UNTRACKED_FILES = 0,
SHOW_NORMAL_UNTRACKED_FILES,
SHOW_ALL_UNTRACKED_FILES
};
@@ -94,6 +95,7 @@ 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;
@@ -129,6 +131,7 @@ struct wt_status {
int rename_score;
int rename_limit;
enum wt_status_format status_format;
+ unsigned char added_cut_line; /* boolean */
struct wt_status_state state;
struct object_id oid_commit; /* when not Initial */
@@ -146,7 +149,7 @@ struct wt_status {
size_t wt_status_locate_end(const char *s, size_t len);
void wt_status_append_cut_line(struct strbuf *buf);
-void wt_status_add_cut_line(FILE *fp);
+void wt_status_add_cut_line(struct wt_status *s);
void wt_status_prepare(struct repository *r, struct wt_status *s);
void wt_status_print(struct wt_status *s);
void wt_status_collect(struct wt_status *s);
diff --git a/xdiff-interface.c b/xdiff-interface.c
index adcea109fa..16ed8ac492 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "gettext.h"
#include "config.h"
#include "hex.h"
#include "object-store-ll.h"
@@ -6,8 +7,6 @@
#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 {
@@ -306,6 +305,22 @@ int xdiff_compare_lines(const char *l1, long s1,
return xdl_recmatch(l1, s1, l2, s2, flags);
}
+int parse_conflict_style_name(const char *value)
+{
+ if (!strcmp(value, "diff3"))
+ return XDL_MERGE_DIFF3;
+ else if (!strcmp(value, "zdiff3"))
+ return XDL_MERGE_ZEALOUS_DIFF3;
+ else if (!strcmp(value, "merge"))
+ return 0;
+ /*
+ * Please update _git_checkout() in git-completion.bash when
+ * you add new merge config
+ */
+ else
+ return -1;
+}
+
int git_xmerge_style = -1;
int git_xmerge_config(const char *var, const char *value,
@@ -313,20 +328,11 @@ int git_xmerge_config(const char *var, const char *value,
{
if (!strcmp(var, "merge.conflictstyle")) {
if (!value)
- die("'%s' is not a boolean", var);
- if (!strcmp(value, "diff3"))
- git_xmerge_style = XDL_MERGE_DIFF3;
- else if (!strcmp(value, "zdiff3"))
- git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
- else if (!strcmp(value, "merge"))
- git_xmerge_style = 0;
- /*
- * Please update _git_checkout() in
- * git-completion.bash when you add new merge config
- */
- else
- die("unknown style '%s' given for '%s'",
- value, var);
+ return config_error_nonbool(var);
+ git_xmerge_style = parse_conflict_style_name(value);
+ if (git_xmerge_style == -1)
+ return error(_("unknown style '%s' given for '%s'"),
+ value, var);
return 0;
}
return git_default_config(var, value, ctx, cb);
diff --git a/xdiff-interface.h b/xdiff-interface.h
index e6f80df046..38537169b7 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -51,6 +51,7 @@ 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);
struct config_context;
+int parse_conflict_style_name(const char *value);
int git_xmerge_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
extern int git_xmerge_style;