summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/l10n.yml105
-rw-r--r--.github/workflows/main.yml10
-rw-r--r--.gitignore2
-rw-r--r--Documentation/.gitignore1
-rw-r--r--Documentation/Makefile68
-rw-r--r--Documentation/MyFirstContribution.txt42
-rw-r--r--Documentation/RelNotes/2.33.1.txt138
-rw-r--r--Documentation/RelNotes/2.34.0.txt221
-rw-r--r--Documentation/blame-options.txt11
-rw-r--r--Documentation/config/branch.txt4
-rw-r--r--Documentation/config/color.txt31
-rw-r--r--Documentation/config/gpg.txt43
-rw-r--r--Documentation/config/pack.txt4
-rw-r--r--Documentation/config/pull.txt4
-rw-r--r--Documentation/config/user.txt7
-rw-r--r--Documentation/diff-format.txt2
-rw-r--r--Documentation/git-add.txt9
-rw-r--r--Documentation/git-am.txt2
-rw-r--r--Documentation/git-blame.txt17
-rw-r--r--Documentation/git-cat-file.txt6
-rw-r--r--Documentation/git-checkout.txt5
-rw-r--r--Documentation/git-config.txt3
-rw-r--r--Documentation/git-cvsserver.txt27
-rw-r--r--Documentation/git-help.txt9
-rw-r--r--Documentation/git-http-backend.txt26
-rw-r--r--Documentation/git-multi-pack-index.txt25
-rw-r--r--Documentation/git-pull.txt6
-rw-r--r--Documentation/git-read-tree.txt23
-rw-r--r--Documentation/git-rebase.txt54
-rw-r--r--Documentation/git-repack.txt18
-rw-r--r--Documentation/git-reset.txt3
-rw-r--r--Documentation/git-rm.txt6
-rw-r--r--Documentation/git-send-pack.txt4
-rw-r--r--Documentation/git-status.txt25
-rw-r--r--Documentation/git-svn.txt1
-rw-r--r--Documentation/git-upload-pack.txt8
-rw-r--r--Documentation/git-version.txt28
-rw-r--r--Documentation/git.txt38
-rwxr-xr-xDocumentation/lint-gitlink.perl10
-rwxr-xr-xDocumentation/lint-man-end-blurb.perl2
-rwxr-xr-xDocumentation/lint-man-section-order.perl2
-rw-r--r--Documentation/technical/api-parse-options.txt5
-rw-r--r--Documentation/technical/api-trace2.txt40
-rw-r--r--Documentation/technical/multi-pack-index.txt4
-rw-r--r--Documentation/technical/protocol-v2.txt14
-rw-r--r--Documentation/technical/signature-format.txt24
-rw-r--r--INSTALL15
-rw-r--r--Makefile49
-rw-r--r--add-interactive.c8
-rw-r--r--advice.c11
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c32
-rw-r--r--builtin/am.c7
-rw-r--r--builtin/bisect--helper.c160
-rw-r--r--builtin/blame.c33
-rw-r--r--builtin/branch.c2
-rw-r--r--builtin/bugreport.c46
-rw-r--r--builtin/bundle.c3
-rw-r--r--builtin/cat-file.c54
-rw-r--r--builtin/checkout--worker.c4
-rw-r--r--builtin/checkout.c10
-rw-r--r--builtin/clone.c34
-rw-r--r--builtin/commit-graph.c10
-rw-r--r--builtin/commit.c3
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/credential-cache.c30
-rw-r--r--builtin/difftool.c159
-rw-r--r--builtin/fast-export.c2
-rw-r--r--builtin/for-each-ref.c2
-rw-r--r--builtin/fsck.c44
-rw-r--r--builtin/gc.c8
-rw-r--r--builtin/grep.c5
-rw-r--r--builtin/help.c140
-rw-r--r--builtin/index-pack.c6
-rw-r--r--builtin/ls-files.c22
-rw-r--r--builtin/ls-remote.c4
-rw-r--r--builtin/merge.c6
-rw-r--r--builtin/mktag.c3
-rw-r--r--builtin/multi-pack-index.c88
-rw-r--r--builtin/mv.c52
-rw-r--r--builtin/prune.c1
-rw-r--r--builtin/pull.c9
-rw-r--r--builtin/read-tree.c26
-rw-r--r--builtin/rebase.c387
-rw-r--r--builtin/receive-pack.c7
-rw-r--r--builtin/reflog.c13
-rw-r--r--builtin/remote.c114
-rw-r--r--builtin/repack.c285
-rw-r--r--builtin/reset.c10
-rw-r--r--builtin/rev-parse.c4
-rw-r--r--builtin/rm.c10
-rw-r--r--builtin/send-pack.c8
-rw-r--r--builtin/shortlog.c3
-rw-r--r--builtin/show-branch.c6
-rw-r--r--builtin/stash.c27
-rw-r--r--builtin/submodule--helper.c42
-rw-r--r--builtin/tag.c4
-rw-r--r--builtin/update-ref.c14
-rw-r--r--builtin/worktree.c1
-rw-r--r--cache-tree.c42
-rw-r--r--cache.h109
-rw-r--r--cbtree.h5
-rw-r--r--checkout.c2
-rwxr-xr-xci/install-dependencies.sh2
-rwxr-xr-xci/lib.sh9
-rw-r--r--compat/simple-ipc/ipc-unix-socket.c14
-rw-r--r--compat/simple-ipc/ipc-win32.c179
-rw-r--r--compat/terminal.c75
-rw-r--r--compat/terminal.h3
-rw-r--r--compat/vcbuild/README2
-rw-r--r--compat/win32/lazyload.h14
-rw-r--r--config.c37
-rw-r--r--config.h2
-rw-r--r--config.mak.dev8
-rw-r--r--config.mak.uname12
-rw-r--r--connect.c2
-rw-r--r--contrib/buildsystems/CMakeLists.txt7
-rw-r--r--contrib/coccinelle/xopen.cocci13
-rw-r--r--contrib/completion/git-completion.bash23
-rw-r--r--contrib/credential/gnome-keyring/git-credential-gnome-keyring.c2
-rw-r--r--contrib/credential/libsecret/git-credential-libsecret.c2
-rwxr-xr-xcontrib/rerere-train.sh2
-rw-r--r--credential.c2
-rw-r--r--daemon.c21
-rw-r--r--diff.c4
-rw-r--r--dir.c58
-rw-r--r--editor.c8
-rw-r--r--entry.h2
-rw-r--r--environment.c12
-rw-r--r--fetch-negotiator.c1
-rw-r--r--fmt-merge-msg.c12
-rwxr-xr-xgenerate-hooklist.sh20
-rwxr-xr-xgit-bisect.sh89
-rw-r--r--git-compat-util.h5
-rw-r--r--git-curl-compat.h129
-rwxr-xr-xgit-cvsserver.perl5
-rw-r--r--git-rebase--preserve-merges.sh1057
-rw-r--r--git-sh-setup.sh6
-rwxr-xr-xgit-submodule.sh5
-rwxr-xr-xgit-svn.perl1
-rw-r--r--git.c1
-rw-r--r--gpg-interface.c585
-rw-r--r--gpg-interface.h8
-rw-r--r--grep.c87
-rw-r--r--grep.h11
-rw-r--r--help.c16
-rw-r--r--help.h2
-rw-r--r--hook.c42
-rw-r--r--hook.h16
-rw-r--r--http-backend.c4
-rw-r--r--http.c47
-rw-r--r--http.h1
-rw-r--r--imap-send.c2
-rw-r--r--list.h5
-rw-r--r--lockfile.h2
-rw-r--r--log-tree.c8
-rw-r--r--log-tree.h2
-rw-r--r--ls-refs.c24
-rw-r--r--merge-ort.c65
-rw-r--r--merge-recursive.c79
-rw-r--r--merge.c8
-rw-r--r--mergesort.c121
-rw-r--r--mergetools/xxdiff7
-rw-r--r--midx.c185
-rw-r--r--midx.h16
-rw-r--r--object-file.c277
-rw-r--r--object-store.h23
-rw-r--r--object.c4
-rw-r--r--object.h2
-rw-r--r--oid-array.h2
-rw-r--r--oidset.c5
-rw-r--r--oidset.h5
-rw-r--r--pack-bitmap.c43
-rw-r--r--pack-bitmap.h2
-rw-r--r--pack-check.c3
-rw-r--r--pack-revindex.h4
-rw-r--r--packfile.c39
-rw-r--r--packfile.h4
-rw-r--r--parallel-checkout.c3
-rw-r--r--parse-options.c176
-rw-r--r--parse-options.h35
-rw-r--r--path.h5
-rw-r--r--pathspec.c5
-rw-r--r--pkt-line.c45
-rw-r--r--pkt-line.h10
-rw-r--r--pretty.c4
-rw-r--r--read-cache.c103
-rw-r--r--rebase-interactive.c29
-rw-r--r--rebase-interactive.h2
-rw-r--r--rebase.c5
-rw-r--r--rebase.h1
-rw-r--r--ref-filter.c24
-rw-r--r--ref-filter.h1
-rw-r--r--reflog-walk.c3
-rw-r--r--refs.c95
-rw-r--r--refs.h20
-rw-r--r--refs/debug.c15
-rw-r--r--refs/files-backend.c292
-rw-r--r--refs/packed-backend.c28
-rw-r--r--refs/packed-backend.h4
-rw-r--r--refs/ref-cache.c97
-rw-r--r--refs/ref-cache.h27
-rw-r--r--refs/refs-internal.h89
-rw-r--r--remote-curl.c10
-rw-r--r--remote.c2
-rw-r--r--repo-settings.c115
-rw-r--r--repository.c24
-rw-r--r--repository.h35
-rw-r--r--reset.c3
-rw-r--r--revision.c20
-rw-r--r--revision.h1
-rw-r--r--run-command.c168
-rw-r--r--run-command.h76
-rw-r--r--send-pack.c8
-rw-r--r--sequencer.c91
-rw-r--r--sequencer.h7
-rw-r--r--serve.c120
-rw-r--r--shallow.h4
-rw-r--r--simple-ipc.h27
-rw-r--r--strbuf.c12
-rw-r--r--strbuf.h8
-rw-r--r--streaming.c27
-rw-r--r--string-list.c8
-rw-r--r--string-list.h5
-rw-r--r--strvec.h8
-rw-r--r--submodule-config.h4
-rw-r--r--submodule.c128
-rw-r--r--submodule.h11
-rw-r--r--t/.gitattributes1
-rw-r--r--t/README14
-rw-r--r--t/helper/test-bitmap.c10
-rw-r--r--t/helper/test-dump-untracked-cache.c6
-rw-r--r--t/helper/test-mergesort.c368
-rw-r--r--t/helper/test-oid-array.c4
-rw-r--r--t/helper/test-oidtree.c3
-rw-r--r--t/helper/test-parse-options.c8
-rw-r--r--t/helper/test-prio-queue.c2
-rw-r--r--t/helper/test-read-midx.c29
-rw-r--r--t/helper/test-run-command.c9
-rw-r--r--t/helper/test-simple-ipc.c233
-rw-r--r--t/helper/test-submodule-nested-repo-config.c4
-rw-r--r--t/lib-diff-data.sh22
-rw-r--r--t/lib-diff.sh2
-rw-r--r--t/lib-diff/COPYING361
-rw-r--r--t/lib-diff/README46
-rw-r--r--t/lib-gpg.sh28
-rw-r--r--t/lib-httpd/apache.conf7
-rw-r--r--t/lib-midx.sh8
-rw-r--r--t/lib-subtest.sh95
-rw-r--r--t/oid-info/oid2
-rwxr-xr-xt/perf/aggregate.perl4
-rw-r--r--t/perf/config2
-rwxr-xr-xt/perf/p0071-sort.sh40
-rwxr-xr-xt/perf/p3400-rebase.sh2
-rwxr-xr-xt/perf/p5326-multi-pack-bitmaps.sh15
-rw-r--r--t/perf/perf-lib.sh5
-rwxr-xr-xt/perf/run2
-rwxr-xr-xt/t0000-basic.sh471
-rwxr-xr-xt/t0001-init.sh1
-rwxr-xr-xt/t0002-gitfile.sh1
-rwxr-xr-xt/t0003-attributes.sh1
-rwxr-xr-xt/t0004-unwritable.sh50
-rwxr-xr-xt/t0005-signals.sh2
-rwxr-xr-xt/t0007-git-var.sh2
-rwxr-xr-xt/t0008-ignores.sh1
-rwxr-xr-xt/t0009-prio-queue.sh2
-rwxr-xr-xt/t0010-racy-git.sh1
-rwxr-xr-xt/t0011-hashmap.sh2
-rwxr-xr-xt/t0012-help.sh65
-rwxr-xr-xt/t0013-sha1dc.sh2
-rwxr-xr-xt/t0016-oidmap.sh2
-rwxr-xr-xt/t0017-env-helper.sh1
-rwxr-xr-xt/t0018-advice.sh1
-rwxr-xr-xt/t0022-crlf-rename.sh1
-rwxr-xr-xt/t0024-crlf-archive.sh1
-rwxr-xr-xt/t0025-crlf-renormalize.sh1
-rwxr-xr-xt/t0026-eol-config.sh1
-rwxr-xr-xt/t0029-core-unsetenvvars.sh1
-rwxr-xr-xt/t0030-stripspace.sh1
-rwxr-xr-xt/t0040-parse-options.sh48
-rwxr-xr-xt/t0052-simple-ipc.sh1
-rwxr-xr-xt/t0055-beyond-symlinks.sh1
-rwxr-xr-xt/t0061-run-command.sh1
-rwxr-xr-xt/t0063-string-list.sh1
-rwxr-xr-xt/t0064-oid-array.sh2
-rwxr-xr-xt/t0065-strcmp-offset.sh1
-rwxr-xr-xt/t0066-dir-iterator.sh1
-rwxr-xr-xt/t0067-parse_pathspec_file.sh1
-rwxr-xr-xt/t0069-oidtree.sh1
-rwxr-xr-xt/t0071-sort.sh11
-rwxr-xr-xt/t0090-cache-tree.sh1
-rwxr-xr-xt/t0091-bugreport.sh1
-rwxr-xr-xt/t0210-trace2-normal.sh2
-rwxr-xr-xt/t0211-trace2-perf.sh2
-rwxr-xr-xt/t0212-trace2-event.sh2
-rwxr-xr-xt/t0301-credential-cache.sh32
-rwxr-xr-xt/t1000-read-tree-m-3way.sh2
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1003-read-tree-prefix.sh1
-rwxr-xr-xt/t1006-cat-file.sh297
-rwxr-xr-xt/t1009-read-tree-new-index.sh1
-rwxr-xr-xt/t1010-mktree.sh1
-rwxr-xr-xt/t1012-read-tree-df.sh1
-rwxr-xr-xt/t1013-read-tree-submodule.sh1
-rwxr-xr-xt/t1014-read-tree-confusing.sh2
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh29
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh77
-rwxr-xr-xt/t1100-commit-tree-options.sh1
-rwxr-xr-xt/t1400-update-ref.sh34
-rwxr-xr-xt/t1430-bad-ref-name.sh3
-rwxr-xr-xt/t1450-fsck.sh97
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh54
-rwxr-xr-xt/t1504-ceiling-dirs.sh2
-rwxr-xr-xt/t1510-repo-setup.sh1
-rwxr-xr-xt/t1600-index.sh13
-rwxr-xr-xt/t1700-split-index.sh34
-rwxr-xr-xt/t2002-checkout-cache-u.sh1
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh1
-rwxr-xr-xt/t2004-checkout-cache-temp.sh1
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh1
-rwxr-xr-xt/t2021-checkout-overwrite.sh1
-rwxr-xr-xt/t2050-git-dir-relative.sh1
-rwxr-xr-xt/t2081-parallel-checkout-collisions.sh1
-rwxr-xr-xt/t2200-add-update.sh3
-rwxr-xr-xt/t2300-cd-to-toplevel.sh1
-rwxr-xr-xt/t2500-untracked-overwriting.sh244
-rwxr-xr-xt/t3000-ls-files-others.sh2
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh5
-rwxr-xr-xt/t3002-ls-files-dashpath.sh2
-rwxr-xr-xt/t3003-ls-files-exclude.sh2
-rwxr-xr-xt/t3004-ls-files-basic.sh1
-rwxr-xr-xt/t3005-ls-files-relative.sh1
-rwxr-xr-xt/t3006-ls-files-long.sh2
-rwxr-xr-xt/t3008-ls-files-lazy-init-name-hash.sh1
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh2
-rwxr-xr-xt/t3070-wildmatch.sh1
-rwxr-xr-xt/t3100-ls-tree-restrict.sh2
-rwxr-xr-xt/t3101-ls-tree-dirname.sh2
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh1
-rwxr-xr-xt/t3103-ls-tree-misc.sh1
-rwxr-xr-xt/t3205-branch-color.sh1
-rwxr-xr-xt/t3211-peel-ref.sh1
-rwxr-xr-xt/t3300-funny-names.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh77
-rwxr-xr-xt/t3407-rebase-abort.sh105
-rwxr-xr-xt/t3408-rebase-multi-line.sh10
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh130
-rwxr-xr-xt/t3410-rebase-preserve-dropped-merges.sh90
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh80
-rwxr-xr-xt/t3412-rebase-root.sh37
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh85
-rwxr-xr-xt/t3418-rebase-continue.sh15
-rwxr-xr-xt/t3421-rebase-topology-linear.sh19
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh11
-rwxr-xr-xt/t3425-rebase-topology-merges.sh151
-rwxr-xr-xt/t3427-rebase-subtree.sh26
-rwxr-xr-xt/t3435-rebase-gpg-sign.sh1
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh1
-rwxr-xr-xt/t3601-rm-pathspec-file.sh1
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh40
-rwxr-xr-xt/t3700-add.sh1
-rwxr-xr-xt/t3705-add-sparse-checkout.sh68
-rwxr-xr-xt/t3902-quoted.sh1
-rwxr-xr-xt/t3903-stash.sh58
-rwxr-xr-xt/t3905-stash-include-untracked.sh6
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4003-diff-rename-1.sh4
-rwxr-xr-xt/t4005-diff-rename-2.sh4
-rwxr-xr-xt/t4007-rename-3.sh5
-rwxr-xr-xt/t4008-diff-break-rewrite.sh4
-rwxr-xr-xt/t4009-diff-rename-4.sh4
-rwxr-xr-xt/t4016-diff-quote.sh1
-rwxr-xr-xt/t4019-diff-wserror.sh1
-rwxr-xr-xt/t4022-diff-rewrite.sh6
-rwxr-xr-xt/t4023-diff-rename-typechange.sh11
-rwxr-xr-xt/t4025-hunk-header.sh1
-rwxr-xr-xt/t4026-color.sh2
-rw-r--r--t/t4034/cpp/expect63
-rw-r--r--t/t4034/cpp/post47
-rw-r--r--t/t4034/cpp/pre41
-rwxr-xr-xt/t4151-am-abort.sh39
-rwxr-xr-xt/t4202-log.sh23
-rwxr-xr-xt/t4300-merge-tree.sh2
-rwxr-xr-xt/t5304-prune.sh1
-rwxr-xr-xt/t5312-prune-corruption.sh48
-rwxr-xr-xt/t5319-multi-pack-index.sh27
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh112
-rwxr-xr-xt/t5516-fetch-push.sh19
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5526-fetch-submodules.sh3
-rwxr-xr-xt/t5531-deep-submodule-push.sh3
-rwxr-xr-xt/t5534-push-signed.sh101
-rwxr-xr-xt/t5545-push-options.sh3
-rwxr-xr-xt/t5551-http-fetch-smart.sh33
-rwxr-xr-xt/t5572-pull-submodule.sh3
-rwxr-xr-xt/t5580-unc-paths.sh1
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh4
-rwxr-xr-xt/t5615-alternate-env.sh2
-rwxr-xr-xt/t5701-git-serve.sh75
-rwxr-xr-xt/t5702-protocol-v2.sh13
-rwxr-xr-xt/t5704-protocol-violations.sh15
-rwxr-xr-xt/t6030-bisect-porcelain.sh18
-rwxr-xr-xt/t6200-fmt-merge-msg.sh28
-rwxr-xr-xt/t6415-merge-dir-to-symlink.sh6
-rwxr-xr-xt/t6424-merge-unrelated-index-changes.sh1
-rwxr-xr-xt/t6430-merge-recursive.sh4
-rwxr-xr-xt/t6436-merge-overwrite.sh3
-rwxr-xr-xt/t6437-submodule-merge.sh3
-rwxr-xr-xt/t7001-mv.sh5
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh189
-rwxr-xr-xt/t7031-verify-tag-signed-ssh.sh161
-rwxr-xr-xt/t7101-reset-empty-subdirs.sh11
-rwxr-xr-xt/t7104-reset-hard.sh1
-rwxr-xr-xt/t7112-reset-submodule.sh1
-rwxr-xr-xt/t7201-co.sh1
-rwxr-xr-xt/t7418-submodule-sparse-gitmodules.sh3
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh1
-rwxr-xr-xt/t7510-signed-commit.sh29
-rwxr-xr-xt/t7517-per-repo-email.sh13
-rwxr-xr-xt/t7518-ident-corner-cases.sh2
-rwxr-xr-xt/t7519-status-fsmonitor.sh68
-rwxr-xr-xt/t7528-signed-commit-ssh.sh398
-rwxr-xr-xt/t7600-merge.sh1
-rwxr-xr-xt/t7604-merge-custom-message.sh1
-rwxr-xr-xt/t7700-repack.sh138
-rwxr-xr-xt/t7703-repack-geometric.sh24
-rwxr-xr-xt/t7800-difftool.sh74
-rwxr-xr-xt/t7813-grep-icase-iso.sh1
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh103
-rwxr-xr-xt/t7816-grep-binary-pattern.sh1
-rwxr-xr-xt/t7900-maintenance.sh9
-rwxr-xr-xt/t9400-git-cvsserver-server.sh9
-rw-r--r--t/test-lib.sh45
-rw-r--r--trace.h2
-rw-r--r--trace2.c31
-rw-r--r--trace2.h27
-rw-r--r--trace2/tr2_tgt.h5
-rw-r--r--trace2/tr2_tgt_event.c22
-rw-r--r--trace2/tr2_tgt_normal.c14
-rw-r--r--trace2/tr2_tgt_perf.c15
-rw-r--r--transport.c2
-rw-r--r--transport.h4
-rw-r--r--tree-diff.c4
-rw-r--r--unicode-width.h44
-rw-r--r--unpack-trees.c64
-rw-r--r--unpack-trees.h14
-rw-r--r--urlmatch.h4
-rw-r--r--userdiff.c10
-rw-r--r--wrapper.c12
449 files changed, 9309 insertions, 6028 deletions
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
new file mode 100644
index 0000000000..27f72f0ff3
--- /dev/null
+++ b/.github/workflows/l10n.yml
@@ -0,0 +1,105 @@
+name: git-l10n
+
+on: [push, pull_request_target]
+
+jobs:
+ git-po-helper:
+ if: >-
+ endsWith(github.repository, '/git-po') ||
+ contains(github.head_ref, 'l10n') ||
+ contains(github.ref, 'l10n')
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Setup base and head objects
+ id: setup-tips
+ run: |
+ if test "${{ github.event_name }}" = "pull_request_target"
+ then
+ base=${{ github.event.pull_request.base.sha }}
+ head=${{ github.event.pull_request.head.sha }}
+ else
+ base=${{ github.event.before }}
+ head=${{ github.event.after }}
+ fi
+ echo "::set-output name=base::$base"
+ echo "::set-output name=head::$head"
+ - name: Run partial clone
+ run: |
+ git -c init.defaultBranch=master init --bare .
+ git remote add \
+ --mirror=fetch \
+ origin \
+ https://github.com/${{ github.repository }}
+ # Fetch tips that may be unreachable from github.ref:
+ # - For a forced push, "$base" may be unreachable.
+ # - For a "pull_request_target" event, "$head" may be unreachable.
+ args=
+ for commit in \
+ ${{ steps.setup-tips.outputs.base }} \
+ ${{ steps.setup-tips.outputs.head }}
+ do
+ case $commit in
+ *[^0]*)
+ args="$args $commit"
+ ;;
+ *)
+ # Should not fetch ZERO-OID.
+ ;;
+ esac
+ done
+ git -c protocol.version=2 fetch \
+ --progress \
+ --no-tags \
+ --no-write-fetch-head \
+ --filter=blob:none \
+ origin \
+ ${{ github.ref }} \
+ $args
+ - uses: actions/setup-go@v2
+ with:
+ go-version: '>=1.16'
+ - name: Install git-po-helper
+ run: go install github.com/git-l10n/git-po-helper@main
+ - name: Install other dependencies
+ run: |
+ sudo apt-get update -q &&
+ sudo apt-get install -q -y gettext
+ - name: Run git-po-helper
+ id: check-commits
+ run: |
+ exit_code=0
+ git-po-helper check-commits \
+ --github-action-event="${{ github.event_name }}" -- \
+ ${{ steps.setup-tips.outputs.base }}..${{ steps.setup-tips.outputs.head }} \
+ >git-po-helper.out 2>&1 || exit_code=$?
+ if test $exit_code -ne 0 || grep -q WARNING git-po-helper.out
+ then
+ # Remove ANSI colors which are proper for console logs but not
+ # proper for PR comment.
+ echo "COMMENT_BODY<<EOF" >>$GITHUB_ENV
+ perl -pe 's/\e\[[0-9;]*m//g; s/\bEOF$//g' git-po-helper.out >>$GITHUB_ENV
+ echo "EOF" >>$GITHUB_ENV
+ fi
+ cat git-po-helper.out
+ exit $exit_code
+ - name: Create comment in pull request for report
+ uses: mshick/add-pr-comment@v1
+ 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
+ [#${{ github.run_number }}](${{ env.GITHUB_SERVER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}):
+
+ ```
+
+ ${{ env.COMMENT_BODY }}
+
+ ```
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index b053b01c66..6ed6a9e807 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -88,7 +88,7 @@ jobs:
env:
HOME: ${{runner.workspace}}
NO_PERL: 1
- run: ci/make-test-artifacts.sh artifacts
+ run: . /etc/profile && ci/make-test-artifacts.sh artifacts
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
@@ -115,7 +115,7 @@ jobs:
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: test
shell: bash
- run: ci/run-test-slice.sh ${{matrix.nr}} 10
+ run: . /etc/profile && ci/run-test-slice.sh ${{matrix.nr}} 10
- name: ci/print-test-failures.sh
if: failure()
shell: bash
@@ -198,8 +198,7 @@ jobs:
shell: bash
env:
NO_SVN_TESTS: 1
- GIT_TEST_SKIP_REBASE_P: 1
- run: ci/run-test-slice.sh ${{matrix.nr}} 10
+ run: . /etc/profile && ci/run-test-slice.sh ${{matrix.nr}} 10
- name: ci/print-test-failures.sh
if: failure()
shell: bash
@@ -232,6 +231,9 @@ jobs:
- jobname: linux-gcc-default
cc: gcc
pool: ubuntu-latest
+ - jobname: linux-leaks
+ cc: gcc
+ pool: ubuntu-latest
env:
CC: ${{matrix.vector.cc}}
jobname: ${{matrix.vector.jobname}}
diff --git a/.gitignore b/.gitignore
index b02250a50c..054249b20a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,7 +125,6 @@
/git-range-diff
/git-read-tree
/git-rebase
-/git-rebase--preserve-merges
/git-receive-pack
/git-reflog
/git-remote
@@ -190,6 +189,7 @@
/gitweb/static/gitweb.min.*
/config-list.h
/command-list.h
+/hook-list.h
*.tar.gz
*.dsc
*.deb
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index 9022d48355..1c3771e7d7 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -14,4 +14,5 @@ manpage-base-url.xsl
SubmittingPatches.txt
tmp-doc-diff/
GIT-ASCIIDOCFLAGS
+/.build/
/GIT-EXCLUDED-PROGRAMS
diff --git a/Documentation/Makefile b/Documentation/Makefile
index f5605b7767..911b6bf79c 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -90,6 +90,7 @@ SP_ARTICLES += $(API_DOCS)
TECH_DOCS += MyFirstContribution
TECH_DOCS += MyFirstObjectWalk
TECH_DOCS += SubmittingPatches
+TECH_DOCS += technical/bundle-format
TECH_DOCS += technical/hash-function-transition
TECH_DOCS += technical/http-protocol
TECH_DOCS += technical/index-format
@@ -225,6 +226,7 @@ endif
ifneq ($(findstring $(MAKEFLAGS),s),s)
ifndef V
+ QUIET = @
QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@;
QUIET_XMLTO = @echo ' ' XMLTO $@;
QUIET_DB2TEXI = @echo ' ' DB2TEXI $@;
@@ -232,11 +234,15 @@ ifndef V
QUIET_DBLATEX = @echo ' ' DBLATEX $@;
QUIET_XSLTPROC = @echo ' ' XSLTPROC $@;
QUIET_GEN = @echo ' ' GEN $@;
- QUIET_LINT = @echo ' ' LINT $@;
QUIET_STDERR = 2> /dev/null
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
+
+ QUIET_LINT_GITLINK = @echo ' ' LINT GITLINK $<;
+ QUIET_LINT_MANSEC = @echo ' ' LINT MAN SEC $<;
+ QUIET_LINT_MANEND = @echo ' ' LINT MAN END $<;
+
export V
endif
endif
@@ -284,7 +290,7 @@ install-html: html
../GIT-VERSION-FILE: FORCE
$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
-ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(filter-out lint-docs clean,$(MAKECMDGOALS)),)
-include ../GIT-VERSION-FILE
endif
@@ -343,6 +349,7 @@ GIT-ASCIIDOCFLAGS: FORCE
fi
clean:
+ $(RM) -rf .build/
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
$(RM) *.pdf
@@ -456,14 +463,61 @@ quick-install-html: require-htmlrepo
print-man1:
@for i in $(MAN1_TXT); do echo $$i; done
-lint-docs::
- $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl \
+## Lint: Common
+.build:
+ $(QUIET)mkdir $@
+.build/lint-docs: | .build
+ $(QUIET)mkdir $@
+
+## Lint: gitlink
+.build/lint-docs/gitlink: | .build/lint-docs
+ $(QUIET)mkdir $@
+.build/lint-docs/gitlink/howto: | .build/lint-docs
+ $(QUIET)mkdir $@
+.build/lint-docs/gitlink/config: | .build/lint-docs
+ $(QUIET)mkdir $@
+LINT_DOCS_GITLINK = $(patsubst %.txt,.build/lint-docs/gitlink/%.ok,$(HOWTO_TXT) $(DOC_DEP_TXT))
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/howto
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/config
+$(LINT_DOCS_GITLINK): lint-gitlink.perl
+$(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt
+ $(QUIET_LINT_GITLINK)$(PERL_PATH) lint-gitlink.perl \
+ $< \
$(HOWTO_TXT) $(DOC_DEP_TXT) \
--section=1 $(MAN1_TXT) \
--section=5 $(MAN5_TXT) \
- --section=7 $(MAN7_TXT); \
- $(PERL_PATH) lint-man-end-blurb.perl $(MAN_TXT); \
- $(PERL_PATH) lint-man-section-order.perl $(MAN_TXT);
+ --section=7 $(MAN7_TXT) >$@
+.PHONY: lint-docs-gitlink
+lint-docs-gitlink: $(LINT_DOCS_GITLINK)
+
+## Lint: man-end-blurb
+.build/lint-docs/man-end-blurb: | .build/lint-docs
+ $(QUIET)mkdir $@
+LINT_DOCS_MAN_END_BLURB = $(patsubst %.txt,.build/lint-docs/man-end-blurb/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_END_BLURB): | .build/lint-docs/man-end-blurb
+$(LINT_DOCS_MAN_END_BLURB): lint-man-end-blurb.perl
+$(LINT_DOCS_MAN_END_BLURB): .build/lint-docs/man-end-blurb/%.ok: %.txt
+ $(QUIET_LINT_MANEND)$(PERL_PATH) lint-man-end-blurb.perl $< >$@
+.PHONY: lint-docs-man-end-blurb
+lint-docs-man-end-blurb: $(LINT_DOCS_MAN_END_BLURB)
+
+## Lint: man-section-order
+.build/lint-docs/man-section-order: | .build/lint-docs
+ $(QUIET)mkdir $@
+LINT_DOCS_MAN_SECTION_ORDER = $(patsubst %.txt,.build/lint-docs/man-section-order/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_SECTION_ORDER): | .build/lint-docs/man-section-order
+$(LINT_DOCS_MAN_SECTION_ORDER): lint-man-section-order.perl
+$(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt
+ $(QUIET_LINT_MANSEC)$(PERL_PATH) lint-man-section-order.perl $< >$@
+.PHONY: lint-docs-man-section-order
+lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER)
+
+## Lint: list of targets above
+.PHONY: lint-docs
+lint-docs: lint-docs-gitlink
+lint-docs: lint-docs-man-end-blurb
+lint-docs: lint-docs-man-section-order
ifeq ($(wildcard po/Makefile),po/Makefile)
doc-l10n install-l10n::
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 015cf24631..b20bc8e914 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1029,22 +1029,42 @@ kidding - be patient!)
[[v2-git-send-email]]
=== Sending v2
-Skip ahead to <<reviewing,Responding to Reviews>> for information on how to
-handle comments from reviewers. Continue this section when your topic branch is
-shaped the way you want it to look for your patchset v2.
+This section will focus on how to send a v2 of your patchset. To learn what
+should go into v2, skip ahead to <<reviewing,Responding to Reviews>> for
+information on how to handle comments from reviewers.
+
+We'll reuse our `psuh` topic branch for v2. Before we make any changes, we'll
+mark the tip of our v1 branch for easy reference:
-When you're ready with the next iteration of your patch, the process is fairly
-similar.
+----
+$ git checkout psuh
+$ git branch psuh-v1
+----
-First, generate your v2 patches again:
+Refine your patch series by using `git rebase -i` to adjust commits based upon
+reviewer comments. Once the patch series is ready for submission, generate your
+patches again, but with some new flags:
----
-$ git format-patch -v2 --cover-letter -o psuh/ master..psuh
+$ git format-patch -v2 --cover-letter -o psuh/ --range-diff master..psuh-v1 master..
----
-This will add your v2 patches, all named like `v2-000n-my-commit-subject.patch`,
-to the `psuh/` directory. You may notice that they are sitting alongside the v1
-patches; that's fine, but be careful when you are ready to send them.
+The `--range-diff master..psuh-v1` parameter tells `format-patch` to include a
+range-diff between `psuh-v1` and `psuh` in the cover letter (see
+linkgit:git-range-diff[1]). This helps tell reviewers about the differences
+between your v1 and v2 patches.
+
+The `-v2` parameter tells `format-patch` to output your patches
+as version "2". For instance, you may notice that your v2 patches are
+all named like `v2-000n-my-commit-subject.patch`. `-v2` will also format
+your patches by prefixing them with "[PATCH v2]" instead of "[PATCH]",
+and your range-diff will be prefaced with "Range-diff against v1".
+
+Afer you run this command, `format-patch` will output the patches to the `psuh/`
+directory, alongside the v1 patches. Using a single directory makes it easy to
+refer to the old v1 patches while proofreading the v2 patches, but you will need
+to be careful to send out only the v2 patches. We will use a pattern like
+"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches).
Edit your cover letter again. Now is a good time to mention what's different
between your last version and now, if it's something significant. You do not
@@ -1082,7 +1102,7 @@ to the command:
----
$ git send-email --to=target@example.com
--in-reply-to="<foo.12345.author@example.com>"
- psuh/v2*
+ psuh/v2-*.patch
----
[[single-patch]]
diff --git a/Documentation/RelNotes/2.33.1.txt b/Documentation/RelNotes/2.33.1.txt
new file mode 100644
index 0000000000..b71738e654
--- /dev/null
+++ b/Documentation/RelNotes/2.33.1.txt
@@ -0,0 +1,138 @@
+Git 2.33.1 Release Notes
+========================
+
+This primarily is to backport various fixes accumulated during the
+development towards Git 2.34, the next feature release.
+
+
+Fixes since v2.33
+-----------------
+
+ * The unicode character width table (used for output alignment) has
+ been updated.
+
+ * Input validation of "git pack-objects --stdin-packs" has been
+ corrected.
+
+ * Bugfix for common ancestor negotiation recently introduced in "git
+ push" codepath.
+
+ * "git pull" had various corner cases that were not well thought out
+ around its --rebase backend, e.g. "git pull --ff-only" did not stop
+ but went ahead and rebased when the history on other side is not a
+ descendant of our history. The series tries to fix them up.
+
+ * "git apply" miscounted the bytes and failed to read to the end of
+ binary hunks.
+
+ * "git range-diff" code clean-up.
+
+ * "git commit --fixup" now works with "--edit" again, after it was
+ broken in v2.32.
+
+ * Use upload-artifacts v1 (instead of v2) for 32-bit linux, as the
+ new version has a blocker bug for that architecture.
+
+ * Checking out all the paths from HEAD during the last conflicted
+ step in "git rebase" and continuing would cause the step to be
+ skipped (which is expected), but leaves MERGE_MSG file behind in
+ $GIT_DIR and confuses the next "git commit", which has been
+ corrected.
+
+ * Various bugs in "git rebase -r" have been fixed.
+
+ * mmap() imitation used to call xmalloc() that dies upon malloc()
+ failure, which has been corrected to just return an error to the
+ caller to be handled.
+
+ * "git diff --relative" segfaulted and/or produced incorrect result
+ when there are unmerged paths.
+
+ * The delayed checkout code path in "git checkout" etc. were chatty
+ even when --quiet and/or --no-progress options were given.
+
+ * "git branch -D <branch>" used to refuse to remove a broken branch
+ ref that points at a missing commit, which has been corrected.
+
+ * Build update for Apple clang.
+
+ * The parser for the "--nl" option of "git column" has been
+ corrected.
+
+ * "git upload-pack" which runs on the other side of "git fetch"
+ forgot to take the ref namespaces into account when handling
+ want-ref requests.
+
+ * The sparse-index support can corrupt the index structure by storing
+ a stale and/or uninitialized data, which has been corrected.
+
+ * Buggy tests could damage repositories outside the throw-away test
+ area we created. We now by default export GIT_CEILING_DIRECTORIES
+ to limit the damage from such a stray test.
+
+ * Even when running "git send-email" without its own threaded
+ discussion support, a threading related header in one message is
+ carried over to the subsequent message to result in an unwanted
+ threading, which has been corrected.
+
+ * The output from "git fast-export", when its anonymization feature
+ is in use, showed an annotated tag incorrectly.
+
+ * Recent "diff -m" changes broke "gitk", which has been corrected.
+
+ * "git maintenance" scheduler fix for macOS.
+
+ * A pathname in an advice message has been made cut-and-paste ready.
+
+ * The "git apply -3" code path learned not to bother the lower level
+ merge machinery when the three-way merge can be trivially resolved
+ without the content level merge.
+
+ * The code that optionally creates the *.rev reverse index file has
+ been optimized to avoid needless computation when it is not writing
+ the file out.
+
+ * "git range-diff -I... <range> <range>" segfaulted, which has been
+ corrected.
+
+ * The order in which various files that make up a single (conceptual)
+ packfile has been reevaluated and straightened up. This matters in
+ correctness, as an incomplete set of files must not be shown to a
+ running Git.
+
+ * The "mode" word is useless in a call to open(2) that does not
+ create a new file. Such a call in the files backend of the ref
+ subsystem has been cleaned up.
+
+ * "git update-ref --stdin" failed to flush its output as needed,
+ which potentially led the conversation to a deadlock.
+
+ * When "git am --abort" fails to abort correctly, it still exited
+ with exit status of 0, which has been corrected.
+
+ * Correct nr and alloc members of strvec struct to be of type size_t.
+
+ * "git stash", where the tentative change involves changing a
+ directory to a file (or vice versa), was confused, which has been
+ corrected.
+
+ * "git clone" from a repository whose HEAD is unborn into a bare
+ repository didn't follow the branch name the other side used, which
+ is corrected.
+
+ * "git cvsserver" had a long-standing bug in its authentication code,
+ which has finally been corrected (it is unclear and is a separate
+ question if anybody is seriously using it, though).
+
+ * "git difftool --dir-diff" mishandled symbolic links.
+
+ * Sensitive data in the HTTP trace were supposed to be redacted, but
+ we failed to do so in HTTP/2 requests.
+
+ * "make clean" has been updated to remove leftover .depend/
+ directories, even when it is not told to use them to compute header
+ dependencies.
+
+ * Protocol v0 clients can get stuck parsing a malformed feature line.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.34.0.txt b/Documentation/RelNotes/2.34.0.txt
index 7ce5ab7cc2..fdf7992193 100644
--- a/Documentation/RelNotes/2.34.0.txt
+++ b/Documentation/RelNotes/2.34.0.txt
@@ -4,6 +4,11 @@ Git 2.34 Release Notes
Updates since Git 2.33
----------------------
+Backward compatibility notes
+
+ * The "--preserve-merges" option of "git rebase" has been removed.
+
+
UI, Workflows & Features
* Pathname expansion (like "~username/") learned a way to specify a
@@ -48,6 +53,32 @@ UI, Workflows & Features
entire directory outside the sparse cone to be removed, which is
especially useful when the sparse patterns change.
+ * Taking advantage of the CGI interface, http-backend has been
+ updated to enable protocol v2 automatically when the other side
+ asks for it.
+
+ * The credential-cache helper has been adjusted to Windows.
+
+ * The error in "git help no-such-git-command" is handled better.
+
+ * The unicode character width table (used for output alignment) has
+ been updated.
+
+ * The ref iteration code used to optionally allow dangling refs to be
+ shown, which has been tightened up.
+
+ * "git add", "git mv", and "git rm" have been adjusted to avoid
+ updating paths outside of the sparse-checkout definition unless
+ the user specifies a "--sparse" option.
+
+ * "git repack" has been taught to generate multi-pack reachability
+ bitmaps.
+
+ * "git fsck" has been taught to report mismatch between expected and
+ actual types of an object better.
+
+ * Use ssh public crypto for object and push-cert signing.
+
Performance, Internal Implementation, Development Support etc.
@@ -119,160 +150,234 @@ Performance, Internal Implementation, Development Support etc.
ask the file descriptors open for packfiles to be closed immediately
before spawning commands that may trigger auto-gc.
+ * An oddball OPTION_ARGUMENT feature has been removed from the
+ parse-options API.
+
+ * The mergesort implementation used to sort linked list has been
+ optimized.
+
+ * Remove external declaration of functions that no longer exist.
+
+ * "git multi-pack-index write --bitmap" learns to propagate the
+ hashcache from original bitmap to resulting bitmap.
+
+ * CI learns to run the leak sanitizer builds.
+
+ * "git grep --recurse-submodules" takes trees and blobs from the
+ submodule repository, but the textconv settings when processing a
+ blob from the submodule is not taken from the submodule repository.
+ A test is added to demonstrate the issue, without fixing it.
+
+ * Teach "git help -c" into helping the command line completion of
+ configuration variables.
+
+ * When "git cmd -h" shows more than one line of usage text (e.g.
+ the cmd subcommand may take sub-sub-command), parse-options API
+ learned to align these lines, even across i18n/l10n.
+
+ * Prevent "make sparse" from running for the source files that
+ haven't been modified.
+
+ * The codepath to write a new version of .midx multi-pack index files
+ has learned to release the mmaped memory holding the current
+ version of .midx before removing them from the disk, as some
+ platforms do not allow removal of a file that still has mapping.
+
Fixes since v2.33
-----------------
* Input validation of "git pack-objects --stdin-packs" has been
corrected.
- (merge 561fa03529 ab/pack-stdin-packs-fix later to maint).
* Bugfix for common ancestor negotiation recently introduced in "git
push" code path.
- (merge 82823118b9 jt/push-negotiation-fixes later to maint).
* "git pull" had various corner cases that were not well thought out
around its --rebase backend, e.g. "git pull --ff-only" did not stop
but went ahead and rebased when the history on other side is not a
descendant of our history. The series tries to fix them up.
- (merge 6f843a3355 en/pull-conflicting-options later to maint).
* "git apply" miscounted the bytes and failed to read to the end of
binary hunks.
- (merge 46d723ce57 jk/apply-binary-hunk-parsing-fix later to maint).
* "git range-diff" code clean-up.
- (merge c4d5907324 jk/range-diff-fixes later to maint).
* "git commit --fixup" now works with "--edit" again, after it was
broken in v2.32.
- (merge 8ef6aad664 jk/commit-edit-fixup-fix later to maint).
* Use upload-artifacts v1 (instead of v2) for 32-bit linux, as the
new version has a blocker bug for that architecture.
- (merge 3cf9bb36bf cb/ci-use-upload-artifacts-v1 later to maint).
* Checking out all the paths from HEAD during the last conflicted
step in "git rebase" and continuing would cause the step to be
skipped (which is expected), but leaves MERGE_MSG file behind in
$GIT_DIR and confuses the next "git commit", which has been
corrected.
- (merge e5ee33e855 pw/rebase-skip-final-fix later to maint).
* Various bugs in "git rebase -r" have been fixed.
- (merge f2563c9ef3 pw/rebase-r-fixes later to maint).
* mmap() imitation used to call xmalloc() that dies upon malloc()
failure, which has been corrected to just return an error to the
caller to be handled.
- (merge 95b4ff3931 rs/git-mmap-uses-malloc later to maint).
* "git diff --relative" segfaulted and/or produced incorrect result
when there are unmerged paths.
- (merge 8174627b3d dd/diff-files-unmerged-fix later to maint).
* The delayed checkout code path in "git checkout" etc. were chatty
even when --quiet and/or --no-progress options were given.
- (merge 7a132c628e mt/quiet-with-delayed-checkout later to maint).
* "git branch -D <branch>" used to refuse to remove a broken branch
ref that points at a missing commit, which has been corrected.
- (merge 597a977489 rs/branch-allow-deleting-dangling later to maint).
* Build update for Apple clang.
- (merge f32c5d3716 cb/makefile-apple-clang later to maint).
* The parser for the "--nl" option of "git column" has been
corrected.
- (merge c93ca46cf5 sg/column-nl later to maint).
* "git upload-pack" which runs on the other side of "git fetch"
forgot to take the ref namespaces into account when handling
want-ref requests.
- (merge 53a66ec37c ka/want-ref-in-namespace later to maint).
* The sparse-index support can corrupt the index structure by storing
a stale and/or uninitialized data, which has been corrected.
- (merge d9e9b44d7a jh/sparse-index-resize-fix later to maint).
* Buggy tests could damage repositories outside the throw-away test
area we created. We now by default export GIT_CEILING_DIRECTORIES
to limit the damage from such a stray test.
- (merge 614c3d8f2e sg/set-ceiling-during-tests later to maint).
* Even when running "git send-email" without its own threaded
discussion support, a threading related header in one message is
carried over to the subsequent message to result in an unwanted
threading, which has been corrected.
- (merge e082113484 mh/send-email-reset-in-reply-to later to maint).
* The output from "git fast-export", when its anonymization feature
is in use, showed an annotated tag incorrectly.
- (merge 2f040a9671 tk/fast-export-anonymized-tag-fix later to maint).
* Doc update plus improved error reporting.
- (merge 1e93770888 jk/log-warn-on-bogus-encoding later to maint).
* Recent "diff -m" changes broke "gitk", which has been corrected.
- (merge 5acffd3473 so/diff-index-regression-fix later to maint).
* Regression fix.
- (merge b996f84989 ab/send-email-config-fix later to maint).
* The "git apply -3" code path learned not to bother the lower level
merge machinery when the three-way merge can be trivially resolved
without the content level merge. This fixes a regression caused by
recent "-3way first and fall back to direct application" change.
- (merge 57f183b698 jc/trivial-threeway-binary-merge later to maint).
* The code that optionally creates the *.rev reverse index file has
been optimized to avoid needless computation when it is not writing
the file out.
- (merge 8fe8bae9d2 ab/reverse-midx-optim later to maint).
* "git range-diff -I... <range> <range>" segfaulted, which has been
corrected.
- (merge 709b3f32d3 rs/range-diff-avoid-segfault-with-I later to maint).
* The order in which various files that make up a single (conceptual)
packfile has been reevaluated and straightened up. This matters in
correctness, as an incomplete set of files must not be shown to a
running Git.
- (merge 4bc1fd6e39 tb/pack-finalize-ordering later to maint).
* The "mode" word is useless in a call to open(2) that does not
create a new file. Such a call in the files backend of the ref
subsystem has been cleaned up.
- (merge 35cf94eaf6 rs/no-mode-to-open-when-appending later to maint).
+
+ * "git update-ref --stdin" failed to flush its output as needed,
+ which potentially led the conversation to a deadlock.
+
+ * When "git am --abort" fails to abort correctly, it still exited
+ with exit status of 0, which has been corrected.
+
+ * Correct nr and alloc members of strvec struct to be of type size_t.
+
+ * "git stash", where the tentative change involves changing a
+ directory to a file (or vice versa), was confused, which has been
+ corrected.
+
+ * "git clone" from a repository whose HEAD is unborn into a bare
+ repository didn't follow the branch name the other side used, which
+ is corrected.
+
+ * "git cvsserver" had a long-standing bug in its authentication code,
+ which has finally been corrected (it is unclear and is a separate
+ question if anybody is seriously using it, though).
+
+ * "git difftool --dir-diff" mishandled symbolic links.
+
+ * Sensitive data in the HTTP trace were supposed to be redacted, but
+ we failed to do so in HTTP/2 requests.
+
+ * "make clean" has been updated to remove leftover .depend/
+ directories, even when it is not told to use them to compute header
+ dependencies.
+
+ * Protocol v0 clients can get stuck parsing a malformed feature line.
+
+ * A few kinds of changes "git status" can show were not documented.
+ (merge d2a534c515 ja/doc-status-types-and-copies later to maint).
+
+ * The mergesort implementation used to sort linked list has been
+ optimized.
+ (merge c90cfc225b rs/mergesort later to maint).
+
+ * An editor session launched during a Git operation (e.g. during 'git
+ commit') can leave the terminal in a funny state. The code path
+ has updated to save the terminal state before, and restore it
+ after, it spawns an editor.
+ (merge 3d411afabc cm/save-restore-terminal later to maint).
+
+ * "git cat-file --batch" with the "--batch-all-objects" option is
+ supposed to iterate over all the objects found in a repository, but
+ it used to translate these object names using the replace mechanism,
+ which defeats the point of enumerating all objects in the repository.
+ This has been corrected.
+ (merge bf972896d7 jk/cat-file-batch-all-wo-replace later to maint).
+
+ * Recent sparse-index work broke safety against attempts to add paths
+ with trailing slashes to the index, which has been corrected.
+ (merge c8ad9d04c6 rs/make-verify-path-really-verify-again later to maint).
+
+ * The "--color-lines" and "--color-by-age" options of "git blame"
+ have been missing, which are now documented.
+ (merge 8c32856133 bs/doc-blame-color-lines later to maint).
+
+ * The PATH used in CI job may be too wide and let incompatible dlls
+ to be grabbed, which can cause the build&test to fail. Tighten it.
+ (merge 7491ef6198 js/windows-ci-path-fix later to maint).
+
+ * Avoid performance measurements from getting ruined by gc and other
+ housekeeping pauses interfering in the middle.
+ (merge be79131a53 rs/disable-gc-during-perf-tests later to maint).
+
+ * Stop "git add --dry-run" from creating new blob and tree objects.
+ (merge e578d0311d rs/add-dry-run-without-objects later to maint).
+
+ * "git commit" gave duplicated error message when the object store
+ was unwritable, which has been corrected.
+ (merge 4ef91a2d79 ab/fix-commit-error-message-upon-unwritable-object-store later to maint).
+
+ * Recent sparse-index addition, namely any use of index_name_pos(),
+ can expand sparse index entries and breaks any code that walks
+ cache-tree or existing index entries. One such instance of such a
+ breakage has been corrected.
+
+ * The xxdiff difftool backend can exit with status 128, which the
+ difftool-helper that launches the backend takes as a significant
+ failure, when it is not significant at all. Work it around.
+ (merge 571f4348dd da/mergetools-special-case-xxdiff-exit-128 later to maint).
+
+ * Improve test framework around unwritable directories.
+ (merge 5d22e18965 ab/test-cleanly-recreate-trash-directory later to maint).
* Other code cleanup, docfix, build fix, etc.
- (merge 1d9c8daef8 ab/bundle-doc later to maint).
- (merge 81483fe613 en/merge-strategy-docs later to maint).
- (merge 626beebdf8 js/log-protocol-version later to maint).
- (merge 00e302da76 cb/builtin-merge-format-string-fix later to maint).
- (merge ad51ae4dc0 cb/ci-freebsd-update later to maint).
- (merge be6444d1ca fc/completion-updates later to maint).
- (merge ff7b83f562 ti/tcsh-completion-regression-fix later to maint).
- (merge 325b06deda sg/make-fix-ar-invocation later to maint).
- (merge bd72824c60 me/t5582-cleanup later to maint).
- (merge f6a5af0f62 ga/send-email-sendmail-cmd later to maint).
- (merge f58c7468cd ab/ls-remote-packet-trace later to maint).
- (merge 0160f7e725 ab/rebase-fatal-fatal-fix later to maint).
- (merge a16eb6b1ff js/maintenance-launchctl-fix later to maint).
- (merge c21b2511c2 jk/t5323-no-pack-test-fix later to maint).
- (merge 5146c2f148 mh/credential-leakfix later to maint).
- (merge 1549577338 dd/t6300-wo-gpg-fix later to maint).
- (merge 66e905b7dd rs/xopen-reports-open-failures later to maint).
- (merge 469888e6a5 es/walken-tutorial-fix later to maint).
- (merge 88682b016d ba/object-info later to maint).
- (merge b45c172e51 ab/gc-log-rephrase later to maint).
- (merge ccdd5d1eb1 ab/mailmap-leakfix later to maint).
- (merge 6540b71614 cb/remote-ndebug-fix later to maint).
- (merge e4f8d27585 rs/show-branch-simplify later to maint).
- (merge e124ecf7f7 rs/archive-use-object-id later to maint).
- (merge cebead1ebf cb/ci-build-pedantic later to maint).
- (merge ca0cc98e03 bs/doc-bugreport-outdir later to maint).
- (merge 72b113e562 ab/no-more-check-bindir later to maint).
- (merge 92a5d1c9b4 jc/prefix-filename-allocates later to maint).
- (merge d9a65b6c0a rs/setup-use-xopen-and-xdup later to maint).
- (merge e8f55568de jk/t5562-racefix later to maint).
+ (merge f188160be9 ab/bundle-remove-verbose-option later to maint).
+ (merge 8c6b4332b4 rs/close-pack-leakfix later to maint).
+ (merge 51b04c05b7 bs/difftool-msg-tweak later to maint).
+ (merge dd20e4a6db ab/make-compdb-fix later to maint).
+ (merge 6ffb990dc4 os/status-docfix later to maint).
+ (merge 100c2da2d3 rs/p3400-lose-tac later to maint).
+ (merge 76f3b69896 tb/aggregate-ignore-leading-whitespaces later to maint).
+ (merge 6e4fd8bfcd tz/doc-link-to-bundle-format-fix later to maint).
+ (merge f6c013dfa1 jc/doc-commit-header-continuation-line later to maint).
+ (merge ec9a37d69b ab/pkt-line-cleanup later to maint).
+ (merge 8650c6298c ab/fix-make-lint-docs later to maint).
+ (merge 1c720357ce ab/test-lib-diff-cleanup later to maint).
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 117f4cf806..9a663535f4 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -136,5 +136,16 @@ take effect.
option. An empty file name, `""`, will clear the list of revs from
previously processed files.
+--color-lines::
+ Color line annotations in the default format differently if they come from
+ the same commit as the preceding line. This makes it easier to distinguish
+ code blocks introduced by different commits. The color defaults to cyan and
+ can be adjusted using the `color.blame.repeatedLines` config option.
+
+--color-by-age::
+ Color line annotations depending on the age of the line in the default format.
+ The `color.blame.highlightRecent` config option controls what color is used for
+ each range of age.
+
-h::
Show help message.
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index cc5f3249fc..d323d7327f 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -85,10 +85,6 @@ When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
-`--preserve-merges` along to 'git rebase' so that locally committed merge
-commits will not be flattened by running 'git pull'.
-+
When the value is `interactive` (or just 'i'), the rebase is run in interactive
mode.
+
diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt
index e05d520a86..6e817f6047 100644
--- a/Documentation/config/color.txt
+++ b/Documentation/config/color.txt
@@ -9,26 +9,27 @@ color.advice.hint::
Use customized color for hints.
color.blame.highlightRecent::
- This can be used to color the metadata of a blame line depending
- on age of the line.
+ Specify the line annotation color for `git blame --color-by-age`
+ depending upon the age of the line.
+
-This setting should be set to a comma-separated list of color and date settings,
-starting and ending with a color, the dates should be set from oldest to newest.
-The metadata will be colored given the colors if the line was introduced
-before the given timestamp, overwriting older timestamped colors.
+This setting should be set to a comma-separated list of color and
+date settings, starting and ending with a color, the dates should be
+set from oldest to newest. The metadata will be colored with the
+specified colors if the line was introduced before the given
+timestamp, overwriting older timestamped colors.
+
-Instead of an absolute timestamp relative timestamps work as well, e.g.
-2.weeks.ago is valid to address anything older than 2 weeks.
+Instead of an absolute timestamp relative timestamps work as well,
+e.g. `2.weeks.ago` is valid to address anything older than 2 weeks.
+
-It defaults to 'blue,12 month ago,white,1 month ago,red', which colors
-everything older than one year blue, recent changes between one month and
-one year old are kept white, and lines introduced within the last month are
-colored red.
+It defaults to `blue,12 month ago,white,1 month ago,red`, which
+colors everything older than one year blue, recent changes between
+one month and one year old are kept white, and lines introduced
+within the last month are colored red.
color.blame.repeatedLines::
- Use the customized color for the part of git-blame output that
- is repeated meta information per line (such as commit id,
- author name, date and timezone). Defaults to cyan.
+ Use the specified color to colorize line annotations for
+ `git blame --color-lines`, if they come from the same commit as the
+ preceding line. Defaults to cyan.
color.branch::
A boolean to enable/disable color in the output of
diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt
index d94025cb36..4f30c7dbdd 100644
--- a/Documentation/config/gpg.txt
+++ b/Documentation/config/gpg.txt
@@ -11,13 +11,13 @@ gpg.program::
gpg.format::
Specifies which key format to use when signing with `--gpg-sign`.
- Default is "openpgp" and another possible value is "x509".
+ Default is "openpgp". Other possible values are "x509", "ssh".
gpg.<format>.program::
Use this to customize the program used for the signing format you
chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
be used as a legacy synonym for `gpg.openpgp.program`. The default
- value for `gpg.x509.program` is "gpgsm".
+ value for `gpg.x509.program` is "gpgsm" and `gpg.ssh.program` is "ssh-keygen".
gpg.minTrustLevel::
Specifies a minimum trust level for signature verification. If
@@ -33,3 +33,42 @@ gpg.minTrustLevel::
* `marginal`
* `fully`
* `ultimate`
+
+gpg.ssh.defaultKeyCommand:
+ This command that will be run when user.signingkey is not set and a ssh
+ signature is requested. On successful exit a valid ssh public key is
+ expected in the first line of its output. To automatically use the first
+ available key from your ssh-agent set this to "ssh-add -L".
+
+gpg.ssh.allowedSignersFile::
+ A file containing ssh public keys which you are willing to trust.
+ The file consists of one or more lines of principals followed by an ssh
+ public key.
+ e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1...
+ See ssh-keygen(1) "ALLOWED SIGNERS" for details.
+ The principal is only used to identify the key and is available when
+ verifying a signature.
++
+SSH has no concept of trust levels like gpg does. To be able to differentiate
+between valid signatures and trusted signatures the trust level of a signature
+verification is set to `fully` when the public key is present in the allowedSignersFile.
+Otherwise the trust level is `undefined` and git verify-commit/tag will fail.
++
+This file can be set to a location outside of the repository and every developer
+maintains their own trust store. A central repository server could generate this
+file automatically from ssh keys with push access to verify the code against.
+In a corporate setting this file is probably generated at a global location
+from automation that already handles developer ssh keys.
++
+A repository that only allows signed commits can store the file
+in the repository itself using a path relative to the top-level of the working tree.
+This way only committers with an already valid key can add or change keys in the keyring.
++
+Using a SSH CA key with the cert-authority option
+(see ssh-keygen(1) "CERTIFICATES") is also valid.
+
+gpg.ssh.revocationFile::
+ Either a SSH KRL or a list of revoked public keys (without the principal prefix).
+ See ssh-keygen(1) for details.
+ If a public key is found in this file then it will always be treated
+ as having trust level "never" and signatures will show as invalid.
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 763f7af7c4..ad7f73a1ea 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -159,6 +159,10 @@ pack.writeBitmapHashCache::
between an older, bitmapped pack and objects that have been
pushed since the last gc). The downside is that it consumes 4
bytes per object of disk space. Defaults to true.
++
+When writing a multi-pack reachability bitmap, no new namehashes are
+computed; instead, any namehashes stored in an existing bitmap are
+permuted into their appropriate location when writing a new bitmap.
pack.writeReverseIndex::
When true, git will write a corresponding .rev file (see:
diff --git a/Documentation/config/pull.txt b/Documentation/config/pull.txt
index 5404830609..9349e09261 100644
--- a/Documentation/config/pull.txt
+++ b/Documentation/config/pull.txt
@@ -18,10 +18,6 @@ When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
-`--preserve-merges` along to 'git rebase' so that locally committed merge
-commits will not be flattened by running 'git pull'.
-+
When the value is `interactive` (or just 'i'), the rebase is run in interactive
mode.
+
diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt
index 59aec7c3ae..ad78dce9ec 100644
--- a/Documentation/config/user.txt
+++ b/Documentation/config/user.txt
@@ -36,3 +36,10 @@ user.signingKey::
commit, you can override the default selection with this variable.
This option is passed unchanged to gpg's --local-user parameter,
so you may specify a key using any method that gpg supports.
+ If gpg.format is set to "ssh" this can contain the literal ssh public
+ key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
+ corresponds to the private key used for signing. The private key
+ needs to be available via ssh-agent. Alternatively it can be set to
+ a file containing a private key directly. If not set git will call
+ gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
+ key available.
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index fbbd410a84..7a9c3b6ff4 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -59,7 +59,7 @@ Possible status letters are:
- D: deletion of a file
- M: modification of the contents or mode of a file
- R: renaming of a file
-- T: change in the type of the file
+- T: change in the type of the file (regular file, symbolic link or submodule)
- U: file is unmerged (you must complete the merge before it can
be committed)
- X: "unknown" change type (most probably a bug, please report it)
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index be5e3ac54b..11eb70f16c 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]]
+ [--edit | -e] [--[no-]all | --[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>...]
@@ -79,6 +79,13 @@ in linkgit:gitglossary[7].
--force::
Allow adding otherwise ignored files.
+--sparse::
+ Allow updating index entries outside of the sparse-checkout cone.
+ Normally, `git add` refuses to update index entries whose paths do
+ not fit within the sparse-checkout cone, since those files might
+ be removed from the working tree without warning. See
+ linkgit:git-sparse-checkout[1] for more details.
+
-i::
--interactive::
Add modified contents in the working tree interactively to
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 8714dfcb76..0a4a984dfd 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -178,6 +178,8 @@ default. You can use `--no-utf8` to override this.
--abort::
Restore the original branch and abort the patching operation.
+ Revert contents of files involved in the am operation to their
+ pre-am state.
--quit::
Abort the patching operation but keep HEAD and the index
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 3bf5d5d8b4..d7a46cc674 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -11,8 +11,8 @@ SYNOPSIS
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--ignore-rev <rev>] [--ignore-revs-file <file>]
- [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
- [--] <file>
+ [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
+ [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
DESCRIPTION
-----------
@@ -93,6 +93,19 @@ include::blame-options.txt[]
is used for a caret to mark the boundary commit.
+THE DEFAULT FORMAT
+------------------
+
+When neither `--porcelain` nor `--incremental` option is specified,
+`git blame` will output annotation for each line with:
+
+- abbreviated object name for the commit the line came from;
+- author ident (by default author name and date, unless `-s` or `-e`
+ is specified); and
+- line number
+
+before the line contents.
+
THE PORCELAIN FORMAT
--------------------
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 4eb0421b3f..27b27e2b30 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -94,8 +94,10 @@ OPTIONS
Instead of reading a list of objects on stdin, perform the
requested batch operation on all objects in the repository and
any alternate object stores (not just reachable objects).
- Requires `--batch` or `--batch-check` be specified. Note that
- the objects are visited in order sorted by their hashes.
+ Requires `--batch` or `--batch-check` be specified. By default,
+ the objects are visited in order sorted by their hashes; see
+ also `--unordered` below. Objects are presented as-is, without
+ respecting the "replace" mechanism of linkgit:git-replace[1].
--buffer::
Normally batch output is flushed after each object is output, so
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index b1a6fe4499..d473c9bf38 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,8 +118,9 @@ OPTIONS
-f::
--force::
When switching branches, proceed even if the index or the
- working tree differs from `HEAD`. This is used to throw away
- local changes.
+ working tree differs from `HEAD`, and even if there are untracked
+ files in the way. This is used to throw away local changes and
+ any untracked files or directories that are in the way.
+
When checking out paths from the index, do not fail upon unmerged
entries; instead, unmerged entries are ignored.
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2dc4bae6da..992225f612 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -71,6 +71,9 @@ codes are:
On success, the command returns the exit code 0.
+A list of all available configuration variables can be obtained using the
+`git help --config` command.
+
[[OPTIONS]]
OPTIONS
-------
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index f2e4a47ebe..4dc57ed254 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -99,7 +99,7 @@ looks like
------
-Only anonymous access is provided by pserve by default. To commit you
+Only anonymous access is provided by pserver by default. To commit you
will have to create pserver accounts, simply add a gitcvs.authdb
setting in the config file of the repositories you want the cvsserver
to allow writes to, for example:
@@ -114,21 +114,20 @@ The format of these files is username followed by the encrypted password,
for example:
------
- myuser:$1Oyx5r9mdGZ2
- myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+ myuser:sqkNi8zPf01HI
+ myuser:$1$9K7FzU28$VfF6EoPYCJEYcVQwATgOP/
+ myuser:$5$.NqmNH1vwfzGpV8B$znZIcumu1tNLATgV2l6e1/mY8RzhUDHMOaVOeL1cxV3
------
You can use the 'htpasswd' facility that comes with Apache to make these
-files, but Apache's MD5 crypt method differs from the one used by most C
-library's crypt() function, so don't use the -m option.
+files, but only with the -d option (or -B if your system suports it).
-Alternatively you can produce the password with perl's crypt() operator:
------
- perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
------
+Preferably use the system specific utility that manages password hash
+creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or
+pwhash in NetBSD) and paste it in the right location.
Then provide your password via the pserver method, for example:
------
- cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+ cvs -d:pserver:someuser:somepassword@server:/path/repo.git co <HEAD_name>
------
No special setup is needed for SSH access, other than having Git tools
in the PATH. If you have clients that do not accept the CVS_SERVER
@@ -138,7 +137,7 @@ Note: Newer CVS versions (>= 1.12.11) also support specifying
CVS_SERVER directly in CVSROOT like
------
-cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
+ cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
------
This has the advantage that it will be saved in your 'CVS/Root' files and
you don't need to worry about always setting the correct environment
@@ -186,8 +185,8 @@ allowing access over SSH.
+
--
------
- export CVSROOT=:ext:user@server:/var/git/project.git
- export CVS_SERVER="git cvsserver"
+ export CVSROOT=:ext:user@server:/var/git/project.git
+ export CVS_SERVER="git cvsserver"
------
--
4. For SSH clients that will make commits, make sure their server-side
@@ -203,7 +202,7 @@ allowing access over SSH.
`project-master` directory:
+
------
- cvs co -d project-master master
+ cvs co -d project-master master
------
[[dbbackend]]
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 44fe8860b3..96d5f598b4 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -8,8 +8,10 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
-'git help' [-a|--all [--[no-]verbose]] [-g|--guides]
- [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
+'git help' [-a|--all [--[no-]verbose]]
+ [[-i|--info] [-m|--man] [-w|--web]] [COMMAND|GUIDE]
+'git help' [-g|--guides]
+'git help' [-c|--config]
DESCRIPTION
-----------
@@ -58,8 +60,7 @@ OPTIONS
-g::
--guides::
- Prints a list of the Git concept guides on the standard output. This
- option overrides any given command or guide name.
+ Prints a list of the Git concept guides on the standard output.
-i::
--info::
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index 558966aa83..0c5c0dde19 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -16,7 +16,9 @@ A simple CGI program to serve the contents of a Git repository to Git
clients accessing the repository over http:// and https:// protocols.
The program supports clients fetching using both the smart HTTP protocol
and the backwards-compatible dumb HTTP protocol, as well as clients
-pushing using the smart HTTP protocol.
+pushing using the smart HTTP protocol. It also supports Git's
+more-efficient "v2" protocol if properly configured; see the
+discussion of `GIT_PROTOCOL` in the ENVIRONMENT section below.
It verifies that the directory has the magic file
"git-daemon-export-ok", and it will refuse to export any Git directory
@@ -77,6 +79,18 @@ Apache 2.x::
SetEnv GIT_PROJECT_ROOT /var/www/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+
+# This is not strictly necessary using Apache and a modern version of
+# git-http-backend, as the webserver will pass along the header in the
+# environment as HTTP_GIT_PROTOCOL, and http-backend will copy that into
+# GIT_PROTOCOL. But you may need this line (or something similar if you
+# are using a different webserver), or if you want to support older Git
+# versions that did not do that copying.
+#
+# Having the webserver set up GIT_PROTOCOL is perfectly fine even with
+# modern versions (and will take precedence over HTTP_GIT_PROTOCOL,
+# which means it can be used to override the client's request).
+SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
----------------------------------------------------------------
+
To enable anonymous read access but authenticated write access,
@@ -264,6 +278,16 @@ a repository with an extremely large number of refs. The value can be
specified with a unit (e.g., `100M` for 100 megabytes). The default is
10 megabytes.
+Clients may probe for optional protocol capabilities (like the v2
+protocol) using the `Git-Protocol` HTTP header. In order to support
+these, the contents of that header must appear in the `GIT_PROTOCOL`
+environment variable. Most webservers will pass this header to the CGI
+via the `HTTP_GIT_PROTOCOL` variable, and `git-http-backend` will
+automatically copy that to `GIT_PROTOCOL`. However, some webservers may
+be more selective about which headers they'll pass, in which case they
+need to be configured explicitly (see the mention of `Git-Protocol` in
+the Apache config from the earlier EXAMPLES section).
+
The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
ensuring that any reflogs created by 'git-receive-pack' contain some
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index a9df3dbd32..b008ce2850 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -9,8 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes
SYNOPSIS
--------
[verse]
-'git multi-pack-index' [--object-dir=<dir>] [--[no-]progress]
- [--preferred-pack=<pack>] [--[no-]bitmap] <subcommand>
+'git multi-pack-index' [--object-dir=<dir>] [--[no-]bitmap] <sub-command>
DESCRIPTION
-----------
@@ -28,7 +27,8 @@ OPTIONS
--[no-]progress::
Turn progress on/off explicitly. If neither is specified, progress is
- shown if standard error is connected to a terminal.
+ shown if standard error is connected to a terminal. Supported by
+ sub-commands `write`, `verify`, `expire`, and `repack.
The following subcommands are available:
@@ -45,6 +45,25 @@ write::
--[no-]bitmap::
Control whether or not a multi-pack bitmap is written.
+
+ --stdin-packs::
+ Write a multi-pack index containing only the set of
+ line-delimited pack index basenames provided over stdin.
+
+ --refs-snapshot=<path>::
+ With `--bitmap`, optionally specify a file which
+ contains a "refs snapshot" taken prior to repacking.
++
+A reference snapshot is composed of line-delimited OIDs corresponding to
+the reference tips, usually taken by `git repack` prior to generating a
+new pack. A line may optionally start with a `+` character to indicate
+that the reference which corresponds to that OID is "preferred" (see
+linkgit:git-config[1]'s `pack.preferBitmapTips`.)
++
+The file given at `<path>` is expected to be readable, and can contain
+duplicates. (If a given OID is given more than once, it is marked as
+preferred if at least one instance of it begins with the special `+`
+marker).
--
verify::
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index aef757ec89..0e14f8b5b2 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -105,7 +105,7 @@ Options related to merging
include::merge-options.txt[]
-r::
---rebase[=false|true|merges|preserve|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
@@ -116,10 +116,6 @@ When set to `merges`, rebase using `git rebase --rebase-merges` so that
the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When set to `preserve` (deprecated in favor of `merges`), rebase with the
-`--preserve-merges` option passed to `git rebase` so that locally created
-merge commits will not be flattened.
-+
When false, merge the upstream branch into the current branch.
+
When `interactive`, enable the interactive mode of rebase.
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 5fa8bab64c..8c3aceb832 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -10,8 +10,7 @@ SYNOPSIS
--------
[verse]
'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
- [-u [--exclude-per-directory=<gitignore>] | -i]]
- [--index-output=<file>] [--no-sparse-checkout]
+ [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
@@ -39,8 +38,9 @@ OPTIONS
--reset::
Same as -m, except that unmerged entries are discarded instead
- of failing. When used with `-u`, updates leading to loss of
- working tree changes will not abort the operation.
+ of failing. When used with `-u`, updates leading to loss of
+ working tree changes or untracked files or directories will not
+ abort the operation.
-u::
After a successful merge, update the files in the work
@@ -88,21 +88,6 @@ OPTIONS
The command will refuse to overwrite entries that already
existed in the original index file.
---exclude-per-directory=<gitignore>::
- When running the command with `-u` and `-m` options, the
- merge result may need to overwrite paths that are not
- tracked in the current branch. The command usually
- refuses to proceed with the merge to avoid losing such a
- path. However this safety valve sometimes gets in the
- way. For example, it often happens that the other
- branch added a file that used to be a generated file in
- your branch, and the safety valve triggers when you try
- to switch to that branch after you ran `make` but before
- running `make clean` to remove the generated file. This
- option tells the command to read per-directory exclude
- file (usually '.gitignore') and allows such an untracked
- but explicitly ignored file to be overwritten.
-
--index-output=<file>::
Instead of writing the results out to `$GIT_INDEX_FILE`,
write the resulting index in the named file. While the
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 506345cb0e..a1af21fcef 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -446,7 +446,8 @@ When --fork-point is active, 'fork_point' will be used instead of
ends up being empty, the <upstream> will be used as a fallback.
+
If <upstream> is given on the command line, then the default is
-`--no-fork-point`, otherwise the default is `--fork-point`.
+`--no-fork-point`, otherwise the default is `--fork-point`. See also
+`rebase.forkpoint` in linkgit:git-config[1].
+
If your branch was based on <upstream> but <upstream> was rewound and
your branch contains commits which were dropped, this option can be used
@@ -526,29 +527,12 @@ i.e. commits that would be excluded by linkgit:git-log[1]'s
the `rebase-cousins` mode is turned on, such commits are instead rebased
onto `<upstream>` (or `<onto>`, if specified).
+
-The `--rebase-merges` mode is similar in spirit to the deprecated
-`--preserve-merges` but works with interactive rebases,
-where commits can be reordered, inserted and dropped at will.
-+
It is currently only possible to recreate the merge commits using the
`ort` merge strategy; different merge strategies can be used only via
explicit `exec git merge -s <strategy> [...]` commands.
+
See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
--p::
---preserve-merges::
- [DEPRECATED: use `--rebase-merges` instead] Recreate merge commits
- instead of flattening the history by replaying commits a merge commit
- introduces. Merge conflict resolutions or manual amendments to merge
- commits are not preserved.
-+
-This uses the `--interactive` machinery internally, but combining it
-with the `--interactive` option explicitly is generally not a good
-idea unless you know what you are doing (see BUGS below).
-+
-See also INCOMPATIBLE OPTIONS below.
-
-x <cmd>::
--exec <cmd>::
Append "exec <cmd>" after each line creating a commit in the
@@ -580,9 +564,6 @@ See also INCOMPATIBLE OPTIONS below.
the root commit(s) on a branch. When used with --onto, it
will skip changes already contained in <newbase> (instead of
<upstream>) whereas without --onto it will operate on every change.
- When used together with both --onto and --preserve-merges,
- 'all' root commits will be rewritten to have <newbase> as parent
- instead.
+
See also INCOMPATIBLE OPTIONS below.
@@ -644,7 +625,6 @@ are incompatible with the following options:
* --allow-empty-message
* --[no-]autosquash
* --rebase-merges
- * --preserve-merges
* --interactive
* --exec
* --no-keep-empty
@@ -655,13 +635,6 @@ are incompatible with the following options:
In addition, the following pairs of options are incompatible:
- * --preserve-merges and --interactive
- * --preserve-merges and --signoff
- * --preserve-merges and --rebase-merges
- * --preserve-merges and --empty=
- * --preserve-merges and --ignore-whitespace
- * --preserve-merges and --committer-date-is-author-date
- * --preserve-merges and --ignore-date
* --keep-base and --onto
* --keep-base and --root
* --fork-point and --root
@@ -1279,29 +1252,6 @@ CONFIGURATION
include::config/rebase.txt[]
include::config/sequencer.txt[]
-BUGS
-----
-The todo list presented by the deprecated `--preserve-merges --interactive`
-does not represent the topology of the revision graph (use `--rebase-merges`
-instead). Editing commits and rewording their commit messages should work
-fine, but attempts to reorder commits tend to produce counterintuitive results.
-Use `--rebase-merges` in such scenarios instead.
-
-For example, an attempt to rearrange
-------------
-1 --- 2 --- 3 --- 4 --- 5
-------------
-to
-------------
-1 --- 2 --- 4 --- 3 --- 5
-------------
-by moving the "pick 4" line will result in the following history:
-------------
- 3
- /
-1 --- 2 --- 4 --- 5
-------------
-
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 24c00c9384..7183fb498f 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS
--------
[verse]
-'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] [--write-midx]
DESCRIPTION
-----------
@@ -128,10 +128,11 @@ depth is 4095.
-b::
--write-bitmap-index::
Write a reachability bitmap index as part of the repack. This
- only makes sense when used with `-a` or `-A`, as the bitmaps
+ only makes sense when used with `-a`, `-A` or `-m`, as the bitmaps
must be able to refer to all reachable objects. This option
- overrides the setting of `repack.writeBitmaps`. This option
- has no effect if multiple packfiles are created.
+ overrides the setting of `repack.writeBitmaps`. This option
+ has no effect if multiple packfiles are created, unless writing a
+ MIDX (in which case a multi-pack bitmap is created).
--pack-kept-objects::
Include objects in `.keep` files when repacking. Note that we
@@ -189,6 +190,15 @@ this "roll-up", without respect to their reachability. This is subject
to change in the future. This option (implying a drastically different
repack mode) is not guaranteed to work with all other combinations of
option to `git repack`.
++
+When writing a multi-pack bitmap, `git repack` selects the largest resulting
+pack as the preferred pack for object selection by the MIDX (see
+linkgit:git-multi-pack-index[1]).
+
+-m::
+--write-midx::
+ Write a multi-pack index (see linkgit:git-multi-pack-index[1])
+ containing the non-redundant packs.
CONFIGURATION
-------------
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 252e2d4e47..6f7685f53d 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -69,7 +69,8 @@ linkgit:git-add[1]).
--hard::
Resets the index and working tree. Any changes to tracked files in the
- working tree since `<commit>` are discarded.
+ working tree since `<commit>` are discarded. Any untracked files or
+ directories in the way of writing any tracked files are simply deleted.
--merge::
Resets the index and updates the files in the working tree that are
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 26e9b28470..81bc23f3cd 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -72,6 +72,12 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
--ignore-unmatch::
Exit with a zero status even if no files matched.
+--sparse::
+ Allow updating index entries outside of the sparse-checkout cone.
+ Normally, `git rm` refuses to update index entries whose paths do
+ not fit within the sparse-checkout cone. See
+ linkgit:git-sparse-checkout[1] for more.
+
-q::
--quiet::
`git rm` normally outputs one line (in the form of an `rm` command)
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 44fd146b91..be41f11974 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -9,10 +9,10 @@ git-send-pack - Push objects over Git protocol to another repository
SYNOPSIS
--------
[verse]
-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
+'git send-pack' [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
[--verbose] [--thin] [--atomic]
[--[no-]signed|--signed=(true|false|if-asked)]
- [<host>:]<directory> [<ref>...]
+ [<host>:]<directory> (--all | <ref>...)
DESCRIPTION
-----------
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 83f38e3198..4a2c3e0408 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -207,26 +207,29 @@ show tracked paths:
* ' ' = unmodified
* 'M' = modified
+* 'T' = file type changed (regular file, symbolic link or submodule)
* 'A' = added
* 'D' = deleted
* 'R' = renamed
-* 'C' = copied
+* 'C' = copied (if config option status.renames is set to "copies")
* 'U' = updated but unmerged
....
X Y Meaning
-------------------------------------------------
[AMD] not updated
-M [ MD] updated in index
-A [ MD] added to index
+M [ MTD] updated in index
+T [ MTD] type changed in index
+A [ MTD] added to index
D deleted from index
-R [ MD] renamed in index
-C [ MD] copied in index
-[MARC] index and work tree matches
-[ MARC] M work tree changed since index
-[ MARC] D deleted in work tree
-[ D] R renamed in work tree
-[ D] C copied in work tree
+R [ MTD] renamed in index
+C [ MTD] copied in index
+[MTARC] index and work tree matches
+[ MTARC] M work tree changed since index
+[ MTARC] T type changed in work tree since index
+[ MTARC] D deleted in work tree
+ R renamed in work tree
+ C copied in work tree
-------------------------------------------------
D D unmerged, both deleted
A U unmerged, added by us
@@ -363,7 +366,7 @@ Field Meaning
Unmerged entries have the following format; the first character is
a "u" to distinguish from ordinary changed entries.
- u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
+ u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
....
Field Meaning
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index d5776ffcfd..222b556d7a 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -678,7 +678,6 @@ config key: svn.authorsProg
--strategy=<strategy>::
-p::
--rebase-merges::
---preserve-merges (DEPRECATED)::
These are only used with the 'dcommit' and 'rebase' commands.
+
Passed directly to 'git rebase' when using 'dcommit' if a
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 739416ec83..8f87b23ea8 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -48,6 +48,14 @@ OPTIONS
<directory>::
The repository to sync from.
+ENVIRONMENT
+-----------
+
+`GIT_PROTOCOL`::
+ Internal variable used for handshaking the wire protocol. Server
+ admins may need to configure some transports to allow this
+ variable to be passed. See the discussion in linkgit:git[1].
+
SEE ALSO
--------
linkgit:gitnamespaces[7]
diff --git a/Documentation/git-version.txt b/Documentation/git-version.txt
new file mode 100644
index 0000000000..80fa7754a6
--- /dev/null
+++ b/Documentation/git-version.txt
@@ -0,0 +1,28 @@
+git-version(1)
+==============
+
+NAME
+----
+git-version - Display version information about Git
+
+SYNOPSIS
+--------
+[verse]
+'git version' [--build-options]
+
+DESCRIPTION
+-----------
+With no options given, the version of 'git' is printed on the standard output.
+
+Note that `git --version` is identical to `git version` because the
+former is internally converted into the latter.
+
+OPTIONS
+-------
+--build-options::
+ Include additional information about how git was built for diagnostic
+ purposes.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6dd241ef83..d63c65e67d 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -41,6 +41,10 @@ OPTIONS
-------
--version::
Prints the Git suite version that the 'git' program came from.
++
+This option is internaly converted to `git version ...` and accepts
+the same options as the linkgit:git-version[1] command. If `--help` is
+also given, it takes precedence over `--version`.
--help::
Prints the synopsis and a list of the most commonly used
@@ -863,15 +867,16 @@ for full details.
end user, to be recorded in the body of the reflog.
`GIT_REF_PARANOIA`::
- If set to `1`, include broken or badly named refs when iterating
- over lists of refs. In a normal, non-corrupted repository, this
- does nothing. However, enabling it may help git to detect and
- abort some operations in the presence of broken refs. Git sets
- this variable automatically when performing destructive
- operations like linkgit:git-prune[1]. You should not need to set
- it yourself unless you want to be paranoid about making sure
- an operation has touched every ref (e.g., because you are
- cloning a repository to make a backup).
+ If set to `0`, ignore broken or badly named refs when iterating
+ over lists of refs. Normally Git will try to include any such
+ refs, which may cause some operations to fail. This is usually
+ preferable, as potentially destructive operations (e.g.,
+ linkgit:git-prune[1]) are better off aborting rather than
+ ignoring broken refs (and thus considering the history they
+ point to as not worth saving). The default value is `1` (i.e.,
+ be paranoid about detecting and aborting all operations). You
+ should not normally need to set this to `0`, but it may be
+ useful when trying to salvage data from a corrupted repository.
`GIT_ALLOW_PROTOCOL`::
If set to a colon-separated list of protocols, behave as if
@@ -894,6 +899,21 @@ for full details.
Contains a colon ':' separated list of keys with optional values
'key[=value]'. Presence of unknown keys and values must be
ignored.
++
+Note that servers may need to be configured to allow this variable to
+pass over some transports. It will be propagated automatically when
+accessing local repositories (i.e., `file://` or a filesystem path), as
+well as over the `git://` protocol. For git-over-http, it should work
+automatically in most configurations, but see the discussion in
+linkgit:git-http-backend[1]. For git-over-ssh, the ssh server may need
+to be configured to allow clients to pass this variable (e.g., by using
+`AcceptEnv GIT_PROTOCOL` with OpenSSH).
++
+This configuration is optional. If the variable is not propagated, then
+clients will fall back to the original "v0" protocol (but may miss out
+on some performance improvements or features). This variable currently
+only affects clones and fetches; it is not yet used for pushes (but may
+be in the future).
`GIT_OPTIONAL_LOCKS`::
If set to `0`, Git will complete any requested operation without
diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl
index b22a367844..1c61dd9512 100755
--- a/Documentation/lint-gitlink.perl
+++ b/Documentation/lint-gitlink.perl
@@ -5,11 +5,12 @@ use warnings;
# Parse arguments, a simple state machine for input like:
#
-# howto/*.txt config/*.txt --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
+# <file-to-check.txt> <valid-files-to-link-to> --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
my %TXT;
my %SECTION;
my $section;
my $lint_these = 0;
+my $to_check = shift @ARGV;
for my $arg (@ARGV) {
if (my ($sec) = $arg =~ /^--section=(\d+)$/s) {
$section = $sec;
@@ -30,13 +31,14 @@ sub report {
my ($pos, $line, $target, $msg) = @_;
substr($line, $pos) = "' <-- HERE";
$line =~ s/^\s+//;
- print "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
- print "$ARGV:$.:\t'$line\n";
+ print STDERR "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
+ print STDERR "$ARGV:$.:\t'$line\n";
$exit_code = 1;
}
@ARGV = sort values %TXT;
-die "BUG: Nothing to process!" unless @ARGV;
+die "BUG: No list of valid linkgit:* files given" unless @ARGV;
+@ARGV = $to_check;
while (<>) {
my $line = $_;
while ($line =~ m/linkgit:((.*?)\[(\d)\])/g) {
diff --git a/Documentation/lint-man-end-blurb.perl b/Documentation/lint-man-end-blurb.perl
index d69312e5db..6bdb13ad9f 100755
--- a/Documentation/lint-man-end-blurb.perl
+++ b/Documentation/lint-man-end-blurb.perl
@@ -6,7 +6,7 @@ use warnings;
my $exit_code = 0;
sub report {
my ($target, $msg) = @_;
- print "error: $target: $msg\n";
+ print STDERR "error: $target: $msg\n";
$exit_code = 1;
}
diff --git a/Documentation/lint-man-section-order.perl b/Documentation/lint-man-section-order.perl
index b05f9156dd..425377dfeb 100755
--- a/Documentation/lint-man-section-order.perl
+++ b/Documentation/lint-man-section-order.perl
@@ -46,7 +46,7 @@ my $SECTION_RX = do {
my $exit_code = 0;
sub report {
my ($msg) = @_;
- print "$ARGV:$.: $msg\n";
+ print STDERR "$ARGV:$.: $msg\n";
$exit_code = 1;
}
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 5a60bbfa7f..acfd5dc1d8 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -198,11 +198,6 @@ There are some macros to easily define options:
The filename will be prefixed by passing the filename along with
the prefix argument of `parse_options()` to `prefix_filename()`.
-`OPT_ARGUMENT(long, &int_var, description)`::
- Introduce a long-option argument that will be kept in `argv[]`.
- If this option was seen, `int_var` will be set to one (except
- if a `NULL` pointer was passed).
-
`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
Recognize numerical options like -123 and feed the integer as
if it was an argument to the function given by `func_ptr`.
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index b9f3198fbe..ef7fe02a8f 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -613,6 +613,46 @@ stopping after the waitpid() and includes OS process creation overhead).
So this time will be slightly larger than the atexit time reported by
the child process itself.
+`"child_ready"`::
+ This event is generated after the current process has started
+ a background process and released all handles to it.
++
+------------
+{
+ "event":"child_ready",
+ ...
+ "child_id":2,
+ "pid":14708, # child PID
+ "ready":"ready", # child ready state
+ "t_rel":0.110605 # observed run-time of child process
+}
+------------
++
+Note that the session-id of the child process is not available to
+the current/spawning process, so the child's PID is reported here as
+a hint for post-processing. (But it is only a hint because the child
+process may be a shell script which doesn't have a session-id.)
++
+This event is generated after the child is started in the background
+and given a little time to boot up and start working. If the child
+startups normally and while the parent is still waiting, the "ready"
+field will have the value "ready".
+If the child is too slow to start and the parent times out, the field
+will have the value "timeout".
+If the child starts but the parent is unable to probe it, the field
+will have the value "error".
++
+After the parent process emits this event, it will release all of its
+handles to the child process and treat the child as a background
+daemon. So even if the child does eventually finish booting up,
+the parent will not emit an updated event.
++
+Note that the `t_rel` field contains the observed run time in seconds
+when the parent released the child process into the background.
+The child is assumed to be a long-running daemon process and may
+outlive the parent process. So the parent's child event times should
+not be compared to the child's atexit times.
+
`"exec"`::
This event is generated before git attempts to `exec()`
another command rather than starting a child process.
diff --git a/Documentation/technical/multi-pack-index.txt b/Documentation/technical/multi-pack-index.txt
index 1a73c3ee20..86f40f2490 100644
--- a/Documentation/technical/multi-pack-index.txt
+++ b/Documentation/technical/multi-pack-index.txt
@@ -36,7 +36,9 @@ Design Details
directory of an alternate. It refers only to packfiles in that
same directory.
-- The core.multiPackIndex config setting must be on to consume MIDX files.
+- The core.multiPackIndex config setting must be on (which is the
+ default) to consume MIDX files. Setting it to `false` prevents
+ Git from reading a MIDX file, even if one exists.
- The file format includes parameters for the object ID hash
function, so a future change of hash algorithm does not require
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 213538f1d0..21e8258ccf 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -42,7 +42,8 @@ Initial Client Request
In general a client can request to speak protocol v2 by sending
`version=2` through the respective side-channel for the transport being
used which inevitably sets `GIT_PROTOCOL`. More information can be
-found in `pack-protocol.txt` and `http-protocol.txt`. In all cases the
+found in `pack-protocol.txt` and `http-protocol.txt`, as well as the
+`GIT_PROTOCOL` definition in `git.txt`. In all cases the
response from the server is the capability advertisement.
Git Transport
@@ -58,6 +59,8 @@ SSH and File Transport
When using either the ssh:// or file:// transport, the GIT_PROTOCOL
environment variable must be set explicitly to include "version=2".
+The server may need to be configured to allow this environment variable
+to pass.
HTTP Transport
~~~~~~~~~~~~~~
@@ -84,6 +87,9 @@ Subsequent requests are then made directly to the service
Uses the `--http-backend-info-refs` option to
linkgit:git-upload-pack[1].
+The server may need to be configured to pass this header's contents via
+the `GIT_PROTOCOL` variable. See the discussion in `git-http-backend.txt`.
+
Capability Advertisement
------------------------
@@ -193,7 +199,11 @@ ls-refs takes in the following arguments:
Show peeled tags.
ref-prefix <prefix>
When specified, only references having a prefix matching one of
- the provided prefixes are displayed.
+ the provided prefixes are displayed. Multiple instances may be
+ given, in which case references matching any prefix will be
+ shown. Note that this is purely for optimization; a server MAY
+ show refs not matching the prefix if it chooses, and clients
+ should filter the result themselves.
If the 'unborn' feature is advertised the following argument can be
included in the client's request.
diff --git a/Documentation/technical/signature-format.txt b/Documentation/technical/signature-format.txt
index 2c9406a56a..166721be6f 100644
--- a/Documentation/technical/signature-format.txt
+++ b/Documentation/technical/signature-format.txt
@@ -13,6 +13,22 @@ Signatures always begin with `-----BEGIN PGP SIGNATURE-----`
and end with `-----END PGP SIGNATURE-----`, unless gpg is told to
produce RFC1991 signatures which use `MESSAGE` instead of `SIGNATURE`.
+Signatures sometimes appear as a part of the normal payload
+(e.g. a signed tag has the signature block appended after the payload
+that the signature applies to), and sometimes appear in the value of
+an object header (e.g. a merge commit that merged a signed tag would
+have the entire tag contents on its "mergetag" header). In the case
+of the latter, the usual multi-line formatting rule for object
+headers applies. I.e. the second and subsequent lines are prefixed
+with a SP to signal that the line is continued from the previous
+line.
+
+This is even true for an originally empty line. In the following
+examples, the end of line that ends with a whitespace letter is
+highlighted with a `$` sign; if you are trying to recreate these
+example by hand, do not cut and paste them---they are there
+primarily to highlight extra whitespace at the end of some lines.
+
The signed payload and the way the signature is embedded depends
on the type of the object resp. transaction.
@@ -78,7 +94,7 @@ author A U Thor <author@example.com> 1465981137 +0000
committer C O Mitter <committer@example.com> 1465981137 +0000
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
-
+ $
iQEcBAABAgAGBQJXYRjRAAoJEGEJLoW3InGJ3IwIAIY4SA6GxY3BjL60YyvsJPh/
HRCJwH+w7wt3Yc/9/bW2F+gF72kdHOOs2jfv+OZhq0q4OAN6fvVSczISY/82LpS7
DVdMQj2/YcHDT4xrDNBnXnviDO9G7am/9OE77kEbXrp7QPxvhjkicHNwy2rEflAA
@@ -128,13 +144,13 @@ mergetag object 04b871796dc0420f8e7561a895b52484b701d51a
type commit
tag signedtag
tagger C O Mitter <committer@example.com> 1465981006 +0000
-
+ $
signed tag
-
+ $
signed tag message body
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
-
+ $
iQEcBAABAgAGBQJXYRhOAAoJEGEJLoW3InGJklkIAIcnhL7RwEb/+QeX9enkXhxn
rxfdqrvWd1K80sl2TOt8Bg/NYwrUBw/RWJ+sg/hhHp4WtvE1HDGHlkEz3y11Lkuh
8tSxS3qKTxXUGozyPGuE90sJfExhZlW4knIQ1wt/yWqM+33E9pN4hzPqLwyrdods
diff --git a/INSTALL b/INSTALL
index 66389ce059..4140a3f5c8 100644
--- a/INSTALL
+++ b/INSTALL
@@ -138,12 +138,15 @@ Issues of note:
BLK_SHA1. Also included is a version optimized for PowerPC
(PPC_SHA1).
- - "libcurl" library is used by git-http-fetch, git-fetch, and, if
- the curl version >= 7.34.0, for git-imap-send. You might also
- want the "curl" executable for debugging purposes. If you do not
- use http:// or https:// repositories, and do not want to put
- patches into an IMAP mailbox, you do not have to have them
- (use NO_CURL).
+ - "libcurl" library is used for fetching and pushing
+ repositories over http:// or https://, as well as by
+ git-imap-send if the curl version is >= 7.34.0. If you do
+ not need that functionality, use NO_CURL to build without
+ it.
+
+ Git requires version "7.19.4" or later of "libcurl" to build
+ without NO_CURL. This version requirement may be bumped in
+ the future.
- "expat" library; git-http-push uses it for remote lock
management over DAV. Similar to "curl" above, this is optional
diff --git a/Makefile b/Makefile
index 38568792f7..12be39ac49 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,6 @@ SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh
SCRIPT_LIB += git-mergetool--lib
-SCRIPT_LIB += git-rebase--preserve-merges
SCRIPT_LIB += git-sh-i18n
SCRIPT_LIB += git-sh-setup
@@ -817,6 +816,10 @@ XDIFF_LIB = xdiff/lib.a
GENERATED_H += command-list.h
GENERATED_H += config-list.h
+GENERATED_H += hook-list.h
+
+.PHONY: generated-hdrs
+generated-hdrs: $(GENERATED_H)
LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
$(FIND) . \
@@ -902,6 +905,7 @@ LIB_OBJS += hash-lookup.o
LIB_OBJS += hashmap.o
LIB_OBJS += help.o
LIB_OBJS += hex.o
+LIB_OBJS += hook.o
LIB_OBJS += ident.o
LIB_OBJS += json-writer.o
LIB_OBJS += kwset.o
@@ -1214,6 +1218,9 @@ PTHREAD_CFLAGS =
SPARSE_FLAGS ?=
SP_EXTRA_FLAGS = -Wno-universal-initializer
+# For informing GIT-BUILD-OPTIONS of the SANITIZE=leak target
+SANITIZE_LEAK =
+
# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
# usually result in less CPU usage at the cost of higher peak memory.
# Setting it to 0 will feed all files in a single spatch invocation.
@@ -1258,6 +1265,7 @@ BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
endif
ifneq ($(filter leak,$(SANITIZERS)),)
BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
+SANITIZE_LEAK = YesCompiledWithIt
endif
ifneq ($(filter address,$(SANITIZERS)),)
NO_REGEX = NeededForASAN
@@ -1278,6 +1286,7 @@ endif
ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto)
dep_check = $(shell $(CC) $(ALL_CFLAGS) \
+ -Wno-pedantic \
-c -MF /dev/null -MQ /dev/null -MMD -MP \
-x c /dev/null -o /dev/null 2>&1; \
echo $$?)
@@ -1303,6 +1312,7 @@ endif
ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
compdb_check = $(shell $(CC) $(ALL_CFLAGS) \
+ -Wno-pedantic \
-c -MJ /dev/null \
-x c /dev/null -o /dev/null 2>&1; \
echo $$?)
@@ -1421,15 +1431,8 @@ else
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
PROGRAM_OBJS += http-fetch.o
PROGRAMS += $(REMOTE_CURL_NAMES)
- curl_check := $(shell (echo 070908; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
- ifeq "$(curl_check)" "070908"
- ifndef NO_EXPAT
- PROGRAM_OBJS += http-push.o
- else
- EXCLUDED_PROGRAMS += git-http-push
- endif
- else
- EXCLUDED_PROGRAMS += git-http-push
+ ifndef NO_EXPAT
+ PROGRAM_OBJS += http-push.o
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"
@@ -2212,8 +2215,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
$(filter %.o,$^) $(LIBS)
help.sp help.s help.o: command-list.h
+hook.sp hook.s hook.o: hook-list.h
-builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: config-list.h hook-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
'-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
@@ -2236,15 +2240,17 @@ $(BUILT_INS): git$X
config-list.h: generate-configlist.sh
config-list.h: Documentation/*config.txt Documentation/config/*.txt
- $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh \
- >$@+ && mv $@+ $@
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh >$@
command-list.h: generate-cmdlist.sh command-list.txt
command-list.h: $(wildcard Documentation/git*.txt)
$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
$(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
- command-list.txt >$@+ && mv $@+ $@
+ command-list.txt >$@
+
+hook-list.h: generate-hooklist.sh Documentation/githooks.txt
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh >$@
SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@ -2466,7 +2472,6 @@ dep_args = -MF $(dep_file) -MQ $@ -MMD -MP
endif
ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
-dep_dirs =
missing_dep_dirs =
dep_args =
endif
@@ -2507,13 +2512,6 @@ ifneq ($(dep_files_present),)
include $(dep_files_present)
endif
else
-# Dependencies on header files, for platforms that do not support
-# the gcc -MMD option.
-#
-# Dependencies on automatically generated headers such as command-list.h
-# should _not_ be included here, since they are necessary even when
-# building an object for the first time.
-
$(OBJECTS): $(LIB_H) $(GENERATED_H)
endif
@@ -2638,7 +2636,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
--keyword=__ --keyword=N__ --keyword="__n:1,2"
LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
LOCALIZED_SH = $(SCRIPT_SH)
-LOCALIZED_SH += git-rebase--preserve-merges.sh
LOCALIZED_SH += git-sh-setup.sh
LOCALIZED_PERL = $(SCRIPT_PERL)
@@ -2803,6 +2800,7 @@ GIT-BUILD-OPTIONS: FORCE
@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
@echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
+ @echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+
@echo X=\'$(X)\' >>$@+
ifdef TEST_OUTPUT_DIRECTORY
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
@@ -2912,7 +2910,7 @@ $(SP_OBJ): %.sp: %.c %.o
.PHONY: sparse
sparse: $(SP_OBJ)
-EXCEPT_HDRS := command-list.h config-list.h unicode-width.h compat/% xdiff/%
+EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
endif
@@ -2934,7 +2932,8 @@ hdr-check: $(HCO)
style:
git clang-format --style file --diff --extensions c,h
-check: config-list.h command-list.h
+.PHONY: check
+check: $(GENERATED_H)
@if sparse; \
then \
echo >&2 "Use 'make sparse' instead"; \
diff --git a/add-interactive.c b/add-interactive.c
index 36ebdbdf7e..6498ae196f 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -102,8 +102,12 @@ struct prefix_item_list {
int *selected; /* for multi-selections */
size_t min_length, max_length;
};
-#define PREFIX_ITEM_LIST_INIT \
- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, NULL, 1, 4 }
+#define PREFIX_ITEM_LIST_INIT { \
+ .items = STRING_LIST_INIT_DUP, \
+ .sorted = STRING_LIST_INIT_NODUP, \
+ .min_length = 1, \
+ .max_length = 4, \
+}
static void prefix_item_list_clear(struct prefix_item_list *list)
{
diff --git a/advice.c b/advice.c
index 2f5499a5e1..1dfc91d176 100644
--- a/advice.c
+++ b/advice.c
@@ -224,15 +224,16 @@ void advise_on_updating_sparse_paths(struct string_list *pathspec_list)
if (!pathspec_list->nr)
return;
- fprintf(stderr, _("The following pathspecs didn't match any"
- " eligible path, but they do match index\n"
- "entries outside the current sparse checkout:\n"));
+ fprintf(stderr, _("The following paths and/or pathspecs matched paths that exist\n"
+ "outside of your sparse-checkout definition, so will not be\n"
+ "updated in the index:\n"));
for_each_string_list_item(item, pathspec_list)
fprintf(stderr, "%s\n", item->string);
advise_if_enabled(ADVICE_UPDATE_SPARSE_PATH,
- _("Disable or modify the sparsity rules if you intend"
- " to update such entries."));
+ _("If you intend to update such entries, try one of the following:\n"
+ "* Use the --sparse option.\n"
+ "* Disable or modify the sparsity rules."));
}
void detach_advice(const char *new_name)
diff --git a/builtin.h b/builtin.h
index 16ecd5586f..8a58743ed6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -225,7 +225,6 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
int cmd_switch(int argc, const char **argv, const char *prefix);
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
int cmd_tag(int argc, const char **argv, const char *prefix);
-int cmd_tar_tree(int argc, const char **argv, const char *prefix);
int cmd_unpack_file(int argc, const char **argv, const char *prefix);
int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
int cmd_update_index(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 24da07578f..ef6b619c45 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -30,6 +30,7 @@ static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
+static int include_sparse;
static const char *pathspec_from_file;
static int legacy_stash_p; /* support for the scripted `git stash` */
@@ -46,7 +47,9 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
struct cache_entry *ce = active_cache[i];
int err;
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
@@ -94,6 +97,10 @@ static void update_callback(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *path = p->one->path;
+
+ if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
+ continue;
+
switch (fix_unmerged_status(p, data)) {
default:
die(_("unexpected diff status %c"), p->status);
@@ -147,7 +154,9 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (ce_stage(ce))
continue; /* do not touch unmerged paths */
@@ -377,6 +386,7 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x",
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
@@ -442,6 +452,7 @@ static void check_embedded_repo(const char *path)
static int add_files(struct dir_struct *dir, int flags)
{
int i, exit_status = 0;
+ struct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP;
if (dir->ignored_nr) {
fprintf(stderr, _(ignore_error));
@@ -455,6 +466,12 @@ static int add_files(struct dir_struct *dir, int flags)
}
for (i = 0; i < dir->nr; i++) {
+ if (!include_sparse &&
+ !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+ string_list_append(&matched_sparse_paths,
+ dir->entries[i]->name);
+ continue;
+ }
if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
@@ -463,6 +480,14 @@ static int add_files(struct dir_struct *dir, int flags)
check_embedded_repo(dir->entries[i]->name);
}
}
+
+ if (matched_sparse_paths.nr) {
+ advise_on_updating_sparse_paths(&matched_sparse_paths);
+ exit_status = 1;
+ }
+
+ string_list_clear(&matched_sparse_paths, 0);
+
return exit_status;
}
@@ -627,7 +652,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (seen[i])
continue;
- if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
+ if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
string_list_append(&only_match_skip_worktree,
pathspec.items[i].original);
continue;
diff --git a/builtin/am.c b/builtin/am.c
index 60f3737f99..8677ea2348 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -11,6 +11,7 @@
#include "parse-options.h"
#include "dir.h"
#include "run-command.h"
+#include "hook.h"
#include "quote.h"
#include "tempfile.h"
#include "lockfile.h"
@@ -1917,7 +1918,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
opts.dst_index = &the_index;
opts.update = 1;
opts.merge = 1;
- opts.reset = 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);
@@ -2105,7 +2107,8 @@ static void am_abort(struct am_state *state)
if (!has_orig_head)
oidcpy(&orig_head, the_hash_algo->empty_tree);
- clean_index(&curr_head, &orig_head);
+ if (clean_index(&curr_head, &orig_head))
+ die(_("failed to clean index"));
if (has_orig_head)
update_ref("am --abort", "HEAD", &orig_head,
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index f184eaeac6..28a2e6a575 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -18,10 +18,10 @@ static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
+static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-reset [<commit>]"),
- N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
@@ -30,6 +30,8 @@ static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
N_("git bisect--helper --bisect-replay <filename>"),
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
+ N_("git bisect--helper --bisect-visualize"),
+ N_("git bisect--helper --bisect-run <cmd>..."),
NULL
};
@@ -143,6 +145,19 @@ static int append_to_file(const char *path, const char *format, ...)
return res;
}
+static int print_file_to_stdout(const char *path)
+{
+ int fd = open(path, O_RDONLY);
+ int ret = 0;
+
+ if (fd < 0)
+ return error_errno(_("cannot open file '%s' for reading"), path);
+ if (copy_fd(fd, 1) < 0)
+ ret = error_errno(_("failed to read '%s'"), path);
+ close(fd);
+ return ret;
+}
+
static int check_term_format(const char *term, const char *orig_term)
{
int res;
@@ -1036,6 +1051,125 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
return res;
}
+static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+{
+ struct strvec args = STRVEC_INIT;
+ int flags = RUN_COMMAND_NO_STDIN, res = 0;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (bisect_next_check(terms, NULL) != 0)
+ return BISECT_FAILED;
+
+ if (!argc) {
+ if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
+ getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
+ strvec_push(&args, "gitk");
+ } else {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ }
+ } else {
+ if (argv[0][0] == '-') {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
+ flags |= RUN_GIT_CMD;
+
+ strvec_pushv(&args, argv);
+ }
+
+ strvec_pushl(&args, "--bisect", "--", NULL);
+
+ strbuf_read_file(&sb, git_path_bisect_names(), 0);
+ sq_dequote_to_strvec(sb.buf, &args);
+ strbuf_release(&sb);
+
+ res = run_command_v_opt(args.v, flags);
+ strvec_clear(&args);
+ return res;
+}
+
+static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+{
+ int res = BISECT_OK;
+ struct strbuf command = STRBUF_INIT;
+ struct strvec args = STRVEC_INIT;
+ struct strvec run_args = STRVEC_INIT;
+ const char *new_state;
+ int temporary_stdout_fd, saved_stdout;
+
+ if (bisect_next_check(terms, NULL))
+ return BISECT_FAILED;
+
+ if (argc)
+ sq_quote_argv(&command, argv);
+ else {
+ error(_("bisect run failed: no command provided."));
+ return BISECT_FAILED;
+ }
+
+ strvec_push(&run_args, command.buf);
+
+ while (1) {
+ strvec_clear(&args);
+
+ printf(_("running %s\n"), command.buf);
+ res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+
+ if (res < 0 || 128 <= res) {
+ error(_("bisect run failed: exit code %d from"
+ " '%s' is < 0 or >= 128"), res, command.buf);
+ strbuf_release(&command);
+ return res;
+ }
+
+ if (res == 125)
+ new_state = "skip";
+ else if (!res)
+ new_state = terms->term_good;
+ else
+ new_state = terms->term_bad;
+
+ temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+
+ if (temporary_stdout_fd < 0)
+ return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
+
+ fflush(stdout);
+ saved_stdout = dup(1);
+ dup2(temporary_stdout_fd, 1);
+
+ res = bisect_state(terms, &new_state, 1);
+
+ fflush(stdout);
+ dup2(saved_stdout, 1);
+ close(saved_stdout);
+ close(temporary_stdout_fd);
+
+ print_file_to_stdout(git_path_bisect_run());
+
+ if (res == BISECT_ONLY_SKIPPED_LEFT)
+ error(_("bisect run cannot continue any more"));
+ else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
+ printf(_("bisect run success"));
+ res = BISECT_OK;
+ } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
+ printf(_("bisect found first bad commit"));
+ res = BISECT_OK;
+ } else if (res) {
+ error(_("bisect run failed: 'git bisect--helper --bisect-state"
+ " %s' exited with error code %d"), args.v[0], res);
+ } else {
+ continue;
+ }
+
+ strbuf_release(&command);
+ strvec_clear(&args);
+ strvec_clear(&run_args);
+ return res;
+ }
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
@@ -1048,7 +1182,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
BISECT_STATE,
BISECT_LOG,
BISECT_REPLAY,
- BISECT_SKIP
+ BISECT_SKIP,
+ BISECT_VISUALIZE,
+ BISECT_RUN,
} cmdmode = 0;
int res = 0, nolog = 0;
struct option options[] = {
@@ -1070,6 +1206,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
N_("replay the bisection process from the given file"), BISECT_REPLAY),
OPT_CMDMODE(0, "bisect-skip", &cmdmode,
N_("skip some commits for checkout"), BISECT_SKIP),
+ OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
+ N_("visualize the bisection"), BISECT_VISUALIZE),
+ OPT_CMDMODE(0, "bisect-run", &cmdmode,
+ N_("use <cmd>... to automatically bisect."), BISECT_RUN),
OPT_BOOL(0, "no-log", &nolog,
N_("no log for BISECT_WRITE")),
OPT_END()
@@ -1089,12 +1229,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
return error(_("--bisect-reset requires either no argument or a commit"));
res = bisect_reset(argc ? argv[0] : NULL);
break;
- case BISECT_NEXT_CHECK:
- if (argc != 2 && argc != 3)
- return error(_("--bisect-next-check requires 2 or 3 arguments"));
- set_terms(&terms, argv[1], argv[0]);
- res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
- break;
case BISECT_TERMS:
if (argc > 1)
return error(_("--bisect-terms requires 0 or 1 argument"));
@@ -1131,6 +1265,16 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
get_terms(&terms);
res = bisect_skip(&terms, argv, argc);
break;
+ case BISECT_VISUALIZE:
+ get_terms(&terms);
+ res = bisect_visualize(&terms, argv, argc);
+ break;
+ case BISECT_RUN:
+ if (!argc)
+ return error(_("bisect run failed: no command provided."));
+ get_terms(&terms);
+ res = bisect_run(&terms, argv, argc);
+ break;
default:
BUG("unknown subcommand %d", cmdmode);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 641523ff9a..f9ee3f8c68 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -101,6 +101,16 @@ struct commit_info {
struct strbuf summary;
};
+#define COMMIT_INFO_INIT { \
+ .author = STRBUF_INIT, \
+ .author_mail = STRBUF_INIT, \
+ .author_tz = STRBUF_INIT, \
+ .committer = STRBUF_INIT, \
+ .committer_mail = STRBUF_INIT, \
+ .committer_tz = STRBUF_INIT, \
+ .summary = STRBUF_INIT, \
+}
+
/*
* Parse author/committer line in the commit object buffer
*/
@@ -160,18 +170,6 @@ static void get_ac_line(const char *inbuf, const char *what,
strbuf_add(name, namebuf, namelen);
}
-static void commit_info_init(struct commit_info *ci)
-{
-
- strbuf_init(&ci->author, 0);
- strbuf_init(&ci->author_mail, 0);
- strbuf_init(&ci->author_tz, 0);
- strbuf_init(&ci->committer, 0);
- strbuf_init(&ci->committer_mail, 0);
- strbuf_init(&ci->committer_tz, 0);
- strbuf_init(&ci->summary, 0);
-}
-
static void commit_info_destroy(struct commit_info *ci)
{
@@ -192,8 +190,6 @@ static void get_commit_info(struct commit *commit,
const char *subject, *encoding;
const char *message;
- commit_info_init(ret);
-
encoding = get_log_output_encoding();
message = logmsg_reencode(commit, NULL, encoding);
get_ac_line(message, "\nauthor ",
@@ -246,7 +242,7 @@ static void write_filename_info(struct blame_origin *suspect)
*/
static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
{
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
return 0;
@@ -440,7 +436,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
int cnt;
const char *cp;
struct blame_origin *suspect = ent->suspect;
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
const char *default_color = NULL, *color = NULL, *reset = NULL;
@@ -630,7 +626,7 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)
if (longest_file < num)
longest_file = num;
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
suspect->commit->object.flags |= METAINFO_SHOWN;
get_commit_info(suspect->commit, &ci, 1);
if (*option & OUTPUT_SHOW_EMAIL)
@@ -917,6 +913,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
for (;;) {
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
diff --git a/builtin/branch.c b/builtin/branch.c
index 03c7b7253a..0b7ed82654 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
memset(&array, 0, sizeof(array));
- filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, filter, filter->kind);
if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 06ed10dc92..9de32bc96e 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -3,7 +3,8 @@
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
-#include "run-command.h"
+#include "hook.h"
+#include "hook-list.h"
static void get_system_info(struct strbuf *sys_info)
@@ -41,39 +42,7 @@ static void get_system_info(struct strbuf *sys_info)
static void get_populated_hooks(struct strbuf *hook_info, int nongit)
{
- /*
- * NEEDSWORK: Doesn't look like there is a list of all possible hooks;
- * so below is a transcription of `git help hooks`. Later, this should
- * be replaced with some programmatically generated list (generated from
- * doc or else taken from some library which tells us about all the
- * hooks)
- */
- static const char *hook[] = {
- "applypatch-msg",
- "pre-applypatch",
- "post-applypatch",
- "pre-commit",
- "pre-merge-commit",
- "prepare-commit-msg",
- "commit-msg",
- "post-commit",
- "pre-rebase",
- "post-checkout",
- "post-merge",
- "pre-push",
- "pre-receive",
- "update",
- "post-receive",
- "post-update",
- "push-to-checkout",
- "pre-auto-gc",
- "post-rewrite",
- "sendemail-validate",
- "fsmonitor-watchman",
- "p4-pre-submit",
- "post-index-change",
- };
- int i;
+ const char **p;
if (nongit) {
strbuf_addstr(hook_info,
@@ -81,9 +50,12 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
return;
}
- for (i = 0; i < ARRAY_SIZE(hook); i++)
- if (find_hook(hook[i]))
- strbuf_addf(hook_info, "%s\n", hook[i]);
+ for (p = hook_name_list; *p; p++) {
+ const char *hook = *p;
+
+ if (hook_exists(hook))
+ strbuf_addf(hook_info, "%s\n", hook);
+ }
}
static const char * const bugreport_usage[] = {
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 91975def2d..5a85d7cd0f 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -39,8 +39,6 @@ static const char * const builtin_bundle_unbundle_usage[] = {
NULL
};
-static int verbose;
-
static int parse_options_cmd_bundle(int argc,
const char **argv,
const char* prefix,
@@ -197,7 +195,6 @@ cleanup:
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct option options[] = {
- OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
OPT_END()
};
int result;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 243fe6844b..86fc03242b 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -355,18 +355,34 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
}
}
+/*
+ * If "pack" is non-NULL, then "offset" is the byte offset within the pack from
+ * which the object may be accessed (though note that we may also rely on
+ * data->oid, too). If "pack" is NULL, then offset is ignored.
+ */
static void batch_object_write(const char *obj_name,
struct strbuf *scratch,
struct batch_options *opt,
- struct expand_data *data)
+ struct expand_data *data,
+ struct packed_git *pack,
+ off_t offset)
{
- if (!data->skip_object_info &&
- oid_object_info_extended(the_repository, &data->oid, &data->info,
- OBJECT_INFO_LOOKUP_REPLACE) < 0) {
- printf("%s missing\n",
- obj_name ? obj_name : oid_to_hex(&data->oid));
- fflush(stdout);
- return;
+ if (!data->skip_object_info) {
+ int ret;
+
+ if (pack)
+ ret = packed_object_info(the_repository, pack, offset,
+ &data->info);
+ else
+ ret = oid_object_info_extended(the_repository,
+ &data->oid, &data->info,
+ OBJECT_INFO_LOOKUP_REPLACE);
+ if (ret < 0) {
+ printf("%s missing\n",
+ obj_name ? obj_name : oid_to_hex(&data->oid));
+ fflush(stdout);
+ return;
+ }
}
strbuf_reset(scratch);
@@ -428,7 +444,7 @@ static void batch_one_object(const char *obj_name,
return;
}
- batch_object_write(obj_name, scratch, opt, data);
+ batch_object_write(obj_name, scratch, opt, data, NULL, 0);
}
struct object_cb_data {
@@ -442,7 +458,8 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
{
struct object_cb_data *data = vdata;
oidcpy(&data->expand->oid, oid);
- batch_object_write(NULL, data->scratch, data->opt, data->expand);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ NULL, 0);
return 0;
}
@@ -463,21 +480,26 @@ static int collect_packed_object(const struct object_id *oid,
return 0;
}
-static int batch_unordered_object(const struct object_id *oid, void *vdata)
+static int batch_unordered_object(const struct object_id *oid,
+ struct packed_git *pack, off_t offset,
+ void *vdata)
{
struct object_cb_data *data = vdata;
if (oidset_insert(data->seen, oid))
return 0;
- return batch_object_cb(oid, data);
+ oidcpy(&data->expand->oid, oid);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ pack, offset);
+ return 0;
}
static int batch_unordered_loose(const struct object_id *oid,
const char *path,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, NULL, 0, data);
}
static int batch_unordered_packed(const struct object_id *oid,
@@ -485,7 +507,9 @@ static int batch_unordered_packed(const struct object_id *oid,
uint32_t pos,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, pack,
+ nth_packed_object_offset(pack, pos),
+ data);
}
static int batch_objects(struct batch_options *opt)
@@ -529,6 +553,8 @@ static int batch_objects(struct batch_options *opt)
if (has_promisor_remote())
warning("This repository uses promisor remotes. Some objects may not be loaded.");
+ read_replace_refs = 0;
+
cb.opt = opt;
cb.expand = &data;
cb.scratch = &output;
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index fb9fd13b73..ede7dc32a4 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -82,8 +82,8 @@ static void worker_loop(struct checkout *state)
size_t i, nr = 0, alloc = 0;
while (1) {
- int len = packet_read(0, NULL, NULL, packet_buffer,
- sizeof(packet_buffer), 0);
+ int len = packet_read(0, packet_buffer, sizeof(packet_buffer),
+ 0);
if (len < 0)
BUG("packet_read() returned negative value");
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8c69dcdf72..cbf73b8c9f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -646,7 +646,9 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.head_idx = -1;
opts.update = worktree;
opts.skip_unmerged = !worktree;
- opts.reset = 1;
+ opts.reset = o->force ? UNPACK_RESET_OVERWRITE_UNTRACKED :
+ UNPACK_RESET_PROTECT_UNTRACKED;
+ opts.preserve_ignored = (!o->force && !o->overwrite_ignore);
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = o->show_progress;
@@ -746,11 +748,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
new_branch_info->commit ?
&new_branch_info->commit->object.oid :
&new_branch_info->oid, NULL);
- if (opts->overwrite_ignore) {
- topts.dir = xcalloc(1, sizeof(*topts.dir));
- topts.dir->flags |= DIR_SHOW_IGNORED;
- setup_standard_excludes(topts.dir);
- }
+ topts.preserve_ignored = !opts->overwrite_ignore;
tree = parse_tree_indirect(old_branch_info->commit ?
&old_branch_info->commit->object.oid :
the_hash_algo->empty_tree);
diff --git a/builtin/clone.c b/builtin/clone.c
index ff1d3d447a..559acf9e03 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -687,6 +687,7 @@ static int checkout(int submodule_progress)
opts.update = 1;
opts.merge = 1;
opts.clone = 1;
+ opts.preserve_ignored = 0;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
@@ -1229,6 +1230,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
our_head_points_at = remote_head_points_at;
}
else {
+ const char *branch;
+ char *ref;
+
if (option_branch)
die(_("Remote branch %s not found in upstream %s"),
option_branch, remote_name);
@@ -1239,24 +1243,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
- if (!option_bare) {
- const char *branch;
- char *ref;
-
- if (transport_ls_refs_options.unborn_head_target &&
- skip_prefix(transport_ls_refs_options.unborn_head_target,
- "refs/heads/", &branch)) {
- ref = transport_ls_refs_options.unborn_head_target;
- transport_ls_refs_options.unborn_head_target = NULL;
- create_symref("HEAD", ref, reflog_msg.buf);
- } else {
- branch = git_default_branch_name(0);
- ref = xstrfmt("refs/heads/%s", branch);
- }
- install_branch_config(0, branch, remote_name, ref);
- free(ref);
+ if (transport_ls_refs_options.unborn_head_target &&
+ skip_prefix(transport_ls_refs_options.unborn_head_target,
+ "refs/heads/", &branch)) {
+ ref = transport_ls_refs_options.unborn_head_target;
+ transport_ls_refs_options.unborn_head_target = NULL;
+ create_symref("HEAD", ref, reflog_msg.buf);
+ } else {
+ branch = git_default_branch_name(0);
+ ref = xstrfmt("refs/heads/%s", branch);
}
+
+ if (!option_bare)
+ install_branch_config(0, branch, remote_name, ref);
+
+ free(ref);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 0386f5c775..ab8e5cd59a 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -50,8 +50,6 @@ static struct option common_opts[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("the object directory to store the graph")),
- OPT_BOOL(0, "progress", &opts.progress,
- N_("force progress reporting")),
OPT_END()
};
@@ -73,6 +71,8 @@ static int graph_verify(int argc, const char **argv)
static struct option builtin_commit_graph_verify_options[] = {
OPT_BOOL(0, "shallow", &opts.shallow,
N_("if the commit-graph is split, only verify the tip file")),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
struct option *options = add_common_options(builtin_commit_graph_verify_options);
@@ -172,8 +172,8 @@ static int write_option_max_new_filters(const struct option *opt,
const char *s;
*to = strtol(arg, (char **)&s, 10);
if (*s)
- return error(_("%s expects a numerical value"),
- optname(opt, opt->flags));
+ return error(_("option `%s' expects a numerical value"),
+ "max-new-filters");
}
return 0;
}
@@ -224,6 +224,8 @@ static int graph_write(int argc, const char **argv)
OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters,
NULL, N_("maximum number of changed-path Bloom filters to compute"),
0, write_option_max_new_filters),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
struct option *options = add_common_options(builtin_commit_graph_write_options);
diff --git a/builtin/commit.c b/builtin/commit.c
index e7320f66f9..883c16256c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -19,6 +19,7 @@
#include "revision.h"
#include "wt-status.h"
#include "run-command.h"
+#include "hook.h"
#include "refs.h"
#include "log-tree.h"
#include "strbuf.h"
@@ -1051,7 +1052,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- if (!no_verify && find_hook("pre-commit")) {
+ if (!no_verify && hook_exists("pre-commit")) {
/*
* Re-read the index as pre-commit hook could have updated it,
* and write it out as a tree. We must do this before we invoke
diff --git a/builtin/config.c b/builtin/config.c
index 865fddd6ce..542d8d02b2 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -575,7 +575,7 @@ static int get_urlmatch(const char *var, const char *url)
int ret;
char *section_tail;
struct string_list_item *item;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct string_list values = STRING_LIST_INIT_DUP;
config.collect_fn = urlmatch_collect_fn;
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index e8a7415747..78c02ad531 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -11,6 +11,32 @@
#define FLAG_SPAWN 0x1
#define FLAG_RELAY 0x2
+#ifdef GIT_WINDOWS_NATIVE
+
+static int connection_closed(int error)
+{
+ return (error == EINVAL);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ENETDOWN);
+}
+
+#else
+
+static int connection_closed(int error)
+{
+ return (error == ECONNRESET);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ECONNREFUSED);
+}
+
+#endif
+
static int send_request(const char *socket, const struct strbuf *out)
{
int got_data = 0;
@@ -28,7 +54,7 @@ static int send_request(const char *socket, const struct strbuf *out)
int r;
r = read_in_full(fd, in, sizeof(in));
- if (r == 0 || (r < 0 && errno == ECONNRESET))
+ if (r == 0 || (r < 0 && connection_closed(errno)))
break;
if (r < 0)
die_errno("read error from cache daemon");
@@ -75,7 +101,7 @@ static void do_cache(const char *socket, const char *action, int timeout,
}
if (send_request(socket, &buf) < 0) {
- if (errno != ENOENT && errno != ECONNREFUSED)
+ if (connection_fatally_broken(errno))
die_errno("unable to connect to cache daemon");
if (flags & FLAG_SPAWN) {
spawn_daemon(socket);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a803..4931c10845 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -252,16 +252,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
strbuf_release(&buf);
}
-static NORETURN void exit_cleanup(const char *tmpdir, int exit_code)
-{
- struct strbuf buf = STRBUF_INIT;
- strbuf_addstr(&buf, tmpdir);
- remove_dir_recursively(&buf, 0);
- if (exit_code)
- warning(_("failed: %d"), exit_code);
- exit(exit_code);
-}
-
static int ensure_leading_directories(char *path)
{
switch (safe_create_leading_directories(path)) {
@@ -330,19 +320,44 @@ static int checkout_path(unsigned mode, struct object_id *oid,
return ret;
}
+static void write_file_in_directory(struct strbuf *dir, size_t dir_len,
+ const char *path, const char *content)
+{
+ add_path(dir, dir_len, path);
+ ensure_leading_directories(dir->buf);
+ unlink(dir->buf);
+ write_file(dir->buf, "%s", content);
+}
+
+/* Write the file contents for the left and right sides of the difftool
+ * dir-diff representation for submodules and symlinks. Symlinks and submodules
+ * are written as regular text files so that external diff tools can diff them
+ * as text files, resulting in behavior that is analogous to to what "git diff"
+ * displays for symlink and submodule diffs.
+ */
+static void write_standin_files(struct pair_entry *entry,
+ struct strbuf *ldir, size_t ldir_len,
+ struct strbuf *rdir, size_t rdir_len)
+{
+ if (*entry->left)
+ write_file_in_directory(ldir, ldir_len, entry->path, entry->left);
+ if (*entry->right)
+ write_file_in_directory(rdir, rdir_len, entry->path, entry->right);
+}
+
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- char tmpdir[PATH_MAX];
struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
struct strbuf wtdir = STRBUF_INIT;
- char *lbase_dir, *rbase_dir;
+ struct strbuf tmpdir = STRBUF_INIT;
+ char *lbase_dir = NULL, *rbase_dir = NULL;
size_t ldir_len, rdir_len, wtdir_len;
const char *workdir, *tmp;
int ret = 0, i;
- FILE *fp;
+ FILE *fp = NULL;
struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp,
NULL);
struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL);
@@ -351,8 +366,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct pair_entry *entry;
struct index_state wtindex;
struct checkout lstate, rstate;
- int rc, flags = RUN_GIT_CMD, err = 0;
- struct child_process child = CHILD_PROCESS_INIT;
+ int flags = RUN_GIT_CMD, err = 0;
const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
struct hashmap wt_modified, tmp_modified;
int indices_loaded = 0;
@@ -361,11 +375,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Setup temp directories */
tmp = getenv("TMPDIR");
- xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
- if (!mkdtemp(tmpdir))
- return error("could not create '%s'", tmpdir);
- strbuf_addf(&ldir, "%s/left/", tmpdir);
- strbuf_addf(&rdir, "%s/right/", tmpdir);
+ strbuf_add_absolute_path(&tmpdir, tmp ? tmp : "/tmp");
+ strbuf_trim_trailing_dir_sep(&tmpdir);
+ strbuf_addstr(&tmpdir, "/git-difftool.XXXXXX");
+ if (!mkdtemp(tmpdir.buf)) {
+ ret = error("could not create '%s'", tmpdir.buf);
+ goto finish;
+ }
+ strbuf_addf(&ldir, "%s/left/", tmpdir.buf);
+ strbuf_addf(&rdir, "%s/right/", tmpdir.buf);
strbuf_addstr(&wtdir, workdir);
if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
strbuf_addch(&wtdir, '/');
@@ -387,19 +405,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
rdir_len = rdir.len;
wtdir_len = wtdir.len;
- child.no_stdin = 1;
- child.git_cmd = 1;
- child.use_shell = 0;
- child.clean_on_exit = 1;
- child.dir = prefix;
- child.out = -1;
- strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
- NULL);
- for (i = 0; i < argc; i++)
- strvec_push(&child.args, argv[i]);
- if (start_command(&child))
+ child->no_stdin = 1;
+ child->git_cmd = 1;
+ child->use_shell = 0;
+ child->clean_on_exit = 1;
+ child->dir = prefix;
+ child->out = -1;
+ if (start_command(child))
die("could not obtain raw diff");
- fp = xfdopen(child.out, "r");
+ fp = xfdopen(child->out, "r");
/* Build index info for left and right sides of the diff */
i = 0;
@@ -410,9 +424,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
const char *src_path, *dst_path;
if (starts_with(info.buf, "::"))
- die(N_("combined diff formats('-c' and '--cc') are "
+ die(N_("combined diff formats ('-c' and '--cc') are "
"not supported in\n"
- "directory diff mode('-d' and '--dir-diff')."));
+ "directory diff mode ('-d' and '--dir-diff')."));
if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
&status))
@@ -525,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
fclose(fp);
fp = NULL;
- if (finish_command(&child)) {
+ if (finish_command(child)) {
ret = error("error occurred running diff --raw");
goto finish;
}
@@ -540,38 +554,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
*/
hashmap_for_each_entry(&submodules, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
}
/*
- * Symbolic links require special treatment.The standard "git diff"
+ * Symbolic links require special treatment. The standard "git diff"
* shows only the link itself, not the contents of the link target.
* This loop replicates that behavior.
*/
hashmap_for_each_entry(&symlinks2, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
- }
- strbuf_release(&buf);
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
+ }
strbuf_setlen(&ldir, ldir_len);
helper_argv[1] = ldir.buf;
@@ -583,7 +578,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
flags = 0;
} else
setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
- rc = run_command_v_opt(helper_argv, flags);
+ ret = run_command_v_opt(helper_argv, flags);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&wtindex);
@@ -617,7 +612,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
if (!indices_loaded) {
struct lock_file lock = LOCK_INIT;
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s/wtindex", tmpdir);
+ strbuf_addf(&buf, "%s/wtindex", tmpdir.buf);
if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
ret = error("could not write %s", buf.buf);
@@ -647,11 +642,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
if (err) {
- warning(_("temporary files exist in '%s'."), tmpdir);
+ warning(_("temporary files exist in '%s'."), tmpdir.buf);
warning(_("you may want to cleanup or recover these."));
- exit(1);
- } else
- exit_cleanup(tmpdir, rc);
+ ret = 1;
+ } else {
+ remove_dir_recursively(&tmpdir, 0);
+ if (ret)
+ warning(_("failed: %d"), ret);
+ }
finish:
if (fp)
@@ -663,30 +661,29 @@ finish:
strbuf_release(&rdir);
strbuf_release(&wtdir);
strbuf_release(&buf);
+ strbuf_release(&tmpdir);
- return ret;
+ return (ret < 0) ? 1 : ret;
}
static int run_file_diff(int prompt, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- struct strvec args = STRVEC_INIT;
const char *env[] = {
"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
NULL
};
- int i;
if (prompt > 0)
env[2] = "GIT_DIFFTOOL_PROMPT=true";
else if (!prompt)
env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+ child->git_cmd = 1;
+ child->dir = prefix;
+ strvec_pushv(&child->env_array, env);
- strvec_push(&args, "diff");
- for (i = 0; i < argc; i++)
- strvec_push(&args, argv[i]);
- return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
+ return run_command(child);
}
int cmd_difftool(int argc, const char **argv, const char *prefix)
@@ -713,12 +710,13 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
"`--tool`")),
OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
N_("make 'git-difftool' exit when an invoked diff "
- "tool returns a non - zero exit code")),
+ "tool returns a non-zero exit code")),
OPT_STRING('x', "extcmd", &extcmd, N_("command"),
N_("specify a custom command for viewing diffs")),
- OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+ OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
OPT_END()
};
+ struct child_process child = CHILD_PROCESS_INIT;
git_config(difftool_config, NULL);
symlinks = has_symlinks;
@@ -768,7 +766,14 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
* will invoke a separate instance of 'git-difftool--helper' for
* each file that changed.
*/
+ strvec_push(&child.args, "diff");
+ if (no_index)
+ strvec_push(&child.args, "--no-index");
+ if (dir_diff)
+ strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
+ strvec_pushv(&child.args, argv);
+
if (dir_diff)
- return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
- return run_file_diff(prompt, prefix, argc, argv);
+ return run_dir_diff(extcmd, symlinks, prefix, &child);
+ return run_file_diff(prompt, prefix, &child);
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 95e8e89e81..8e2caf7281 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -312,7 +312,7 @@ static void export_blob(const struct object_id *oid)
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
- type_name(type)) < 0)
+ type_name(type), NULL) < 0)
die("oid mismatch in blob %s", oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 89cb6307d4..642b4b888f 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -77,7 +77,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
filter.name_patterns = argv;
filter.match_as_path = 1;
- filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, &filter, FILTER_REFS_ALL);
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b42b6fe21f..30a516da29 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -593,18 +593,43 @@ static void get_default_heads(void)
}
}
+struct for_each_loose_cb
+{
+ struct progress *progress;
+ struct strbuf obj_type;
+};
+
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
struct object *obj;
- enum object_type type;
+ enum object_type type = OBJ_NONE;
unsigned long size;
void *contents;
int eaten;
+ struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id real_oid = *null_oid();
+ int err = 0;
- if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+ strbuf_reset(&cb_data->obj_type);
+ oi.type_name = &cb_data->obj_type;
+ oi.sizep = &size;
+ oi.typep = &type;
+
+ if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) {
+ if (contents && !oideq(&real_oid, oid))
+ err = error(_("%s: hash-path mismatch, found at: %s"),
+ oid_to_hex(&real_oid), path);
+ else
+ err = error(_("%s: object corrupt or missing: %s"),
+ oid_to_hex(oid), path);
+ }
+ if (type != OBJ_NONE && type < 0)
+ err = error(_("%s: object is of unknown type '%s': %s"),
+ oid_to_hex(&real_oid), cb_data->obj_type.buf,
+ path);
+ if (err < 0) {
errors_found |= ERROR_OBJECT;
- error(_("%s: object corrupt or missing: %s"),
- oid_to_hex(oid), path);
return 0; /* keep checking other objects */
}
@@ -640,8 +665,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int fsck_subdir(unsigned int nr, const char *path, void *progress)
+static int fsck_subdir(unsigned int nr, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
+ struct progress *progress = cb_data->progress;
display_progress(progress, nr + 1);
return 0;
}
@@ -649,6 +676,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress)
static void fsck_object_dir(const char *path)
{
struct progress *progress = NULL;
+ struct for_each_loose_cb cb_data = {
+ .obj_type = STRBUF_INIT,
+ .progress = progress,
+ };
if (verbose)
fprintf_ln(stderr, _("Checking object directory"));
@@ -657,9 +688,10 @@ static void fsck_object_dir(const char *path)
progress = start_progress(_("Checking object directories"), 256);
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
- progress);
+ &cb_data);
display_progress(progress, 256);
stop_progress(&progress);
+ strbuf_release(&cb_data.obj_type);
}
static int fsck_head_link(const char *head_ref_name,
diff --git a/builtin/gc.c b/builtin/gc.c
index b00b396f55..6b3de3dd51 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1681,9 +1681,7 @@ static int launchctl_remove_plists(void)
static int launchctl_list_contains_plist(const char *name, const char *cmd)
{
- int result;
struct child_process child = CHILD_PROCESS_INIT;
- char *uid = launchctl_get_uid();
strvec_split(&child.args, cmd);
strvec_pushl(&child.args, "list", name, NULL);
@@ -1694,12 +1692,8 @@ static int launchctl_list_contains_plist(const char *name, const char *cmd)
if (start_command(&child))
die(_("failed to start launchctl"));
- result = finish_command(&child);
-
- free(uid);
-
/* Returns failure if 'name' doesn't exist. */
- return !result;
+ return !finish_command(&child);
}
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule)
diff --git a/builtin/grep.c b/builtin/grep.c
index 51278b01fa..8af5249a7b 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -433,17 +433,14 @@ static int grep_submodule(struct grep_opt *opt,
{
struct repository *subrepo;
struct repository *superproject = opt->repo;
- const struct submodule *sub;
struct grep_opt subopt;
int hit = 0;
- sub = submodule_from_path(superproject, null_oid(), path);
-
if (!is_submodule_active(superproject, path))
return 0;
subrepo = xmalloc(sizeof(*subrepo));
- if (repo_submodule_init(subrepo, superproject, sub)) {
+ if (repo_submodule_init(subrepo, superproject, path, null_oid())) {
free(subrepo);
return 0;
}
diff --git a/builtin/help.c b/builtin/help.c
index b7eec06c3d..75cd2fb407 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -7,7 +7,6 @@
#include "exec-cmd.h"
#include "parse-options.h"
#include "run-command.h"
-#include "column.h"
#include "config-list.h"
#include "help.h"
#include "alias.h"
@@ -34,32 +33,52 @@ enum help_format {
HELP_FORMAT_WEB
};
-static const char *html_path;
+enum show_config_type {
+ SHOW_CONFIG_HUMAN,
+ SHOW_CONFIG_VARS,
+ SHOW_CONFIG_SECTIONS,
+};
+
+static enum help_action {
+ HELP_ACTION_ALL = 1,
+ HELP_ACTION_GUIDES,
+ HELP_ACTION_CONFIG,
+ HELP_ACTION_CONFIG_FOR_COMPLETION,
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
+} cmd_mode;
-static int show_all = 0;
-static int show_guides = 0;
-static int show_config;
+static const char *html_path;
static int verbose = 1;
-static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
static struct option builtin_help_options[] = {
- OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+ OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
+ HELP_ACTION_ALL),
OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
- OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
- OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
- OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
OPT_SET_INT('i', "info", &help_format, N_("show info page"),
HELP_FORMAT_INFO),
OPT__VERBOSE(&verbose, N_("print command description")),
+
+ OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
+ HELP_ACTION_GUIDES),
+ OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
+ HELP_ACTION_CONFIG),
+ OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+
OPT_END(),
};
static const char * const builtin_help_usage[] = {
- N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"),
+ N_("git help [-a|--all] [--[no-]verbose]]\n"
+ " [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
+ N_("git help [-g|--guides]"),
+ N_("git help [-c|--config]"),
NULL
};
@@ -70,7 +89,7 @@ struct slot_expansion {
int found;
};
-static void list_config_help(int for_human)
+static void list_config_help(enum show_config_type type)
{
struct slot_expansion slot_expansions[] = {
{ "advice", "*", list_config_advices },
@@ -88,6 +107,8 @@ static void list_config_help(int for_human)
const char **p;
struct slot_expansion *e;
struct string_list keys = STRING_LIST_INIT_DUP;
+ struct string_list keys_uniq = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
int i;
for (p = config_name_list; *p; p++) {
@@ -118,34 +139,46 @@ static void list_config_help(int for_human)
for (i = 0; i < keys.nr; i++) {
const char *var = keys.items[i].string;
const char *wildcard, *tag, *cut;
+ const char *dot = NULL;
+ struct strbuf sb = STRBUF_INIT;
- if (for_human) {
+ switch (type) {
+ case SHOW_CONFIG_HUMAN:
puts(var);
continue;
+ case SHOW_CONFIG_SECTIONS:
+ dot = strchr(var, '.');
+ break;
+ case SHOW_CONFIG_VARS:
+ break;
}
-
wildcard = strchr(var, '*');
tag = strchr(var, '<');
- if (!wildcard && !tag) {
- puts(var);
+ if (!dot && !wildcard && !tag) {
+ string_list_append(&keys_uniq, var);
continue;
}
- if (wildcard && !tag)
+ if (dot)
+ cut = dot;
+ else if (wildcard && !tag)
cut = wildcard;
else if (!wildcard && tag)
cut = tag;
else
cut = wildcard < tag ? wildcard : tag;
- /*
- * We may produce duplicates, but that's up to
- * git-completion.bash to handle
- */
- printf("%.*s\n", (int)(cut - var), var);
+ strbuf_add(&sb, var, cut - var);
+ string_list_append(&keys_uniq, sb.buf);
+ strbuf_release(&sb);
+
}
string_list_clear(&keys, 0);
+ string_list_remove_duplicates(&keys_uniq, 0);
+ for_each_string_list_item(item, &keys_uniq)
+ puts(item->string);
+ string_list_clear(&keys_uniq, 0);
}
static enum help_format parse_help_format(const char *format)
@@ -349,8 +382,6 @@ static int add_man_viewer_info(const char *var, const char *value)
static int git_help_config(const char *var, const char *value, void *cb)
{
- if (starts_with(var, "column."))
- return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) {
if (!value)
return config_error_nonbool(var);
@@ -467,11 +498,14 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
if (!html_path)
html_path = to_free = system_path(GIT_HTML_PATH);
- /* Check that we have a git documentation directory. */
+ /*
+ * Check that the page we're looking for exists.
+ */
if (!strstr(html_path, "://")) {
- if (stat(mkpath("%s/git.html", html_path), &st)
+ if (stat(mkpath("%s/%s.html", html_path, page), &st)
|| !S_ISREG(st.st_mode))
- die("'%s': not a documentation directory.", html_path);
+ die("'%s/%s.html': documentation file not found.",
+ html_path, page);
}
strbuf_init(page_path, 0);
@@ -541,6 +575,13 @@ static const char *check_git_cmd(const char* cmd)
return cmd;
}
+static void no_extra_argc(int argc)
+{
+ if (argc)
+ usage_msg_opt(_("this option doesn't take any other arguments"),
+ builtin_help_usage, builtin_help_options);
+}
+
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
@@ -551,8 +592,8 @@ int cmd_help(int argc, const char **argv, const char *prefix)
builtin_help_usage, 0);
parsed_help_format = help_format;
- if (show_all) {
- git_config(git_help_config, NULL);
+ switch (cmd_mode) {
+ case HELP_ACTION_ALL:
if (verbose) {
setup_pager();
list_all_cmds_help();
@@ -560,30 +601,27 @@ int cmd_help(int argc, const char **argv, const char *prefix)
}
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
load_command_list("git-", &main_cmds, &other_cmds);
- list_commands(colopts, &main_cmds, &other_cmds);
- }
-
- if (show_config) {
- int for_human = show_config == 1;
-
- if (!for_human) {
- list_config_help(for_human);
- return 0;
- }
- setup_pager();
- list_config_help(for_human);
- printf("\n%s\n", _("'git help config' for more information"));
- return 0;
- }
-
- if (show_guides)
+ list_commands(&main_cmds, &other_cmds);
+ printf("%s\n", _(git_more_info_string));
+ break;
+ case HELP_ACTION_GUIDES:
+ no_extra_argc(argc);
list_guides_help();
-
- if (show_all || show_guides) {
printf("%s\n", _(git_more_info_string));
- /*
- * We're done. Ignore any remaining args
- */
+ return 0;
+ case HELP_ACTION_CONFIG_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_VARS);
+ return 0;
+ case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_SECTIONS);
+ return 0;
+ case HELP_ACTION_CONFIG:
+ no_extra_argc(argc);
+ setup_pager();
+ list_config_help(SHOW_CONFIG_HUMAN);
+ printf("\n%s\n", _("'git help config' for more information"));
return 0;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 8b52bea84d..15ae406e6b 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -188,9 +188,7 @@ static void init_thread(void)
pthread_key_create(&key, NULL);
CALLOC_ARRAY(thread_data, nr_threads);
for (i = 0; i < nr_threads; i++) {
- thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
- if (thread_data[i].pack_fd == -1)
- die_errno(_("unable to open %s"), curr_pack);
+ thread_data[i].pack_fd = xopen(curr_pack, O_RDONLY);
}
threads_active = 1;
@@ -1417,7 +1415,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
if (check_object_signature(the_repository, &d->oid,
data, size,
- type_name(type)))
+ type_name(type), NULL))
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
/*
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 29a26ad8ae..031fef1bca 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -209,10 +209,8 @@ static void show_submodule(struct repository *superproject,
struct dir_struct *dir, const char *path)
{
struct repository subrepo;
- const struct submodule *sub = submodule_from_path(superproject,
- null_oid(), path);
- if (repo_submodule_init(&subrepo, superproject, sub))
+ if (repo_submodule_init(&subrepo, superproject, path, null_oid()))
return;
if (repo_read_index(&subrepo) < 0)
@@ -614,7 +612,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
struct option builtin_ls_files_options[] = {
/* Think twice before adding "--nul" synonym to this */
OPT_SET_INT('z', NULL, &line_terminator,
- N_("paths are separated with NUL character"), '\0'),
+ N_("separate paths with the NUL character"), '\0'),
OPT_BOOL('t', NULL, &show_tag,
N_("identify the file status with tags")),
OPT_BOOL('v', NULL, &show_valid_bit,
@@ -651,7 +649,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("skip files matching pattern"),
PARSE_OPT_NONEG, option_parse_exclude),
OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"),
- N_("exclude patterns are read from <file>"),
+ N_("read exclude patterns from <file>"),
PARSE_OPT_NONEG, option_parse_exclude_from),
OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
N_("read additional per-directory exclude patterns in <file>")),
@@ -674,6 +672,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("suppress duplicate entries")),
OPT_END()
};
+ int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
@@ -777,16 +776,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (show_resolve_undo)
show_ru_info(the_repository->index);
- if (ps_matched) {
- int bad;
- bad = report_path_error(ps_matched, &pathspec);
- if (bad)
- fprintf(stderr, "Did you forget to 'git add'?\n");
-
- return bad ? 1 : 0;
+ if (ps_matched && report_path_error(ps_matched, &pathspec)) {
+ fprintf(stderr, "Did you forget to 'git add'?\n");
+ ret = 1;
}
+ string_list_clear(&exclude_list, 0);
dir_clear(&dir);
free(max_prefix);
- return 0;
+ return ret;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index f4fd823af8..318949c3d7 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -7,8 +7,8 @@
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
- " [-q | --quiet] [--exit-code] [--get-url]\n"
- " [--symref] [<repository> [<refs>...]]"),
+ " [-q | --quiet] [--exit-code] [--get-url]\n"
+ " [--symref] [<repository> [<refs>...]]"),
NULL
};
diff --git a/builtin/merge.c b/builtin/merge.c
index 3fbdacc7db..ea3112e0c0 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -13,6 +13,7 @@
#include "builtin.h"
#include "lockfile.h"
#include "run-command.h"
+#include "hook.h"
#include "diff.h"
#include "diff-merges.h"
#include "refs.h"
@@ -680,6 +681,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
trees[nr_trees] = parse_tree_indirect(common);
if (!trees[nr_trees++])
return -1;
@@ -848,7 +850,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* and write it out as a tree. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- if (find_hook("pre-merge-commit"))
+ if (hook_exists("pre-merge-commit"))
discard_cache();
read_cache_from(index_file);
strbuf_addbuf(&msg, &merge_msg);
@@ -1576,6 +1578,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
remove_merge_branch_state(the_repository);
+ strbuf_release(&msg);
goto done;
} else if (!remoteheads->next && common->next)
;
@@ -1746,6 +1749,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = suggest_conflicts();
done:
+ strbuf_release(&buf);
free(branch_to_free);
return ret;
}
diff --git a/builtin/mktag.c b/builtin/mktag.c
index dddcccdd36..3b2dbbb37e 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
repl = lookup_replace_object(the_repository, tagged_oid);
ret = check_object_signature(the_repository, repl,
- buffer, size, type_name(*tagged_type));
+ buffer, size, type_name(*tagged_type),
+ NULL);
free(buffer);
return ret;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 66de6efd41..075d15d706 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -7,7 +7,8 @@
#include "object-store.h"
#define BUILTIN_MIDX_WRITE_USAGE \
- N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]")
+ N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
+ "[--refs-snapshot=<path>]")
#define BUILTIN_MIDX_VERIFY_USAGE \
N_("git multi-pack-index [<options>] verify")
@@ -45,14 +46,15 @@ static char const * const builtin_multi_pack_index_usage[] = {
static struct opts_multi_pack_index {
const char *object_dir;
const char *preferred_pack;
+ const char *refs_snapshot;
unsigned long batch_size;
unsigned flags;
+ int stdin_packs;
} opts;
static struct option common_opts[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
- OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -61,6 +63,33 @@ static struct option *add_common_options(struct option *prev)
return parse_options_concat(common_opts, prev);
}
+static int git_multi_pack_index_write_config(const char *var, const char *value,
+ void *cb)
+{
+ if (!strcmp(var, "pack.writebitmaphashcache")) {
+ if (git_config_bool(var, value))
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+ else
+ opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE;
+ }
+
+ /*
+ * We should never make a fall-back call to 'git_default_config', since
+ * this was already called in 'cmd_multi_pack_index()'.
+ */
+ return 0;
+}
+
+static void read_packs_from_stdin(struct string_list *to)
+{
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF)
+ string_list_append(to, buf.buf);
+ string_list_sort(to);
+
+ strbuf_release(&buf);
+}
+
static int cmd_multi_pack_index_write(int argc, const char **argv)
{
struct option *options;
@@ -70,13 +99,25 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
N_("pack for reuse when computing a multi-pack bitmap")),
OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"),
MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
+ N_("write multi-pack index containing only given indexes")),
+ OPT_FILENAME(0, "refs-snapshot", &opts.refs_snapshot,
+ N_("refs snapshot for selecting bitmap commits")),
OPT_END(),
};
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+
+ git_config(git_multi_pack_index_write_config, NULL);
+
options = add_common_options(builtin_multi_pack_index_write_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_write_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -86,16 +127,39 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
FREE_AND_NULL(options);
+ if (opts.stdin_packs) {
+ struct string_list packs = STRING_LIST_INIT_DUP;
+ int ret;
+
+ read_packs_from_stdin(&packs);
+
+ ret = write_midx_file_only(opts.object_dir, &packs,
+ opts.preferred_pack,
+ opts.refs_snapshot, opts.flags);
+
+ string_list_clear(&packs, 0);
+
+ return ret;
+
+ }
return write_midx_file(opts.object_dir, opts.preferred_pack,
- opts.flags);
+ opts.refs_snapshot, opts.flags);
}
static int cmd_multi_pack_index_verify(int argc, const char **argv)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_verify_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_verify_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_verify_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -108,10 +172,18 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
static int cmd_multi_pack_index_expire(int argc, const char **argv)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_expire_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_expire_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_expire_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -128,6 +200,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
static struct option builtin_multi_pack_index_repack_options[] = {
OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -135,6 +209,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options,
builtin_multi_pack_index_repack_usage,
@@ -156,8 +232,6 @@ int cmd_multi_pack_index(int argc, const char **argv,
git_config(git_default_config, NULL);
- if (isatty(2))
- opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage,
diff --git a/builtin/mv.c b/builtin/mv.c
index c2f96c8e89..83a465ba83 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -118,21 +118,23 @@ static int index_range_of_same_dir(const char *src, int length,
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
- int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+ int verbose = 0, show_only = 0, force = 0, ignore_errors = 0, ignore_sparse = 0;
struct option builtin_mv_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT__DRY_RUN(&show_only, N_("dry run")),
OPT__FORCE(&force, N_("force move/rename even if target exists"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
+ OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_END(),
};
const char **source, **destination, **dest_path, **submodule_gitfile;
- enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+ enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX, SPARSE } *modes;
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
@@ -176,14 +178,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
const char *src = source[i], *dst = destination[i];
int length, src_is_dir;
const char *bad = NULL;
+ int skip_sparse = 0;
if (show_only)
printf(_("Checking rename of '%s' to '%s'\n"), src, dst);
length = strlen(src);
- if (lstat(src, &st) < 0)
- bad = _("bad source");
- else if (!strncmp(src, dst, length) &&
+ if (lstat(src, &st) < 0) {
+ /* only error if existence is expected. */
+ if (modes[i] != SPARSE)
+ bad = _("bad source");
+ } else if (!strncmp(src, dst, length) &&
(dst[length] == 0 || dst[length] == '/')) {
bad = _("can not move directory into itself");
} else if ((src_is_dir = S_ISDIR(st.st_mode))
@@ -212,11 +217,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
dst_len = strlen(dst);
for (j = 0; j < last - first; j++) {
- const char *path = active_cache[first + j]->name;
+ const struct cache_entry *ce = active_cache[first + j];
+ const char *path = ce->name;
source[argc + j] = path;
destination[argc + j] =
prefix_path(dst, dst_len, path + length + 1);
- modes[argc + j] = INDEX;
+ modes[argc + j] = ce_skip_worktree(ce) ? SPARSE : INDEX;
submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
@@ -244,14 +250,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
bad = _("multiple sources for the same target");
else if (is_dir_sep(dst[strlen(dst) - 1]))
bad = _("destination directory does not exist");
- else
+ else {
+ /*
+ * We check if the paths are in the sparse-checkout
+ * definition as a very final check, since that
+ * allows us to point the user to the --sparse
+ * option as a way to have a successful run.
+ */
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(src, &the_index)) {
+ string_list_append(&only_match_skip_worktree, src);
+ skip_sparse = 1;
+ }
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(dst, &the_index)) {
+ string_list_append(&only_match_skip_worktree, dst);
+ skip_sparse = 1;
+ }
+
+ if (skip_sparse)
+ goto remove_entry;
+
string_list_insert(&src_for_dst, dst);
+ }
if (!bad)
continue;
if (!ignore_errors)
die(_("%s, source=%s, destination=%s"),
bad, src, dst);
+remove_entry:
if (--argc > 0) {
int n = argc - i;
memmove(source + i, source + i + 1,
@@ -266,6 +294,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
}
+ if (only_match_skip_worktree.nr) {
+ advise_on_updating_sparse_paths(&only_match_skip_worktree);
+ if (!ignore_errors)
+ return 1;
+ }
+
for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
enum update_mode mode = modes[i];
@@ -274,7 +308,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
printf(_("Renaming %s to %s\n"), src, dst);
if (show_only)
continue;
- if (mode != INDEX && rename(src, dst) < 0) {
+ if (mode != INDEX && mode != SPARSE && rename(src, dst) < 0) {
if (ignore_errors)
continue;
die_errno(_("renaming '%s' failed"), src);
diff --git a/builtin/prune.c b/builtin/prune.c
index 02c6ab7cba..485c9a3c56 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
expire = TIME_MAX;
save_commit_buffer = 0;
read_replace_refs = 0;
- ref_paranoia = 1;
repo_init_revisions(the_repository, &revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index cf6c56e2d8..ae9f5bd7cc 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -31,9 +31,8 @@
/**
* Parses the value of --rebase. If value is a false value, returns
* REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
- * "merges", returns REBASE_MERGES. If value is "preserve", returns
- * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
- * fatal is true, otherwise returns REBASE_INVALID.
+ * "merges", returns REBASE_MERGES. If value is a invalid value, dies with
+ * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
*/
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
@@ -127,7 +126,7 @@ static struct option pull_options[] = {
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
OPT_CALLBACK_F('r', "rebase", &opt_rebase,
- "(false|true|merges|preserve|interactive)",
+ "(false|true|merges|interactive)",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
@@ -884,8 +883,6 @@ static int run_rebase(const struct object_id *newbase,
/* Options passed to git-rebase */
if (opt_rebase == REBASE_MERGES)
strvec_push(&args, "--rebase-merges");
- else if (opt_rebase == REBASE_PRESERVE)
- strvec_push(&args, "--preserve-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
strvec_push(&args, "--interactive");
if (opt_diffstat)
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 485e7b0479..2109c4c9e5 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -38,7 +38,7 @@ static int list_tree(struct object_id *oid)
}
static const char * const read_tree_usage[] = {
- N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+ N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
NULL
};
@@ -53,24 +53,16 @@ static int index_output_cb(const struct option *opt, const char *arg,
static int exclude_per_directory_cb(const struct option *opt, const char *arg,
int unset)
{
- struct dir_struct *dir;
struct unpack_trees_options *opts;
BUG_ON_OPT_NEG(unset);
opts = (struct unpack_trees_options *)opt->value;
- if (opts->dir)
- die("more than one --exclude-per-directory given.");
-
- dir = xcalloc(1, sizeof(*opts->dir));
- dir->flags |= DIR_SHOW_IGNORED;
- dir->exclude_per_dir = arg;
- opts->dir = dir;
- /* We do not need to nor want to do read-directory
- * here; we are merely interested in reusing the
- * per directory ignore stack mechanism.
- */
+ if (!opts->update)
+ die("--exclude-per-directory is meaningless unless -u");
+ if (strcmp(arg, ".gitignore"))
+ die("--exclude-per-directory argument must be .gitignore");
return 0;
}
@@ -174,6 +166,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
+ if (opts.reset)
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+
/*
* NEEDSWORK
*
@@ -209,8 +204,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if ((opts.update || opts.index_only) && !opts.merge)
die("%s is meaningless without -m, --reset, or --prefix",
opts.update ? "-u" : "-i");
- if ((opts.dir && !opts.update))
- die("--exclude-per-directory is meaningless unless -u");
+ if (opts.update && !opts.reset)
+ opts.preserve_ignored = 0;
+ /* otherwise, opts.preserve_ignored is irrelevant */
if (opts.merge && !opts.index_only)
setup_work_tree();
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6c2463037f..34b4744e5f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -48,8 +48,7 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
enum rebase_type {
REBASE_UNSPECIFIED = -1,
REBASE_APPLY,
- REBASE_MERGE,
- REBASE_PRESERVE_MERGES
+ REBASE_MERGE
};
enum empty_type {
@@ -163,12 +162,7 @@ enum action {
ACTION_ABORT,
ACTION_QUIT,
ACTION_EDIT_TODO,
- ACTION_SHOW_CURRENT_PATCH,
- ACTION_SHORTEN_OIDS,
- ACTION_EXPAND_OIDS,
- ACTION_CHECK_TODO_LIST,
- ACTION_REARRANGE_SQUASH,
- ACTION_ADD_EXEC
+ ACTION_SHOW_CURRENT_PATCH
};
static const char *action_names[] = { "undefined",
@@ -179,81 +173,6 @@ static const char *action_names[] = { "undefined",
"edit_todo",
"show_current_patch" };
-static int add_exec_commands(struct string_list *commands)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- todo_list_add_exec_commands(&todo_list, commands);
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int rearrange_squash_in_todo_file(void)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res = 0;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_rearrange_squash(&todo_list);
- if (!res)
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
-
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int transform_todo_file(unsigned flags)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
- NULL, NULL, -1, flags);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
static int edit_todo_file(unsigned flags)
{
const char *todo_file = rebase_path_todo();
@@ -403,7 +322,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
- flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
@@ -439,24 +357,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
break;
}
- case ACTION_SHORTEN_OIDS:
- case ACTION_EXPAND_OIDS:
- ret = transform_todo_file(flags);
- break;
- case ACTION_CHECK_TODO_LIST:
- ret = check_todo_list_from_file(the_repository);
- break;
- case ACTION_REARRANGE_SQUASH:
- ret = rearrange_squash_in_todo_file();
- break;
- case ACTION_ADD_EXEC: {
- struct string_list commands = STRING_LIST_INIT_DUP;
-
- split_exec_commands(opts->cmd, &commands);
- ret = add_exec_commands(&commands);
- string_list_clear(&commands, 0);
- break;
- }
default:
BUG("invalid command '%d'", command);
}
@@ -478,105 +378,9 @@ static int parse_opt_keep_empty(const struct option *opt, const char *arg,
return 0;
}
-static const char * const builtin_rebase_interactive_usage[] = {
- N_("git rebase--interactive [<options>]"),
- NULL
-};
-
-int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
-{
- struct rebase_options opts = REBASE_OPTIONS_INIT;
- struct object_id squash_onto = *null_oid();
- enum action command = ACTION_NONE;
- struct option options[] = {
- OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
- REBASE_FORCE),
- OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
- N_("keep commits which start empty"),
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
- parse_opt_keep_empty),
- OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
- N_("allow commits with empty messages"),
- PARSE_OPT_HIDDEN),
- OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
- OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
- N_("keep original branch points of cousins")),
- OPT_BOOL(0, "autosquash", &opts.autosquash,
- N_("move commits that begin with squash!/fixup!")),
- OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
- OPT_BIT('v', "verbose", &opts.flags,
- N_("display a diffstat of what changed upstream"),
- REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
- OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
- ACTION_CONTINUE),
- OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
- OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
- ACTION_EDIT_TODO),
- OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
- ACTION_SHOW_CURRENT_PATCH),
- OPT_CMDMODE(0, "shorten-ids", &command,
- N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
- OPT_CMDMODE(0, "expand-ids", &command,
- N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
- OPT_CMDMODE(0, "check-todo-list", &command,
- N_("check the todo list"), ACTION_CHECK_TODO_LIST),
- OPT_CMDMODE(0, "rearrange-squash", &command,
- N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
- OPT_CMDMODE(0, "add-exec-commands", &command,
- N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
- { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
- N_("restrict-revision"), N_("restrict revision"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
- N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
- { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
- N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
- 0 },
- OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
- { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
- N_("GPG-sign commits"),
- PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
- N_("rebase strategy")),
- OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
- N_("strategy options")),
- OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
- N_("the branch or commit to checkout")),
- OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
- OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
- OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
- OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
- N_("automatically re-schedule any `exec` that fails")),
- OPT_END()
- };
-
- opts.rebase_cousins = -1;
-
- if (argc == 1)
- usage_with_options(builtin_rebase_interactive_usage, options);
-
- argc = parse_options(argc, argv, prefix, options,
- builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
-
- prepare_repo_settings(the_repository);
- the_repository->settings.command_requires_full_index = 0;
-
- if (!is_null_oid(&squash_onto))
- opts.squash_onto = &squash_onto;
-
- if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
- warning(_("--[no-]rebase-cousins has no effect without "
- "--rebase-merges"));
-
- return !!run_sequencer_rebase(&opts, command);
-}
-
static int is_merge(struct rebase_options *opts)
{
- return opts->type == REBASE_MERGE ||
- opts->type == REBASE_PRESERVE_MERGES;
+ return opts->type == REBASE_MERGE;
}
static void imply_merge(struct rebase_options *opts, const char *option)
@@ -586,7 +390,6 @@ static void imply_merge(struct rebase_options *opts, const char *option)
die(_("%s requires the merge backend"), option);
break;
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
break;
default:
opts->type = REBASE_MERGE; /* implied */
@@ -765,28 +568,6 @@ static int finish_rebase(struct rebase_options *opts)
return ret;
}
-static struct commit *peel_committish(const char *name)
-{
- struct object *obj;
- struct object_id oid;
-
- if (get_oid(name, &oid))
- return NULL;
- obj = parse_object(the_repository, &oid);
- return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-static void add_var(struct strbuf *buf, const char *name, const char *value)
-{
- if (!value)
- strbuf_addf(buf, "unset %s; ", name);
- else {
- strbuf_addf(buf, "%s=", name);
- sq_quote_buf(buf, value);
- strbuf_addstr(buf, "; ");
- }
-}
-
static int move_to_original_branch(struct rebase_options *opts)
{
struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
@@ -943,10 +724,7 @@ static int run_am(struct rebase_options *opts)
static int run_specific_rebase(struct rebase_options *opts, enum action action)
{
- const char *argv[] = { NULL, NULL };
- struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
int status;
- const char *backend, *backend_func;
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
@@ -963,87 +741,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
}
status = run_sequencer_rebase(opts, action);
- goto finished_rebase;
- }
-
- if (opts->type == REBASE_APPLY) {
+ } else if (opts->type == REBASE_APPLY)
status = run_am(opts);
- goto finished_rebase;
- }
-
- add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
- add_var(&script_snippet, "state_dir", opts->state_dir);
-
- add_var(&script_snippet, "upstream_name", opts->upstream_name);
- add_var(&script_snippet, "upstream", opts->upstream ?
- oid_to_hex(&opts->upstream->object.oid) : NULL);
- add_var(&script_snippet, "head_name",
- opts->head_name ? opts->head_name : "detached HEAD");
- add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
- add_var(&script_snippet, "onto", opts->onto ?
- oid_to_hex(&opts->onto->object.oid) : NULL);
- add_var(&script_snippet, "onto_name", opts->onto_name);
- add_var(&script_snippet, "revisions", opts->revisions);
- add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
- oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- sq_quote_argv_pretty(&buf, opts->git_am_opts.v);
- add_var(&script_snippet, "git_am_opt", buf.buf);
- strbuf_release(&buf);
- add_var(&script_snippet, "verbose",
- opts->flags & REBASE_VERBOSE ? "t" : "");
- add_var(&script_snippet, "diffstat",
- opts->flags & REBASE_DIFFSTAT ? "t" : "");
- add_var(&script_snippet, "force_rebase",
- opts->flags & REBASE_FORCE ? "t" : "");
- if (opts->switch_to)
- add_var(&script_snippet, "switch_to", opts->switch_to);
- add_var(&script_snippet, "action", opts->action ? opts->action : "");
- add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
- add_var(&script_snippet, "allow_rerere_autoupdate",
- opts->allow_rerere_autoupdate ?
- opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
- "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
- add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
- add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
- add_var(&script_snippet, "cmd", opts->cmd);
- add_var(&script_snippet, "allow_empty_message",
- opts->allow_empty_message ? "--allow-empty-message" : "");
- add_var(&script_snippet, "rebase_merges",
- opts->rebase_merges ? "t" : "");
- add_var(&script_snippet, "rebase_cousins",
- opts->rebase_cousins ? "t" : "");
- add_var(&script_snippet, "strategy", opts->strategy);
- add_var(&script_snippet, "strategy_opts", opts->strategy_opts);
- add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
- add_var(&script_snippet, "squash_onto",
- opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
- add_var(&script_snippet, "git_format_patch_opt",
- opts->git_format_patch_opt.buf);
-
- if (is_merge(opts) &&
- !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
- strbuf_addstr(&script_snippet,
- "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
- opts->autosquash = 0;
- }
-
- switch (opts->type) {
- case REBASE_PRESERVE_MERGES:
- backend = "git-rebase--preserve-merges";
- backend_func = "git_rebase__preserve_merges";
- break;
- default:
+ else
BUG("Unhandled rebase type %d", opts->type);
- break;
- }
- strbuf_addf(&script_snippet,
- ". git-sh-setup && . %s && %s", backend, backend_func);
- argv[0] = script_snippet.buf;
-
- status = run_command_v_opt(argv, RUN_USING_SHELL);
-finished_rebase:
if (opts->dont_finish_rebase)
; /* do nothing */
else if (opts->type == REBASE_MERGE)
@@ -1061,8 +763,6 @@ finished_rebase:
die("Nothing to do");
}
- strbuf_release(&script_snippet);
-
return status ? -1 : 0;
}
@@ -1198,7 +898,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
return 0;
}
-/* -i followed by -p is still explicitly interactive, but -p alone is not */
+/* -i followed by -r is still explicitly interactive, but -r alone is not */
static int parse_opt_interactive(const struct option *opt, const char *arg,
int unset)
{
@@ -1316,6 +1016,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
+ int preserve_merges_selected = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1380,10 +1081,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive),
- OPT_SET_INT_F('p', "preserve-merges", &options.type,
+ OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"),
- REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
+ 1, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
N_("how to handle commits that become empty"),
@@ -1454,8 +1155,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/rewritten", merge_dir());
if (is_directory(buf.buf)) {
- options.type = REBASE_PRESERVE_MERGES;
- options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+ die("`rebase -p` is no longer supported");
} else {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
@@ -1476,6 +1176,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
builtin_rebase_options,
builtin_rebase_usage, 0);
+ if (preserve_merges_selected)
+ die(_("--preserve-merges was replaced by --rebase-merges"));
+
if (action != ACTION_NONE && total_argc != 2) {
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
@@ -1485,10 +1188,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
- if (options.type == REBASE_PRESERVE_MERGES)
- warning(_("git rebase --preserve-merges is deprecated. "
- "Use --rebase-merges instead."));
-
if (keep_base) {
if (options.onto_name)
die(_("cannot combine '--keep-base' with '--onto'"));
@@ -1580,7 +1279,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository, 0);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
@@ -1589,11 +1288,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
- ret = !!sequencer_remove_state(&replay);
+ ret = sequencer_remove_state(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
- ret = !!remove_dir_recursively(&buf, 0);
+ ret = remove_dir_recursively(&buf, 0);
if (ret)
error(_("could not remove '%s'"),
options.state_dir);
@@ -1708,7 +1407,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.ignore_date)
strvec_push(&options.git_am_opts, "--ignore-date");
} else {
- /* REBASE_MERGE and PRESERVE_MERGES */
+ /* REBASE_MERGE */
if (ignore_whitespace) {
string_list_append(&strategy_options,
"ignore-space-change");
@@ -1734,7 +1433,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
case REBASE_APPLY:
die(_("--strategy requires --merge or --interactive"));
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
/* compatible */
break;
case REBASE_UNSPECIFIED:
@@ -1786,7 +1484,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
switch (options.type) {
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
options.state_dir = merge_dir();
break;
case REBASE_APPLY:
@@ -1811,28 +1508,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.signoff) {
- if (options.type == REBASE_PRESERVE_MERGES)
- die("cannot combine '--signoff' with "
- "'--preserve-merges'");
strvec_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}
- if (options.type == REBASE_PRESERVE_MERGES) {
- /*
- * Note: incompatibility with --signoff handled in signoff block above
- * Note: incompatibility with --interactive is just a strong warning;
- * git-rebase.txt caveats with "unless you know what you are doing"
- */
- if (options.rebase_merges)
- die(_("cannot combine '--preserve-merges' with "
- "'--rebase-merges'"));
-
- if (options.reschedule_failed_exec)
- die(_("error: cannot combine '--preserve-merges' with "
- "'--reschedule-failed-exec'"));
- }
-
if (!options.root) {
if (argc < 1) {
struct branch *branch;
@@ -1851,7 +1530,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!strcmp(options.upstream_name, "-"))
options.upstream_name = "@{-1}";
}
- options.upstream = peel_committish(options.upstream_name);
+ options.upstream =
+ lookup_commit_reference_by_name(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
@@ -1894,7 +1574,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.onto = lookup_commit_or_die(&merge_base,
options.onto_name);
} else {
- options.onto = peel_committish(options.onto_name);
+ options.onto =
+ lookup_commit_reference_by_name(options.onto_name);
if (!options.onto)
die(_("Does not point to a valid commit '%s'"),
options.onto_name);
@@ -1919,13 +1600,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
- } else if (!get_oid(branch_name, &options.orig_head) &&
- lookup_commit_reference(the_repository,
- &options.orig_head))
+ } else {
+ struct commit *commit =
+ lookup_commit_reference_by_name(branch_name);
+ if (!commit)
+ die(_("no such branch/commit '%s'"),
+ branch_name);
+ oidcpy(&options.orig_head, &commit->object.oid);
options.head_name = NULL;
- else
- die(_("no such branch/commit '%s'"),
- branch_name);
+ }
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
@@ -1965,7 +1648,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (require_clean_work_tree(the_repository, "rebase",
_("Please commit or stash them."), 1, 1)) {
- ret = 1;
+ ret = -1;
goto cleanup;
}
@@ -2000,7 +1683,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, buf.buf,
DEFAULT_REFLOG_ACTION) < 0) {
- ret = !!error(_("could not switch to "
+ ret = error(_("could not switch to "
"%s"),
options.switch_to);
goto cleanup;
@@ -2015,7 +1698,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
else
printf(_("Current branch %s is up to date.\n"),
branch_name);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
} else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
@@ -2093,7 +1776,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
DEFAULT_REFLOG_ACTION);
strbuf_release(&msg);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
@@ -2107,7 +1790,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.revisions = revisions.buf;
run_rebase:
- ret = !!run_specific_rebase(&options, action);
+ ret = run_specific_rebase(&options, action);
cleanup:
strbuf_release(&buf);
@@ -2118,5 +1801,5 @@ cleanup:
free(options.strategy);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
- return ret;
+ return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 48960a9575..49b846d960 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -7,6 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
+#include "hook.h"
#include "exec-cmd.h"
#include "commit.h"
#include "object.h"
@@ -134,6 +135,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
if (status)
return status;
+ status = git_gpg_config(var, value, NULL);
+ if (status)
+ return status;
+
if (strcmp(var, "receive.denydeletes") == 0) {
deny_deletes = git_config_bool(var, value);
return 0;
@@ -1463,7 +1468,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
- if (!find_hook(push_to_checkout_hook))
+ if (!hook_exists(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
else
retval = push_to_checkout(sha1, &env, work_tree);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 09541d1c80..bd4c669918 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -629,8 +629,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
free_worktrees(worktrees);
for (i = 0; i < collected.nr; i++) {
struct collected_reflog *e = collected.e[i];
+
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
- status |= reflog_expire(e->reflog, &e->oid, flags,
+ status |= reflog_expire(e->reflog, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -642,13 +643,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (; i < argc; i++) {
char *ref;
- struct object_id oid;
- if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
+ if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -700,7 +700,6 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
for ( ; i < argc; i++) {
const char *spec = strstr(argv[i], "@{");
- struct object_id oid;
char *ep, *ref;
int recno;
@@ -709,7 +708,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
continue;
}
- if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
+ if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
status |= error(_("no reflog for '%s'"), argv[i]);
continue;
}
@@ -724,7 +723,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
cb.cmd.expire_total = 0;
}
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
diff --git a/builtin/remote.c b/builtin/remote.c
index 7f88e6ce9d..299c466116 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -318,6 +318,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
* truth value with >= REBASE_TRUE.
*/
info->rebase = rebase_parse_value(value);
+ if (info->rebase == REBASE_INVALID)
+ warning(_("unhandled branch.%s.rebase=%s; assuming "
+ "'true'"), name, value);
break;
case PUSH_REMOTE:
if (info->push_remote_name)
@@ -344,6 +347,14 @@ struct ref_states {
int queried;
};
+#define REF_STATES_INIT { \
+ .new_refs = STRING_LIST_INIT_DUP, \
+ .stale = STRING_LIST_INIT_DUP, \
+ .tracked = STRING_LIST_INIT_DUP, \
+ .heads = STRING_LIST_INIT_DUP, \
+ .push = STRING_LIST_INIT_DUP, \
+}
+
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
{
struct ref *fetch_map = NULL, **tail = &fetch_map;
@@ -355,9 +366,6 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
die(_("Could not get fetch map for refspec %s"),
states->remote->fetch.raw[i]);
- states->new_refs.strdup_strings = 1;
- states->tracked.strdup_strings = 1;
- states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
@@ -406,7 +414,6 @@ static int get_push_ref_states(const struct ref *remote_refs,
match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
- states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
struct string_list_item *item;
struct push_info *info;
@@ -449,7 +456,6 @@ static int get_push_ref_states_noquery(struct ref_states *states)
if (remote->mirror)
return 0;
- states->push.strdup_strings = 1;
if (!remote->push.nr) {
item = string_list_append(&states->push, _("(matching)"));
info = item->util = xcalloc(1, sizeof(struct push_info));
@@ -483,7 +489,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
refspec.force = 0;
refspec.pattern = 1;
refspec.src = refspec.dst = "refs/heads/*";
- states->heads.strdup_strings = 1;
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
@@ -970,26 +975,31 @@ static int get_remote_ref_states(const char *name,
}
struct show_info {
- struct string_list *list;
- struct ref_states *states;
+ struct string_list list;
+ struct ref_states states;
int width, width2;
int any_rebase;
};
+#define SHOW_INFO_INIT { \
+ .list = STRING_LIST_INIT_DUP, \
+ .states = REF_STATES_INIT, \
+}
+
static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
int n = strlen(item->string);
if (n > info->width)
info->width = n;
- string_list_insert(info->list, item->string);
+ string_list_insert(&info->list, item->string);
return 0;
}
static int show_remote_info_item(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
- struct ref_states *states = info->states;
+ struct ref_states *states = &info->states;
const char *name = item->string;
if (states->queried) {
@@ -1016,7 +1026,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
{
struct show_info *show_info = cb_data;
- struct ref_states *states = show_info->states;
+ struct ref_states *states = &show_info->states;
struct branch_info *branch_info = branch_item->util;
struct string_list_item *item;
int n;
@@ -1029,7 +1039,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
if (branch_info->rebase >= REBASE_TRUE)
show_info->any_rebase = 1;
- item = string_list_insert(show_info->list, branch_item->string);
+ item = string_list_insert(&show_info->list, branch_item->string);
item->util = branch_info;
return 0;
@@ -1084,7 +1094,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
show_info->width = n;
if ((n = strlen(push_info->dest)) > show_info->width2)
show_info->width2 = n;
- item = string_list_append(show_info->list, push_item->string);
+ item = string_list_append(&show_info->list, push_item->string);
item->util = push_item->util;
return 0;
}
@@ -1212,9 +1222,7 @@ static int show(int argc, const char **argv)
OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")),
OPT_END()
};
- struct ref_states states;
- struct string_list info_list = STRING_LIST_INIT_NODUP;
- struct show_info info;
+ struct show_info info = SHOW_INFO_INIT;
argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
0);
@@ -1225,26 +1233,22 @@ static int show(int argc, const char **argv)
if (!no_query)
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
- memset(&states, 0, sizeof(states));
- memset(&info, 0, sizeof(info));
- info.states = &states;
- info.list = &info_list;
for (; argc; argc--, argv++) {
int i;
const char **url;
int url_nr;
- get_remote_ref_states(*argv, &states, query_flag);
+ get_remote_ref_states(*argv, &info.states, query_flag);
printf_ln(_("* remote %s"), *argv);
- printf_ln(_(" Fetch URL: %s"), states.remote->url_nr > 0 ?
- states.remote->url[0] : _("(no URL)"));
- if (states.remote->pushurl_nr) {
- url = states.remote->pushurl;
- url_nr = states.remote->pushurl_nr;
+ printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ?
+ info.states.remote->url[0] : _("(no URL)"));
+ if (info.states.remote->pushurl_nr) {
+ url = info.states.remote->pushurl;
+ url_nr = info.states.remote->pushurl_nr;
} else {
- url = states.remote->url;
- url_nr = states.remote->url_nr;
+ url = info.states.remote->url;
+ url_nr = info.states.remote->url_nr;
}
for (i = 0; i < url_nr; i++)
/*
@@ -1257,57 +1261,57 @@ static int show(int argc, const char **argv)
printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
printf_ln(_(" HEAD branch: %s"), _("(not queried)"));
- else if (!states.heads.nr)
+ else if (!info.states.heads.nr)
printf_ln(_(" HEAD branch: %s"), _("(unknown)"));
- else if (states.heads.nr == 1)
- printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string);
+ else if (info.states.heads.nr == 1)
+ printf_ln(_(" HEAD branch: %s"), info.states.heads.items[0].string);
else {
printf(_(" HEAD branch (remote HEAD is ambiguous,"
" may be one of the following):\n"));
- for (i = 0; i < states.heads.nr; i++)
- printf(" %s\n", states.heads.items[i].string);
+ for (i = 0; i < info.states.heads.nr; i++)
+ printf(" %s\n", info.states.heads.items[i].string);
}
/* remote branch info */
info.width = 0;
- for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
- for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
- for_each_string_list(&states.stale, add_remote_to_show_info, &info);
- if (info.list->nr)
+ for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
+ if (info.list.nr)
printf_ln(Q_(" Remote branch:%s",
" Remote branches:%s",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_remote_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_remote_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git pull info */
info.width = 0;
info.any_rebase = 0;
for_each_string_list(&branch_list, add_local_to_show_info, &info);
- if (info.list->nr)
+ if (info.list.nr)
printf_ln(Q_(" Local branch configured for 'git pull':",
" Local branches configured for 'git pull':",
- info.list->nr));
- for_each_string_list(info.list, show_local_info_item, &info);
- string_list_clear(info.list, 0);
+ info.list.nr));
+ for_each_string_list(&info.list, show_local_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git push info */
- if (states.remote->mirror)
+ if (info.states.remote->mirror)
printf_ln(_(" Local refs will be mirrored by 'git push'"));
info.width = info.width2 = 0;
- for_each_string_list(&states.push, add_push_to_show_info, &info);
- QSORT(info.list->items, info.list->nr, cmp_string_with_push);
- if (info.list->nr)
+ for_each_string_list(&info.states.push, add_push_to_show_info, &info);
+ QSORT(info.list.items, info.list.nr, cmp_string_with_push);
+ if (info.list.nr)
printf_ln(Q_(" Local ref configured for 'git push'%s:",
" Local refs configured for 'git push'%s:",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_push_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_push_info_item, &info);
+ string_list_clear(&info.list, 0);
- free_remote_ref_states(&states);
+ free_remote_ref_states(&info.states);
}
return result;
@@ -1334,8 +1338,7 @@ static int set_head(int argc, const char **argv)
if (!opt_a && !opt_d && argc == 2) {
head_name = xstrdup(argv[1]);
} else if (opt_a && !opt_d && argc == 1) {
- struct ref_states states;
- memset(&states, 0, sizeof(states));
+ struct ref_states states = REF_STATES_INIT;
get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
if (!states.heads.nr)
result |= error(_("Cannot determine remote HEAD"));
@@ -1374,14 +1377,13 @@ static int set_head(int argc, const char **argv)
static int prune_remote(const char *remote, int dry_run)
{
int result = 0;
- struct ref_states states;
+ struct ref_states states = REF_STATES_INIT;
struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
const char *dangling_msg = dry_run
? _(" %s will become dangling!")
: _(" %s has become dangling!");
- memset(&states, 0, sizeof(states));
get_remote_ref_states(remote, &states, GET_REF_STATES);
if (!states.stale.nr) {
diff --git a/builtin/repack.c b/builtin/repack.c
index c1a209013b..0b2d1e5d82 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -15,6 +15,8 @@
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
+#include "pack-bitmap.h"
+#include "refs.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
@@ -94,12 +96,14 @@ static void remove_pack_on_signal(int signo)
}
/*
- * Adds all packs hex strings to the fname list, which do not
- * have a corresponding .keep file. These packs are not to
- * be kept if we are going to pack everything into one file.
+ * Adds all packs hex strings to either fname_nonkept_list or
+ * fname_kept_list based on whether each pack has a corresponding
+ * .keep file or not. Packs without a .keep file are not to be kept
+ * if we are going to pack everything into one file.
*/
-static void get_non_kept_pack_filenames(struct string_list *fname_list,
- const struct string_list *extra_keep)
+static void collect_pack_filenames(struct string_list *fname_nonkept_list,
+ struct string_list *fname_kept_list,
+ const struct string_list *extra_keep)
{
DIR *dir;
struct dirent *e;
@@ -112,21 +116,20 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
size_t len;
int i;
+ if (!strip_suffix(e->d_name, ".pack", &len))
+ continue;
+
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
break;
- if (extra_keep->nr > 0 && i < extra_keep->nr)
- continue;
-
- if (!strip_suffix(e->d_name, ".pack", &len))
- continue;
fname = xmemdupz(e->d_name, len);
- if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
- string_list_append_nodup(fname_list, fname);
+ if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
+ (file_exists(mkpath("%s/%s.keep", packdir, fname))))
+ string_list_append_nodup(fname_kept_list, fname);
else
- free(fname);
+ string_list_append_nodup(fname_nonkept_list, fname);
}
closedir(dir);
}
@@ -422,6 +425,25 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
geometry->split = split;
}
+static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+{
+ if (!geometry) {
+ /*
+ * No geometry means either an all-into-one repack (in which
+ * case there is only one pack left and it is the largest) or an
+ * incremental one.
+ *
+ * If repacking incrementally, then we could check the size of
+ * all packs to determine which should be preferred, but leave
+ * this for later.
+ */
+ return NULL;
+ }
+ if (geometry->split == geometry->pack_nr)
+ return NULL;
+ return geometry->pack[geometry->pack_nr - 1];
+}
+
static void clear_pack_geometry(struct pack_geometry *geometry)
{
if (!geometry)
@@ -433,17 +455,162 @@ static void clear_pack_geometry(struct pack_geometry *geometry)
geometry->split = 0;
}
+struct midx_snapshot_ref_data {
+ struct tempfile *f;
+ struct oidset seen;
+ int preferred;
+};
+
+static int midx_snapshot_ref_one(const char *refname,
+ const struct object_id *oid,
+ int flag, void *_data)
+{
+ struct midx_snapshot_ref_data *data = _data;
+ struct object_id peeled;
+
+ if (!peel_iterated_oid(oid, &peeled))
+ oid = &peeled;
+
+ if (oidset_insert(&data->seen, oid))
+ return 0; /* already seen */
+
+ if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
+ return 0;
+
+ fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
+ oid_to_hex(oid));
+
+ return 0;
+}
+
+static void midx_snapshot_refs(struct tempfile *f)
+{
+ struct midx_snapshot_ref_data data;
+ const struct string_list *preferred = bitmap_preferred_tips(the_repository);
+
+ data.f = f;
+ data.preferred = 0;
+ oidset_init(&data.seen, 0);
+
+ if (!fdopen_tempfile(f, "w"))
+ die(_("could not open tempfile %s for writing"),
+ get_tempfile_path(f));
+
+ if (preferred) {
+ struct string_list_item *item;
+
+ data.preferred = 1;
+ for_each_string_list_item(item, preferred)
+ for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+ data.preferred = 0;
+ }
+
+ for_each_ref(midx_snapshot_ref_one, &data);
+
+ if (close_tempfile_gently(f)) {
+ int save_errno = errno;
+ delete_tempfile(&f);
+ errno = save_errno;
+ die_errno(_("could not close refs snapshot tempfile"));
+ }
+
+ oidset_clear(&data.seen);
+}
+
+static void midx_included_packs(struct string_list *include,
+ struct string_list *existing_nonkept_packs,
+ struct string_list *existing_kept_packs,
+ struct string_list *names,
+ struct pack_geometry *geometry)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, existing_kept_packs)
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ for_each_string_list_item(item, names)
+ string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
+ if (geometry) {
+ struct strbuf buf = STRBUF_INIT;
+ uint32_t i;
+ for (i = geometry->split; i < geometry->pack_nr; i++) {
+ struct packed_git *p = geometry->pack[i];
+
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+ strbuf_addstr(&buf, ".idx");
+
+ string_list_insert(include, strbuf_detach(&buf, NULL));
+ }
+ } else {
+ for_each_string_list_item(item, existing_nonkept_packs) {
+ if (item->util)
+ continue;
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ }
+ }
+}
+
+static int write_midx_included_packs(struct string_list *include,
+ struct pack_geometry *geometry,
+ const char *refs_snapshot,
+ int show_progress, int write_bitmaps)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct string_list_item *item;
+ struct packed_git *largest = get_largest_active_pack(geometry);
+ FILE *in;
+ int ret;
+
+ if (!include->nr)
+ return 0;
+
+ cmd.in = -1;
+ cmd.git_cmd = 1;
+
+ strvec_push(&cmd.args, "multi-pack-index");
+ strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
+
+ if (show_progress)
+ strvec_push(&cmd.args, "--progress");
+ else
+ strvec_push(&cmd.args, "--no-progress");
+
+ if (write_bitmaps)
+ strvec_push(&cmd.args, "--bitmap");
+
+ if (largest)
+ strvec_pushf(&cmd.args, "--preferred-pack=%s",
+ pack_basename(largest));
+
+ if (refs_snapshot)
+ strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
+
+ ret = start_command(&cmd);
+ if (ret)
+ return ret;
+
+ in = xfdopen(cmd.in, "w");
+ for_each_string_list_item(item, include)
+ fprintf(in, "%s\n", item->string);
+ fclose(in);
+
+ return finish_command(&cmd);
+}
+
int cmd_repack(int argc, const char **argv, const char *prefix)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
struct string_list rollback = STRING_LIST_INIT_NODUP;
- struct string_list existing_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL;
struct strbuf line = STRBUF_INIT;
+ struct tempfile *refs_snapshot = NULL;
int i, ext, ret;
FILE *out;
+ int show_progress = isatty(2);
/* variables to be filled by option parsing */
int pack_everything = 0;
@@ -454,6 +621,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0;
struct pack_objects_args po_args = {NULL};
int geometric_factor = 0;
+ int write_midx = 0;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -496,6 +664,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("do not repack this pack")),
OPT_INTEGER('g', "geometric", &geometric_factor,
N_("find a geometric progression with factor <N>")),
+ OPT_BOOL('m', "write-midx", &write_midx,
+ N_("write a multi-pack index of the resulting packs")),
OPT_END()
};
@@ -512,8 +682,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
die(_("--keep-unreachable and -A are incompatible"));
if (write_bitmaps < 0) {
- if (!(pack_everything & ALL_INTO_ONE) ||
- !is_bare_repository())
+ if (!write_midx &&
+ (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
write_bitmaps = 0;
} else if (write_bitmaps &&
git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0) &&
@@ -523,9 +693,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps > 0;
- if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
+ if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
+ if (write_midx && write_bitmaps) {
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addf(&path, "%s/%s_XXXXXX", get_object_directory(),
+ "bitmap-ref-tips");
+
+ refs_snapshot = xmks_tempfile(path.buf);
+ midx_snapshot_refs(refs_snapshot);
+
+ strbuf_release(&path);
+ }
+
if (geometric_factor) {
if (pack_everything)
die(_("--geometric is incompatible with -A, -a"));
@@ -565,19 +747,22 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
if (has_promisor_remote())
strvec_push(&cmd.args, "--exclude-promisor-objects");
- if (write_bitmaps > 0)
- strvec_push(&cmd.args, "--write-bitmap-index");
- else if (write_bitmaps < 0)
- strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ if (!write_midx) {
+ if (write_bitmaps > 0)
+ strvec_push(&cmd.args, "--write-bitmap-index");
+ else if (write_bitmaps < 0)
+ strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ }
if (use_delta_islands)
strvec_push(&cmd.args, "--delta-islands");
- if (pack_everything & ALL_INTO_ONE) {
- get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
+ collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
+ &keep_pack_list);
+ if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(&po_args, &names);
- if (existing_packs.nr && delete_redundant) {
+ if (existing_nonkept_packs.nr && delete_redundant) {
for_each_string_list_item(item, &names) {
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
packtmp_name, item->string);
@@ -586,15 +771,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_pushf(&cmd.args,
"--unpack-unreachable=%s",
unpack_unreachable);
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
} else if (pack_everything & LOOSEN_UNREACHABLE) {
strvec_push(&cmd.args,
"--unpack-unreachable");
} else if (keep_unreachable) {
strvec_push(&cmd.args, "--keep-unreachable");
strvec_push(&cmd.args, "--pack-loose-unreachable");
- } else {
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
}
} else if (geometry) {
@@ -680,20 +862,48 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
/* End of pack replacement. */
- reprepare_packed_git(the_repository);
-
- if (delete_redundant) {
+ if (delete_redundant && pack_everything & ALL_INTO_ONE) {
const int hexsz = the_hash_algo->hexsz;
- int opts = 0;
string_list_sort(&names);
- for_each_string_list_item(item, &existing_packs) {
+ for_each_string_list_item(item, &existing_nonkept_packs) {
char *sha1;
size_t len = strlen(item->string);
if (len < hexsz)
continue;
sha1 = item->string + len - hexsz;
- if (!string_list_has_string(&names, sha1))
- remove_redundant_pack(packdir, item->string);
+ /*
+ * Mark this pack for deletion, which ensures that this
+ * pack won't be included in a MIDX (if `--write-midx`
+ * was given) and that we will actually delete this pack
+ * (if `-d` was given).
+ */
+ item->util = (void*)(intptr_t)!string_list_has_string(&names, sha1);
+ }
+ }
+
+ if (write_midx) {
+ struct string_list include = STRING_LIST_INIT_NODUP;
+ midx_included_packs(&include, &existing_nonkept_packs,
+ &existing_kept_packs, &names, geometry);
+
+ ret = write_midx_included_packs(&include, geometry,
+ refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
+ show_progress, write_bitmaps > 0);
+
+ string_list_clear(&include, 0);
+
+ if (ret)
+ return ret;
+ }
+
+ reprepare_packed_git(the_repository);
+
+ if (delete_redundant) {
+ int opts = 0;
+ for_each_string_list_item(item, &existing_nonkept_packs) {
+ if (!item->util)
+ continue;
+ remove_redundant_pack(packdir, item->string);
}
if (geometry) {
@@ -714,7 +924,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
}
- if (!po_args.quiet && isatty(2))
+ if (!po_args.quiet && show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -733,12 +943,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
unsigned flags = 0;
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0))
flags |= MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX;
- write_midx_file(get_object_directory(), NULL, flags);
+ write_midx_file(get_object_directory(), NULL, NULL, flags);
}
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
- string_list_clear(&existing_packs, 0);
+ string_list_clear(&existing_nonkept_packs, 0);
+ string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);
strbuf_release(&line);
diff --git a/builtin/reset.c b/builtin/reset.c
index 51c9e2f43f..7393595349 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -67,12 +67,18 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
case KEEP:
case MERGE:
opts.update = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
break;
case HARD:
opts.update = 1;
- /* fallthrough */
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+ break;
+ case MIXED:
+ opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+ /* but opts.update=0, so working tree not updated */
+ break;
default:
- opts.reset = 1;
+ BUG("invalid reset_type passed to reset_index");
}
read_cache_unmerged();
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 22c4e1a4ff..8480a59f57 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
- for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
+ for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
+ for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
diff --git a/builtin/rm.c b/builtin/rm.c
index 3b44b807e5..3d0967cdc1 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -237,6 +237,7 @@ static int check_local_mod(struct object_id *head, int index_only)
static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
static int ignore_unmatch = 0, pathspec_file_nul;
+static int include_sparse;
static char *pathspec_from_file;
static struct option builtin_rm_options[] = {
@@ -247,6 +248,7 @@ static struct option builtin_rm_options[] = {
OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")),
OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
N_("exit with a zero status even if nothing matched")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
@@ -298,7 +300,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
ensure_full_index(&the_index);
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (!ce_path_match(&the_index, ce, &pathspec, seen))
continue;
@@ -322,7 +327,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
seen_any = 1;
else if (ignore_unmatch)
continue;
- else if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
+ else if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
string_list_append(&only_match_skip_worktree, original);
else
die(_("pathspec '%s' did not match any files"), original);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 729dea1d25..8932142312 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -17,10 +17,10 @@
#include "protocol.h"
static const char * const send_pack_usage[] = {
- N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
- "[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] "
- "[<host>:]<directory> [<ref>...]\n"
- " --all and explicit <ref> specification are mutually exclusive."),
+ N_("git send-pack [--mirror] [--dry-run] [--force]\n"
+ " [--receive-pack=<git-receive-pack>]\n"
+ " [--verbose] [--thin] [--atomic]\n"
+ " [<host>:]<directory> (--all | <ref>...)"),
NULL,
};
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3e7ab1ca82..e7f7af5de3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -374,6 +374,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
for (;;) {
switch (parse_options_step(&ctx, options, shortlog_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index bea4bbf468..082449293b 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -11,9 +11,9 @@
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
- " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
- " [--more=<n> | --list | --independent | --merge-base]\n"
- " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+ " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+ " [--more=<n> | --list | --independent | --merge-base]\n"
+ " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
NULL
};
diff --git a/builtin/stash.c b/builtin/stash.c
index 8f42360ca9..a0ccc8654d 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -85,7 +85,7 @@ static const char * const git_stash_push_usage[] = {
static const char * const git_stash_save_usage[] = {
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [<message>]"),
+ " [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -256,8 +256,10 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.merge = 1;
- opts.reset = reset;
+ opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.update = update;
+ if (update)
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = oneway_merge;
if (unpack_trees(nr_trees, t, &opts))
@@ -313,6 +315,17 @@ static int reset_head(void)
return run_command(&cp);
}
+static int is_path_a_directory(const char *path)
+{
+ /*
+ * This function differs from abspath.c:is_directory() in that
+ * here we use lstat() instead of stat(); we do not want to
+ * follow symbolic links here.
+ */
+ struct stat st;
+ return (!lstat(path, &st) && S_ISDIR(st.st_mode));
+}
+
static void add_diff_to_buf(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
@@ -320,6 +333,9 @@ static void add_diff_to_buf(struct diff_queue_struct *q,
int i;
for (i = 0; i < q->nr; i++) {
+ if (is_path_a_directory(q->queue[i]->one->path))
+ continue;
+
strbuf_addstr(data, q->queue[i]->one->path);
/* NUL-terminate: will be fed to update-index -z */
@@ -521,9 +537,6 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
}
}
- if (info->has_u && restore_untracked(&info->u_tree))
- return error(_("could not restore untracked files from stash"));
-
init_merge_options(&o, the_repository);
o.branch1 = "Updated upstream";
@@ -558,6 +571,9 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
unstage_changes_unless_new(&c_tree);
}
+ if (info->has_u && restore_untracked(&info->u_tree))
+ return error(_("could not restore untracked files from stash"));
+
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1519,6 +1535,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
} else {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ /* BUG: this nukes untracked files in the way */
strvec_pushl(&cp.args, "reset", "--hard", "-q",
"--no-recurse-submodules", NULL);
if (run_command(&cp)) {
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f8fb70c826..6298cbdd4e 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -307,7 +307,7 @@ struct module_list {
const struct cache_entry **entries;
int alloc, nr;
};
-#define MODULE_LIST_INIT { NULL, 0, 0 }
+#define MODULE_LIST_INIT { 0 }
static int module_list_compute(int argc, const char **argv,
const char *prefix,
@@ -588,7 +588,7 @@ struct init_cb {
const char *prefix;
unsigned int flags;
};
-#define INIT_CB_INIT { NULL, 0 }
+#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -717,7 +717,7 @@ struct status_cb {
const char *prefix;
unsigned int flags;
};
-#define STATUS_CB_INIT { NULL, 0 }
+#define STATUS_CB_INIT { 0 }
static void print_status(unsigned int flags, char state, const char *path,
const struct object_id *oid, const char *displaypath)
@@ -911,13 +911,13 @@ struct module_cb {
char status;
const char *sm_path;
};
-#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+#define MODULE_CB_INIT { 0 }
struct module_cb_list {
struct module_cb **entries;
int alloc, nr;
};
-#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+#define MODULE_CB_LIST_INIT { 0 }
struct summary_cb {
int argc;
@@ -928,7 +928,7 @@ struct summary_cb {
unsigned int files: 1;
int summary_limit;
};
-#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+#define SUMMARY_CB_INIT { 0 }
enum diff_cmd {
DIFF_INDEX,
@@ -1334,7 +1334,7 @@ struct sync_cb {
const char *prefix;
unsigned int flags;
};
-#define SYNC_CB_INIT { NULL, 0 }
+#define SYNC_CB_INIT { 0 }
static void sync_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1480,7 +1480,7 @@ struct deinit_cb {
const char *prefix;
unsigned int flags;
};
-#define DEINIT_CB_INIT { NULL, 0 }
+#define DEINIT_CB_INIT { 0 }
static void deinit_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1647,8 +1647,9 @@ struct submodule_alternate_setup {
} error_mode;
struct string_list *reference;
};
-#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
- SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+#define SUBMODULE_ALTERNATE_SETUP_INIT { \
+ .error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE, \
+}
static const char alternate_error_advice[] = N_(
"An alternate computed from a superproject's alternate is invalid.\n"
@@ -1668,18 +1669,24 @@ static int add_possible_reference_from_superproject(
* standard layout with .git/(modules/<name>)+/objects
*/
if (strip_suffix(odb->path, "/objects", &len)) {
+ struct repository alternate;
char *sm_alternate;
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
strbuf_add(&sb, odb->path, len);
+ repo_init(&alternate, sb.buf, NULL);
+
/*
* We need to end the new path with '/' to mark it as a dir,
* otherwise a submodule name containing '/' will be broken
* as the last part of a missing submodule reference would
* be taken as a file name.
*/
- strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
+ strbuf_reset(&sb);
+ submodule_name_to_gitdir(&sb, &alternate, sas->submodule_name);
+ strbuf_addch(&sb, '/');
+ repo_clear(&alternate);
sm_alternate = compute_alternate_path(sb.buf, &err);
if (sm_alternate) {
@@ -1749,7 +1756,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
struct strbuf sb = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
- strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), clone_data->name);
+ submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
sm_gitdir = absolute_pathdup(sb.buf);
strbuf_reset(&sb);
@@ -2760,7 +2767,6 @@ static int push_check(int argc, const char **argv, const char *prefix)
static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
{
- const struct submodule *sub;
const char *path;
const char *cw;
struct repository subrepo;
@@ -2770,11 +2776,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
path = argv[1];
- sub = submodule_from_path(the_repository, null_oid(), path);
- if (!sub)
- BUG("We could get the submodule handle before?");
-
- if (repo_submodule_init(&subrepo, the_repository, sub))
+ if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
die(_("could not get a repository handle for submodule '%s'"), path);
if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) {
@@ -3084,6 +3086,10 @@ static int add_submodule(const struct add_data *add_data)
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.dir = add_data->sm_path;
+ /*
+ * NOTE: we only get here if add_data->force is true, so
+ * passing --force to checkout is reasonable.
+ */
strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL);
if (add_data->branch) {
diff --git a/builtin/tag.c b/builtin/tag.c
index 065b6bf093..6535ed27ee 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,10 +23,10 @@
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
- "\t\t<tagname> [<head>]"),
+ " <tagname> [<head>]"),
N_("git tag -d <tagname>..."),
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
- "\t\t[--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
+ " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
N_("git tag -v [--format=<format>] <tagname>..."),
NULL
};
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6029a80544..a84e7b47a2 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -302,6 +302,12 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
strbuf_release(&err);
}
+static void report_ok(const char *command)
+{
+ fprintf(stdout, "%s: ok\n", command);
+ fflush(stdout);
+}
+
static void parse_cmd_option(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -317,7 +323,7 @@ static void parse_cmd_start(struct ref_transaction *transaction,
{
if (*next != line_termination)
die("start: extra input: %s", next);
- puts("start: ok");
+ report_ok("start");
}
static void parse_cmd_prepare(struct ref_transaction *transaction,
@@ -328,7 +334,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
die("prepare: extra input: %s", next);
if (ref_transaction_prepare(transaction, &error))
die("prepare: %s", error.buf);
- puts("prepare: ok");
+ report_ok("prepare");
}
static void parse_cmd_abort(struct ref_transaction *transaction,
@@ -339,7 +345,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
die("abort: extra input: %s", next);
if (ref_transaction_abort(transaction, &error))
die("abort: %s", error.buf);
- puts("abort: ok");
+ report_ok("abort");
}
static void parse_cmd_commit(struct ref_transaction *transaction,
@@ -350,7 +356,7 @@ static void parse_cmd_commit(struct ref_transaction *transaction,
die("commit: extra input: %s", next);
if (ref_transaction_commit(transaction, &error))
die("commit: %s", error.buf);
- puts("commit: ok");
+ report_ok("commit");
ref_transaction_free(transaction);
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 0d0a80da61..d22ece93e1 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -8,6 +8,7 @@
#include "branch.h"
#include "refs.h"
#include "run-command.h"
+#include "hook.h"
#include "sigchain.h"
#include "submodule.h"
#include "utf8.h"
diff --git a/cache-tree.c b/cache-tree.c
index 90919f9e34..79d168192d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -440,8 +440,9 @@ static int update_one(struct cache_tree *it,
} else if (dryrun) {
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
tree_type, &it->oid);
- } else if (write_object_file(buffer.buf, buffer.len, tree_type,
- &it->oid)) {
+ } else if (write_object_file_flags(buffer.buf, buffer.len, tree_type,
+ &it->oid, flags & WRITE_TREE_SILENT
+ ? HASH_SILENT : 0)) {
strbuf_release(&buffer);
return -1;
}
@@ -826,10 +827,17 @@ static void verify_one_sparse(struct repository *r,
path->buf);
}
-static void verify_one(struct repository *r,
- struct index_state *istate,
- struct cache_tree *it,
- struct strbuf *path)
+/*
+ * Returns:
+ * 0 - Verification completed.
+ * 1 - Restart verification - a call to ensure_full_index() freed the cache
+ * tree that is being verified and verification needs to be restarted from
+ * the new toplevel cache tree.
+ */
+static int verify_one(struct repository *r,
+ struct index_state *istate,
+ struct cache_tree *it,
+ struct strbuf *path)
{
int i, pos, len = path->len;
struct strbuf tree_buf = STRBUF_INIT;
@@ -837,21 +845,30 @@ static void verify_one(struct repository *r,
for (i = 0; i < it->subtree_nr; i++) {
strbuf_addf(path, "%s/", it->down[i]->name);
- verify_one(r, istate, it->down[i]->cache_tree, path);
+ if (verify_one(r, istate, it->down[i]->cache_tree, path))
+ return 1;
strbuf_setlen(path, len);
}
if (it->entry_count < 0 ||
/* no verification on tests (t7003) that replace trees */
lookup_replace_object(r, &it->oid) != &it->oid)
- return;
+ return 0;
if (path->len) {
+ /*
+ * If the index is sparse and the cache tree is not
+ * index_name_pos() may trigger ensure_full_index() which will
+ * free the tree that is being verified.
+ */
+ int is_sparse = istate->sparse_index;
pos = index_name_pos(istate, path->buf, path->len);
+ if (is_sparse && !istate->sparse_index)
+ return 1;
if (pos >= 0) {
verify_one_sparse(r, istate, it, path, pos);
- return;
+ return 0;
}
pos = -pos - 1;
@@ -899,6 +916,7 @@ static void verify_one(struct repository *r,
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
strbuf_setlen(path, len);
strbuf_release(&tree_buf);
+ return 0;
}
void cache_tree_verify(struct repository *r, struct index_state *istate)
@@ -907,6 +925,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate)
if (!istate->cache_tree)
return;
- verify_one(r, istate, istate->cache_tree, &path);
+ if (verify_one(r, istate, istate->cache_tree, &path)) {
+ strbuf_reset(&path);
+ if (verify_one(r, istate, istate->cache_tree, &path))
+ BUG("ensure_full_index() called twice while verifying cache tree");
+ }
strbuf_release(&path);
}
diff --git a/cache.h b/cache.h
index 0c245d4f10..eba12487b9 100644
--- a/cache.h
+++ b/cache.h
@@ -887,6 +887,7 @@ int ie_modified(struct index_state *, const struct cache_entry *, struct stat *,
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
#define HASH_RENORMALIZE 4
+#define HASH_SILENT 8
int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
@@ -958,7 +959,6 @@ extern char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
extern const char *git_hooks_path;
extern int zlib_compression_level;
-extern int core_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
@@ -996,14 +996,6 @@ extern int core_apply_sparse_checkout;
extern int core_sparse_checkout_cone;
/*
- * Include broken refs in all ref iterations, which will
- * generally choke dangerous operations rather than letting
- * them silently proceed without taking the broken ref into
- * account.
- */
-extern int ref_paranoia;
-
-/*
* Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
*/
int use_optional_locks(void);
@@ -1211,49 +1203,6 @@ enum scld_error safe_create_leading_directories(char *path);
enum scld_error safe_create_leading_directories_const(const char *path);
enum scld_error safe_create_leading_directories_no_share(char *path);
-/*
- * Callback function for raceproof_create_file(). This function is
- * expected to do something that makes dirname(path) permanent despite
- * the fact that other processes might be cleaning up empty
- * directories at the same time. Usually it will create a file named
- * path, but alternatively it could create another file in that
- * directory, or even chdir() into that directory. The function should
- * return 0 if the action was completed successfully. On error, it
- * should return a nonzero result and set errno.
- * raceproof_create_file() treats two errno values specially:
- *
- * - ENOENT -- dirname(path) does not exist. In this case,
- * raceproof_create_file() tries creating dirname(path)
- * (and any parent directories, if necessary) and calls
- * the function again.
- *
- * - EISDIR -- the file already exists and is a directory. In this
- * case, raceproof_create_file() removes the directory if
- * it is empty (and recursively any empty directories that
- * it contains) and calls the function again.
- *
- * Any other errno causes raceproof_create_file() to fail with the
- * callback's return value and errno.
- *
- * Obviously, this function should be OK with being called again if it
- * fails with ENOENT or EISDIR. In other scenarios it will not be
- * called again.
- */
-typedef int create_file_fn(const char *path, void *cb);
-
-/*
- * Create a file in dirname(path) by calling fn, creating leading
- * directories if necessary. Retry a few times in case we are racing
- * with another process that is trying to clean up the directory that
- * contains path. See the documentation for create_file_fn for more
- * details.
- *
- * Return the value and set the errno that resulted from the most
- * recent call of fn. fn is always called at least once, and will be
- * called more than once if it returns ENOENT or EISDIR.
- */
-int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
-
int mkdir_in_gitdir(const char *path);
char *interpolate_path(const char *path, int real_home);
/* NEEDSWORK: remove this synonym once in-flight topics have migrated */
@@ -1320,11 +1269,50 @@ char *xdg_cache_home(const char *filename);
int git_open_cloexec(const char *name, int flags);
#define git_open(name) git_open_cloexec(name, O_RDONLY)
-int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
-int parse_loose_header(const char *hdr, unsigned long *sizep);
+
+/**
+ * unpack_loose_header() initializes the data stream needed to unpack
+ * a loose object header.
+ *
+ * Returns:
+ *
+ * - ULHR_OK on success
+ * - ULHR_BAD on error
+ * - ULHR_TOO_LONG if the header was too long
+ *
+ * It will only parse up to MAX_HEADER_LEN bytes unless an optional
+ * "hdrbuf" argument is non-NULL. This is intended for use with
+ * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
+ * reporting. The full header will be extracted to "hdrbuf" for use
+ * with parse_loose_header(), ULHR_TOO_LONG will still be returned
+ * from this function to indicate that the header was too long.
+ */
+enum unpack_loose_header_result {
+ ULHR_OK,
+ ULHR_BAD,
+ ULHR_TOO_LONG,
+};
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+ unsigned char *map,
+ unsigned long mapsize,
+ void *buffer,
+ unsigned long bufsiz,
+ struct strbuf *hdrbuf);
+
+/**
+ * parse_loose_header() parses the starting "<type> <len>\0" of an
+ * object. If it doesn't follow that format -1 is returned. To check
+ * the validity of the <type> populate the "typep" in the "struct
+ * object_info". It will be OBJ_BAD if the object type is unknown. The
+ * parsed <len> can be retrieved via "oi->sizep", and from there
+ * passed to unpack_loose_rest().
+ */
+struct object_info;
+int parse_loose_header(const char *hdr, struct object_info *oi);
int check_object_signature(struct repository *r, const struct object_id *oid,
- void *buf, unsigned long size, const char *type);
+ void *buf, unsigned long size, const char *type,
+ struct object_id *real_oidp);
int finalize_object_file(const char *tmpfile, const char *filename);
@@ -1669,7 +1657,9 @@ struct cache_def {
int track_flags;
int prefix_len_stat_func;
};
-#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
+#define CACHE_DEF_INIT { \
+ .path = STRBUF_INIT, \
+}
static inline void cache_def_clear(struct cache_def *cache)
{
strbuf_release(&cache->path);
@@ -1726,13 +1716,6 @@ int update_server_info(int);
const char *get_log_output_encoding(void);
const char *get_commit_output_encoding(void);
-/*
- * This is a hack for test programs like test-dump-untracked-cache to
- * ensure that they do not modify the untracked cache when reading it.
- * Do not use it otherwise!
- */
-extern int ignore_untracked_cache_config;
-
int committer_ident_sufficiently_given(void);
int author_ident_sufficiently_given(void);
diff --git a/cbtree.h b/cbtree.h
index a04a312c3f..dedbb8e2a4 100644
--- a/cbtree.h
+++ b/cbtree.h
@@ -37,11 +37,12 @@ enum cb_next {
CB_BREAK = 1
};
-#define CBTREE_INIT { .root = NULL }
+#define CBTREE_INIT { 0 }
static inline void cb_init(struct cb_tree *t)
{
- t->root = NULL;
+ struct cb_tree blank = CBTREE_INIT;
+ memcpy(t, &blank, sizeof(*t));
}
struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
diff --git a/checkout.c b/checkout.c
index 6586e30ca5..2e39dae684 100644
--- a/checkout.c
+++ b/checkout.c
@@ -14,7 +14,7 @@ struct tracking_name_data {
struct object_id *default_dst_oid;
};
-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }
+#define TRACKING_NAME_DATA_INIT { 0 }
static int check_tracking_name(struct remote *remote, void *cb_data)
{
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 5772081b6e..1d0e48f451 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -12,7 +12,7 @@ UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
case "$jobname" in
-linux-clang|linux-gcc)
+linux-clang|linux-gcc|linux-leaks)
sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
sudo apt-get -q update
sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
diff --git a/ci/lib.sh b/ci/lib.sh
index 476c3f369f..82cb17f8ee 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -183,7 +183,7 @@ export GIT_TEST_CLONE_2GB=true
export SKIP_DASHED_BUILT_INS=YesPlease
case "$jobname" in
-linux-clang|linux-gcc)
+linux-clang|linux-gcc|linux-leaks)
if [ "$jobname" = linux-gcc ]
then
export CC=gcc-8
@@ -233,4 +233,11 @@ linux-musl)
;;
esac
+case "$jobname" in
+linux-leaks)
+ export SANITIZE=leak
+ export GIT_TEST_PASSING_SANITIZE_LEAK=true
+ ;;
+esac
+
MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
diff --git a/compat/simple-ipc/ipc-unix-socket.c b/compat/simple-ipc/ipc-unix-socket.c
index 1927e6ef4b..4e28857a0a 100644
--- a/compat/simple-ipc/ipc-unix-socket.c
+++ b/compat/simple-ipc/ipc-unix-socket.c
@@ -168,7 +168,8 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
int ipc_client_send_command_to_connection(
struct ipc_client_connection *connection,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = 0;
@@ -176,7 +177,7 @@ int ipc_client_send_command_to_connection(
trace2_region_enter("ipc-client", "send-command", NULL);
- if (write_packetized_from_buf_no_flush(message, strlen(message),
+ if (write_packetized_from_buf_no_flush(message, message_len,
connection->fd) < 0 ||
packet_flush_gently(connection->fd) < 0) {
ret = error(_("could not send IPC command"));
@@ -197,7 +198,8 @@ done:
int ipc_client_send_command(const char *path,
const struct ipc_client_connect_options *options,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = -1;
enum ipc_active_state state;
@@ -208,7 +210,9 @@ int ipc_client_send_command(const char *path,
if (state != IPC_STATE__LISTENING)
return ret;
- ret = ipc_client_send_command_to_connection(connection, message, answer);
+ ret = ipc_client_send_command_to_connection(connection,
+ message, message_len,
+ answer);
ipc_client_close_connection(connection);
@@ -503,7 +507,7 @@ static int worker_thread__do_io(
if (ret >= 0) {
ret = worker_thread_data->server_data->application_cb(
worker_thread_data->server_data->application_data,
- buf.buf, do_io_reply_callback, &reply_data);
+ buf.buf, buf.len, do_io_reply_callback, &reply_data);
packet_flush_gently(reply_data.fd);
}
diff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c
index 8dc7bda087..20ea7b65e0 100644
--- a/compat/simple-ipc/ipc-win32.c
+++ b/compat/simple-ipc/ipc-win32.c
@@ -3,6 +3,8 @@
#include "strbuf.h"
#include "pkt-line.h"
#include "thread-utils.h"
+#include "accctrl.h"
+#include "aclapi.h"
#ifndef SUPPORTS_SIMPLE_IPC
/*
@@ -49,6 +51,9 @@ static enum ipc_active_state get_active_state(wchar_t *pipe_path)
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return IPC_STATE__PATH_NOT_FOUND;
+ trace2_data_intmax("ipc-debug", NULL, "getstate/waitpipe/gle",
+ (intmax_t)GetLastError());
+
return IPC_STATE__OTHER_ERROR;
}
@@ -109,9 +114,15 @@ static enum ipc_active_state connect_to_server(
t_start_ms = (DWORD)(getnanotime() / 1000000);
if (!WaitNamedPipeW(wpath, timeout_ms)) {
- if (GetLastError() == ERROR_SEM_TIMEOUT)
+ DWORD gleWait = GetLastError();
+
+ if (gleWait == ERROR_SEM_TIMEOUT)
return IPC_STATE__NOT_LISTENING;
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/waitpipe/gle",
+ (intmax_t)gleWait);
+
return IPC_STATE__OTHER_ERROR;
}
@@ -133,17 +144,31 @@ static enum ipc_active_state connect_to_server(
break; /* try again */
default:
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/createfile/gle",
+ (intmax_t)gle);
+
return IPC_STATE__OTHER_ERROR;
}
}
if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) {
+ gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/setpipestate/gle",
+ (intmax_t)gle);
+
CloseHandle(hPipe);
return IPC_STATE__OTHER_ERROR;
}
*pfd = _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
if (*pfd < 0) {
+ gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/openosfhandle/gle",
+ (intmax_t)gle);
+
CloseHandle(hPipe);
return IPC_STATE__OTHER_ERROR;
}
@@ -208,7 +233,8 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
int ipc_client_send_command_to_connection(
struct ipc_client_connection *connection,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = 0;
@@ -216,7 +242,7 @@ int ipc_client_send_command_to_connection(
trace2_region_enter("ipc-client", "send-command", NULL);
- if (write_packetized_from_buf_no_flush(message, strlen(message),
+ if (write_packetized_from_buf_no_flush(message, message_len,
connection->fd) < 0 ||
packet_flush_gently(connection->fd) < 0) {
ret = error(_("could not send IPC command"));
@@ -239,7 +265,8 @@ done:
int ipc_client_send_command(const char *path,
const struct ipc_client_connect_options *options,
- const char *message, struct strbuf *response)
+ const char *message, size_t message_len,
+ struct strbuf *response)
{
int ret = -1;
enum ipc_active_state state;
@@ -250,7 +277,9 @@ int ipc_client_send_command(const char *path,
if (state != IPC_STATE__LISTENING)
return ret;
- ret = ipc_client_send_command_to_connection(connection, message, response);
+ ret = ipc_client_send_command_to_connection(connection,
+ message, message_len,
+ response);
ipc_client_close_connection(connection);
@@ -458,7 +487,7 @@ static int do_io(struct ipc_server_thread_data *server_thread_data)
if (ret >= 0) {
ret = server_thread_data->server_data->application_cb(
server_thread_data->server_data->application_data,
- buf.buf, do_io_reply_callback, &reply_data);
+ buf.buf, buf.len, do_io_reply_callback, &reply_data);
packet_flush_gently(reply_data.fd);
@@ -565,11 +594,132 @@ finished:
return NULL;
}
+/*
+ * We need to build a Windows "SECURITY_ATTRIBUTES" object and use it
+ * to apply an ACL when we create the initial instance of the Named
+ * Pipe. The construction is somewhat involved and consists of
+ * several sequential steps and intermediate objects.
+ *
+ * We use this structure to hold these intermediate pointers so that
+ * we can free them as a group. (It is unclear from the docs whether
+ * some of these intermediate pointers can be freed before we are
+ * finished using the "lpSA" member.)
+ */
+struct my_sa_data
+{
+ PSID pEveryoneSID;
+ PACL pACL;
+ PSECURITY_DESCRIPTOR pSD;
+ LPSECURITY_ATTRIBUTES lpSA;
+};
+
+static void init_sa(struct my_sa_data *d)
+{
+ memset(d, 0, sizeof(*d));
+}
+
+static void release_sa(struct my_sa_data *d)
+{
+ if (d->pEveryoneSID)
+ FreeSid(d->pEveryoneSID);
+ if (d->pACL)
+ LocalFree(d->pACL);
+ if (d->pSD)
+ LocalFree(d->pSD);
+ if (d->lpSA)
+ LocalFree(d->lpSA);
+
+ memset(d, 0, sizeof(*d));
+}
+
+/*
+ * Create SECURITY_ATTRIBUTES to apply to the initial named pipe. The
+ * creator of the first server instance gets to set the ACLs on it.
+ *
+ * We allow the well-known group `EVERYONE` to have read+write access
+ * to the named pipe so that clients can send queries to the daemon
+ * and receive the response.
+ *
+ * Normally, this is not necessary since the daemon is usually
+ * automatically started by a foreground command like `git status`,
+ * but in those cases where an elevated Git command started the daemon
+ * (such that the daemon itself runs with elevation), we need to add
+ * the ACL so that non-elevated commands can write to it.
+ *
+ * The following document was helpful:
+ * https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
+ *
+ * Returns d->lpSA set to a SA or NULL.
+ */
+static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)
+{
+ SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY;
+#define NR_EA (1)
+ EXPLICIT_ACCESS ea[NR_EA];
+ DWORD dwResult;
+
+ if (!AllocateAndInitializeSid(&sid_auth_world, 1,
+ SECURITY_WORLD_RID, 0,0,0,0,0,0,0,
+ &d->pEveryoneSID)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "alloc-world-sid/gle",
+ (intmax_t)gle);
+ goto fail;
+ }
+
+ memset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));
+
+ ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
+ ea[0].grfAccessMode = SET_ACCESS;
+ ea[0].grfInheritance = NO_INHERITANCE;
+ ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+ ea[0].Trustee.ptstrName = (LPTSTR)d->pEveryoneSID;
+
+ dwResult = SetEntriesInAcl(NR_EA, ea, NULL, &d->pACL);
+ if (dwResult != ERROR_SUCCESS) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/gle",
+ (intmax_t)gle);
+ trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/dw",
+ (intmax_t)dwResult);
+ goto fail;
+ }
+
+ d->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(
+ LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (!InitializeSecurityDescriptor(d->pSD, SECURITY_DESCRIPTOR_REVISION)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "init-sd/gle", (intmax_t)gle);
+ goto fail;
+ }
+
+ if (!SetSecurityDescriptorDacl(d->pSD, TRUE, d->pACL, FALSE)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "set-sd-dacl/gle", (intmax_t)gle);
+ goto fail;
+ }
+
+ d->lpSA = (LPSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+ d->lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
+ d->lpSA->lpSecurityDescriptor = d->pSD;
+ d->lpSA->bInheritHandle = FALSE;
+
+ return d->lpSA;
+
+fail:
+ release_sa(d);
+ return NULL;
+}
+
static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
{
HANDLE hPipe;
DWORD dwOpenMode, dwPipeMode;
- LPSECURITY_ATTRIBUTES lpsa = NULL;
+ struct my_sa_data my_sa_data;
+
+ init_sa(&my_sa_data);
dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED;
@@ -585,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
* set the ACL / Security Attributes on the named
* pipe; subsequent instances inherit and cannot
* change them.
- *
- * TODO Should we allow the application layer to
- * specify security attributes, such as `LocalService`
- * or `LocalSystem`, when we create the named pipe?
- * This question is probably not important when the
- * daemon is started by a foreground user process and
- * only needs to talk to the current user, but may be
- * if the daemon is run via the Control Panel as a
- * System Service.
*/
+ get_sa(&my_sa_data);
}
hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
- PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, lpsa);
+ PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0,
+ my_sa_data.lpSA);
+
+ release_sa(&my_sa_data);
return hPipe;
}
diff --git a/compat/terminal.c b/compat/terminal.c
index 43b73ddc75..5b903e7c7e 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -8,8 +8,6 @@
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
-static void restore_term(void);
-
static void restore_term_on_signal(int sig)
{
restore_term();
@@ -25,7 +23,7 @@ static void restore_term_on_signal(int sig)
static int term_fd = -1;
static struct termios old_term;
-static void restore_term(void)
+void restore_term(void)
{
if (term_fd < 0)
return;
@@ -35,15 +33,22 @@ static void restore_term(void)
term_fd = -1;
}
+int save_term(int full_duplex)
+{
+ if (term_fd < 0)
+ term_fd = open("/dev/tty", O_RDWR);
+
+ return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
+}
+
static int disable_bits(tcflag_t bits)
{
struct termios t;
- term_fd = open("/dev/tty", O_RDWR);
- if (tcgetattr(term_fd, &t) < 0)
+ if (save_term(0) < 0)
goto error;
- old_term = t;
+ t = old_term;
sigchain_push_common(restore_term_on_signal);
t.c_lflag &= ~bits;
@@ -75,9 +80,10 @@ static int enable_non_canonical(void)
static int use_stty = 1;
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
static HANDLE hconin = INVALID_HANDLE_VALUE;
-static DWORD cmode;
+static HANDLE hconout = INVALID_HANDLE_VALUE;
+static DWORD cmode_in, cmode_out;
-static void restore_term(void)
+void restore_term(void)
{
if (use_stty) {
int i;
@@ -97,9 +103,42 @@ static void restore_term(void)
if (hconin == INVALID_HANDLE_VALUE)
return;
- SetConsoleMode(hconin, cmode);
+ SetConsoleMode(hconin, cmode_in);
+ CloseHandle(hconin);
+ if (cmode_out) {
+ assert(hconout != INVALID_HANDLE_VALUE);
+ SetConsoleMode(hconout, cmode_out);
+ CloseHandle(hconout);
+ }
+
+ hconin = hconout = INVALID_HANDLE_VALUE;
+}
+
+int save_term(int full_duplex)
+{
+ hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hconin == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (full_duplex) {
+ hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hconout == INVALID_HANDLE_VALUE)
+ goto error;
+
+ GetConsoleMode(hconout, &cmode_out);
+ }
+
+ GetConsoleMode(hconin, &cmode_in);
+ use_stty = 0;
+ return 0;
+error:
CloseHandle(hconin);
hconin = INVALID_HANDLE_VALUE;
+ return -1;
}
static int disable_bits(DWORD bits)
@@ -135,15 +174,11 @@ static int disable_bits(DWORD bits)
use_stty = 0;
}
- hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (hconin == INVALID_HANDLE_VALUE)
+ if (save_term(0) < 0)
return -1;
- GetConsoleMode(hconin, &cmode);
sigchain_push_common(restore_term_on_signal);
- if (!SetConsoleMode(hconin, cmode & ~bits)) {
+ if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
CloseHandle(hconin);
hconin = INVALID_HANDLE_VALUE;
return -1;
@@ -361,6 +396,16 @@ int read_key_without_echo(struct strbuf *buf)
#else
+int save_term(int full_duplex)
+{
+ /* full_duplex == 1, but no support available */
+ return -full_duplex;
+}
+
+void restore_term(void)
+{
+}
+
char *git_terminal_prompt(const char *prompt, int echo)
{
return getpass(prompt);
diff --git a/compat/terminal.h b/compat/terminal.h
index a9d52b8464..e1770c575b 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -1,6 +1,9 @@
#ifndef COMPAT_TERMINAL_H
#define COMPAT_TERMINAL_H
+int save_term(int full_duplex);
+void restore_term(void);
+
char *git_terminal_prompt(const char *prompt, int echo);
/* Read a single keystroke, without echoing it to the terminal */
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 51fb083dbb..29ec1d0f10 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -92,7 +92,7 @@ The Steps of Build Git with VS2008
the git operations.
3. Inside Git's directory run the command:
- make command-list.h config-list.h
+ make generated-hdrs
to generate the header file needed to compile git.
4. Then either build Git with the GNU Make Makefile in the Git projects
diff --git a/compat/win32/lazyload.h b/compat/win32/lazyload.h
index d2056cdadf..2b3637135f 100644
--- a/compat/win32/lazyload.h
+++ b/compat/win32/lazyload.h
@@ -15,10 +15,12 @@
* source, target);
*/
+typedef void (*FARVOIDPROC)(void);
+
struct proc_addr {
const char *const dll;
const char *const function;
- FARPROC pfunction;
+ FARVOIDPROC pfunction;
unsigned initialized : 1;
};
@@ -26,7 +28,8 @@ struct proc_addr {
#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
static struct proc_addr proc_addr_##function = \
{ #dll, #function, NULL, 0 }; \
- static rettype (WINAPI *function)(__VA_ARGS__)
+ typedef rettype (WINAPI *proc_type_##function)(__VA_ARGS__); \
+ static proc_type_##function function
/*
* Loads a function from a DLL (once-only).
@@ -35,9 +38,9 @@ struct proc_addr {
* This function is not thread-safe.
*/
#define INIT_PROC_ADDR(function) \
- (function = get_proc_addr(&proc_addr_##function))
+ (function = (proc_type_##function)get_proc_addr(&proc_addr_##function))
-static inline FARPROC get_proc_addr(struct proc_addr *proc)
+static inline FARVOIDPROC get_proc_addr(struct proc_addr *proc)
{
/* only do this once */
if (!proc->initialized) {
@@ -46,7 +49,8 @@ static inline FARPROC get_proc_addr(struct proc_addr *proc)
hnd = LoadLibraryExA(proc->dll, NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hnd)
- proc->pfunction = GetProcAddress(hnd, proc->function);
+ proc->pfunction = (FARVOIDPROC)GetProcAddress(hnd,
+ proc->function);
}
/* set ENOSYS if DLL or function was not found */
if (!proc->pfunction)
diff --git a/config.c b/config.c
index 696d77c08f..2dcbe901b6 100644
--- a/config.c
+++ b/config.c
@@ -76,7 +76,6 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
-static int core_compression_seen;
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -426,7 +425,7 @@ static inline int iskeychar(int c)
* baselen - pointer to size_t which will hold the length of the
* section + subsection part, can be NULL
*/
-static int git_config_parse_key_1(const char *key, char **store_key, size_t *baselen_, int quiet)
+int git_config_parse_key(const char *key, char **store_key, size_t *baselen_)
{
size_t i, baselen;
int dot;
@@ -438,14 +437,12 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
*/
if (last_dot == NULL || last_dot == key) {
- if (!quiet)
- error(_("key does not contain a section: %s"), key);
+ error(_("key does not contain a section: %s"), key);
return -CONFIG_NO_SECTION_OR_NAME;
}
if (!last_dot[1]) {
- if (!quiet)
- error(_("key does not contain variable name: %s"), key);
+ error(_("key does not contain variable name: %s"), key);
return -CONFIG_NO_SECTION_OR_NAME;
}
@@ -456,8 +453,7 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
/*
* Validate the key and while at it, lower case it for matching.
*/
- if (store_key)
- *store_key = xmallocz(strlen(key));
+ *store_key = xmallocz(strlen(key));
dot = 0;
for (i = 0; key[i]; i++) {
@@ -468,39 +464,24 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
if (!dot || i > baselen) {
if (!iskeychar(c) ||
(i == baselen + 1 && !isalpha(c))) {
- if (!quiet)
- error(_("invalid key: %s"), key);
+ error(_("invalid key: %s"), key);
goto out_free_ret_1;
}
c = tolower(c);
} else if (c == '\n') {
- if (!quiet)
- error(_("invalid key (newline): %s"), key);
+ error(_("invalid key (newline): %s"), key);
goto out_free_ret_1;
}
- if (store_key)
- (*store_key)[i] = c;
+ (*store_key)[i] = c;
}
return 0;
out_free_ret_1:
- if (store_key) {
- FREE_AND_NULL(*store_key);
- }
+ FREE_AND_NULL(*store_key);
return -CONFIG_INVALID_KEY;
}
-int git_config_parse_key(const char *key, char **store_key, size_t *baselen)
-{
- return git_config_parse_key_1(key, store_key, baselen, 0);
-}
-
-int git_config_key_is_valid(const char *key)
-{
- return !git_config_parse_key_1(key, NULL, NULL, 1);
-}
-
static int config_parse_pair(const char *key, const char *value,
config_fn_t fn, void *data)
{
@@ -1400,8 +1381,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad zlib compression level %d"), level);
- core_compression_level = level;
- core_compression_seen = 1;
if (!zlib_compression_seen)
zlib_compression_level = level;
if (!pack_compression_seen)
diff --git a/config.h b/config.h
index 147f5e0490..f119de0130 100644
--- a/config.h
+++ b/config.h
@@ -259,7 +259,6 @@ int git_config_set_gently(const char *, const char *);
void git_config_set(const char *, const char *);
int git_config_parse_key(const char *, char **, size_t *);
-int git_config_key_is_valid(const char *key);
/*
* The following macros specify flag bits that alter the behavior
@@ -609,7 +608,6 @@ int git_config_get_maybe_bool(const char *key, int *dest);
int git_config_get_pathname(const char *key, const char **dest);
int git_config_get_index_threads(int *dest);
-int git_config_get_untracked_cache(void);
int git_config_get_split_index(void);
int git_config_get_max_percent_split_change(void);
int git_config_get_fsmonitor(void);
diff --git a/config.mak.dev b/config.mak.dev
index c080ac0231..7673fed114 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -6,13 +6,17 @@ ifeq ($(filter no-error,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -Werror
SPARSE_FLAGS += -Wsparse-error
endif
+
DEVELOPER_CFLAGS += -Wall
ifeq ($(filter no-pedantic,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -pedantic
+ifneq (($or $(filter gcc5,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),)
DEVELOPER_CFLAGS += -Wpedantic
-ifneq ($(filter gcc5,$(COMPILER_FEATURES)),)
+ifneq ($(filter gcc10,$(COMPILER_FEATURES)),)
+ifeq ($(uname_S),MINGW)
DEVELOPER_CFLAGS += -Wno-pedantic-ms-format
-DEVELOPER_CFLAGS += -Wno-incompatible-pointer-types
+endif
+endif
endif
endif
DEVELOPER_CFLAGS += -Wdeclaration-after-statement
diff --git a/config.mak.uname b/config.mak.uname
index 76516aaa9a..3236a4918a 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -11,6 +11,10 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
+ifneq ($(findstring MINGW,$(uname_S)),)
+ uname_S := MINGW
+endif
+
ifdef MSVC
# avoid the MingW and Cygwin configuration sections
uname_S := Windows
@@ -588,7 +592,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
SHELL_PATH = /usr/coreutils/bin/bash
endif
-ifneq (,$(findstring MINGW,$(uname_S)))
+ifeq ($(uname_S),MINGW)
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
@@ -735,9 +739,9 @@ vcxproj:
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
- # Add command-list.h and config-list.h
- $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 config-list.h command-list.h
- git add -f config-list.h command-list.h
+ # Add generated headers
+ $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(GENERATED_H)
+ git add -f $(GENERATED_H)
# Add scripts
rm -f perl/perl.mak
diff --git a/connect.c b/connect.c
index aff13a270e..eaf7d6d261 100644
--- a/connect.c
+++ b/connect.c
@@ -557,6 +557,8 @@ const char *parse_feature_value(const char *feature_list, const char *feature, i
if (!*value || isspace(*value)) {
if (lenp)
*lenp = 0;
+ if (offset)
+ *offset = found + len - feature_list;
return value;
}
/* feature with a value (e.g., "agent=git/1.2.3") */
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 171b4124af..fd1399c440 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -624,6 +624,13 @@ if(NOT EXISTS ${CMAKE_BINARY_DIR}/config-list.h)
OUTPUT_FILE ${CMAKE_BINARY_DIR}/config-list.h)
endif()
+if(NOT EXISTS ${CMAKE_BINARY_DIR}/hook-list.h)
+ message("Generating hook-list.h")
+ execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-hooklist.sh
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_FILE ${CMAKE_BINARY_DIR}/hook-list.h)
+endif()
+
include_directories(${CMAKE_BINARY_DIR})
#build
diff --git a/contrib/coccinelle/xopen.cocci b/contrib/coccinelle/xopen.cocci
index 814d7b8a1a..b71db67019 100644
--- a/contrib/coccinelle/xopen.cocci
+++ b/contrib/coccinelle/xopen.cocci
@@ -2,15 +2,18 @@
identifier fd;
identifier die_fn =~ "^(die|die_errno)$";
@@
-(
- fd =
+ int fd =
- open
+ xopen
(...);
-|
- int fd =
+- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
+
+@@
+expression fd;
+identifier die_fn =~ "^(die|die_errno)$";
+@@
+ fd =
- open
+ xopen
(...);
-)
- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8108eda1e8..eb5fd4783d 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2503,7 +2503,14 @@ __git_config_vars=
__git_compute_config_vars ()
{
test -n "$__git_config_vars" ||
- __git_config_vars="$(git help --config-for-completion | sort -u)"
+ __git_config_vars="$(git help --config-for-completion)"
+}
+
+__git_config_sections=
+__git_compute_config_sections ()
+{
+ test -n "$__git_config_sections" ||
+ __git_config_sections="$(git help --config-sections-for-completion)"
}
# Completes possible values of various configuration variables.
@@ -2543,7 +2550,7 @@ __git_complete_config_variable_value ()
return
;;
branch.*.rebase)
- __gitcomp "false true merges preserve interactive" "" "$cur_"
+ __gitcomp "false true merges interactive" "" "$cur_"
return
;;
remote.pushdefault)
@@ -2717,16 +2724,8 @@ __git_complete_config_variable_name ()
__gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
;;
*)
- __git_compute_config_vars
- __gitcomp "$(echo "$__git_config_vars" |
- awk -F . '{
- sections[$1] = 1
- }
- END {
- for (s in sections)
- print s "."
- }
- ')" "" "$cur_"
+ __git_compute_config_sections
+ __gitcomp "$__git_config_sections" "" "$cur_" "."
;;
esac
}
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
index d389bfadce..5927e27ae6 100644
--- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
+++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
@@ -138,7 +138,7 @@ struct credential {
char *password;
};
-#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
+#define CREDENTIAL_INIT { 0 }
typedef int (*credential_op_cb)(struct credential *);
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index e6598b6383..2c5d76d789 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -41,7 +41,7 @@ struct credential {
char *password;
};
-#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
+#define CREDENTIAL_INIT { 0 }
typedef int (*credential_op_cb)(struct credential *);
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
index eeee45dd34..75125d6ae0 100755
--- a/contrib/rerere-train.sh
+++ b/contrib/rerere-train.sh
@@ -91,7 +91,7 @@ do
git checkout -q $commit -- .
git rerere
fi
- git reset -q --hard
+ git reset -q --hard # Might nuke untracked files...
done
if test -z "$branch"
diff --git a/credential.c b/credential.c
index 000ac7a8d4..e7240f3f63 100644
--- a/credential.c
+++ b/credential.c
@@ -105,7 +105,7 @@ static int match_partial_url(const char *url, void *cb)
static void credential_apply_config(struct credential *c)
{
char *normalized_url;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct strbuf url = STRBUF_INIT;
if (!c->host)
diff --git a/daemon.c b/daemon.c
index 5c4cbad62d..b1fcbe0d6f 100644
--- a/daemon.c
+++ b/daemon.c
@@ -63,6 +63,12 @@ struct hostinfo {
unsigned int hostname_lookup_done:1;
unsigned int saw_extended_args:1;
};
+#define HOSTINFO_INIT { \
+ .hostname = STRBUF_INIT, \
+ .canon_hostname = STRBUF_INIT, \
+ .ip_address = STRBUF_INIT, \
+ .tcp_port = STRBUF_INIT, \
+}
static void lookup_hostname(struct hostinfo *hi);
@@ -727,15 +733,6 @@ static void lookup_hostname(struct hostinfo *hi)
}
}
-static void hostinfo_init(struct hostinfo *hi)
-{
- memset(hi, 0, sizeof(*hi));
- strbuf_init(&hi->hostname, 0);
- strbuf_init(&hi->canon_hostname, 0);
- strbuf_init(&hi->ip_address, 0);
- strbuf_init(&hi->tcp_port, 0);
-}
-
static void hostinfo_clear(struct hostinfo *hi)
{
strbuf_release(&hi->hostname);
@@ -760,17 +757,15 @@ static int execute(void)
char *line = packet_buffer;
int pktlen, len, i;
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
- struct hostinfo hi;
+ struct hostinfo hi = HOSTINFO_INIT;
struct strvec env = STRVEC_INIT;
- hostinfo_init(&hi);
-
if (addr)
loginfo("Connection from %s:%s", addr, port);
set_keep_alive(0);
alarm(init_timeout ? init_timeout : timeout);
- pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
+ pktlen = packet_read(0, packet_buffer, sizeof(packet_buffer), 0);
alarm(0);
len = strlen(line);
diff --git a/diff.c b/diff.c
index c8f530ffdb..861282db1c 100644
--- a/diff.c
+++ b/diff.c
@@ -775,13 +775,13 @@ struct emitted_diff_symbol {
int indent_width; /* The visual width of the indentation */
enum diff_symbol s;
};
-#define EMITTED_DIFF_SYMBOL_INIT {NULL}
+#define EMITTED_DIFF_SYMBOL_INIT { 0 }
struct emitted_diff_symbols {
struct emitted_diff_symbol *buf;
int nr, alloc;
};
-#define EMITTED_DIFF_SYMBOLS_INIT {NULL, 0, 0}
+#define EMITTED_DIFF_SYMBOLS_INIT { 0 }
static void append_emitted_diff_symbol(struct diff_options *o,
struct emitted_diff_symbol *e)
diff --git a/dir.c b/dir.c
index 8f5936a9cf..a4306ab874 100644
--- a/dir.c
+++ b/dir.c
@@ -1294,7 +1294,7 @@ int match_pathname(const char *pathname, int pathlen,
* then our prefix match is all we need; we
* do not need to call fnmatch at all.
*/
- if (!patternlen && !namelen)
+ if (!patternlen && (!namelen || (flags & PATTERN_FLAG_MUSTBEDIR)))
return 1;
}
@@ -1303,6 +1303,44 @@ int match_pathname(const char *pathname, int pathlen,
WM_PATHNAME) == 0;
}
+static int path_matches_dir_pattern(const char *pathname,
+ int pathlen,
+ struct strbuf **path_parent,
+ int *dtype,
+ struct path_pattern *pattern,
+ struct index_state *istate)
+{
+ if (!*path_parent) {
+ char *slash;
+ CALLOC_ARRAY(*path_parent, 1);
+ strbuf_add(*path_parent, pathname, pathlen);
+ slash = find_last_dir_sep((*path_parent)->buf);
+
+ if (slash)
+ strbuf_setlen(*path_parent, slash - (*path_parent)->buf);
+ else
+ strbuf_setlen(*path_parent, 0);
+ }
+
+ /*
+ * If the parent directory matches the pattern, then we do not
+ * need to check for dtype.
+ */
+ if ((*path_parent)->len &&
+ match_pathname((*path_parent)->buf, (*path_parent)->len,
+ pattern->base,
+ pattern->baselen ? pattern->baselen - 1 : 0,
+ pattern->pattern, pattern->nowildcardlen,
+ pattern->patternlen, pattern->flags))
+ return 1;
+
+ *dtype = resolve_dtype(*dtype, istate, pathname, pathlen);
+ if (*dtype != DT_DIR)
+ return 0;
+
+ return 1;
+}
+
/*
* Scan the given exclude list in reverse to see whether pathname
* should be ignored. The first match (i.e. the last on the list), if
@@ -1318,6 +1356,7 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
{
struct path_pattern *res = NULL; /* undecided */
int i;
+ struct strbuf *path_parent = NULL;
if (!pl->nr)
return NULL; /* undefined */
@@ -1327,11 +1366,10 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
const char *exclude = pattern->pattern;
int prefix = pattern->nowildcardlen;
- if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) {
- *dtype = resolve_dtype(*dtype, istate, pathname, pathlen);
- if (*dtype != DT_DIR)
- continue;
- }
+ if (pattern->flags & PATTERN_FLAG_MUSTBEDIR &&
+ !path_matches_dir_pattern(pathname, pathlen, &path_parent,
+ dtype, pattern, istate))
+ continue;
if (pattern->flags & PATTERN_FLAG_NODIR) {
if (match_basename(basename,
@@ -1355,6 +1393,12 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
break;
}
}
+
+ if (path_parent) {
+ strbuf_release(path_parent);
+ free(path_parent);
+ }
+
return res;
}
@@ -3799,7 +3843,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
strbuf_reset(&sub_wt);
strbuf_reset(&sub_gd);
strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
- strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
+ submodule_name_to_gitdir(&sub_gd, &subrepo, sub->name);
connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
}
diff --git a/editor.c b/editor.c
index fdd3eeafa9..674309eed8 100644
--- a/editor.c
+++ b/editor.c
@@ -3,6 +3,7 @@
#include "strbuf.h"
#include "run-command.h"
#include "sigchain.h"
+#include "compat/terminal.h"
#ifndef DEFAULT_EDITOR
#define DEFAULT_EDITOR "vi"
@@ -50,6 +51,8 @@ const char *git_sequence_editor(void)
static int launch_specified_editor(const char *editor, const char *path,
struct strbuf *buffer, const char *const *env)
{
+ int term_fail;
+
if (!editor)
return error("Terminal is dumb, but EDITOR unset");
@@ -83,7 +86,10 @@ static int launch_specified_editor(const char *editor, const char *path,
p.env = env;
p.use_shell = 1;
p.trace2_child_class = "editor";
+ term_fail = save_term(1);
if (start_command(&p) < 0) {
+ if (!term_fail)
+ restore_term();
strbuf_release(&realpath);
return error("unable to start editor '%s'", editor);
}
@@ -91,6 +97,8 @@ static int launch_specified_editor(const char *editor, const char *path,
sigchain_push(SIGINT, SIG_IGN);
sigchain_push(SIGQUIT, SIG_IGN);
ret = finish_command(&p);
+ if (!term_fail)
+ restore_term();
strbuf_release(&realpath);
sig = ret - 128;
sigchain_pop(SIGINT);
diff --git a/entry.h b/entry.h
index 7c889e58fd..2254c62727 100644
--- a/entry.h
+++ b/entry.h
@@ -16,7 +16,7 @@ struct checkout {
clone:1,
refresh_cache:1;
};
-#define CHECKOUT_INIT { NULL, "" }
+#define CHECKOUT_INIT { .base_dir = "" }
#define TEMPORARY_FILENAME_LENGTH 25
/*
diff --git a/environment.c b/environment.c
index d6b22ede7e..9da7f3c1a1 100644
--- a/environment.c
+++ b/environment.c
@@ -31,7 +31,6 @@ int prefer_symlink_refs;
int is_bare_repository_cfg = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
-int ref_paranoia = -1;
int repository_format_precious_objects;
int repository_format_worktree_config;
const char *git_commit_encoding;
@@ -41,7 +40,6 @@ char *apply_default_ignorewhitespace;
const char *git_attributes_file;
const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
-int core_compression_level;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
@@ -96,13 +94,6 @@ int auto_comment_line_char;
/* Parallel index stat data preload? */
int core_preload_index = 1;
-/*
- * This is a hack for test programs like test-dump-untracked-cache to
- * ensure that they do not modify the untracked cache when reading it.
- * Do not use it otherwise!
- */
-int ignore_untracked_cache_config;
-
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
@@ -330,8 +321,7 @@ char *get_graft_file(struct repository *r)
static void set_git_dir_1(const char *path)
{
- if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
- die(_("could not set GIT_DIR to '%s'"), path);
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
setup_git_env(path);
}
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
index 57ed5784e1..273390229f 100644
--- a/fetch-negotiator.c
+++ b/fetch-negotiator.c
@@ -19,7 +19,6 @@ void fetch_negotiator_init(struct repository *r,
return;
case FETCH_NEGOTIATION_DEFAULT:
- default:
default_negotiator_init(negotiator);
return;
}
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index b969dc6ebb..5216191488 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -9,6 +9,7 @@
#include "branch.h"
#include "fmt-merge-msg.h"
#include "commit-reach.h"
+#include "gpg-interface.h"
static int use_branch_desc;
static int suppress_dest_pattern_seen;
@@ -16,6 +17,8 @@ static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
+ int status = 0;
+
if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
int is_bool;
merge_log_config = git_config_bool_or_int(key, value, &is_bool);
@@ -34,6 +37,9 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
string_list_append(&suppress_dest_patterns, value);
suppress_dest_pattern_seen = 1;
} else {
+ status = git_gpg_config(key, value, NULL);
+ if (status)
+ return status;
return git_default_config(key, value, cb);
}
return 0;
@@ -528,11 +534,11 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
buf = payload.buf;
len = payload.len;
if (check_signature(payload.buf, payload.len, sig.buf,
- sig.len, &sigc) &&
- !sigc.gpg_output)
+ sig.len, &sigc) &&
+ !sigc.output)
strbuf_addstr(&sig, "gpg verification failed.\n");
else
- strbuf_addstr(&sig, sigc.gpg_output);
+ strbuf_addstr(&sig, sigc.output);
}
signature_check_clear(&sigc);
diff --git a/generate-hooklist.sh b/generate-hooklist.sh
new file mode 100755
index 0000000000..2f9f54eb54
--- /dev/null
+++ b/generate-hooklist.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Usage: ./generate-hooklist.sh >hook-list.h
+
+cat <<EOF
+/* Automatically generated by generate-hooklist.sh */
+
+static const char *hook_name_list[] = {
+EOF
+
+sed -n \
+ -e '/^~~~~*$/ {x; s/^.*$/ "&",/; p;}' \
+ -e 'x' \
+ <Documentation/githooks.txt |
+ LC_ALL=C sort
+
+cat <<EOF
+ NULL,
+};
+EOF
diff --git a/git-bisect.sh b/git-bisect.sh
index 6a7afaea8d..405cf76f2a 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -34,94 +34,9 @@ Please use "git help bisect" to get the full man page.'
OPTIONS_SPEC=
. git-sh-setup
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
TERM_BAD=bad
TERM_GOOD=good
-bisect_visualize() {
- git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
-
- if test $# = 0
- then
- if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
- type gitk >/dev/null 2>&1
- then
- set gitk
- else
- set git log
- fi
- else
- case "$1" in
- git*|tig) ;;
- -*) set git log "$@" ;;
- *) set git "$@" ;;
- esac
- fi
-
- eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
-}
-
-bisect_run () {
- git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
-
- test -n "$*" || die "$(gettext "bisect run failed: no command provided.")"
-
- while true
- do
- command="$@"
- eval_gettextln "running \$command"
- "$@"
- res=$?
-
- # Check for really bad run error.
- if [ $res -lt 0 -o $res -ge 128 ]
- then
- eval_gettextln "bisect run failed:
-exit code \$res from '\$command' is < 0 or >= 128" >&2
- exit $res
- fi
-
- # Find current state depending on run success or failure.
- # A special exit code of 125 means cannot test.
- if [ $res -eq 125 ]
- then
- state='skip'
- elif [ $res -gt 0 ]
- then
- state="$TERM_BAD"
- else
- state="$TERM_GOOD"
- fi
-
- git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN"
- res=$?
-
- cat "$GIT_DIR/BISECT_RUN"
-
- if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \
- >/dev/null
- then
- gettextln "bisect run cannot continue any more" >&2
- exit $res
- fi
-
- if [ $res -ne 0 ]
- then
- eval_gettextln "bisect run failed:
-'bisect-state \$state' exited with error code \$res" >&2
- exit $res
- fi
-
- if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null
- then
- gettextln "bisect run success"
- exit 0;
- fi
-
- done
-}
-
get_terms () {
if test -s "$GIT_DIR/BISECT_TERMS"
then
@@ -152,7 +67,7 @@ case "$#" in
# Not sure we want "next" at the UI level anymore.
git bisect--helper --bisect-next "$@" || exit ;;
visualize|view)
- bisect_visualize "$@" ;;
+ git bisect--helper --bisect-visualize "$@" || exit;;
reset)
git bisect--helper --bisect-reset "$@" ;;
replay)
@@ -160,7 +75,7 @@ case "$#" in
log)
git bisect--helper --bisect-log || exit ;;
run)
- bisect_run "$@" ;;
+ git bisect--helper --bisect-run "$@" || exit;;
terms)
git bisect--helper --bisect-terms "$@" || exit;;
*)
diff --git a/git-compat-util.h b/git-compat-util.h
index ddc65ff61d..141bb86351 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -160,6 +160,9 @@
# endif
#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
#include <winsock2.h>
+#ifndef NO_UNIX_SOCKETS
+#include <afunix.h>
+#endif
#include <windows.h>
#define GIT_WINDOWS_NATIVE
#endif
@@ -875,6 +878,8 @@ void *xmemdupz(const void *data, size_t len);
char *xstrndup(const char *str, size_t len);
void *xrealloc(void *ptr, size_t size);
void *xcalloc(size_t nmemb, size_t size);
+void xsetenv(const char *name, const char *value, int overwrite);
+void xunsetenv(const char *name);
void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
const char *mmap_os_err(void);
void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
diff --git a/git-curl-compat.h b/git-curl-compat.h
new file mode 100644
index 0000000000..56a83b6bbd
--- /dev/null
+++ b/git-curl-compat.h
@@ -0,0 +1,129 @@
+#ifndef GIT_CURL_COMPAT_H
+#define GIT_CURL_COMPAT_H
+#include <curl/curl.h>
+
+/**
+ * This header centralizes the declaration of our libcurl dependencies
+ * to make it easy to discover the oldest versions we support, and to
+ * inform decisions about removing support for older libcurl in the
+ * future.
+ *
+ * The oldest supported version of curl is documented in the "INSTALL"
+ * document.
+ *
+ * The source of truth for what versions have which symbols is
+ * https://github.com/curl/curl/blob/master/docs/libcurl/symbols-in-versions;
+ * the release dates are taken from curl.git (at
+ * https://github.com/curl/curl/).
+ *
+ * For each X symbol we need from curl we define our own
+ * GIT_CURL_HAVE_X. If multiple similar symbols with the same prefix
+ * were defined in the same version we pick one and check for that name.
+ *
+ * We may also define a missing CURL_* symbol to its known value, if
+ * doing so is sufficient to add support for it to older versions that
+ * don't have it.
+ *
+ * Keep any symbols in date order of when their support was
+ * introduced, oldest first, in the official version of cURL library.
+ */
+
+/**
+ * CURL_SOCKOPT_OK was added in 7.21.5, released in April 2011.
+ */
+#if LIBCURL_VERSION_NUM < 0x071505
+#define CURL_SOCKOPT_OK 0
+#endif
+
+/**
+ * CURLOPT_TCP_KEEPALIVE was added in 7.25.0, released in March 2012.
+ */
+#if LIBCURL_VERSION_NUM >= 0x071900
+#define GITCURL_HAVE_CURLOPT_TCP_KEEPALIVE 1
+#endif
+
+
+/**
+ * CURLOPT_LOGIN_OPTIONS was added in 7.34.0, released in December
+ * 2013.
+ *
+ * If we start requiring 7.34.0 we might also be able to remove the
+ * code conditional on USE_CURL_FOR_IMAP_SEND in imap-send.c, see
+ * 1e16b255b95 (git-imap-send: use libcurl for implementation,
+ * 2014-11-09) and the check it added for "072200" in the Makefile.
+
+ */
+#if LIBCURL_VERSION_NUM >= 0x072200
+#define GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS 1
+#endif
+
+/**
+ * CURL_SSLVERSION_TLSv1_[012] was added in 7.34.0, released in
+ * December 2013.
+ */
+#if LIBCURL_VERSION_NUM >= 0x072200
+#define GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_0
+#endif
+
+/**
+ * CURLOPT_PINNEDPUBLICKEY was added in 7.39.0, released in November
+ * 2014. CURLE_SSL_PINNEDPUBKEYNOTMATCH was added in that same version.
+ */
+#if LIBCURL_VERSION_NUM >= 0x072c00
+#define GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY 1
+#define GIT_CURL_HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH 1
+#endif
+
+/**
+ * CURL_HTTP_VERSION_2 was added in 7.43.0, released in June 2015.
+ *
+ * The CURL_HTTP_VERSION_2 alias (but not CURL_HTTP_VERSION_2_0) has
+ * always been a macro, not an enum field (checked on curl version
+ * 7.78.0)
+ */
+#if LIBCURL_VERSION_NUM >= 0x072b00
+#define GIT_CURL_HAVE_CURL_HTTP_VERSION_2 1
+#endif
+
+/**
+ * CURLSSLOPT_NO_REVOKE was added in 7.44.0, released in August 2015.
+ *
+ * The CURLSSLOPT_NO_REVOKE is, has always been a macro, not an enum
+ * field (checked on curl version 7.78.0)
+ */
+#if LIBCURL_VERSION_NUM >= 0x072c00
+#define GIT_CURL_HAVE_CURLSSLOPT_NO_REVOKE 1
+#endif
+
+/**
+ * CURLOPT_PROXY_CAINFO was added in 7.52.0, released in August 2017.
+ */
+#if LIBCURL_VERSION_NUM >= 0x073400
+#define GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO 1
+#endif
+
+/**
+ * CURLOPT_PROXY_{KEYPASSWD,SSLCERT,SSLKEY} was added in 7.52.0,
+ * released in August 2017.
+ */
+#if LIBCURL_VERSION_NUM >= 0x073400
+#define GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD 1
+#endif
+
+/**
+ * CURL_SSLVERSION_TLSv1_3 was added in 7.53.0, released in February
+ * 2017.
+ */
+#if LIBCURL_VERSION_NUM >= 0x073400
+#define GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_3 1
+#endif
+
+/**
+ * CURLSSLSET_{NO_BACKENDS,OK,TOO_LATE,UNKNOWN_BACKEND} were added in
+ * 7.56.0, released in September 2017.
+ */
+#if LIBCURL_VERSION_NUM >= 0x073800
+#define GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS
+#endif
+
+#endif
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ed035f32c2..64319bed43 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -222,10 +222,11 @@ if ($state->{method} eq 'pserver') {
open my $passwd, "<", $authdb or die $!;
while (<$passwd>) {
if (m{^\Q$user\E:(.*)}) {
- if (crypt($user, descramble($password)) eq $1) {
+ my $hash = crypt(descramble($password), $1);
+ if (defined $hash and $hash eq $1) {
$auth_ok = 1;
}
- };
+ }
}
close $passwd;
diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh
deleted file mode 100644
index b9c71d2a71..0000000000
--- a/git-rebase--preserve-merges.sh
+++ /dev/null
@@ -1,1057 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement its
-# preserve-merges mode.
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-# The file containing rebase commands, comments, and empty lines.
-# This file is created by "git rebase -i" then edited by the user. As
-# the lines are processed, they are removed from the front of this
-# file and written to the tail of $done.
-todo="$state_dir"/git-rebase-todo
-
-# The rebase command lines that have already been processed. A line
-# is moved here when it is first handled, before any associated user
-# actions.
-done="$state_dir"/done
-
-# The commit message that is planned to be used for any changes that
-# need to be committed following a user interaction.
-msg="$state_dir"/message
-
-# The file into which is accumulated the suggested commit message for
-# squash/fixup commands. When the first of a series of squash/fixups
-# is seen, the file is created and the commit message from the
-# previous commit and from the first squash/fixup commit are written
-# to it. The commit message for each subsequent squash/fixup commit
-# is appended to the file as it is processed.
-#
-# The first line of the file is of the form
-# # This is a combination of $count commits.
-# where $count is the number of commits whose messages have been
-# written to the file so far (including the initial "pick" commit).
-# Each time that a commit message is processed, this line is read and
-# updated. It is deleted just before the combined commit is made.
-squash_msg="$state_dir"/message-squash
-
-# If the current series of squash/fixups has not yet included a squash
-# command, then this file exists and holds the commit message of the
-# original "pick" commit. (If the series ends without a "squash"
-# command, then this can be used as the commit message of the combined
-# commit without opening the editor.)
-fixup_msg="$state_dir"/message-fixup
-
-# $rewritten is the name of a directory containing files for each
-# commit that is reachable by at least one merge base of $head and
-# $upstream. They are not necessarily rewritten, but their children
-# might be. This ensures that commits on merged, but otherwise
-# unrelated side branches are left alone. (Think "X" in the man page's
-# example.)
-rewritten="$state_dir"/rewritten
-
-dropped="$state_dir"/dropped
-
-end="$state_dir"/end
-msgnum="$state_dir"/msgnum
-
-# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
-# GIT_AUTHOR_DATE that will be used for the commit that is currently
-# being rebased.
-author_script="$state_dir"/author-script
-
-# When an "edit" rebase command is being processed, the SHA1 of the
-# commit to be edited is recorded in this file. When "git rebase
-# --continue" is executed, if there are any staged changes then they
-# will be amended to the HEAD commit, but only provided the HEAD
-# commit is still the commit to be edited. When any other rebase
-# command is processed, this file is deleted.
-amend="$state_dir"/amend
-
-# For the post-rewrite hook, we make a list of rewritten commits and
-# their new sha1s. The rewritten-pending list keeps the sha1s of
-# commits that have been processed, but not committed yet,
-# e.g. because they are waiting for a 'squash' command.
-rewritten_list="$state_dir"/rewritten-list
-rewritten_pending="$state_dir"/rewritten-pending
-
-# Work around Git for Windows' Bash whose "read" does not strip CRLF
-# and leaves CR at the end instead.
-cr=$(printf "\015")
-
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm <conflicted_files>", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase --abort".')
-"
-
-write_basic_state () {
- echo "$head_name" > "$state_dir"/head-name &&
- echo "$onto" > "$state_dir"/onto &&
- echo "$orig_head" > "$state_dir"/orig-head &&
- test t = "$GIT_QUIET" && : > "$state_dir"/quiet
- test t = "$verbose" && : > "$state_dir"/verbose
- test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
- test -n "$strategy_opts" && echo "$strategy_opts" > \
- "$state_dir"/strategy_opts
- test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
- "$state_dir"/allow_rerere_autoupdate
- test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > "$state_dir"/gpg_sign_opt
- test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
- test -n "$reschedule_failed_exec" && : > "$state_dir"/reschedule-failed-exec
-}
-
-apply_autostash () {
- if test -f "$state_dir/autostash"
- then
- stash_sha1=$(cat "$state_dir/autostash")
- if git stash apply $stash_sha1 >/dev/null 2>&1
- then
- echo "$(gettext 'Applied autostash.')" >&2
- else
- git stash store -m "autostash" -q $stash_sha1 ||
- die "$(eval_gettext "Cannot store \$stash_sha1")"
- gettext 'Applying autostash resulted in conflicts.
-Your changes are safe in the stash.
-You can run "git stash pop" or "git stash drop" at any time.
-' >&2
- fi
- fi
-}
-
-output () {
- case "$verbose" in
- '')
- output=$("$@" 2>&1 )
- status=$?
- test $status != 0 && printf "%s\n" "$output"
- return $status
- ;;
- *)
- "$@"
- ;;
- esac
-}
-
-strategy_args=${strategy:+--strategy=$strategy}
-test -n "$strategy_opts" &&
-eval '
- for strategy_opt in '"$strategy_opts"'
- do
- strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
- done
-'
-
-GIT_CHERRY_PICK_HELP="$resolvemsg"
-export GIT_CHERRY_PICK_HELP
-
-comment_char=$(git config --get core.commentchar 2>/dev/null)
-case "$comment_char" in
-'' | auto)
- comment_char="#"
- ;;
-?)
- ;;
-*)
- comment_char=$(echo "$comment_char" | cut -c1)
- ;;
-esac
-
-warn () {
- printf '%s\n' "$*" >&2
-}
-
-# Output the commit message for the specified commit.
-commit_message () {
- git cat-file commit "$1" | sed "1,/^$/d"
-}
-
-orig_reflog_action="$GIT_REFLOG_ACTION"
-
-comment_for_reflog () {
- case "$orig_reflog_action" in
- ''|rebase*)
- GIT_REFLOG_ACTION="rebase -i ($1)"
- export GIT_REFLOG_ACTION
- ;;
- esac
-}
-
-last_count=
-mark_action_done () {
- sed -e 1q < "$todo" >> "$done"
- sed -e 1d < "$todo" >> "$todo".new
- mv -f "$todo".new "$todo"
- new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
- echo $new_count >"$msgnum"
- total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
- echo $total >"$end"
- if test "$last_count" != "$new_count"
- then
- last_count=$new_count
- eval_gettext "Rebasing (\$new_count/\$total)"; printf "\r"
- test -z "$verbose" || echo
- fi
-}
-
-append_todo_help () {
- gettext "
-Commands:
-p, pick <commit> = use commit
-r, reword <commit> = use commit, but edit the commit message
-e, edit <commit> = use commit, but stop for amending
-s, squash <commit> = use commit, but meld into previous commit
-f, fixup <commit> = like \"squash\", but discard this commit's log message
-x, exec <commit> = run command (the rest of the line) using shell
-d, drop <commit> = remove commit
-l, label <label> = label current HEAD with a name
-t, reset <label> = reset HEAD to a label
-m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
-. create a merge commit using the original merge commit's
-. message (or the oneline, if no original merge commit was
-. specified). Use -c <commit> to reword the commit message.
-
-These lines can be re-ordered; they are executed from top to bottom.
-" | git stripspace --comment-lines >>"$todo"
-
- if test $(get_missing_commit_check_level) = error
- then
- gettext "
-Do not remove any line. Use 'drop' explicitly to remove a commit.
-" | git stripspace --comment-lines >>"$todo"
- else
- gettext "
-If you remove a line here THAT COMMIT WILL BE LOST.
-" | git stripspace --comment-lines >>"$todo"
- fi
-}
-
-make_patch () {
- sha1_and_parents="$(git rev-list --parents -1 "$1")"
- case "$sha1_and_parents" in
- ?*' '?*' '?*)
- git diff --cc $sha1_and_parents
- ;;
- ?*' '?*)
- git diff-tree -p "$1^!"
- ;;
- *)
- echo "Root commit"
- ;;
- esac > "$state_dir"/patch
- test -f "$msg" ||
- commit_message "$1" > "$msg"
- test -f "$author_script" ||
- get_author_ident_from_commit "$1" > "$author_script"
-}
-
-die_with_patch () {
- echo "$1" > "$state_dir"/stopped-sha
- git update-ref REBASE_HEAD "$1"
- make_patch "$1"
- die "$2"
-}
-
-exit_with_patch () {
- echo "$1" > "$state_dir"/stopped-sha
- git update-ref REBASE_HEAD "$1"
- make_patch $1
- git rev-parse --verify HEAD > "$amend"
- gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
- warn "$(eval_gettext "\
-You can amend the commit now, with
-
- git commit --amend \$gpg_sign_opt_quoted
-
-Once you are satisfied with your changes, run
-
- git rebase --continue")"
- warn
- exit $2
-}
-
-die_abort () {
- apply_autostash
- rm -rf "$state_dir"
- die "$1"
-}
-
-has_action () {
- test -n "$(git stripspace --strip-comments <"$1")"
-}
-
-is_empty_commit() {
- tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null) || {
- sha1=$1
- die "$(eval_gettext "\$sha1: not a commit that can be picked")"
- }
- ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) ||
- ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
- test "$tree" = "$ptree"
-}
-
-is_merge_commit()
-{
- git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1
-}
-
-# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
-# GIT_AUTHOR_DATE exported from the current environment.
-do_with_author () {
- (
- export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
- "$@"
- )
-}
-
-git_sequence_editor () {
- if test -z "$GIT_SEQUENCE_EDITOR"
- then
- GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
- if [ -z "$GIT_SEQUENCE_EDITOR" ]
- then
- GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
- fi
- fi
-
- eval "$GIT_SEQUENCE_EDITOR" '"$@"'
-}
-
-pick_one () {
- ff=--ff
-
- case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
- case "$force_rebase" in '') ;; ?*) ff= ;; esac
- output git rev-parse --verify $sha1 || die "$(eval_gettext "Invalid commit name: \$sha1")"
-
- if is_empty_commit "$sha1"
- then
- empty_args="--allow-empty"
- fi
-
- pick_one_preserving_merges "$@"
-}
-
-pick_one_preserving_merges () {
- fast_forward=t
- case "$1" in
- -n)
- fast_forward=f
- sha1=$2
- ;;
- *)
- sha1=$1
- ;;
- esac
- sha1=$(git rev-parse $sha1)
-
- if test -f "$state_dir"/current-commit && test "$fast_forward" = t
- then
- while read current_commit
- do
- git rev-parse HEAD > "$rewritten"/$current_commit
- done <"$state_dir"/current-commit
- rm "$state_dir"/current-commit ||
- die "$(gettext "Cannot write current commit's replacement sha1")"
- fi
-
- echo $sha1 >> "$state_dir"/current-commit
-
- # rewrite parents; if none were rewritten, we can fast-forward.
- new_parents=
- pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
- if test "$pend" = " "
- then
- pend=" root"
- fi
- while [ "$pend" != "" ]
- do
- p=$(expr "$pend" : ' \([^ ]*\)')
- pend="${pend# $p}"
-
- if test -f "$rewritten"/$p
- then
- new_p=$(cat "$rewritten"/$p)
-
- # If the todo reordered commits, and our parent is marked for
- # rewriting, but hasn't been gotten to yet, assume the user meant to
- # drop it on top of the current HEAD
- if test -z "$new_p"
- then
- new_p=$(git rev-parse HEAD)
- fi
-
- test $p != $new_p && fast_forward=f
- case "$new_parents" in
- *$new_p*)
- ;; # do nothing; that parent is already there
- *)
- new_parents="$new_parents $new_p"
- ;;
- esac
- else
- if test -f "$dropped"/$p
- then
- fast_forward=f
- replacement="$(cat "$dropped"/$p)"
- test -z "$replacement" && replacement=root
- pend=" $replacement$pend"
- else
- new_parents="$new_parents $p"
- fi
- fi
- done
- case $fast_forward in
- t)
- output warn "$(eval_gettext "Fast-forward to \$sha1")"
- output git reset --hard $sha1 ||
- die "$(eval_gettext "Cannot fast-forward to \$sha1")"
- ;;
- f)
- first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-
- if [ "$1" != "-n" ]
- then
- # detach HEAD to current parent
- output git checkout $first_parent 2> /dev/null ||
- die "$(eval_gettext "Cannot move HEAD to \$first_parent")"
- fi
-
- case "$new_parents" in
- ' '*' '*)
- test "a$1" = a-n && die "$(eval_gettext "Refusing to squash a merge: \$sha1")"
-
- # redo merge
- author_script_content=$(get_author_ident_from_commit $sha1)
- eval "$author_script_content"
- msg_content="$(commit_message $sha1)"
- # No point in merging the first parent, that's HEAD
- new_parents=${new_parents# $first_parent}
- merge_args="--no-log --no-ff"
- if ! do_with_author output eval \
- git merge ${gpg_sign_opt:+$(git rev-parse \
- --sq-quote "$gpg_sign_opt")} \
- $allow_rerere_autoupdate "$merge_args" \
- "$strategy_args" \
- -m "$(git rev-parse --sq-quote "$msg_content")" \
- "$new_parents"
- then
- printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
- die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
- fi
- echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
- ;;
- *)
- output eval git cherry-pick $allow_rerere_autoupdate \
- $allow_empty_message \
- ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
- "$strategy_args" "$@" ||
- die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
- ;;
- esac
- ;;
- esac
-}
-
-this_nth_commit_message () {
- n=$1
- eval_gettext "This is the commit message #\${n}:"
-}
-
-skip_nth_commit_message () {
- n=$1
- eval_gettext "The commit message #\${n} will be skipped:"
-}
-
-update_squash_messages () {
- if test -f "$squash_msg"; then
- mv "$squash_msg" "$squash_msg".bak || exit
- count=$(($(sed -n \
- -e "1s/^$comment_char[^0-9]*\([0-9][0-9]*\).*/\1/p" \
- -e "q" < "$squash_msg".bak)+1))
- {
- printf '%s\n' "$comment_char $(eval_ngettext \
- "This is a combination of \$count commit." \
- "This is a combination of \$count commits." \
- $count)"
- sed -e 1d -e '2,/^./{
- /^$/d
- }' <"$squash_msg".bak
- } >"$squash_msg"
- else
- commit_message HEAD >"$fixup_msg" ||
- die "$(eval_gettext "Cannot write \$fixup_msg")"
- count=2
- {
- printf '%s\n' "$comment_char $(gettext "This is a combination of 2 commits.")"
- printf '%s\n' "$comment_char $(gettext "This is the 1st commit message:")"
- echo
- cat "$fixup_msg"
- } >"$squash_msg"
- fi
- case $1 in
- squash)
- rm -f "$fixup_msg"
- echo
- printf '%s\n' "$comment_char $(this_nth_commit_message $count)"
- echo
- commit_message $2
- ;;
- fixup)
- echo
- printf '%s\n' "$comment_char $(skip_nth_commit_message $count)"
- echo
- # Change the space after the comment character to TAB:
- commit_message $2 | git stripspace --comment-lines | sed -e 's/ / /'
- ;;
- esac >>"$squash_msg"
-}
-
-peek_next_command () {
- git stripspace --strip-comments <"$todo" | sed -n -e 's/ .*//p' -e q
-}
-
-# A squash/fixup has failed. Prepare the long version of the squash
-# commit message, then die_with_patch. This code path requires the
-# user to edit the combined commit message for all commits that have
-# been squashed/fixedup so far. So also erase the old squash
-# messages, effectively causing the combined commit to be used as the
-# new basis for any further squash/fixups. Args: sha1 rest
-die_failed_squash() {
- sha1=$1
- rest=$2
- mv "$squash_msg" "$msg" || exit
- rm -f "$fixup_msg"
- cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
- warn
- warn "$(eval_gettext "Could not apply \$sha1... \$rest")"
- die_with_patch $sha1 ""
-}
-
-flush_rewritten_pending() {
- test -s "$rewritten_pending" || return
- newsha1="$(git rev-parse HEAD^0)"
- sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list"
- rm -f "$rewritten_pending"
-}
-
-record_in_rewritten() {
- oldsha1="$(git rev-parse $1)"
- echo "$oldsha1" >> "$rewritten_pending"
-
- case "$(peek_next_command)" in
- squash|s|fixup|f)
- ;;
- *)
- flush_rewritten_pending
- ;;
- esac
-}
-
-do_pick () {
- sha1=$1
- rest=$2
- if test "$(git rev-parse HEAD)" = "$squash_onto"
- then
- # Set the correct commit message and author info on the
- # sentinel root before cherry-picking the original changes
- # without committing (-n). Finally, update the sentinel again
- # to include these changes. If the cherry-pick results in a
- # conflict, this means our behaviour is similar to a standard
- # failed cherry-pick during rebase, with a dirty index to
- # resolve before manually running git commit --amend then git
- # rebase --continue.
- git commit --allow-empty --allow-empty-message --amend \
- --no-post-rewrite -n -q -C $sha1 $signoff &&
- pick_one -n $sha1 &&
- git commit --allow-empty --allow-empty-message \
- --amend --no-post-rewrite -n -q -C $sha1 $signoff \
- ${gpg_sign_opt:+"$gpg_sign_opt"} ||
- die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
- else
- pick_one $sha1 ||
- die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
- fi
-}
-
-do_next () {
- rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
- read -r command sha1 rest < "$todo"
- case "$command" in
- "$comment_char"*|''|noop|drop|d)
- mark_action_done
- ;;
- "$cr")
- # Work around CR left by "read" (e.g. with Git for Windows' Bash).
- mark_action_done
- ;;
- pick|p)
- comment_for_reflog pick
-
- mark_action_done
- do_pick $sha1 "$rest"
- record_in_rewritten $sha1
- ;;
- reword|r)
- comment_for_reflog reword
-
- mark_action_done
- do_pick $sha1 "$rest"
- git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} \
- $allow_empty_message || {
- warn "$(eval_gettext "\
-Could not amend commit after successfully picking \$sha1... \$rest
-This is most likely due to an empty commit message, or the pre-commit hook
-failed. If the pre-commit hook failed, you may need to resolve the issue before
-you are able to reword the commit.")"
- exit_with_patch $sha1 1
- }
- record_in_rewritten $sha1
- ;;
- edit|e)
- comment_for_reflog edit
-
- mark_action_done
- do_pick $sha1 "$rest"
- sha1_abbrev=$(git rev-parse --short $sha1)
- warn "$(eval_gettext "Stopped at \$sha1_abbrev... \$rest")"
- exit_with_patch $sha1 0
- ;;
- squash|s|fixup|f)
- case "$command" in
- squash|s)
- squash_style=squash
- ;;
- fixup|f)
- squash_style=fixup
- ;;
- esac
- comment_for_reflog $squash_style
-
- test -f "$done" && has_action "$done" ||
- die "$(eval_gettext "Cannot '\$squash_style' without a previous commit")"
-
- mark_action_done
- update_squash_messages $squash_style $sha1
- author_script_content=$(get_author_ident_from_commit HEAD)
- echo "$author_script_content" > "$author_script"
- eval "$author_script_content"
- if ! pick_one -n $sha1
- then
- git rev-parse --verify HEAD >"$amend"
- die_failed_squash $sha1 "$rest"
- fi
- case "$(peek_next_command)" in
- squash|s|fixup|f)
- # This is an intermediate commit; its message will only be
- # used in case of trouble. So use the long version:
- do_with_author output git commit --amend --no-verify -F "$squash_msg" \
- ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
- die_failed_squash $sha1 "$rest"
- ;;
- *)
- # This is the final command of this squash/fixup group
- if test -f "$fixup_msg"
- then
- do_with_author git commit --amend --no-verify -F "$fixup_msg" \
- ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
- die_failed_squash $sha1 "$rest"
- else
- cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
- rm -f "$GIT_DIR"/MERGE_MSG
- do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
- ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
- die_failed_squash $sha1 "$rest"
- fi
- rm -f "$squash_msg" "$fixup_msg"
- ;;
- esac
- record_in_rewritten $sha1
- ;;
- x|"exec")
- read -r command rest < "$todo"
- mark_action_done
- eval_gettextln "Executing: \$rest"
- "${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
- status=$?
- # Run in subshell because require_clean_work_tree can die.
- dirty=f
- (require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
- if test "$status" -ne 0
- then
- warn "$(eval_gettext "Execution failed: \$rest")"
- test "$dirty" = f ||
- warn "$(gettext "and made changes to the index and/or the working tree")"
-
- warn "$(gettext "\
-You can fix the problem, and then run
-
- git rebase --continue")"
- warn
- if test $status -eq 127 # command not found
- then
- status=1
- fi
- exit "$status"
- elif test "$dirty" = t
- then
- # TRANSLATORS: after these lines is a command to be issued by the user
- warn "$(eval_gettext "\
-Execution succeeded: \$rest
-but left changes to the index and/or the working tree
-Commit or stash your changes, and then run
-
- git rebase --continue")"
- warn
- exit 1
- fi
- ;;
- *)
- warn "$(eval_gettext "Unknown command: \$command \$sha1 \$rest")"
- fixtodo="$(gettext "Please fix this using 'git rebase --edit-todo'.")"
- if git rev-parse --verify -q "$sha1" >/dev/null
- then
- die_with_patch $sha1 "$fixtodo"
- else
- die "$fixtodo"
- fi
- ;;
- esac
- test -s "$todo" && return
-
- comment_for_reflog finish &&
- newhead=$(git rev-parse HEAD) &&
- case $head_name in
- refs/*)
- message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
- git update-ref -m "$message" $head_name $newhead $orig_head &&
- git symbolic-ref \
- -m "$GIT_REFLOG_ACTION: returning to $head_name" \
- HEAD $head_name
- ;;
- esac && {
- test ! -f "$state_dir"/verbose ||
- git diff-tree --stat $orig_head..HEAD
- } &&
- {
- test -s "$rewritten_list" &&
- git notes copy --for-rewrite=rebase < "$rewritten_list" ||
- true # we don't care if this copying failed
- } &&
- hook="$(git rev-parse --git-path hooks/post-rewrite)"
- if test -x "$hook" && test -s "$rewritten_list"; then
- "$hook" rebase < "$rewritten_list"
- true # we don't care if this hook failed
- fi &&
- warn "$(eval_gettext "Successfully rebased and updated \$head_name.")"
-
- return 1 # not failure; just to break the do_rest loop
-}
-
-# can only return 0, when the infinite loop breaks
-do_rest () {
- while :
- do
- do_next || break
- done
-}
-
-expand_todo_ids() {
- git rebase--interactive --expand-ids
-}
-
-collapse_todo_ids() {
- git rebase--interactive --shorten-ids
-}
-
-# Switch to the branch in $into and notify it in the reflog
-checkout_onto () {
- GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
- output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
- git update-ref ORIG_HEAD $orig_head
-}
-
-get_missing_commit_check_level () {
- check_level=$(git config --get rebase.missingCommitsCheck)
- check_level=${check_level:-ignore}
- # Don't be case sensitive
- printf '%s' "$check_level" | tr 'A-Z' 'a-z'
-}
-
-# Initiate an action. If the cannot be any
-# further action it may exec a command
-# or exit and not return.
-#
-# TODO: Consider a cleaner return model so it
-# never exits and always return 0 if process
-# is complete.
-#
-# Parameter 1 is the action to initiate.
-#
-# Returns 0 if the action was able to complete
-# and if 1 if further processing is required.
-initiate_action () {
- case "$1" in
- continue)
- # do we have anything to commit?
- if git diff-index --cached --quiet HEAD --
- then
- # Nothing to commit -- skip this commit
-
- test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
- rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
- die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
- else
- if ! test -f "$author_script"
- then
- gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
- die "$(eval_gettext "\
-You have staged changes in your working tree.
-If these changes are meant to be
-squashed into the previous commit, run:
-
- git commit --amend \$gpg_sign_opt_quoted
-
-If they are meant to go into a new commit, run:
-
- git commit \$gpg_sign_opt_quoted
-
-In both cases, once you're done, continue with:
-
- git rebase --continue
-")"
- fi
- . "$author_script" ||
- die "$(gettext "Error trying to find the author identity to amend commit")"
- if test -f "$amend"
- then
- current_head=$(git rev-parse --verify HEAD)
- test "$current_head" = $(cat "$amend") ||
- die "$(gettext "\
-You have uncommitted changes in your working tree. Please commit them
-first and then run 'git rebase --continue' again.")"
- do_with_author git commit --amend --no-verify -F "$msg" -e \
- ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
- die "$(gettext "Could not commit staged changes.")"
- else
- do_with_author git commit --no-verify -F "$msg" -e \
- ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
- die "$(gettext "Could not commit staged changes.")"
- fi
- fi
-
- if test -r "$state_dir"/stopped-sha
- then
- record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
- fi
-
- require_clean_work_tree "rebase"
- do_rest
- return 0
- ;;
- skip)
- git rerere clear
- do_rest
- return 0
- ;;
- edit-todo)
- git stripspace --strip-comments <"$todo" >"$todo".new
- mv -f "$todo".new "$todo"
- collapse_todo_ids
- append_todo_help
- gettext "
-You are editing the todo file of an ongoing interactive rebase.
-To continue rebase after editing, run:
- git rebase --continue
-
-" | git stripspace --comment-lines >>"$todo"
-
- git_sequence_editor "$todo" ||
- die "$(gettext "Could not execute editor")"
- expand_todo_ids
-
- exit
- ;;
- show-current-patch)
- exec git show REBASE_HEAD --
- ;;
- *)
- return 1 # continue
- ;;
- esac
-}
-
-setup_reflog_action () {
- comment_for_reflog start
-
- if test ! -z "$switch_to"
- then
- GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
- output git checkout "$switch_to" -- ||
- die "$(eval_gettext "Could not checkout \$switch_to")"
-
- comment_for_reflog start
- fi
-}
-
-init_basic_state () {
- orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
- mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
- rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-
- : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
- write_basic_state
-}
-
-init_revisions_and_shortrevisions () {
- shorthead=$(git rev-parse --short $orig_head)
- shortonto=$(git rev-parse --short $onto)
- if test -z "$rebase_root"
- # this is now equivalent to ! -z "$upstream"
- then
- shortupstream=$(git rev-parse --short $upstream)
- revisions=$upstream...$orig_head
- shortrevisions=$shortupstream..$shorthead
- else
- revisions=$onto...$orig_head
- shortrevisions=$shorthead
- test -z "$squash_onto" ||
- echo "$squash_onto" >"$state_dir"/squash-onto
- fi
-}
-
-complete_action() {
- test -s "$todo" || echo noop >> "$todo"
- test -z "$autosquash" || git rebase--interactive --rearrange-squash || exit
- test -n "$cmd" && git rebase--interactive --add-exec-commands --cmd "$cmd"
-
- todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
- todocount=${todocount##* }
-
-cat >>"$todo" <<EOF
-
-$comment_char $(eval_ngettext \
- "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
- "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
- "$todocount")
-EOF
- append_todo_help
- gettext "
-However, if you remove everything, the rebase will be aborted.
-
-" | git stripspace --comment-lines >>"$todo"
-
- if test -z "$keep_empty"
- then
- printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
- fi
-
-
- has_action "$todo" ||
- return 2
-
- cp "$todo" "$todo".backup
- collapse_todo_ids
- git_sequence_editor "$todo" ||
- die_abort "$(gettext "Could not execute editor")"
-
- has_action "$todo" ||
- return 2
-
- git rebase--interactive --check-todo-list || {
- ret=$?
- checkout_onto
- exit $ret
- }
-
- expand_todo_ids
- checkout_onto
- do_rest
-}
-
-git_rebase__preserve_merges () {
- initiate_action "$action"
- ret=$?
- if test $ret = 0; then
- return 0
- fi
-
- setup_reflog_action
- init_basic_state
-
- if test -z "$rebase_root"
- then
- mkdir "$rewritten" &&
- for c in $(git merge-base --all $orig_head $upstream)
- do
- echo $onto > "$rewritten"/$c ||
- die "$(gettext "Could not init rewritten commits")"
- done
- else
- mkdir "$rewritten" &&
- echo $onto > "$rewritten"/root ||
- die "$(gettext "Could not init rewritten commits")"
- fi
-
- init_revisions_and_shortrevisions
-
- format=$(git config --get rebase.instructionFormat)
- # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
- git rev-list --format="%m%H ${format:-%s}" \
- --reverse --left-right --topo-order \
- $revisions ${restrict_revision+^$restrict_revision} | \
- sed -n "s/^>//p" |
- while read -r sha1 rest
- do
- if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
- then
- comment_out="$comment_char "
- else
- comment_out=
- fi
-
- if test -z "$rebase_root"
- then
- preserve=t
- for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
- do
- if test -f "$rewritten"/$p
- then
- preserve=f
- fi
- done
- else
- preserve=f
- fi
- if test f = "$preserve"
- then
- touch "$rewritten"/$sha1
- printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
- fi
- done
-
- # Watch for commits that been dropped by --cherry-pick
- mkdir "$dropped"
- # Save all non-cherry-picked changes
- git rev-list $revisions --left-right --cherry-pick | \
- sed -n "s/^>//p" > "$state_dir"/not-cherry-picks
- # Now all commits and note which ones are missing in
- # not-cherry-picks and hence being dropped
- git rev-list $revisions |
- while read rev
- do
- if test -f "$rewritten"/$rev &&
- ! sane_grep "$rev" "$state_dir"/not-cherry-picks >/dev/null
- then
- # Use -f2 because if rev-list is telling us this commit is
- # not worthwhile, we don't want to track its multiple heads,
- # just the history of its first-parent for others that will
- # be rebasing on top of it
- git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev
- sha1=$(git rev-list -1 $rev)
- sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
- rm "$rewritten"/$rev
- fi
- done
-
- complete_action
-}
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 10d9764185..cee053cdc3 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -223,9 +223,6 @@ require_clean_work_tree () {
"rewrite branches")
gettextln "Cannot rewrite branches: You have unstaged changes." >&2
;;
- "pull with rebase")
- gettextln "Cannot pull with rebase: You have unstaged changes." >&2
- ;;
*)
eval_gettextln "Cannot \$action: You have unstaged changes." >&2
;;
@@ -242,9 +239,6 @@ require_clean_work_tree () {
rebase)
gettextln "Cannot rebase: Your index contains uncommitted changes." >&2
;;
- "pull with rebase")
- gettextln "Cannot pull with rebase: Your index contains uncommitted changes." >&2
- ;;
*)
eval_gettextln "Cannot \$action: Your index contains uncommitted changes." >&2
;;
diff --git a/git-submodule.sh b/git-submodule.sh
index 27efdc5774..652861aa66 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -63,11 +63,6 @@ isnumber()
n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
}
-# Given a full hex object ID, is this the zero OID?
-is_zero_oid () {
- echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1
-}
-
# Sanitize the local git environment for use within a submodule. We
# can't simply use clear_local_git_env since we want to preserve some
# of the settings from GIT_CONFIG_PARAMETERS.
diff --git a/git-svn.perl b/git-svn.perl
index 70cb5e2a83..be987e316f 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -273,7 +273,6 @@ my %cmd = (
'fetch-all|all' => \$_fetch_all,
'dry-run|n' => \$_dry_run,
'rebase-merges|p' => \$_rebase_merges,
- 'preserve-merges|p' => \$_rebase_merges,
%fc_opts } ],
'commit-diff' => [ \&cmd_commit_diff,
'Commit a diff between two trees',
diff --git a/git.c b/git.c
index 60c2784be4..5ff21be21f 100644
--- a/git.c
+++ b/git.c
@@ -577,7 +577,6 @@ static struct cmd_struct commands[] = {
{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
{ "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
- { "rebase--interactive", cmd_rebase__interactive, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
diff --git a/gpg-interface.c b/gpg-interface.c
index 127aecfc2b..800d8caa67 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -3,11 +3,14 @@
#include "config.h"
#include "run-command.h"
#include "strbuf.h"
+#include "dir.h"
#include "gpg-interface.h"
#include "sigchain.h"
#include "tempfile.h"
+#include "alias.h"
static char *configured_signing_key;
+static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
struct gpg_format {
@@ -15,6 +18,14 @@ struct gpg_format {
const char *program;
const char **verify_args;
const char **sigs;
+ int (*verify_signed_buffer)(struct signature_check *sigc,
+ struct gpg_format *fmt, const char *payload,
+ size_t payload_size, const char *signature,
+ size_t signature_size);
+ int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
+ const char *signing_key);
+ const char *(*get_default_key)(void);
+ const char *(*get_key_id)(void);
};
static const char *openpgp_verify_args[] = {
@@ -35,14 +46,59 @@ static const char *x509_sigs[] = {
NULL
};
+static const char *ssh_verify_args[] = { NULL };
+static const char *ssh_sigs[] = {
+ "-----BEGIN SSH SIGNATURE-----",
+ NULL
+};
+
+static int verify_gpg_signed_buffer(struct signature_check *sigc,
+ struct gpg_format *fmt, const char *payload,
+ size_t payload_size, const char *signature,
+ size_t signature_size);
+static int verify_ssh_signed_buffer(struct signature_check *sigc,
+ struct gpg_format *fmt, const char *payload,
+ size_t payload_size, const char *signature,
+ size_t signature_size);
+static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
+ const char *signing_key);
+static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
+ const char *signing_key);
+
+static const char *get_default_ssh_signing_key(void);
+
+static const char *get_ssh_key_id(void);
+
static struct gpg_format gpg_format[] = {
- { .name = "openpgp", .program = "gpg",
- .verify_args = openpgp_verify_args,
- .sigs = openpgp_sigs
+ {
+ .name = "openpgp",
+ .program = "gpg",
+ .verify_args = openpgp_verify_args,
+ .sigs = openpgp_sigs,
+ .verify_signed_buffer = verify_gpg_signed_buffer,
+ .sign_buffer = sign_buffer_gpg,
+ .get_default_key = NULL,
+ .get_key_id = NULL,
+ },
+ {
+ .name = "x509",
+ .program = "gpgsm",
+ .verify_args = x509_verify_args,
+ .sigs = x509_sigs,
+ .verify_signed_buffer = verify_gpg_signed_buffer,
+ .sign_buffer = sign_buffer_gpg,
+ .get_default_key = NULL,
+ .get_key_id = NULL,
},
- { .name = "x509", .program = "gpgsm",
- .verify_args = x509_verify_args,
- .sigs = x509_sigs
+ {
+ .name = "ssh",
+ .program = "ssh-keygen",
+ .verify_args = ssh_verify_args,
+ .sigs = ssh_sigs,
+ .verify_signed_buffer = verify_ssh_signed_buffer,
+ .sign_buffer = sign_buffer_ssh,
+ .get_default_key = get_default_ssh_signing_key,
+ .get_key_id = get_ssh_key_id,
},
};
@@ -72,7 +128,7 @@ static struct gpg_format *get_format_by_sig(const char *sig)
void signature_check_clear(struct signature_check *sigc)
{
FREE_AND_NULL(sigc->payload);
- FREE_AND_NULL(sigc->gpg_output);
+ FREE_AND_NULL(sigc->output);
FREE_AND_NULL(sigc->gpg_status);
FREE_AND_NULL(sigc->signer);
FREE_AND_NULL(sigc->key);
@@ -257,16 +313,16 @@ error:
FREE_AND_NULL(sigc->key);
}
-static int verify_signed_buffer(const char *payload, size_t payload_size,
- const char *signature, size_t signature_size,
- struct strbuf *gpg_output,
- struct strbuf *gpg_status)
+static int verify_gpg_signed_buffer(struct signature_check *sigc,
+ struct gpg_format *fmt, const char *payload,
+ size_t payload_size, const char *signature,
+ size_t signature_size)
{
struct child_process gpg = CHILD_PROCESS_INIT;
- struct gpg_format *fmt;
struct tempfile *temp;
int ret;
- struct strbuf buf = STRBUF_INIT;
+ struct strbuf gpg_stdout = STRBUF_INIT;
+ struct strbuf gpg_stderr = STRBUF_INIT;
temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
if (!temp)
@@ -279,10 +335,6 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
return -1;
}
- fmt = get_format_by_sig(signature);
- if (!fmt)
- BUG("bad signature '%s'", signature);
-
strvec_push(&gpg.args, fmt->program);
strvec_pushv(&gpg.args, fmt->verify_args);
strvec_pushl(&gpg.args,
@@ -290,18 +342,220 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
"--verify", temp->filename.buf, "-",
NULL);
- if (!gpg_status)
- gpg_status = &buf;
-
sigchain_push(SIGPIPE, SIG_IGN);
- ret = pipe_command(&gpg, payload, payload_size,
- gpg_status, 0, gpg_output, 0);
+ ret = pipe_command(&gpg, payload, payload_size, &gpg_stdout, 0,
+ &gpg_stderr, 0);
sigchain_pop(SIGPIPE);
delete_tempfile(&temp);
- ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
- strbuf_release(&buf); /* no matter it was used or not */
+ ret |= !strstr(gpg_stdout.buf, "\n[GNUPG:] GOODSIG ");
+ sigc->payload = xmemdupz(payload, payload_size);
+ sigc->output = strbuf_detach(&gpg_stderr, NULL);
+ sigc->gpg_status = strbuf_detach(&gpg_stdout, NULL);
+
+ parse_gpg_output(sigc);
+
+ strbuf_release(&gpg_stdout);
+ strbuf_release(&gpg_stderr);
+
+ return ret;
+}
+
+static void parse_ssh_output(struct signature_check *sigc)
+{
+ const char *line, *principal, *search;
+ char *to_free;
+ char *key = NULL;
+
+ /*
+ * ssh-keygen output should be:
+ * Good "git" signature for PRINCIPAL with RSA key SHA256:FINGERPRINT
+ *
+ * or for valid but unknown keys:
+ * Good "git" signature with RSA key SHA256:FINGERPRINT
+ *
+ * Note that "PRINCIPAL" can contain whitespace, "RSA" and
+ * "SHA256" part could be a different token that names of
+ * the algorithms used, and "FINGERPRINT" is a hexadecimal
+ * string. By finding the last occurence of " with ", we can
+ * reliably parse out the PRINCIPAL.
+ */
+ sigc->result = 'B';
+ sigc->trust_level = TRUST_NEVER;
+
+ line = to_free = xmemdupz(sigc->output, strcspn(sigc->output, "\n"));
+
+ if (skip_prefix(line, "Good \"git\" signature for ", &line)) {
+ /* Valid signature and known principal */
+ sigc->result = 'G';
+ sigc->trust_level = TRUST_FULLY;
+
+ /* Search for the last "with" to get the full principal */
+ principal = line;
+ do {
+ search = strstr(line, " with ");
+ if (search)
+ line = search + 1;
+ } while (search != NULL);
+ sigc->signer = xmemdupz(principal, line - principal - 1);
+ } else if (skip_prefix(line, "Good \"git\" signature with ", &line)) {
+ /* Valid signature, but key unknown */
+ sigc->result = 'G';
+ sigc->trust_level = TRUST_UNDEFINED;
+ } else {
+ goto cleanup;
+ }
+
+ key = strstr(line, "key");
+ if (key) {
+ sigc->fingerprint = xstrdup(strstr(line, "key") + 4);
+ sigc->key = xstrdup(sigc->fingerprint);
+ } else {
+ /*
+ * Output did not match what we expected
+ * Treat the signature as bad
+ */
+ sigc->result = 'B';
+ }
+
+cleanup:
+ free(to_free);
+}
+
+static int verify_ssh_signed_buffer(struct signature_check *sigc,
+ struct gpg_format *fmt, const char *payload,
+ size_t payload_size, const char *signature,
+ size_t signature_size)
+{
+ struct child_process ssh_keygen = CHILD_PROCESS_INIT;
+ struct tempfile *buffer_file;
+ int ret = -1;
+ const char *line;
+ size_t trust_size;
+ char *principal;
+ struct strbuf ssh_principals_out = STRBUF_INIT;
+ struct strbuf ssh_principals_err = STRBUF_INIT;
+ struct strbuf ssh_keygen_out = STRBUF_INIT;
+ struct strbuf ssh_keygen_err = STRBUF_INIT;
+
+ if (!ssh_allowed_signers) {
+ error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));
+ return -1;
+ }
+
+ buffer_file = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+ if (!buffer_file)
+ return error_errno(_("could not create temporary file"));
+ if (write_in_full(buffer_file->fd, signature, signature_size) < 0 ||
+ close_tempfile_gently(buffer_file) < 0) {
+ error_errno(_("failed writing detached signature to '%s'"),
+ buffer_file->filename.buf);
+ delete_tempfile(&buffer_file);
+ return -1;
+ }
+
+ /* Find the principal from the signers */
+ strvec_pushl(&ssh_keygen.args, fmt->program,
+ "-Y", "find-principals",
+ "-f", ssh_allowed_signers,
+ "-s", buffer_file->filename.buf,
+ NULL);
+ ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
+ &ssh_principals_err, 0);
+ if (ret && strstr(ssh_principals_err.buf, "usage:")) {
+ error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)"));
+ goto out;
+ }
+ if (ret || !ssh_principals_out.len) {
+ /*
+ * We did not find a matching principal in the allowedSigners
+ * Check without validation
+ */
+ child_process_init(&ssh_keygen);
+ strvec_pushl(&ssh_keygen.args, fmt->program,
+ "-Y", "check-novalidate",
+ "-n", "git",
+ "-s", buffer_file->filename.buf,
+ NULL);
+ pipe_command(&ssh_keygen, payload, payload_size,
+ &ssh_keygen_out, 0, &ssh_keygen_err, 0);
+
+ /*
+ * Fail on unknown keys
+ * we still call check-novalidate to display the signature info
+ */
+ ret = -1;
+ } else {
+ /* Check every principal we found (one per line) */
+ for (line = ssh_principals_out.buf; *line;
+ line = strchrnul(line + 1, '\n')) {
+ while (*line == '\n')
+ line++;
+ if (!*line)
+ break;
+
+ trust_size = strcspn(line, "\n");
+ principal = xmemdupz(line, trust_size);
+
+ child_process_init(&ssh_keygen);
+ strbuf_release(&ssh_keygen_out);
+ strbuf_release(&ssh_keygen_err);
+ strvec_push(&ssh_keygen.args, fmt->program);
+ /*
+ * We found principals
+ * Try with each until we find a match
+ */
+ strvec_pushl(&ssh_keygen.args, "-Y", "verify",
+ "-n", "git",
+ "-f", ssh_allowed_signers,
+ "-I", principal,
+ "-s", buffer_file->filename.buf,
+ NULL);
+
+ if (ssh_revocation_file) {
+ if (file_exists(ssh_revocation_file)) {
+ strvec_pushl(&ssh_keygen.args, "-r",
+ ssh_revocation_file, NULL);
+ } else {
+ warning(_("ssh signing revocation file configured but not found: %s"),
+ ssh_revocation_file);
+ }
+ }
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+ ret = pipe_command(&ssh_keygen, payload, payload_size,
+ &ssh_keygen_out, 0, &ssh_keygen_err, 0);
+ sigchain_pop(SIGPIPE);
+
+ FREE_AND_NULL(principal);
+
+ if (!ret)
+ ret = !starts_with(ssh_keygen_out.buf, "Good");
+
+ if (!ret)
+ break;
+ }
+ }
+
+ sigc->payload = xmemdupz(payload, payload_size);
+ strbuf_stripspace(&ssh_keygen_out, 0);
+ strbuf_stripspace(&ssh_keygen_err, 0);
+ /* Add stderr outputs to show the user actual ssh-keygen errors */
+ strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
+ strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
+ sigc->output = strbuf_detach(&ssh_keygen_out, NULL);
+ sigc->gpg_status = xstrdup(sigc->output);
+
+ parse_ssh_output(sigc);
+
+out:
+ if (buffer_file)
+ delete_tempfile(&buffer_file);
+ strbuf_release(&ssh_principals_out);
+ strbuf_release(&ssh_principals_err);
+ strbuf_release(&ssh_keygen_out);
+ strbuf_release(&ssh_keygen_err);
return ret;
}
@@ -309,35 +563,32 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
int check_signature(const char *payload, size_t plen, const char *signature,
size_t slen, struct signature_check *sigc)
{
- struct strbuf gpg_output = STRBUF_INIT;
- struct strbuf gpg_status = STRBUF_INIT;
+ struct gpg_format *fmt;
int status;
sigc->result = 'N';
sigc->trust_level = -1;
- status = verify_signed_buffer(payload, plen, signature, slen,
- &gpg_output, &gpg_status);
- if (status && !gpg_output.len)
- goto out;
- sigc->payload = xmemdupz(payload, plen);
- sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
- sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
- parse_gpg_output(sigc);
+ fmt = get_format_by_sig(signature);
+ if (!fmt)
+ die(_("bad/incompatible signature '%s'"), signature);
+
+ status = fmt->verify_signed_buffer(sigc, fmt, payload, plen, signature,
+ slen);
+
+ if (status && !sigc->output)
+ return !!status;
+
status |= sigc->result != 'G';
status |= sigc->trust_level < configured_min_trust_level;
- out:
- strbuf_release(&gpg_status);
- strbuf_release(&gpg_output);
-
return !!status;
}
void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
{
- const char *output = flags & GPG_VERIFY_RAW ?
- sigc->gpg_status : sigc->gpg_output;
+ const char *output = flags & GPG_VERIFY_RAW ? sigc->gpg_status :
+ sigc->output;
if (flags & GPG_VERIFY_VERBOSE && sigc->payload)
fputs(sigc->payload, stdout);
@@ -419,12 +670,33 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return git_config_string(&ssh_default_key_command, var, value);
+ }
+
+ if (!strcmp(var, "gpg.ssh.allowedsignersfile")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return git_config_pathname(&ssh_allowed_signers, var, value);
+ }
+
+ if (!strcmp(var, "gpg.ssh.revocationfile")) {
+ if (!value)
+ return config_error_nonbool(var);
+ return git_config_pathname(&ssh_revocation_file, var, value);
+ }
+
if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
fmtname = "openpgp";
if (!strcmp(var, "gpg.x509.program"))
fmtname = "x509";
+ if (!strcmp(var, "gpg.ssh.program"))
+ fmtname = "ssh";
+
if (fmtname) {
fmt = get_format_by_name(fmtname);
return git_config_string(&fmt->program, var, value);
@@ -433,18 +705,148 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0;
}
+static char *get_ssh_key_fingerprint(const char *signing_key)
+{
+ struct child_process ssh_keygen = CHILD_PROCESS_INIT;
+ int ret = -1;
+ struct strbuf fingerprint_stdout = STRBUF_INIT;
+ struct strbuf **fingerprint;
+ char *fingerprint_ret;
+
+ /*
+ * With SSH Signing this can contain a filename or a public key
+ * For textual representation we usually want a fingerprint
+ */
+ if (starts_with(signing_key, "ssh-")) {
+ strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL);
+ ret = pipe_command(&ssh_keygen, signing_key,
+ strlen(signing_key), &fingerprint_stdout, 0,
+ NULL, 0);
+ } else {
+ strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf",
+ configured_signing_key, NULL);
+ ret = pipe_command(&ssh_keygen, NULL, 0, &fingerprint_stdout, 0,
+ NULL, 0);
+ }
+
+ if (!!ret)
+ die_errno(_("failed to get the ssh fingerprint for key '%s'"),
+ signing_key);
+
+ fingerprint = strbuf_split_max(&fingerprint_stdout, ' ', 3);
+ if (!fingerprint[1])
+ die_errno(_("failed to get the ssh fingerprint for key '%s'"),
+ signing_key);
+
+ fingerprint_ret = strbuf_detach(fingerprint[1], NULL);
+ strbuf_list_free(fingerprint);
+ strbuf_release(&fingerprint_stdout);
+ return fingerprint_ret;
+}
+
+/* Returns the first public key from an ssh-agent to use for signing */
+static const char *get_default_ssh_signing_key(void)
+{
+ struct child_process ssh_default_key = CHILD_PROCESS_INIT;
+ int ret = -1;
+ struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT;
+ struct strbuf **keys;
+ char *key_command = NULL;
+ const char **argv;
+ int n;
+ char *default_key = NULL;
+
+ if (!ssh_default_key_command)
+ die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
+
+ key_command = xstrdup(ssh_default_key_command);
+ n = split_cmdline(key_command, &argv);
+
+ if (n < 0)
+ die("malformed build-time gpg.ssh.defaultKeyCommand: %s",
+ split_cmdline_strerror(n));
+
+ strvec_pushv(&ssh_default_key.args, argv);
+ ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0,
+ &key_stderr, 0);
+
+ if (!ret) {
+ keys = strbuf_split_max(&key_stdout, '\n', 2);
+ if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
+ default_key = strbuf_detach(keys[0], NULL);
+ } else {
+ warning(_("gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s"),
+ key_stderr.buf, key_stdout.buf);
+ }
+
+ strbuf_list_free(keys);
+ } else {
+ warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"),
+ key_stderr.buf, key_stdout.buf);
+ }
+
+ free(key_command);
+ free(argv);
+ strbuf_release(&key_stdout);
+
+ return default_key;
+}
+
+static const char *get_ssh_key_id(void) {
+ return get_ssh_key_fingerprint(get_signing_key());
+}
+
+/* Returns a textual but unique representation of the signing key */
+const char *get_signing_key_id(void)
+{
+ if (use_format->get_key_id) {
+ return use_format->get_key_id();
+ }
+
+ /* GPG/GPGSM only store a key id on this variable */
+ return get_signing_key();
+}
+
const char *get_signing_key(void)
{
if (configured_signing_key)
return configured_signing_key;
- return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+ if (use_format->get_default_key) {
+ return use_format->get_default_key();
+ }
+
+ return git_committer_info(IDENT_STRICT | IDENT_NO_DATE);
}
int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
{
+ return use_format->sign_buffer(buffer, signature, signing_key);
+}
+
+/*
+ * Strip CR from the line endings, in case we are on Windows.
+ * NEEDSWORK: make it trim only CRs before LFs and rename
+ */
+static void remove_cr_after(struct strbuf *buffer, size_t offset)
+{
+ size_t i, j;
+
+ for (i = j = offset; i < buffer->len; i++) {
+ if (buffer->buf[i] != '\r') {
+ if (i != j)
+ buffer->buf[j] = buffer->buf[i];
+ j++;
+ }
+ }
+ strbuf_setlen(buffer, j);
+}
+
+static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
+ const char *signing_key)
+{
struct child_process gpg = CHILD_PROCESS_INIT;
int ret;
- size_t i, j, bottom;
+ size_t bottom;
struct strbuf gpg_status = STRBUF_INIT;
strvec_pushl(&gpg.args,
@@ -470,13 +872,98 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
return error(_("gpg failed to sign the data"));
/* Strip CR from the line endings, in case we are on Windows. */
- for (i = j = bottom; i < signature->len; i++)
- if (signature->buf[i] != '\r') {
- if (i != j)
- signature->buf[j] = signature->buf[i];
- j++;
- }
- strbuf_setlen(signature, j);
+ remove_cr_after(signature, bottom);
return 0;
}
+
+static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
+ const char *signing_key)
+{
+ struct child_process signer = CHILD_PROCESS_INIT;
+ int ret = -1;
+ size_t bottom, keylen;
+ struct strbuf signer_stderr = STRBUF_INIT;
+ struct tempfile *key_file = NULL, *buffer_file = NULL;
+ char *ssh_signing_key_file = NULL;
+ struct strbuf ssh_signature_filename = STRBUF_INIT;
+
+ if (!signing_key || signing_key[0] == '\0')
+ return error(
+ _("user.signingkey needs to be set for ssh signing"));
+
+ if (starts_with(signing_key, "ssh-")) {
+ /* A literal ssh key */
+ key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
+ if (!key_file)
+ return error_errno(
+ _("could not create temporary file"));
+ keylen = strlen(signing_key);
+ if (write_in_full(key_file->fd, signing_key, keylen) < 0 ||
+ close_tempfile_gently(key_file) < 0) {
+ error_errno(_("failed writing ssh signing key to '%s'"),
+ key_file->filename.buf);
+ goto out;
+ }
+ ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL);
+ } else {
+ /* We assume a file */
+ ssh_signing_key_file = expand_user_path(signing_key, 1);
+ }
+
+ buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");
+ if (!buffer_file) {
+ error_errno(_("could not create temporary file"));
+ goto out;
+ }
+
+ if (write_in_full(buffer_file->fd, buffer->buf, buffer->len) < 0 ||
+ close_tempfile_gently(buffer_file) < 0) {
+ error_errno(_("failed writing ssh signing key buffer to '%s'"),
+ buffer_file->filename.buf);
+ goto out;
+ }
+
+ strvec_pushl(&signer.args, use_format->program,
+ "-Y", "sign",
+ "-n", "git",
+ "-f", ssh_signing_key_file,
+ buffer_file->filename.buf,
+ NULL);
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+ ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
+ sigchain_pop(SIGPIPE);
+
+ if (ret) {
+ 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);
+ goto out;
+ }
+
+ bottom = signature->len;
+
+ strbuf_addbuf(&ssh_signature_filename, &buffer_file->filename);
+ strbuf_addstr(&ssh_signature_filename, ".sig");
+ if (strbuf_read_file(signature, ssh_signature_filename.buf, 0) < 0) {
+ error_errno(
+ _("failed reading ssh signing data buffer from '%s'"),
+ ssh_signature_filename.buf);
+ }
+ unlink_or_warn(ssh_signature_filename.buf);
+
+ /* Strip CR from the line endings, in case we are on Windows. */
+ remove_cr_after(signature, bottom);
+
+out:
+ if (key_file)
+ delete_tempfile(&key_file);
+ if (buffer_file)
+ delete_tempfile(&buffer_file);
+ strbuf_release(&signer_stderr);
+ strbuf_release(&ssh_signature_filename);
+ FREE_AND_NULL(ssh_signing_key_file);
+ return ret;
+}
diff --git a/gpg-interface.h b/gpg-interface.h
index 80567e4894..beefacbb1e 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -17,7 +17,7 @@ enum signature_trust_level {
struct signature_check {
char *payload;
- char *gpg_output;
+ char *output;
char *gpg_status;
/*
@@ -64,6 +64,12 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
int git_gpg_config(const char *, const char *, void *);
void set_signing_key(const char *);
const char *get_signing_key(void);
+
+/*
+ * Returns a textual unique representation of the signing key in use
+ * Either a GPG KeyID or a SSH Key Fingerprint
+ */
+const char *get_signing_key_id(void);
int check_signature(const char *payload, size_t plen,
const char *signature, size_t slen,
struct signature_check *sigc);
diff --git a/grep.c b/grep.c
index 79598f245f..14fe8a0fd2 100644
--- a/grep.c
+++ b/grep.c
@@ -867,7 +867,7 @@ void free_grep_patterns(struct grep_opt *opt)
free_pattern_expr(opt->pattern_expression);
}
-static char *end_of_line(char *cp, unsigned long *left)
+static const char *end_of_line(const char *cp, unsigned long *left)
{
unsigned long l = *left;
while (l && *cp != '\n') {
@@ -908,7 +908,8 @@ static void show_name(struct grep_opt *opt, const char *name)
opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
}
-static int patmatch(struct grep_pat *p, char *line, char *eol,
+static int patmatch(struct grep_pat *p,
+ const char *line, const char *eol,
regmatch_t *match, int eflags)
{
int hit;
@@ -922,20 +923,16 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
return hit;
}
-static int strip_timestamp(char *bol, char **eol_p)
+static void strip_timestamp(const char *bol, const char **eol_p)
{
- char *eol = *eol_p;
- int ch;
+ const char *eol = *eol_p;
while (bol < --eol) {
if (*eol != '>')
continue;
*eol_p = ++eol;
- ch = *eol;
- *eol = '\0';
- return ch;
+ break;
}
- return 0;
}
static struct {
@@ -947,12 +944,12 @@ static struct {
{ "reflog ", 7 },
};
-static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
+static int match_one_pattern(struct grep_pat *p,
+ const char *bol, const char *eol,
enum grep_context ctx,
regmatch_t *pmatch, int eflags)
{
int hit = 0;
- int saved_ch = 0;
const char *start = bol;
if ((p->token != GREP_PATTERN) &&
@@ -971,7 +968,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
switch (p->field) {
case GREP_HEADER_AUTHOR:
case GREP_HEADER_COMMITTER:
- saved_ch = strip_timestamp(bol, &eol);
+ strip_timestamp(bol, &eol);
break;
default:
break;
@@ -1021,8 +1018,6 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
goto again;
}
}
- if (p->token == GREP_PATTERN_HEAD && saved_ch)
- *eol = saved_ch;
if (hit) {
pmatch[0].rm_so += bol - start;
pmatch[0].rm_eo += bol - start;
@@ -1030,8 +1025,9 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
return hit;
}
-static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol,
- char *eol, enum grep_context ctx, ssize_t *col,
+static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
+ const char *bol, const char *eol,
+ enum grep_context ctx, ssize_t *col,
ssize_t *icol, int collect_hits)
{
int h = 0;
@@ -1098,7 +1094,8 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol,
return h;
}
-static int match_expr(struct grep_opt *opt, char *bol, char *eol,
+static int match_expr(struct grep_opt *opt,
+ const char *bol, const char *eol,
enum grep_context ctx, ssize_t *col,
ssize_t *icol, int collect_hits)
{
@@ -1106,7 +1103,8 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol,
return match_expr_eval(opt, x, bol, eol, ctx, col, icol, collect_hits);
}
-static int match_line(struct grep_opt *opt, char *bol, char *eol,
+static int match_line(struct grep_opt *opt,
+ const char *bol, const char *eol,
ssize_t *col, ssize_t *icol,
enum grep_context ctx, int collect_hits)
{
@@ -1138,7 +1136,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
return hit;
}
-static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
+static int match_next_pattern(struct grep_pat *p,
+ const char *bol, const char *eol,
enum grep_context ctx,
regmatch_t *pmatch, int eflags)
{
@@ -1159,7 +1158,8 @@ static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
return 1;
}
-static int next_match(struct grep_opt *opt, char *bol, char *eol,
+static int next_match(struct grep_opt *opt,
+ const char *bol, const char *eol,
enum grep_context ctx, regmatch_t *pmatch, int eflags)
{
struct grep_pat *p;
@@ -1215,7 +1215,8 @@ static void show_line_header(struct grep_opt *opt, const char *name,
}
}
-static void show_line(struct grep_opt *opt, char *bol, char *eol,
+static void show_line(struct grep_opt *opt,
+ const char *bol, const char *eol,
const char *name, unsigned lno, ssize_t cno, char sign)
{
int rest = eol - bol;
@@ -1246,7 +1247,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
if (opt->color || opt->only_matching) {
regmatch_t match;
enum grep_context ctx = GREP_CONTEXT_BODY;
- int ch = *eol;
int eflags = 0;
if (opt->color) {
@@ -1261,7 +1261,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
else if (sign == '=')
line_color = opt->colors[GREP_COLOR_FUNCTION];
}
- *eol = '\0';
while (next_match(opt, bol, eol, ctx, &match, eflags)) {
if (match.rm_so == match.rm_eo)
break;
@@ -1279,7 +1278,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
rest -= match.rm_eo;
eflags = REG_NOTBOL;
}
- *eol = ch;
}
if (!opt->only_matching) {
output_color(opt, bol, rest, line_color);
@@ -1307,7 +1305,8 @@ static inline void grep_attr_unlock(void)
pthread_mutex_unlock(&grep_attr_mutex);
}
-static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol)
+static int match_funcname(struct grep_opt *opt, struct grep_source *gs,
+ const char *bol, const char *eol)
{
xdemitconf_t *xecfg = opt->priv;
if (xecfg && !xecfg->find_func) {
@@ -1334,10 +1333,10 @@ static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bo
}
static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
- char *bol, unsigned lno)
+ const char *bol, unsigned lno)
{
while (bol > gs->buf) {
- char *eol = --bol;
+ const char *eol = --bol;
while (bol > gs->buf && bol[-1] != '\n')
bol--;
@@ -1356,7 +1355,7 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
static int is_empty_line(const char *bol, const char *eol);
static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
- char *bol, char *end, unsigned lno)
+ const char *bol, const char *end, unsigned lno)
{
unsigned cur = lno, from = 1, funcname_lno = 0, orig_from;
int funcname_needed = !!opt->funcname, comment_needed = 0;
@@ -1376,8 +1375,8 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
/* Rewind. */
while (bol > gs->buf && cur > from) {
- char *next_bol = bol;
- char *eol = --bol;
+ const char *next_bol = bol;
+ const char *eol = --bol;
while (bol > gs->buf && bol[-1] != '\n')
bol--;
@@ -1408,7 +1407,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
/* Back forward. */
while (cur < lno) {
- char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
+ const char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
while (*eol != '\n')
eol++;
@@ -1436,12 +1435,12 @@ static int should_lookahead(struct grep_opt *opt)
static int look_ahead(struct grep_opt *opt,
unsigned long *left_p,
unsigned *lno_p,
- char **bol_p)
+ const char **bol_p)
{
unsigned lno = *lno_p;
- char *bol = *bol_p;
+ const char *bol = *bol_p;
struct grep_pat *p;
- char *sp, *last_bol;
+ const char *sp, *last_bol;
regoff_t earliest = -1;
for (p = opt->pattern_list; p; p = p->next) {
@@ -1543,8 +1542,8 @@ static int is_empty_line(const char *bol, const char *eol)
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
{
- char *bol;
- char *peek_bol = NULL;
+ const char *bol;
+ const char *peek_bol = NULL;
unsigned long left;
unsigned lno = 1;
unsigned last_hit = 0;
@@ -1626,7 +1625,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
bol = gs->buf;
left = gs->size;
while (left) {
- char *eol, ch;
+ const char *eol;
int hit;
ssize_t cno;
ssize_t col = -1, icol = -1;
@@ -1647,14 +1646,11 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
&& look_ahead(opt, &left, &lno, &bol))
break;
eol = end_of_line(bol, &left);
- ch = *eol;
- *eol = 0;
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
ctx = GREP_CONTEXT_BODY;
hit = match_line(opt, bol, eol, &col, &icol, ctx, collect_hits);
- *eol = ch;
if (collect_hits)
goto next_line;
@@ -1713,7 +1709,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
}
if (show_function && (!peek_bol || peek_bol < bol)) {
unsigned long peek_left = left;
- char *peek_eol = eol;
+ const char *peek_eol = eol;
/*
* Trailing empty lines are not interesting.
@@ -1825,7 +1821,8 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs)
return grep_source_1(opt, gs, 0);
}
-static void grep_source_init_buf(struct grep_source *gs, char *buf,
+static void grep_source_init_buf(struct grep_source *gs,
+ const char *buf,
unsigned long size)
{
gs->type = GREP_SOURCE_BUF;
@@ -1837,7 +1834,7 @@ static void grep_source_init_buf(struct grep_source *gs, char *buf,
gs->identifier = NULL;
}
-int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
+int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size)
{
struct grep_source gs;
int r;
@@ -1889,7 +1886,9 @@ void grep_source_clear_data(struct grep_source *gs)
switch (gs->type) {
case GREP_SOURCE_FILE:
case GREP_SOURCE_OID:
- FREE_AND_NULL(gs->buf);
+ /* these types own the buffer */
+ free((char *)gs->buf);
+ gs->buf = NULL;
gs->size = 0;
break;
case GREP_SOURCE_BUF:
diff --git a/grep.h b/grep.h
index 128007db65..3c75ed1fd8 100644
--- a/grep.h
+++ b/grep.h
@@ -128,9 +128,9 @@ struct grep_opt {
* instead.
*
* This is potentially the cause of at least one bug - "git grep"
- * ignoring the textconv attributes from submodules. See [1] for more
- * information.
- * [1] https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
+ * using the textconv attributes from the superproject on the
+ * submodules. See the failing "git grep --textconv" tests in
+ * t7814-grep-recurse-submodules.sh for more information.
*/
struct repository *repo;
@@ -189,7 +189,7 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *orig
void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
void compile_grep_patterns(struct grep_opt *opt);
void free_grep_patterns(struct grep_opt *opt);
-int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
+int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size);
struct grep_source {
char *name;
@@ -202,7 +202,7 @@ struct grep_source {
void *identifier;
struct repository *repo; /* if GREP_SOURCE_OID */
- char *buf;
+ const char *buf;
unsigned long size;
char *path; /* for attribute lookups */
@@ -223,7 +223,6 @@ void grep_source_load_driver(struct grep_source *gs,
int grep_source(struct grep_opt *opt, struct grep_source *gs);
struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
-int grep_threads_ok(const struct grep_opt *opt);
/*
* Mutex used around access to the attributes machinery if
diff --git a/help.c b/help.c
index be2fa64241..973e47cdc3 100644
--- a/help.c
+++ b/help.c
@@ -293,9 +293,21 @@ void load_command_list(const char *prefix,
exclude_cmds(other_cmds, main_cmds);
}
-void list_commands(unsigned int colopts,
- struct cmdnames *main_cmds, struct cmdnames *other_cmds)
+static int get_colopts(const char *var, const char *value, void *data)
{
+ unsigned int *colopts = data;
+
+ if (starts_with(var, "column."))
+ return git_column_config(var, value, "help", colopts);
+
+ return 0;
+}
+
+void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds)
+{
+ unsigned int colopts = 0;
+ git_config(get_colopts, &colopts);
+
if (main_cmds->cnt) {
const char *exec_path = git_exec_path();
printf_ln(_("available git commands in '%s'"), exec_path);
diff --git a/help.h b/help.h
index 5871e93ba2..9d383f1a0b 100644
--- a/help.h
+++ b/help.h
@@ -37,7 +37,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len);
/* Here we require that excludes is a sorted list. */
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
int is_in_cmdlist(struct cmdnames *cmds, const char *name);
-void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
+void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds);
void get_version_info(struct strbuf *buf, int show_build_options);
/*
diff --git a/hook.c b/hook.c
new file mode 100644
index 0000000000..55e1145a4b
--- /dev/null
+++ b/hook.c
@@ -0,0 +1,42 @@
+#include "cache.h"
+#include "hook.h"
+#include "run-command.h"
+
+const char *find_hook(const char *name)
+{
+ static struct strbuf path = STRBUF_INIT;
+
+ strbuf_reset(&path);
+ strbuf_git_path(&path, "hooks/%s", name);
+ if (access(path.buf, X_OK) < 0) {
+ int err = errno;
+
+#ifdef STRIP_EXTENSION
+ strbuf_addstr(&path, STRIP_EXTENSION);
+ if (access(path.buf, X_OK) >= 0)
+ return path.buf;
+ if (errno == EACCES)
+ err = errno;
+#endif
+
+ if (err == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
+ static struct string_list advise_given = STRING_LIST_INIT_DUP;
+
+ if (!string_list_lookup(&advise_given, name)) {
+ string_list_insert(&advise_given, name);
+ advise(_("The '%s' hook was ignored because "
+ "it's not set as executable.\n"
+ "You can disable this warning with "
+ "`git config advice.ignoredHook false`."),
+ path.buf);
+ }
+ }
+ return NULL;
+ }
+ return path.buf;
+}
+
+int hook_exists(const char *name)
+{
+ return !!find_hook(name);
+}
diff --git a/hook.h b/hook.h
new file mode 100644
index 0000000000..6aa36fc7ff
--- /dev/null
+++ b/hook.h
@@ -0,0 +1,16 @@
+#ifndef HOOK_H
+#define HOOK_H
+
+/*
+ * Returns the path to the hook file, or NULL if the hook is missing
+ * or disabled. Note that this points to static storage that will be
+ * overwritten by further calls to find_hook and run_hook_*.
+ */
+const char *find_hook(const char *name);
+
+/**
+ * A boolean version of find_hook()
+ */
+int hook_exists(const char *hookname);
+
+#endif
diff --git a/http-backend.c b/http-backend.c
index 838374edb9..e7c0eeab23 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -739,6 +739,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
int cmd_main(int argc, const char **argv)
{
char *method = getenv("REQUEST_METHOD");
+ const char *proto_header;
char *dir;
struct service_cmd *cmd = NULL;
char *cmd_arg = NULL;
@@ -789,6 +790,9 @@ int cmd_main(int argc, const char **argv)
http_config();
max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
max_request_buffer);
+ proto_header = getenv("HTTP_GIT_PROTOCOL");
+ if (proto_header)
+ setenv(GIT_PROTOCOL_ENVIRONMENT, proto_header, 0);
cmd->imp(&hdr, cmd_arg);
return 0;
diff --git a/http.c b/http.c
index a0f169d2fe..f92859f43f 100644
--- a/http.c
+++ b/http.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "git-curl-compat.h"
#include "http.h"
#include "config.h"
#include "pack.h"
@@ -47,19 +48,19 @@ static struct {
{ "sslv2", CURL_SSLVERSION_SSLv2 },
{ "sslv3", CURL_SSLVERSION_SSLv3 },
{ "tlsv1", CURL_SSLVERSION_TLSv1 },
-#if LIBCURL_VERSION_NUM >= 0x072200
+#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_0
{ "tlsv1.0", CURL_SSLVERSION_TLSv1_0 },
{ "tlsv1.1", CURL_SSLVERSION_TLSv1_1 },
{ "tlsv1.2", CURL_SSLVERSION_TLSv1_2 },
#endif
-#if LIBCURL_VERSION_NUM >= 0x073400
+#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_3
{ "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
#endif
};
static const char *ssl_key;
static const char *ssl_capath;
static const char *curl_no_proxy;
-#if LIBCURL_VERSION_NUM >= 0x072c00
+#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
static const char *ssl_pinnedkey;
#endif
static const char *ssl_cainfo;
@@ -373,10 +374,10 @@ static int http_options(const char *var, const char *value, void *cb)
}
if (!strcmp("http.pinnedpubkey", var)) {
-#if LIBCURL_VERSION_NUM >= 0x072c00
+#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
return git_config_pathname(&ssl_pinnedkey, var, value);
#else
- warning(_("Public key pinning not supported with cURL < 7.44.0"));
+ warning(_("Public key pinning not supported with cURL < 7.39.0"));
return 0;
#endif
}
@@ -500,7 +501,7 @@ static int has_cert_password(void)
return 1;
}
-#if LIBCURL_VERSION_NUM >= 0x073400
+#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD
static int has_proxy_cert_password(void)
{
if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
@@ -516,7 +517,7 @@ static int has_proxy_cert_password(void)
}
#endif
-#if LIBCURL_VERSION_NUM >= 0x071900
+#ifdef GITCURL_HAVE_CURLOPT_TCP_KEEPALIVE
static void set_curl_keepalive(CURL *c)
{
curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1);
@@ -536,7 +537,7 @@ static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)
if (rc < 0)
warning_errno("unable to set SO_KEEPALIVE on socket");
- return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
+ return CURL_SOCKOPT_OK;
}
static void set_curl_keepalive(CURL *c)
@@ -550,8 +551,8 @@ static void redact_sensitive_header(struct strbuf *header)
const char *sensitive_header;
if (trace_curl_redact &&
- (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
- skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header))) {
+ (skip_iprefix(header->buf, "Authorization:", &sensitive_header) ||
+ skip_iprefix(header->buf, "Proxy-Authorization:", &sensitive_header))) {
/* The first token is the type, which is OK to log */
while (isspace(*sensitive_header))
sensitive_header++;
@@ -561,7 +562,7 @@ static void redact_sensitive_header(struct strbuf *header)
strbuf_setlen(header, sensitive_header - header->buf);
strbuf_addstr(header, " <redacted>");
} else if (trace_curl_redact &&
- skip_prefix(header->buf, "Cookie:", &sensitive_header)) {
+ skip_iprefix(header->buf, "Cookie:", &sensitive_header)) {
struct strbuf redacted_header = STRBUF_INIT;
const char *cookie;
@@ -732,7 +733,7 @@ static long get_curl_allowed_protocols(int from_user)
return allowed_protocols;
}
-#if LIBCURL_VERSION_NUM >=0x072f00
+#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_2
static int get_curl_http_version_opt(const char *version_string, long *opt)
{
int i;
@@ -774,7 +775,7 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
}
-#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0
+#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_2
if (curl_http_version) {
long opt;
if (!get_curl_http_version_opt(curl_http_version, &opt)) {
@@ -805,7 +806,7 @@ static CURL *get_curl_handle(void)
if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
!http_schannel_check_revoke) {
-#if LIBCURL_VERSION_NUM >= 0x072c00
+#ifdef GIT_CURL_HAVE_CURLSSLOPT_NO_REVOKE
curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
#else
warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));
@@ -845,20 +846,20 @@ static CURL *get_curl_handle(void)
curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
if (ssl_capath != NULL)
curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
-#if LIBCURL_VERSION_NUM >= 0x072c00
+#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
if (ssl_pinnedkey != NULL)
curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);
#endif
if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
!http_schannel_use_ssl_cainfo) {
curl_easy_setopt(result, CURLOPT_CAINFO, NULL);
-#if LIBCURL_VERSION_NUM >= 0x073400
+#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
#endif
} else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
if (ssl_cainfo != NULL)
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
-#if LIBCURL_VERSION_NUM >= 0x073400
+#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO
if (http_proxy_ssl_ca_info != NULL)
curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
#endif
@@ -927,7 +928,6 @@ static CURL *get_curl_handle(void)
*/
curl_easy_setopt(result, CURLOPT_PROXY, "");
} else if (curl_http_proxy) {
-#if LIBCURL_VERSION_NUM >= 0x071800
if (starts_with(curl_http_proxy, "socks5h"))
curl_easy_setopt(result,
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
@@ -940,8 +940,7 @@ static CURL *get_curl_handle(void)
else if (starts_with(curl_http_proxy, "socks"))
curl_easy_setopt(result,
CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
-#endif
-#if LIBCURL_VERSION_NUM >= 0x073400
+#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD
else if (starts_with(curl_http_proxy, "https")) {
curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
@@ -991,7 +990,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
char *low_speed_limit;
char *low_speed_time;
char *normalized_url;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
config.section = "http";
config.key = NULL;
@@ -1006,7 +1005,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
free(normalized_url);
string_list_clear(&config.vars, 1);
-#if LIBCURL_VERSION_NUM >= 0x073800
+#ifdef GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS
if (http_ssl_backend) {
const curl_ssl_backend **backends;
struct strbuf buf = STRBUF_INIT;
@@ -1490,6 +1489,10 @@ static int handle_curl_result(struct slot_results *results)
*/
credential_reject(&cert_auth);
return HTTP_NOAUTH;
+#ifdef GIT_CURL_HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH
+ } else if (results->curl_result == CURLE_SSL_PINNEDPUBKEYNOTMATCH) {
+ return HTTP_NOMATCHPUBLICKEY;
+#endif
} else if (missing_target(results))
return HTTP_MISSING_TARGET;
else if (results->http_code == 401) {
diff --git a/http.h b/http.h
index 3db5a0cf32..df1590e53a 100644
--- a/http.h
+++ b/http.h
@@ -154,6 +154,7 @@ struct http_get_options {
#define HTTP_START_FAILED 3
#define HTTP_REAUTH 4
#define HTTP_NOAUTH 5
+#define HTTP_NOMATCHPUBLICKEY 6
/*
* Requests a URL and stores the result in a strbuf.
diff --git a/imap-send.c b/imap-send.c
index 49a5f8aa59..e6090a0346 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1441,7 +1441,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, server.port);
if (server.auth_method) {
-#if LIBCURL_VERSION_NUM < 0x072200
+#ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS
warning("No LOGIN_OPTIONS support in this cURL version");
#else
struct strbuf auth = STRBUF_INIT;
diff --git a/list.h b/list.h
index eb601192f4..362a4cd7f5 100644
--- a/list.h
+++ b/list.h
@@ -46,7 +46,10 @@ struct list_head {
#define INIT_LIST_HEAD(ptr) \
(ptr)->next = (ptr)->prev = (ptr)
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
+#define LIST_HEAD_INIT(name) { \
+ .next = &(name), \
+ .prev = &(name), \
+}
/* Add new element at the head of the list. */
static inline void list_add(struct list_head *newp, struct list_head *head)
diff --git a/lockfile.h b/lockfile.h
index db93e6ba73..90af4e66b2 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -121,7 +121,7 @@ struct lock_file {
struct tempfile *tempfile;
};
-#define LOCK_INIT { NULL }
+#define LOCK_INIT { 0 }
/* String appended to a filename to derive the lockfile name: */
#define LOCK_SUFFIX ".lock"
diff --git a/log-tree.c b/log-tree.c
index 6dc4412268..644893fd8c 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -515,10 +515,10 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
status = check_signature(payload.buf, payload.len, signature.buf,
signature.len, &sigc);
- if (status && !sigc.gpg_output)
+ if (status && !sigc.output)
show_sig_lines(opt, status, "No signature\n");
else
- show_sig_lines(opt, status, sigc.gpg_output);
+ show_sig_lines(opt, status, sigc.output);
signature_check_clear(&sigc);
out:
@@ -585,8 +585,8 @@ static int show_one_mergetag(struct commit *commit,
/* could have a good signature */
status = check_signature(payload.buf, payload.len,
signature.buf, signature.len, &sigc);
- if (sigc.gpg_output)
- strbuf_addstr(&verify_message, sigc.gpg_output);
+ if (sigc.output)
+ strbuf_addstr(&verify_message, sigc.output);
else
strbuf_addstr(&verify_message, "No signature\n");
signature_check_clear(&sigc);
diff --git a/log-tree.h b/log-tree.h
index 1e8c91dbf2..e7e4641cf8 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -14,10 +14,8 @@ struct decoration_filter {
};
int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
-void init_log_tree_opt(struct rev_info *);
int log_tree_diff_flush(struct rev_info *);
int log_tree_commit(struct rev_info *, struct commit *);
-int log_tree_opt_parse(struct rev_info *, const char **, int);
void show_log(struct rev_info *opt);
void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
int use_color,
diff --git a/ls-refs.c b/ls-refs.c
index be09568910..54078323dc 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -41,6 +41,12 @@ static void ensure_config_read(void)
}
/*
+ * If we see this many or more "ref-prefix" lines from the client, we consider
+ * it "too many" and will avoid using the prefix feature entirely.
+ */
+#define TOO_MANY_PREFIXES 65536
+
+/*
* Check if one of the prefixes is a prefix of the ref.
* If no prefixes were provided, all refs match.
*/
@@ -158,20 +164,32 @@ int ls_refs(struct repository *r, struct packet_reader *request)
data.peel = 1;
else if (!strcmp("symrefs", arg))
data.symrefs = 1;
- else if (skip_prefix(arg, "ref-prefix ", &out))
- strvec_push(&data.prefixes, out);
+ else if (skip_prefix(arg, "ref-prefix ", &out)) {
+ if (data.prefixes.nr < TOO_MANY_PREFIXES)
+ strvec_push(&data.prefixes, out);
+ }
else if (!strcmp("unborn", arg))
data.unborn = allow_unborn;
+ else
+ die(_("unexpected line: '%s'"), arg);
}
if (request->status != PACKET_READ_FLUSH)
die(_("expected flush after ls-refs arguments"));
+ /*
+ * If we saw too many prefixes, we must avoid using them at all; as
+ * soon as we have any prefix, they are meant to form a comprehensive
+ * list.
+ */
+ if (data.prefixes.nr >= TOO_MANY_PREFIXES)
+ strvec_clear(&data.prefixes);
+
send_possibly_unborn_head(&data);
if (!data.prefixes.nr)
strvec_push(&data.prefixes, "");
for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
- send_ref, &data, 0);
+ send_ref, &data);
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
diff --git a/merge-ort.c b/merge-ort.c
index 35aa979c3a..0342f10483 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -32,6 +32,7 @@
#include "promisor-remote.h"
#include "revision.h"
#include "strmap.h"
+#include "submodule-config.h"
#include "submodule.h"
#include "tree.h"
#include "unpack-trees.h"
@@ -608,6 +609,7 @@ static int err(struct merge_options *opt, const char *err, ...)
static void format_commit(struct strbuf *sb,
int indent,
+ struct repository *repo,
struct commit *commit)
{
struct merge_remote_desc *desc;
@@ -621,7 +623,7 @@ static void format_commit(struct strbuf *sb,
return;
}
- format_commit_message(commit, "%h %s", sb, &ctx);
+ repo_format_commit_message(repo, commit, "%h %s", sb, &ctx);
strbuf_addch(sb, '\n');
}
@@ -1511,7 +1513,6 @@ static int find_first_merges(struct repository *repo,
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
oid_to_hex(&a->object.oid));
repo_init_revisions(repo, &revs, NULL);
- rev_opts.submodule = path;
/* FIXME: can't handle linked worktrees in submodules yet */
revs.single_worktree = path != NULL;
setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
@@ -1521,7 +1522,7 @@ 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 (in_merge_bases(b, commit))
+ if (repo_in_merge_bases(repo, b, commit))
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1536,7 +1537,7 @@ 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 && in_merge_bases(m2, m1)) {
+ if (i != j && repo_in_merge_bases(repo, m2, m1)) {
contains_another = 1;
break;
}
@@ -1557,10 +1558,12 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *b,
struct object_id *result)
{
+ struct repository subrepo;
+ struct strbuf sb = STRBUF_INIT;
+ int ret = 0;
struct commit *commit_o, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
- struct strbuf sb = STRBUF_INIT;
int i;
int search = !opt->priv->call_depth;
@@ -1576,46 +1579,48 @@ static int merge_submodule(struct merge_options *opt,
if (is_null_oid(b))
return 0;
- if (add_submodule_odb(path)) {
+ if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
path_msg(opt, path, 0,
- _("Failed to merge submodule %s (not checked out)"),
- path);
+ _("Failed to merge submodule %s (not checked out)"),
+ path);
return 0;
}
- if (!(commit_o = lookup_commit_reference(opt->repo, o)) ||
- !(commit_a = lookup_commit_reference(opt->repo, a)) ||
- !(commit_b = lookup_commit_reference(opt->repo, b))) {
+ if (!(commit_o = lookup_commit_reference(&subrepo, o)) ||
+ !(commit_a = lookup_commit_reference(&subrepo, a)) ||
+ !(commit_b = lookup_commit_reference(&subrepo, b))) {
path_msg(opt, path, 0,
_("Failed to merge submodule %s (commits not present)"),
path);
- return 0;
+ goto cleanup;
}
/* check whether both changes are forward */
- if (!in_merge_bases(commit_o, commit_a) ||
- !in_merge_bases(commit_o, commit_b)) {
+ if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
+ !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
path_msg(opt, path, 0,
_("Failed to merge submodule %s "
"(commits don't follow merge-base)"),
path);
- return 0;
+ goto cleanup;
}
/* Case #1: a is contained in b or vice versa */
- if (in_merge_bases(commit_a, commit_b)) {
+ if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
oidcpy(result, b);
path_msg(opt, path, 1,
_("Note: Fast-forwarding submodule %s to %s"),
path, oid_to_hex(b));
- return 1;
+ ret = 1;
+ goto cleanup;
}
- if (in_merge_bases(commit_b, commit_a)) {
+ if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
oidcpy(result, a);
path_msg(opt, path, 1,
_("Note: Fast-forwarding submodule %s to %s"),
path, oid_to_hex(a));
- return 1;
+ ret = 1;
+ goto cleanup;
}
/*
@@ -1627,10 +1632,10 @@ static int merge_submodule(struct merge_options *opt,
/* Skip the search if makes no sense to the calling context. */
if (!search)
- return 0;
+ goto cleanup;
/* find commit which merges them */
- parent_count = find_first_merges(opt->repo, path, commit_a, commit_b,
+ parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
&merges);
switch (parent_count) {
case 0:
@@ -1638,7 +1643,7 @@ static int merge_submodule(struct merge_options *opt,
break;
case 1:
- format_commit(&sb, 4,
+ format_commit(&sb, 4, &subrepo,
(struct commit *)merges.objects[0].item);
path_msg(opt, path, 0,
_("Failed to merge submodule %s, but a possible merge "
@@ -1655,7 +1660,7 @@ static int merge_submodule(struct merge_options *opt,
break;
default:
for (i = 0; i < merges.nr; i++)
- format_commit(&sb, 4,
+ format_commit(&sb, 4, &subrepo,
(struct commit *)merges.objects[i].item);
path_msg(opt, path, 0,
_("Failed to merge submodule %s, but multiple "
@@ -1664,7 +1669,9 @@ static int merge_submodule(struct merge_options *opt,
}
object_array_clear(&merges);
- return 0;
+cleanup:
+ repo_clear(&subrepo);
+ return ret;
}
static void initialize_attr_index(struct merge_options *opt)
@@ -4045,11 +4052,7 @@ static int checkout(struct merge_options *opt,
unpack_opts.quiet = 0; /* FIXME: sequencer might want quiet? */
unpack_opts.verbose_update = (opt->verbosity > 2);
unpack_opts.fn = twoway_merge;
- if (1/* FIXME: opts->overwrite_ignore*/) {
- CALLOC_ARRAY(unpack_opts.dir, 1);
- unpack_opts.dir->flags |= DIR_SHOW_IGNORED;
- setup_standard_excludes(unpack_opts.dir);
- }
+ unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
parse_tree(prev);
init_tree_desc(&trees[0], prev->buffer, prev->size);
parse_tree(next);
@@ -4057,8 +4060,6 @@ static int checkout(struct merge_options *opt,
ret = unpack_trees(2, trees, &unpack_opts);
clear_unpack_trees_porcelain(&unpack_opts);
- dir_clear(unpack_opts.dir);
- FREE_AND_NULL(unpack_opts.dir);
return ret;
}
@@ -4096,7 +4097,7 @@ static int record_conflicted_index_entries(struct merge_options *opt)
state.istate = index;
original_cache_nr = index->cache_nr;
- /* Put every entry from paths into plist, then sort */
+ /* Append every entry from conflicted into index, then sort */
strmap_for_each_entry(&opt->priv->conflicted, &iter, e) {
const char *path = e->key;
struct conflict_info *ci = e->value;
diff --git a/merge-recursive.c b/merge-recursive.c
index e594d4c3fa..d9457797db 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -24,6 +24,7 @@
#include "repository.h"
#include "revision.h"
#include "string-list.h"
+#include "submodule-config.h"
#include "submodule.h"
#include "tag.h"
#include "tree-walk.h"
@@ -333,7 +334,9 @@ static void output(struct merge_options *opt, int v, const char *fmt, ...)
flush_output(opt);
}
-static void output_commit_title(struct merge_options *opt, struct commit *commit)
+static void repo_output_commit_title(struct merge_options *opt,
+ struct repository *repo,
+ struct commit *commit)
{
struct merge_remote_desc *desc;
@@ -342,23 +345,29 @@ static void output_commit_title(struct merge_options *opt, struct commit *commit
if (desc)
strbuf_addf(&opt->obuf, "virtual %s\n", desc->name);
else {
- strbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,
- DEFAULT_ABBREV);
+ strbuf_repo_add_unique_abbrev(&opt->obuf, repo,
+ &commit->object.oid,
+ DEFAULT_ABBREV);
strbuf_addch(&opt->obuf, ' ');
- if (parse_commit(commit) != 0)
+ if (repo_parse_commit(repo, commit) != 0)
strbuf_addstr(&opt->obuf, _("(bad commit)\n"));
else {
const char *title;
- const char *msg = get_commit_buffer(commit, NULL);
+ const char *msg = repo_get_commit_buffer(repo, commit, NULL);
int len = find_commit_subject(msg, &title);
if (len)
strbuf_addf(&opt->obuf, "%.*s\n", len, title);
- unuse_commit_buffer(commit, msg);
+ repo_unuse_commit_buffer(repo, commit, msg);
}
}
flush_output(opt);
}
+static void output_commit_title(struct merge_options *opt, struct commit *commit)
+{
+ repo_output_commit_title(opt, the_repository, commit);
+}
+
static int add_cacheinfo(struct merge_options *opt,
const struct diff_filespec *blob,
const char *path, int stage, int refresh, int options)
@@ -408,8 +417,11 @@ static int unpack_trees_start(struct merge_options *opt,
memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
if (opt->priv->call_depth)
opt->priv->unpack_opts.index_only = 1;
- else
+ else {
opt->priv->unpack_opts.update = 1;
+ /* FIXME: should only do this if !overwrite_ignore */
+ opt->priv->unpack_opts.preserve_ignored = 0;
+ }
opt->priv->unpack_opts.merge = 1;
opt->priv->unpack_opts.head_idx = 2;
opt->priv->unpack_opts.fn = threeway_merge;
@@ -1110,7 +1122,6 @@ static int find_first_merges(struct repository *repo,
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
oid_to_hex(&a->object.oid));
repo_init_revisions(repo, &revs, NULL);
- rev_opts.submodule = path;
/* FIXME: can't handle linked worktrees in submodules yet */
revs.single_worktree = path != NULL;
setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
@@ -1120,7 +1131,7 @@ 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 (in_merge_bases(b, commit))
+ if (repo_in_merge_bases(repo, b, commit))
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1135,7 +1146,7 @@ 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 && in_merge_bases(m2, m1)) {
+ if (i != j && repo_in_merge_bases(repo, m2, m1)) {
contains_another = 1;
break;
}
@@ -1149,14 +1160,14 @@ static int find_first_merges(struct repository *repo,
return result->nr;
}
-static void print_commit(struct commit *commit)
+static void print_commit(struct repository *repo, struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.date_mode.type = DATE_NORMAL;
/* FIXME: Merge this with output_commit_title() */
assert(!merge_remote_util(commit));
- format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+ repo_format_commit_message(repo, commit, " %h: %m %s", &sb, &ctx);
fprintf(stderr, "%s\n", sb.buf);
strbuf_release(&sb);
}
@@ -1171,6 +1182,8 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *base, const struct object_id *a,
const struct object_id *b)
{
+ struct repository subrepo;
+ int ret = 0;
struct commit *commit_base, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@@ -1194,49 +1207,51 @@ static int merge_submodule(struct merge_options *opt,
if (is_null_oid(b))
return 0;
- if (add_submodule_odb(path)) {
+ if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
return 0;
}
- if (!(commit_base = lookup_commit_reference(opt->repo, base)) ||
- !(commit_a = lookup_commit_reference(opt->repo, a)) ||
- !(commit_b = lookup_commit_reference(opt->repo, b))) {
+ if (!(commit_base = lookup_commit_reference(&subrepo, base)) ||
+ !(commit_a = lookup_commit_reference(&subrepo, a)) ||
+ !(commit_b = lookup_commit_reference(&subrepo, b))) {
output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path);
- return 0;
+ goto cleanup;
}
/* check whether both changes are forward */
- if (!in_merge_bases(commit_base, commit_a) ||
- !in_merge_bases(commit_base, commit_b)) {
+ if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
+ !repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
- return 0;
+ goto cleanup;
}
/* Case #1: a is contained in b or vice versa */
- if (in_merge_bases(commit_a, commit_b)) {
+ if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
oidcpy(result, b);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
- output_commit_title(opt, commit_b);
+ repo_output_commit_title(opt, &subrepo, commit_b);
} else if (show(opt, 2))
output(opt, 2, _("Fast-forwarding submodule %s"), path);
else
; /* no output */
- return 1;
+ ret = 1;
+ goto cleanup;
}
- if (in_merge_bases(commit_b, commit_a)) {
+ if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
oidcpy(result, a);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
- output_commit_title(opt, commit_a);
+ repo_output_commit_title(opt, &subrepo, commit_a);
} else if (show(opt, 2))
output(opt, 2, _("Fast-forwarding submodule %s"), path);
else
; /* no output */
- return 1;
+ ret = 1;
+ goto cleanup;
}
/*
@@ -1248,10 +1263,10 @@ static int merge_submodule(struct merge_options *opt,
/* Skip the search if makes no sense to the calling context. */
if (!search)
- return 0;
+ goto cleanup;
/* find commit which merges them */
- parent_count = find_first_merges(opt->repo, &merges, path,
+ parent_count = find_first_merges(&subrepo, &merges, path,
commit_a, commit_b);
switch (parent_count) {
case 0:
@@ -1261,7 +1276,7 @@ static int merge_submodule(struct merge_options *opt,
case 1:
output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
output(opt, 2, _("Found a possible merge resolution for the submodule:\n"));
- print_commit((struct commit *) merges.objects[0].item);
+ print_commit(&subrepo, (struct commit *) merges.objects[0].item);
output(opt, 2, _(
"If this is correct simply add it to the index "
"for example\n"
@@ -1274,11 +1289,13 @@ static int merge_submodule(struct merge_options *opt,
default:
output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
for (i = 0; i < merges.nr; i++)
- print_commit((struct commit *) merges.objects[i].item);
+ print_commit(&subrepo, (struct commit *) merges.objects[i].item);
}
object_array_clear(&merges);
- return 0;
+cleanup:
+ repo_clear(&subrepo);
+ return ret;
}
static int merge_mode_and_contents(struct merge_options *opt,
diff --git a/merge.c b/merge.c
index 6e736881d9..2382ff66d3 100644
--- a/merge.c
+++ b/merge.c
@@ -53,7 +53,6 @@ int checkout_fast_forward(struct repository *r,
struct unpack_trees_options opts;
struct tree_desc t[MAX_UNPACK_TREES];
int i, nr_trees = 0;
- struct dir_struct dir = DIR_INIT;
struct lock_file lock_file = LOCK_INIT;
refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
@@ -80,11 +79,7 @@ int checkout_fast_forward(struct repository *r,
}
memset(&opts, 0, sizeof(opts));
- if (overwrite_ignore) {
- dir.flags |= DIR_SHOW_IGNORED;
- setup_standard_excludes(&dir);
- opts.dir = &dir;
- }
+ opts.preserve_ignored = !overwrite_ignore;
opts.head_idx = 1;
opts.src_index = r->index;
@@ -101,7 +96,6 @@ int checkout_fast_forward(struct repository *r,
clear_unpack_trees_porcelain(&opts);
return -1;
}
- dir_clear(&dir);
clear_unpack_trees_porcelain(&opts);
if (write_locked_index(r->index, &lock_file, COMMIT_LOCK))
diff --git a/mergesort.c b/mergesort.c
index e5fdf2ee4a..6216835566 100644
--- a/mergesort.c
+++ b/mergesort.c
@@ -1,73 +1,84 @@
#include "cache.h"
#include "mergesort.h"
-struct mergesort_sublist {
- void *ptr;
- unsigned long len;
-};
-
-static void *get_nth_next(void *list, unsigned long n,
- void *(*get_next_fn)(const void *))
+/* Combine two sorted lists. Take from `list` on equality. */
+static void *llist_merge(void *list, void *other,
+ void *(*get_next_fn)(const void *),
+ void (*set_next_fn)(void *, void *),
+ int (*compare_fn)(const void *, const void *))
{
- while (n-- && list)
- list = get_next_fn(list);
- return list;
-}
+ void *result = list, *tail;
-static void *pop_item(struct mergesort_sublist *l,
- void *(*get_next_fn)(const void *))
-{
- void *p = l->ptr;
- l->ptr = get_next_fn(l->ptr);
- l->len = l->ptr ? (l->len - 1) : 0;
- return p;
+ if (compare_fn(list, other) > 0) {
+ result = other;
+ goto other;
+ }
+ for (;;) {
+ do {
+ tail = list;
+ list = get_next_fn(list);
+ if (!list) {
+ set_next_fn(tail, other);
+ return result;
+ }
+ } while (compare_fn(list, other) <= 0);
+ set_next_fn(tail, other);
+ other:
+ do {
+ tail = other;
+ other = get_next_fn(other);
+ if (!other) {
+ set_next_fn(tail, list);
+ return result;
+ }
+ } while (compare_fn(list, other) > 0);
+ set_next_fn(tail, list);
+ }
}
+/*
+ * Perform an iterative mergesort using an array of sublists.
+ *
+ * n is the number of items.
+ * ranks[i] is undefined if n & 2^i == 0, and assumed empty.
+ * ranks[i] contains a sublist of length 2^i otherwise.
+ *
+ * The number of bits in a void pointer limits the number of objects
+ * that can be created, and thus the number of array elements necessary
+ * to be able to sort any valid list.
+ *
+ * Adding an item to this array is like incrementing a binary number;
+ * positional values for set bits correspond to sublist lengths.
+ */
void *llist_mergesort(void *list,
void *(*get_next_fn)(const void *),
void (*set_next_fn)(void *, void *),
int (*compare_fn)(const void *, const void *))
{
- unsigned long l;
-
- if (!list)
- return NULL;
- for (l = 1; ; l *= 2) {
- void *curr;
- struct mergesort_sublist p, q;
+ void *ranks[bitsizeof(void *)];
+ size_t n = 0;
+ int i;
- p.ptr = list;
- q.ptr = get_nth_next(p.ptr, l, get_next_fn);
- if (!q.ptr)
- break;
- p.len = q.len = l;
+ while (list) {
+ void *next = get_next_fn(list);
+ if (next)
+ set_next_fn(list, NULL);
+ for (i = 0; n & (1 << i); i++)
+ list = llist_merge(ranks[i], list, get_next_fn,
+ set_next_fn, compare_fn);
+ n++;
+ ranks[i] = list;
+ list = next;
+ }
- if (compare_fn(p.ptr, q.ptr) > 0)
- list = curr = pop_item(&q, get_next_fn);
+ for (i = 0; n; i++, n >>= 1) {
+ if (!(n & 1))
+ continue;
+ if (list)
+ list = llist_merge(ranks[i], list, get_next_fn,
+ set_next_fn, compare_fn);
else
- list = curr = pop_item(&p, get_next_fn);
-
- while (p.ptr) {
- while (p.len || q.len) {
- void *prev = curr;
-
- if (!p.len)
- curr = pop_item(&q, get_next_fn);
- else if (!q.len)
- curr = pop_item(&p, get_next_fn);
- else if (compare_fn(p.ptr, q.ptr) > 0)
- curr = pop_item(&q, get_next_fn);
- else
- curr = pop_item(&p, get_next_fn);
- set_next_fn(prev, curr);
- }
- p.ptr = q.ptr;
- p.len = l;
- q.ptr = get_nth_next(p.ptr, l, get_next_fn);
- q.len = q.ptr ? l : 0;
-
- }
- set_next_fn(curr, NULL);
+ list = ranks[i];
}
return list;
}
diff --git a/mergetools/xxdiff b/mergetools/xxdiff
index ce5b8e9f29..d5ce467995 100644
--- a/mergetools/xxdiff
+++ b/mergetools/xxdiff
@@ -3,6 +3,13 @@ diff_cmd () {
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl+G"' \
"$LOCAL" "$REMOTE"
+
+ # xxdiff can segfault on binary files which are often uninteresting.
+ # Do not allow segfaults to stop us from continuing on to the next file.
+ if test $? = 128
+ then
+ return 1
+ fi
}
merge_cmd () {
diff --git a/midx.c b/midx.c
index 864034a6ad..8433086ac1 100644
--- a/midx.c
+++ b/midx.c
@@ -283,14 +283,18 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos)
(off_t)pos * MIDX_CHUNK_OFFSET_WIDTH);
}
-static int nth_midxed_pack_entry(struct repository *r,
- struct multi_pack_index *m,
- struct pack_entry *e,
- uint32_t pos)
+int fill_midx_entry(struct repository * r,
+ const struct object_id *oid,
+ struct pack_entry *e,
+ struct multi_pack_index *m)
{
+ uint32_t pos;
uint32_t pack_int_id;
struct packed_git *p;
+ if (!bsearch_midx(oid, m, &pos))
+ return 0;
+
if (pos >= m->num_objects)
return 0;
@@ -310,15 +314,9 @@ static int nth_midxed_pack_entry(struct repository *r,
if (!is_pack_valid(p))
return 0;
- if (p->num_bad_objects) {
- uint32_t i;
- struct object_id oid;
- nth_midxed_object_oid(&oid, m, pos);
- for (i = 0; i < p->num_bad_objects; i++)
- if (hasheq(oid.hash,
- p->bad_object_sha1 + the_hash_algo->rawsz * i))
- return 0;
- }
+ if (oidset_size(&p->bad_objects) &&
+ oidset_contains(&p->bad_objects, oid))
+ return 0;
e->offset = nth_midxed_offset(m, pos);
e->p = p;
@@ -326,19 +324,6 @@ static int nth_midxed_pack_entry(struct repository *r,
return 1;
}
-int fill_midx_entry(struct repository * r,
- const struct object_id *oid,
- struct pack_entry *e,
- struct multi_pack_index *m)
-{
- uint32_t pos;
-
- if (!bsearch_midx(oid, m, &pos))
- return 0;
-
- return nth_midxed_pack_entry(r, m, e, pos);
-}
-
/* 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)
@@ -475,6 +460,8 @@ struct write_midx_context {
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,
@@ -484,8 +471,26 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
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);
@@ -963,7 +968,43 @@ static void bitmap_show_commit(struct commit *commit, void *_data)
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;
@@ -972,8 +1013,12 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
cb.ctx = ctx;
repo_init_revisions(the_repository, &revs, NULL);
- setup_revisions(0, NULL, &revs, NULL);
- for_each_ref(add_ref_to_pending, &revs);
+ 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
@@ -1002,18 +1047,23 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash,
struct write_midx_context *ctx,
+ const char *refs_snapshot,
unsigned flags)
{
struct packing_data pdata;
struct pack_idx_entry **index;
struct commit **commits = NULL;
uint32_t i, commits_nr;
+ uint16_t options = 0;
char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name, hash_to_hex(midx_hash));
int ret;
+ if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
+ options |= BITMAP_OPT_HASH_CACHE;
+
prepare_midx_packing_data(&pdata, ctx);
- commits = find_commits_for_midx_bitmap(&commits_nr, ctx);
+ commits = find_commits_for_midx_bitmap(&commits_nr, refs_snapshot, ctx);
/*
* Build the MIDX-order index based on pdata.objects (which is already
@@ -1049,7 +1099,7 @@ static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash,
goto cleanup;
bitmap_writer_set_checksum(midx_hash);
- bitmap_writer_finish(index, pdata.nr_objects, bitmap_name, 0);
+ bitmap_writer_finish(index, pdata.nr_objects, bitmap_name, options);
cleanup:
free(index);
@@ -1057,9 +1107,27 @@ cleanup:
return ret;
}
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+ const char *object_dir)
+{
+ struct multi_pack_index *cur;
+
+ /* Ensure the given object_dir is local, or a known alternate. */
+ find_odb(r, object_dir);
+
+ for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+ if (!strcmp(object_dir, cur->object_dir))
+ return cur;
+ }
+
+ return NULL;
+}
+
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)
{
char *midx_name;
@@ -1068,25 +1136,23 @@ static int write_midx_internal(const char *object_dir,
struct hashfile *f = NULL;
struct lock_file lk;
struct write_midx_context ctx = { 0 };
- struct multi_pack_index *cur;
int pack_name_concat_len = 0;
int dropped_packs = 0;
int result = 0;
struct chunkfile *cf;
- /* Ensure the given object_dir is local, or a known alternate. */
- find_odb(the_repository, object_dir);
-
midx_name = get_midx_filename(object_dir);
if (safe_create_leading_directories(midx_name))
die_errno(_("unable to create leading directories of %s"),
midx_name);
- for (cur = get_multi_pack_index(the_repository); cur; cur = cur->next) {
- if (!strcmp(object_dir, cur->object_dir)) {
- ctx.m = cur;
- break;
- }
+ 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)) {
@@ -1136,10 +1202,13 @@ static int write_midx_internal(const char *object_dir,
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_drop) {
+ 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;
@@ -1343,7 +1412,8 @@ static int write_midx_internal(const char *object_dir,
if (flags & MIDX_WRITE_REV_INDEX)
write_midx_reverse_index(midx_name, midx_hash, &ctx);
if (flags & MIDX_WRITE_BITMAP) {
- if (write_midx_bitmap(midx_name, midx_hash, &ctx, flags) < 0) {
+ if (write_midx_bitmap(midx_name, midx_hash, &ctx,
+ refs_snapshot, flags) < 0) {
error(_("could not write multi-pack bitmap"));
result = 1;
goto cleanup;
@@ -1353,7 +1423,8 @@ static int write_midx_internal(const char *object_dir,
if (ctx.m)
close_object_store(the_repository->objects);
- commit_lock_file(&lk);
+ 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);
@@ -1378,9 +1449,21 @@ cleanup:
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, preferred_pack_name, 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 {
@@ -1614,7 +1697,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
{
uint32_t i, *count, result = 0;
struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
- struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+ struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
struct progress *progress = NULL;
if (!m)
@@ -1659,12 +1742,11 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
free(count);
- if (packs_to_drop.nr) {
- result = write_midx_internal(object_dir, &packs_to_drop, NULL, flags);
- m = NULL;
- }
+ 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;
}
@@ -1780,7 +1862,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
struct child_process cmd = CHILD_PROCESS_INIT;
FILE *cmd_in;
struct strbuf base_name = STRBUF_INIT;
- struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+ struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
/*
* When updating the default for these configuration
@@ -1851,12 +1933,9 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
goto cleanup;
}
- result = write_midx_internal(object_dir, NULL, NULL, flags);
- m = NULL;
+ result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
cleanup:
- if (m)
- close_midx(m);
free(include_pack);
return result;
}
diff --git a/midx.h b/midx.h
index aa3da557bb..6e32297fa3 100644
--- a/midx.h
+++ b/midx.h
@@ -2,6 +2,7 @@
#define MIDX_H
#include "repository.h"
+#include "string-list.h"
struct object_id;
struct pack_entry;
@@ -44,6 +45,7 @@ struct multi_pack_index {
#define MIDX_PROGRESS (1 << 0)
#define MIDX_WRITE_REV_INDEX (1 << 1)
#define MIDX_WRITE_BITMAP (1 << 2)
+#define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3)
const unsigned char *get_midx_checksum(struct multi_pack_index *m);
char *get_midx_filename(const char *object_dir);
@@ -61,7 +63,19 @@ int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pa
int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
-int write_midx_file(const char *object_dir, const char *preferred_pack_name, unsigned flags);
+/*
+ * Variant of write_midx_file which writes a MIDX containing only the packs
+ * specified in packs_to_include.
+ */
+int write_midx_file(const char *object_dir,
+ const char *preferred_pack_name,
+ const char *refs_snapshot,
+ unsigned 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);
void clear_midx_file(struct repository *r);
int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags);
int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags);
diff --git a/object-file.c b/object-file.c
index c1f7ece055..02b7970274 100644
--- a/object-file.c
+++ b/object-file.c
@@ -415,74 +415,6 @@ enum scld_error safe_create_leading_directories_const(const char *path)
return result;
}
-int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
-{
- /*
- * The number of times we will try to remove empty directories
- * in the way of path. This is only 1 because if another
- * process is racily creating directories that conflict with
- * us, we don't want to fight against them.
- */
- int remove_directories_remaining = 1;
-
- /*
- * The number of times that we will try to create the
- * directories containing path. We are willing to attempt this
- * more than once, because another process could be trying to
- * clean up empty directories at the same time as we are
- * trying to create them.
- */
- int create_directories_remaining = 3;
-
- /* A scratch copy of path, filled lazily if we need it: */
- struct strbuf path_copy = STRBUF_INIT;
-
- int ret, save_errno;
-
- /* Sanity check: */
- assert(*path);
-
-retry_fn:
- ret = fn(path, cb);
- save_errno = errno;
- if (!ret)
- goto out;
-
- if (errno == EISDIR && remove_directories_remaining-- > 0) {
- /*
- * A directory is in the way. Maybe it is empty; try
- * to remove it:
- */
- if (!path_copy.len)
- strbuf_addstr(&path_copy, path);
-
- if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
- goto retry_fn;
- } else if (errno == ENOENT && create_directories_remaining-- > 0) {
- /*
- * Maybe the containing directory didn't exist, or
- * maybe it was just deleted by a process that is
- * racing with us to clean up empty directories. Try
- * to create it:
- */
- enum scld_error scld_result;
-
- if (!path_copy.len)
- strbuf_addstr(&path_copy, path);
-
- do {
- scld_result = safe_create_leading_directories(path_copy.buf);
- if (scld_result == SCLD_OK)
- goto retry_fn;
- } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
- }
-
-out:
- strbuf_release(&path_copy);
- errno = save_errno;
- return ret;
-}
-
static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
{
int i;
@@ -1084,9 +1016,11 @@ void *xmmap(void *start, size_t length,
* the streaming interface and rehash it to do the same.
*/
int check_object_signature(struct repository *r, const struct object_id *oid,
- void *map, unsigned long size, const char *type)
+ void *map, unsigned long size, const char *type,
+ struct object_id *real_oidp)
{
- struct object_id real_oid;
+ struct object_id tmp;
+ struct object_id *real_oid = real_oidp ? real_oidp : &tmp;
enum object_type obj_type;
struct git_istream *st;
git_hash_ctx c;
@@ -1094,8 +1028,8 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
int hdrlen;
if (map) {
- hash_object_file(r->hash_algo, map, size, type, &real_oid);
- return !oideq(oid, &real_oid) ? -1 : 0;
+ hash_object_file(r->hash_algo, map, size, type, real_oid);
+ return !oideq(oid, real_oid) ? -1 : 0;
}
st = open_istream(r, oid, &obj_type, &size, NULL);
@@ -1120,9 +1054,9 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
break;
r->hash_algo->update_fn(&c, buf, readlen);
}
- r->hash_algo->final_oid_fn(&real_oid, &c);
+ r->hash_algo->final_oid_fn(real_oid, &c);
close_istream(st);
- return !oideq(oid, &real_oid) ? -1 : 0;
+ return !oideq(oid, real_oid) ? -1 : 0;
}
int git_open_cloexec(const char *name, int flags)
@@ -1255,11 +1189,14 @@ void *map_loose_object(struct repository *r,
return map_loose_object_1(r, NULL, oid, size);
}
-static int unpack_loose_short_header(git_zstream *stream,
- unsigned char *map, unsigned long mapsize,
- void *buffer, unsigned long bufsiz)
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+ unsigned char *map,
+ unsigned long mapsize,
+ void *buffer,
+ unsigned long bufsiz,
+ struct strbuf *header)
{
- int ret;
+ int status;
/* Get the data stream */
memset(stream, 0, sizeof(*stream));
@@ -1270,43 +1207,24 @@ static int unpack_loose_short_header(git_zstream *stream,
git_inflate_init(stream);
obj_read_unlock();
- ret = git_inflate(stream, 0);
+ status = git_inflate(stream, 0);
obj_read_lock();
-
- return ret;
-}
-
-int unpack_loose_header(git_zstream *stream,
- unsigned char *map, unsigned long mapsize,
- void *buffer, unsigned long bufsiz)
-{
- int status = unpack_loose_short_header(stream, map, mapsize,
- buffer, bufsiz);
-
- if (status < Z_OK)
- return status;
-
- /* Make sure we have the terminating NUL */
- if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
- return -1;
- return 0;
-}
-
-static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map,
- unsigned long mapsize, void *buffer,
- unsigned long bufsiz, struct strbuf *header)
-{
- int status;
-
- status = unpack_loose_short_header(stream, map, mapsize, buffer, bufsiz);
if (status < Z_OK)
- return -1;
+ return ULHR_BAD;
/*
* Check if entire header is unpacked in the first iteration.
*/
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
- return 0;
+ return ULHR_OK;
+
+ /*
+ * We have a header longer than MAX_HEADER_LEN. The "header"
+ * here is only non-NULL when we run "cat-file
+ * --allow-unknown-type".
+ */
+ if (!header)
+ return ULHR_TOO_LONG;
/*
* buffer[0..bufsiz] was not large enough. Copy the partial
@@ -1327,7 +1245,7 @@ static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map
stream->next_out = buffer;
stream->avail_out = bufsiz;
} while (status != Z_STREAM_END);
- return -1;
+ return ULHR_TOO_LONG;
}
static void *unpack_loose_rest(git_zstream *stream,
@@ -1385,8 +1303,7 @@ static void *unpack_loose_rest(git_zstream *stream,
* too permissive for what we want to check. So do an anal
* object header parse by hand.
*/
-static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
- unsigned int flags)
+int parse_loose_header(const char *hdr, struct object_info *oi)
{
const char *type_buf = hdr;
unsigned long size;
@@ -1408,15 +1325,6 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
type = type_from_string_gently(type_buf, type_len, 1);
if (oi->type_name)
strbuf_add(oi->type_name, type_buf, type_len);
- /*
- * Set type to 0 if its an unknown object and
- * we're obtaining the type using '--allow-unknown-type'
- * option.
- */
- if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0))
- type = 0;
- else if (type < 0)
- die(_("invalid object type"));
if (oi->typep)
*oi->typep = type;
@@ -1443,15 +1351,14 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
/*
* The length must be followed by a zero byte
*/
- return *hdr ? -1 : type;
-}
-
-int parse_loose_header(const char *hdr, unsigned long *sizep)
-{
- struct object_info oi = OBJECT_INFO_INIT;
+ if (*hdr)
+ return -1;
- oi.sizep = sizep;
- return parse_loose_header_extended(hdr, &oi, 0);
+ /*
+ * The format is valid, but the type may still be bogus. The
+ * Caller needs to check its oi->typep.
+ */
+ return 0;
}
static int loose_object_info(struct repository *r,
@@ -1465,6 +1372,8 @@ static int loose_object_info(struct repository *r,
char hdr[MAX_HEADER_LEN];
struct strbuf hdrbuf = STRBUF_INIT;
unsigned long size_scratch;
+ enum object_type type_scratch;
+ int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
if (oi->delta_base_oid)
oidclr(oi->delta_base_oid);
@@ -1495,43 +1404,48 @@ static int loose_object_info(struct repository *r,
if (!oi->sizep)
oi->sizep = &size_scratch;
+ if (!oi->typep)
+ oi->typep = &type_scratch;
if (oi->disk_sizep)
*oi->disk_sizep = mapsize;
- if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) {
- if (unpack_loose_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
- status = error(_("unable to unpack %s header with --allow-unknown-type"),
- oid_to_hex(oid));
- } else if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
+
+ switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
+ allow_unknown ? &hdrbuf : NULL)) {
+ case ULHR_OK:
+ if (parse_loose_header(hdrbuf.len ? hdrbuf.buf : hdr, oi) < 0)
+ status = error(_("unable to parse %s header"), oid_to_hex(oid));
+ else if (!allow_unknown && *oi->typep < 0)
+ die(_("invalid object type"));
+
+ if (!oi->contentp)
+ break;
+ *oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
+ if (*oi->contentp)
+ goto cleanup;
+
+ status = -1;
+ break;
+ case ULHR_BAD:
status = error(_("unable to unpack %s header"),
oid_to_hex(oid));
- if (status < 0)
- ; /* Do nothing */
- else if (hdrbuf.len) {
- if ((status = parse_loose_header_extended(hdrbuf.buf, oi, flags)) < 0)
- status = error(_("unable to parse %s header with --allow-unknown-type"),
- oid_to_hex(oid));
- } else if ((status = parse_loose_header_extended(hdr, oi, flags)) < 0)
- status = error(_("unable to parse %s header"), oid_to_hex(oid));
-
- if (status >= 0 && oi->contentp) {
- *oi->contentp = unpack_loose_rest(&stream, hdr,
- *oi->sizep, oid);
- if (!*oi->contentp) {
- git_inflate_end(&stream);
- status = -1;
- }
- } else
- git_inflate_end(&stream);
+ break;
+ case ULHR_TOO_LONG:
+ status = error(_("header for %s too long, exceeds %d bytes"),
+ oid_to_hex(oid), MAX_HEADER_LEN);
+ break;
+ }
+ git_inflate_end(&stream);
+cleanup:
munmap(map, mapsize);
- if (status && oi->typep)
- *oi->typep = status;
if (oi->sizep == &size_scratch)
oi->sizep = NULL;
strbuf_release(&hdrbuf);
+ if (oi->typep == &type_scratch)
+ oi->typep = NULL;
oi->whence = OI_LOOSE;
- return (status < 0) ? status : 0;
+ return status;
}
int obj_read_use_lock = 0;
@@ -1614,7 +1528,14 @@ static int do_oid_object_info_extended(struct repository *r,
break;
}
- if (register_all_submodule_odb_as_alternates())
+ /*
+ * If r is the_repository, this might be an attempt at
+ * accessing a submodule object as if it were in the_repository
+ * (having called add_submodule_odb() on that submodule's ODB).
+ * If any such ODBs exist, register them and try again.
+ */
+ if (r == the_repository &&
+ register_all_submodule_odb_as_alternates())
/* We added some alternates; retry */
continue;
@@ -1642,7 +1563,7 @@ static int do_oid_object_info_extended(struct repository *r,
return 0;
rtype = packed_object_info(r, e.p, e.offset, oi);
if (rtype < 0) {
- mark_bad_packed_object(e.p, real->hash);
+ mark_bad_packed_object(e.p, real);
return do_oid_object_info_extended(r, real, oi, 0);
} else if (oi->whence == OI_PACKED) {
oi->u.packed.offset = e.offset;
@@ -1751,7 +1672,7 @@ void *read_object_file_extended(struct repository *r,
die(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(repl), path);
- if ((p = has_packed_and_bad(r, repl->hash)) != NULL)
+ if ((p = has_packed_and_bad(r, repl)) != NULL)
die(_("packed object %s (stored in %s) is corrupt"),
oid_to_hex(repl), p->pack_name);
obj_read_unlock();
@@ -1941,7 +1862,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
static int write_loose_object(const struct object_id *oid, char *hdr,
int hdrlen, const void *buf, unsigned long len,
- time_t mtime)
+ time_t mtime, unsigned flags)
{
int fd, ret;
unsigned char compressed[4096];
@@ -1955,7 +1876,9 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
fd = create_tmpfile(&tmp_file, filename.buf);
if (fd < 0) {
- if (errno == EACCES)
+ if (flags & HASH_SILENT)
+ return -1;
+ else if (errno == EACCES)
return error(_("insufficient permission for adding an object to repository database %s"), get_object_directory());
else
return error_errno(_("unable to create temporary file"));
@@ -2005,7 +1928,8 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
struct utimbuf utb;
utb.actime = mtime;
utb.modtime = mtime;
- if (utime(tmp_file.buf, &utb) < 0)
+ if (utime(tmp_file.buf, &utb) < 0 &&
+ !(flags & HASH_SILENT))
warning_errno(_("failed utime() on %s"), tmp_file.buf);
}
@@ -2030,8 +1954,9 @@ static int freshen_packed_object(const struct object_id *oid)
return 1;
}
-int write_object_file(const void *buf, unsigned long len, const char *type,
- struct object_id *oid)
+int write_object_file_flags(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid,
+ unsigned flags)
{
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);
@@ -2043,7 +1968,7 @@ int write_object_file(const void *buf, unsigned long len, const char *type,
&hdrlen);
if (freshen_packed_object(oid) || freshen_loose_object(oid))
return 0;
- return write_loose_object(oid, hdr, hdrlen, buf, len, 0);
+ return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
}
int hash_object_file_literally(const void *buf, unsigned long len,
@@ -2063,7 +1988,7 @@ int hash_object_file_literally(const void *buf, unsigned long len,
goto cleanup;
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
- status = write_loose_object(oid, header, hdrlen, buf, len, 0);
+ status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
cleanup:
free(header);
@@ -2085,7 +2010,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
if (!buf)
return error(_("cannot read object for %s"), oid_to_hex(oid));
hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
- ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime);
+ ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
free(buf);
return ret;
@@ -2592,17 +2517,16 @@ static int check_stream_oid(git_zstream *stream,
int read_loose_object(const char *path,
const struct object_id *expected_oid,
- enum object_type *type,
- unsigned long *size,
- void **contents)
+ struct object_id *real_oid,
+ void **contents,
+ struct object_info *oi)
{
int ret = -1;
void *map = NULL;
unsigned long mapsize;
git_zstream stream;
char hdr[MAX_HEADER_LEN];
-
- *contents = NULL;
+ unsigned long *size = oi->sizep;
map = map_loose_object_1(the_repository, path, NULL, &mapsize);
if (!map) {
@@ -2610,19 +2534,19 @@ int read_loose_object(const char *path,
goto out;
}
- if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) {
+ if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
+ NULL) < 0) {
error(_("unable to unpack header of %s"), path);
goto out;
}
- *type = parse_loose_header(hdr, size);
- if (*type < 0) {
+ if (parse_loose_header(hdr, oi) < 0) {
error(_("unable to parse header of %s"), path);
git_inflate_end(&stream);
goto out;
}
- if (*type == OBJ_BLOB && *size > big_file_threshold) {
+ if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
goto out;
} else {
@@ -2633,10 +2557,7 @@ int read_loose_object(const char *path,
goto out;
}
if (check_object_signature(the_repository, expected_oid,
- *contents, *size,
- type_name(*type))) {
- error(_("hash mismatch for %s (expected %s)"), path,
- oid_to_hex(expected_oid));
+ *contents, *size, oi->type_name->buf, real_oid)) {
free(*contents);
goto out;
}
diff --git a/object-store.h b/object-store.h
index ebc55274e6..952efb6a4b 100644
--- a/object-store.h
+++ b/object-store.h
@@ -10,6 +10,7 @@
#include "khash.h"
#include "dir.h"
#include "oidtree.h"
+#include "oidset.h"
struct object_directory {
struct object_directory *next;
@@ -76,9 +77,8 @@ struct packed_git {
const void *index_data;
size_t index_size;
uint32_t num_objects;
- uint32_t num_bad_objects;
uint32_t crc_offset;
- unsigned char *bad_object_sha1;
+ struct oidset bad_objects;
int index_version;
time_t mtime;
int pack_fd;
@@ -223,8 +223,14 @@ int hash_object_file(const struct git_hash_algo *algo, const void *buf,
unsigned long len, const char *type,
struct object_id *oid);
-int write_object_file(const void *buf, unsigned long len,
- const char *type, struct object_id *oid);
+int write_object_file_flags(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid,
+ unsigned flags);
+static inline int write_object_file(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid)
+{
+ return write_object_file_flags(buf, len, type, oid, 0);
+}
int hash_object_file_literally(const void *buf, unsigned long len,
const char *type, struct object_id *oid,
@@ -245,6 +251,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
/*
* Open the loose object at path, check its hash, and return the contents,
+ * use the "oi" argument to assert things about the object, or e.g. populate its
* type, and size. If the object is a blob, then "contents" may return NULL,
* to allow streaming of large blobs.
*
@@ -252,9 +259,9 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
*/
int read_loose_object(const char *path,
const struct object_id *expected_oid,
- enum object_type *type,
- unsigned long *size,
- void **contents);
+ struct object_id *real_oid,
+ void **contents,
+ struct object_info *oi);
/* Retry packed storage after checking packed and loose storage */
#define HAS_OBJECT_RECHECK_PACKED 1
@@ -371,7 +378,7 @@ struct object_info {
* Initializer for a "struct object_info" that wants no items. You may
* also memset() the memory to all-zeroes.
*/
-#define OBJECT_INFO_INIT {NULL}
+#define OBJECT_INFO_INIT { 0 }
/* Invoke lookup_replace_object() on the given hash */
#define OBJECT_INFO_LOOKUP_REPLACE 1
diff --git a/object.c b/object.c
index 4e85955a94..23a24e678a 100644
--- a/object.c
+++ b/object.c
@@ -279,7 +279,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
(!obj && repo_has_object_file(r, oid) &&
oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
- if (check_object_signature(r, repl, NULL, 0, NULL) < 0) {
+ if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) {
error(_("hash mismatch %s"), oid_to_hex(oid));
return NULL;
}
@@ -290,7 +290,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
buffer = repo_read_object_file(r, oid, &type, &size);
if (buffer) {
if (check_object_signature(r, repl, buffer, size,
- type_name(type)) < 0) {
+ type_name(type), NULL) < 0) {
free(buffer);
error(_("hash mismatch %s"), oid_to_hex(repl));
return NULL;
diff --git a/object.h b/object.h
index 549f2d256b..cb556ab775 100644
--- a/object.h
+++ b/object.h
@@ -55,7 +55,7 @@ struct object_array {
} *objects;
};
-#define OBJECT_ARRAY_INIT { 0, 0, NULL }
+#define OBJECT_ARRAY_INIT { 0 }
/*
* object flag allocation:
diff --git a/oid-array.h b/oid-array.h
index 72bca78b7d..f60f9af674 100644
--- a/oid-array.h
+++ b/oid-array.h
@@ -56,7 +56,7 @@ struct oid_array {
int sorted;
};
-#define OID_ARRAY_INIT { NULL, 0, 0, 0 }
+#define OID_ARRAY_INIT { 0 }
/**
* Add an item to the set. The object ID will be placed at the end of the array
diff --git a/oidset.c b/oidset.c
index 5aac633c1f..b36a2bae86 100644
--- a/oidset.c
+++ b/oidset.c
@@ -36,11 +36,6 @@ void oidset_clear(struct oidset *set)
oidset_init(set, 0);
}
-int oidset_size(struct oidset *set)
-{
- return kh_size(&set->set);
-}
-
void oidset_parse_file(struct oidset *set, const char *path)
{
oidset_parse_file_carefully(set, path, NULL, NULL);
diff --git a/oidset.h b/oidset.h
index 01f6560283..ba4a5a2cd3 100644
--- a/oidset.h
+++ b/oidset.h
@@ -57,7 +57,10 @@ int oidset_remove(struct oidset *set, const struct object_id *oid);
/**
* Returns the number of oids in the set.
*/
-int oidset_size(struct oidset *set);
+static inline int oidset_size(const struct oidset *set)
+{
+ return kh_size(&set->set);
+}
/**
* Remove all entries from the oidset, freeing any resources associated with
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 8504110a4d..f47a0a7db4 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -1418,7 +1418,7 @@ static int try_partial_reuse(struct packed_git *pack,
return 0;
}
-static uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
+uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
{
struct multi_pack_index *m = bitmap_git->midx;
if (!m)
@@ -1742,6 +1742,33 @@ int test_bitmap_commits(struct repository *r)
return 0;
}
+int test_bitmap_hashes(struct repository *r)
+{
+ struct bitmap_index *bitmap_git = prepare_bitmap_git(r);
+ struct object_id oid;
+ uint32_t i, index_pos;
+
+ if (!bitmap_git->hashes)
+ goto cleanup;
+
+ for (i = 0; i < bitmap_num_objects(bitmap_git); i++) {
+ if (bitmap_is_midx(bitmap_git))
+ index_pos = pack_pos_to_midx(bitmap_git->midx, i);
+ else
+ index_pos = pack_pos_to_index(bitmap_git->pack, i);
+
+ nth_bitmap_object_oid(bitmap_git, &oid, index_pos);
+
+ printf("%s %"PRIu32"\n",
+ oid_to_hex(&oid), get_be32(bitmap_git->hashes + index_pos));
+ }
+
+cleanup:
+ free_bitmap_index(bitmap_git);
+
+ return 0;
+}
+
int rebuild_bitmap(const uint32_t *reposition,
struct ewah_bitmap *source,
struct bitmap *dest)
@@ -1791,18 +1818,20 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
for (i = 0; i < num_objects; ++i) {
struct object_id oid;
struct object_entry *oe;
+ uint32_t index_pos;
if (bitmap_is_midx(bitmap_git))
- nth_midxed_object_oid(&oid,
- bitmap_git->midx,
- pack_pos_to_midx(bitmap_git->midx, i));
+ index_pos = pack_pos_to_midx(bitmap_git->midx, i);
else
- nth_packed_object_id(&oid, bitmap_git->pack,
- pack_pos_to_index(bitmap_git->pack, i));
+ index_pos = pack_pos_to_index(bitmap_git->pack, i);
+ nth_bitmap_object_oid(bitmap_git, &oid, index_pos);
oe = packlist_find(mapping, &oid);
- if (oe)
+ if (oe) {
reposition[i] = oe_in_pack_pos(mapping, oe) + 1;
+ if (bitmap_git->hashes && !oe->hash)
+ oe->hash = get_be32(bitmap_git->hashes + index_pos);
+ }
}
return reposition;
diff --git a/pack-bitmap.h b/pack-bitmap.h
index 469090bad2..19a63fa1ab 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -52,9 +52,11 @@ void traverse_bitmap_commit_list(struct bitmap_index *,
show_reachable_fn show_reachable);
void test_bitmap_walk(struct rev_info *revs);
int test_bitmap_commits(struct repository *r);
+int test_bitmap_hashes(struct repository *r);
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
struct list_objects_filter_options *filter,
int filter_provided_objects);
+uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
struct packed_git **packfile,
uint32_t *entries,
diff --git a/pack-check.c b/pack-check.c
index c8e560d71a..3f418e3a6a 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -142,7 +142,8 @@ static int verify_packfile(struct repository *r,
err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
oid_to_hex(&oid), p->pack_name,
(uintmax_t)entries[i].offset);
- else if (check_object_signature(r, &oid, data, size, type_name(type)))
+ else if (check_object_signature(r, &oid, data, size,
+ type_name(type), NULL))
err = error("packed %s from %s is corrupt",
oid_to_hex(&oid), p->pack_name);
else if (fn) {
diff --git a/pack-revindex.h b/pack-revindex.h
index 479b8f2f9c..74f4eae668 100644
--- a/pack-revindex.h
+++ b/pack-revindex.h
@@ -109,7 +109,7 @@ off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);
* If the reverse index has not yet been loaded, or the position is out of
* bounds, this function aborts.
*
- * This function runs in time O(log N) with the number of objects in the MIDX.
+ * This function runs in constant time.
*/
uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
@@ -120,7 +120,7 @@ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
* If the reverse index has not yet been loaded, or the position is out of
* bounds, this function aborts.
*
- * This function runs in constant time.
+ * This function runs in time O(log N) with the number of objects in the MIDX.
*/
int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos);
diff --git a/packfile.c b/packfile.c
index 2aea4737b4..89402cfc69 100644
--- a/packfile.c
+++ b/packfile.c
@@ -339,6 +339,7 @@ void close_pack(struct packed_git *p)
close_pack_fd(p);
close_pack_index(p);
close_pack_revindex(p);
+ oidset_clear(&p->bad_objects);
}
void close_object_store(struct raw_object_store *o)
@@ -1161,31 +1162,19 @@ int unpack_object_header(struct packed_git *p,
return type;
}
-void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
+void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid)
{
- unsigned i;
- const unsigned hashsz = the_hash_algo->rawsz;
- for (i = 0; i < p->num_bad_objects; i++)
- if (hasheq(sha1, p->bad_object_sha1 + hashsz * i))
- return;
- p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
- st_mult(GIT_MAX_RAWSZ,
- st_add(p->num_bad_objects, 1)));
- hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, sha1);
- p->num_bad_objects++;
+ oidset_insert(&p->bad_objects, oid);
}
const struct packed_git *has_packed_and_bad(struct repository *r,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
struct packed_git *p;
- unsigned i;
for (p = r->objects->packed_git; p; p = p->next)
- for (i = 0; i < p->num_bad_objects; i++)
- if (hasheq(sha1,
- p->bad_object_sha1 + the_hash_algo->rawsz * i))
- return p;
+ if (oidset_contains(&p->bad_objects, oid))
+ return p;
return NULL;
}
@@ -1272,7 +1261,7 @@ static int retry_bad_packed_offset(struct repository *r,
if (offset_to_pack_pos(p, obj_offset, &pos) < 0)
return OBJ_BAD;
nth_packed_object_id(&oid, p, pack_pos_to_index(p, pos));
- mark_bad_packed_object(p, oid.hash);
+ mark_bad_packed_object(p, &oid);
type = oid_object_info(r, &oid, NULL);
if (type <= OBJ_NONE)
return OBJ_BAD;
@@ -1722,7 +1711,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
nth_packed_object_id(&oid, p, index_pos);
error("bad packed object CRC for %s",
oid_to_hex(&oid));
- mark_bad_packed_object(p, oid.hash);
+ mark_bad_packed_object(p, &oid);
data = NULL;
goto out;
}
@@ -1811,7 +1800,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset,
" at offset %"PRIuMAX" from %s",
oid_to_hex(&base_oid), (uintmax_t)obj_offset,
p->pack_name);
- mark_bad_packed_object(p, base_oid.hash);
+ mark_bad_packed_object(p, &base_oid);
base = read_object(r, &base_oid, &type, &base_size);
external_base = base;
}
@@ -2016,13 +2005,9 @@ static int fill_pack_entry(const struct object_id *oid,
{
off_t offset;
- if (p->num_bad_objects) {
- unsigned i;
- for (i = 0; i < p->num_bad_objects; i++)
- if (hasheq(oid->hash,
- p->bad_object_sha1 + the_hash_algo->rawsz * i))
- return 0;
- }
+ if (oidset_size(&p->bad_objects) &&
+ oidset_contains(&p->bad_objects, oid))
+ return 0;
offset = find_pack_entry_one(oid->hash, p);
if (!offset)
diff --git a/packfile.h b/packfile.h
index 3ae117a8ae..186146779d 100644
--- a/packfile.h
+++ b/packfile.h
@@ -159,8 +159,8 @@ int packed_object_info(struct repository *r,
struct packed_git *pack,
off_t offset, struct object_info *);
-void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
-const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
+void mark_bad_packed_object(struct packed_git *, const struct object_id *);
+const struct packed_git *has_packed_and_bad(struct repository *, const struct object_id *);
#define ON_DISK_KEEP_PACKS 1
#define IN_CORE_KEEP_PACKS 2
diff --git a/parallel-checkout.c b/parallel-checkout.c
index ddc0ff3c06..ed9c999520 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -603,8 +603,7 @@ static void gather_results_from_workers(struct pc_worker *workers,
continue;
if (pfd->revents & POLLIN) {
- int len = packet_read(pfd->fd, NULL, NULL,
- packet_buffer,
+ int len = packet_read(pfd->fd, packet_buffer,
sizeof(packet_buffer), 0);
if (len < 0) {
diff --git a/parse-options.c b/parse-options.c
index 2abff136a1..9a0484c883 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -8,10 +8,13 @@
static int disallow_abbreviated_options;
-#define OPT_SHORT 1
-#define OPT_UNSET 2
+enum opt_parsed {
+ OPT_LONG = 0,
+ OPT_SHORT = 1<<0,
+ OPT_UNSET = 1<<1,
+};
-int optbug(const struct option *opt, const char *reason)
+static int optbug(const struct option *opt, const char *reason)
{
if (opt->long_name) {
if (opt->short_name)
@@ -22,9 +25,26 @@ int optbug(const struct option *opt, const char *reason)
return error("BUG: switch '%c' %s", opt->short_name, reason);
}
+static const char *optname(const struct option *opt, enum opt_parsed flags)
+{
+ static struct strbuf sb = STRBUF_INIT;
+
+ strbuf_reset(&sb);
+ if (flags & OPT_SHORT)
+ strbuf_addf(&sb, "switch `%c'", opt->short_name);
+ else if (flags & OPT_UNSET)
+ strbuf_addf(&sb, "option `no-%s'", opt->long_name);
+ else if (flags == OPT_LONG)
+ strbuf_addf(&sb, "option `%s'", opt->long_name);
+ else
+ BUG("optname() got unknown flags %d", flags);
+
+ return sb.buf;
+}
+
static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
const struct option *opt,
- int flags, const char **arg)
+ enum opt_parsed flags, const char **arg)
{
if (p->opt) {
*arg = p->opt;
@@ -50,7 +70,7 @@ static void fix_filename(const char *prefix, const char **file)
static enum parse_opt_result opt_command_mode_error(
const struct option *opt,
const struct option *all_opts,
- int flags)
+ enum opt_parsed flags)
{
const struct option *that;
struct strbuf that_name = STRBUF_INIT;
@@ -82,7 +102,7 @@ static enum parse_opt_result opt_command_mode_error(
static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
const struct option *opt,
const struct option *all_opts,
- int flags)
+ enum opt_parsed flags)
{
const char *s, *arg;
const int unset = flags & OPT_UNSET;
@@ -298,11 +318,11 @@ static enum parse_opt_result parse_long_opt(
const struct option *all_opts = options;
const char *arg_end = strchrnul(arg, '=');
const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
- int abbrev_flags = 0, ambiguous_flags = 0;
+ enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
for (; options->type != OPTION_END; options++) {
const char *rest, *long_name = options->long_name;
- int flags = 0, opt_flags = 0;
+ enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG;
if (!long_name)
continue;
@@ -310,19 +330,6 @@ static enum parse_opt_result parse_long_opt(
again:
if (!skip_prefix(arg, long_name, &rest))
rest = NULL;
- if (options->type == OPTION_ARGUMENT) {
- if (!rest)
- continue;
- if (*rest == '=')
- return error(_("%s takes no value"),
- optname(options, flags));
- if (*rest)
- continue;
- if (options->value)
- *(int *)options->value = options->defval;
- p->out[p->cpidx++] = arg - 2;
- return PARSE_OPT_DONE;
- }
if (!rest) {
/* abbreviated? */
if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
@@ -494,7 +501,8 @@ static void parse_options_check(const struct option *opts)
static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, const char *prefix,
- const struct option *options, int flags)
+ const struct option *options,
+ enum parse_opt_flags flags)
{
ctx->argc = argc;
ctx->argv = argv;
@@ -519,7 +527,8 @@ static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, const char *prefix,
- const struct option *options, int flags)
+ const struct option *options,
+ enum parse_opt_flags flags)
{
memset(ctx, 0, sizeof(*ctx));
parse_options_start_1(ctx, argc, argv, prefix, options, flags);
@@ -710,13 +719,14 @@ static void free_preprocessed_options(struct option *options)
free(options);
}
-static int usage_with_options_internal(struct parse_opt_ctx_t *,
- const char * const *,
- const struct option *, int, int);
+static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *,
+ const char * const *,
+ const struct option *,
+ int, int);
-int parse_options_step(struct parse_opt_ctx_t *ctx,
- const struct option *options,
- const char * const usagestr[])
+enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
@@ -850,9 +860,11 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
return ctx->cpidx + ctx->argc;
}
-int parse_options(int argc, const char **argv, const char *prefix,
- const struct option *options, const char * const usagestr[],
- int flags)
+enum parse_opt_result parse_options(int argc, const char **argv,
+ const char *prefix,
+ const struct option *options,
+ const char * const usagestr[],
+ enum parse_opt_flags flags)
{
struct parse_opt_ctx_t ctx;
struct option *real_options;
@@ -874,7 +886,7 @@ int parse_options(int argc, const char **argv, const char *prefix,
case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
break;
- default: /* PARSE_OPT_UNKNOWN */
+ case PARSE_OPT_UNKNOWN:
if (ctx.argv[0][1] == '-') {
error(_("unknown option `%s'"), ctx.argv[0] + 2);
} else if (isascii(*ctx.opt)) {
@@ -910,32 +922,85 @@ static int usage_argh(const struct option *opts, FILE *outfile)
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
- const char * const *usagestr,
- const struct option *opts, int full, int err)
+static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts,
+ int full, int err)
{
FILE *outfile = err ? stderr : stdout;
int need_newline;
+ const char *usage_prefix = _("usage: %s");
+ /*
+ * The translation could be anything, but we can count on
+ * msgfmt(1)'s --check option to have asserted that "%s" is in
+ * the translation. So compute the length of the "usage: "
+ * part. We are assuming that the translator wasn't overly
+ * clever and used e.g. "%1$s" instead of "%s", there's only
+ * one "%s" in "usage_prefix" above, so there's no reason to
+ * do so even with a RTL language.
+ */
+ size_t usage_len = strlen(usage_prefix) - strlen("%s");
+ /*
+ * TRANSLATORS: the colon here should align with the
+ * one in "usage: %s" translation.
+ */
+ const char *or_prefix = _(" or: %s");
+ /*
+ * TRANSLATORS: You should only need to translate this format
+ * string if your language is a RTL language (e.g. Arabic,
+ * Hebrew etc.), not if it's a LTR language (e.g. German,
+ * Russian, Chinese etc.).
+ *
+ * When a translated usage string has an embedded "\n" it's
+ * because options have wrapped to the next line. The line
+ * after the "\n" will then be padded to align with the
+ * command name, such as N_("git cmd [opt]\n<8
+ * spaces>[opt2]"), where the 8 spaces are the same length as
+ * "git cmd ".
+ *
+ * This format string prints out that already-translated
+ * line. The "%*s" is whitespace padding to account for the
+ * padding at the start of the line that we add in this
+ * function. The "%s" is a line in the (hopefully already
+ * translated) N_() usage string, which contained embedded
+ * newlines before we split it up.
+ */
+ const char *usage_continued = _("%*s%s");
+ const char *prefix = usage_prefix;
+ int saw_empty_line = 0;
+
if (!usagestr)
return PARSE_OPT_HELP;
if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
fprintf(outfile, "cat <<\\EOF\n");
- fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
- while (*usagestr && **usagestr)
- /*
- * TRANSLATORS: the colon here should align with the
- * one in "usage: %s" translation.
- */
- fprintf_ln(outfile, _(" or: %s"), _(*usagestr++));
while (*usagestr) {
- if (**usagestr)
- fprintf_ln(outfile, _(" %s"), _(*usagestr));
- else
- fputc('\n', outfile);
- usagestr++;
+ const char *str = _(*usagestr++);
+ struct string_list list = STRING_LIST_INIT_DUP;
+ unsigned int j;
+
+ if (!saw_empty_line && !*str)
+ saw_empty_line = 1;
+
+ string_list_split(&list, str, '\n', -1);
+ for (j = 0; j < list.nr; j++) {
+ const char *line = list.items[j].string;
+
+ if (saw_empty_line && *line)
+ fprintf_ln(outfile, _(" %s"), line);
+ else if (saw_empty_line)
+ fputc('\n', outfile);
+ else if (!j)
+ fprintf_ln(outfile, prefix, line);
+ else
+ fprintf_ln(outfile, usage_continued,
+ (int)usage_len, "", line);
+ }
+ string_list_clear(&list, 0);
+
+ prefix = or_prefix;
}
need_newline = 1;
@@ -1013,18 +1078,3 @@ void NORETURN usage_msg_opt(const char *msg,
fprintf(stderr, "fatal: %s\n\n", msg);
usage_with_options(usagestr, options);
}
-
-const char *optname(const struct option *opt, int flags)
-{
- static struct strbuf sb = STRBUF_INIT;
-
- strbuf_reset(&sb);
- if (flags & OPT_SHORT)
- strbuf_addf(&sb, "switch `%c'", opt->short_name);
- else if (flags & OPT_UNSET)
- strbuf_addf(&sb, "option `no-%s'", opt->long_name);
- else
- strbuf_addf(&sb, "option `%s'", opt->long_name);
-
- return sb.buf;
-}
diff --git a/parse-options.h b/parse-options.h
index a845a9d952..bdea052c39 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -8,7 +8,6 @@
enum parse_opt_type {
/* special types */
OPTION_END,
- OPTION_ARGUMENT,
OPTION_GROUP,
OPTION_NUMBER,
OPTION_ALIAS,
@@ -34,6 +33,7 @@ enum parse_opt_flags {
PARSE_OPT_KEEP_UNKNOWN = 1 << 3,
PARSE_OPT_NO_INTERNAL_HELP = 1 << 4,
PARSE_OPT_ONE_SHOT = 1 << 5,
+ PARSE_OPT_SHELL_EVAL = 1 << 6,
};
enum parse_opt_option_flags {
@@ -45,7 +45,6 @@ enum parse_opt_option_flags {
PARSE_OPT_NODASH = 1 << 5,
PARSE_OPT_LITERAL_ARGHELP = 1 << 6,
PARSE_OPT_FROM_ALIAS = 1 << 7,
- PARSE_OPT_SHELL_EVAL = 1 << 8,
PARSE_OPT_NOCOMPLETE = 1 << 9,
PARSE_OPT_COMP_ARG = 1 << 10,
PARSE_OPT_CMDMODE = 1 << 11,
@@ -135,7 +134,7 @@ struct option {
const char *argh;
const char *help;
- int flags;
+ enum parse_opt_option_flags flags;
parse_opt_cb *callback;
intptr_t defval;
parse_opt_ll_cb *ll_callback;
@@ -155,8 +154,6 @@ struct option {
#define OPT_INTEGER_F(s, l, v, h, f) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
#define OPT_END() { OPTION_END }
-#define OPT_ARGUMENT(l, v, h) { OPTION_ARGUMENT, 0, (l), (v), NULL, \
- (h), PARSE_OPT_NOARG, NULL, 1 }
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
#define OPT_BIT(s, l, v, h, b) OPT_BIT_F(s, l, v, h, b, 0)
#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
@@ -169,8 +166,10 @@ struct option {
#define OPT_BOOL(s, l, v, h) OPT_BOOL_F(s, l, v, h, 0)
#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
(h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
-#define OPT_CMDMODE(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
+#define OPT_CMDMODE_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
+ (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
+#define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
+
#define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
#define OPT_MAGNITUDE(s, l, v, h) { OPTION_MAGNITUDE, (s), (l), (v), \
N_("n"), (h), PARSE_OPT_NONEG }
@@ -214,9 +213,11 @@ struct option {
* untouched and parse_options() returns the number of options
* processed.
*/
-int parse_options(int argc, const char **argv, const char *prefix,
- const struct option *options,
- const char * const usagestr[], int flags);
+enum parse_opt_result parse_options(int argc, const char **argv,
+ const char *prefix,
+ const struct option *options,
+ const char * const usagestr[],
+ enum parse_opt_flags flags);
NORETURN void usage_with_options(const char * const *usagestr,
const struct option *options);
@@ -225,9 +226,6 @@ NORETURN void usage_msg_opt(const char *msg,
const char * const *usagestr,
const struct option *options);
-int optbug(const struct option *opt, const char *reason);
-const char *optname(const struct option *opt, int flags);
-
/*
* Use these assertions for callbacks that expect to be called with NONEG and
* NOARG respectively, and do not otherwise handle the "unset" and "arg"
@@ -265,7 +263,7 @@ struct parse_opt_ctx_t {
const char **out;
int argc, cpidx, total;
const char *opt;
- int flags;
+ enum parse_opt_flags flags;
const char *prefix;
const char **alias_groups; /* must be in groups of 3 elements! */
struct option *updated_options;
@@ -273,11 +271,12 @@ struct parse_opt_ctx_t {
void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, const char *prefix,
- const struct option *options, int flags);
+ const struct option *options,
+ enum parse_opt_flags flags);
-int parse_options_step(struct parse_opt_ctx_t *ctx,
- const struct option *options,
- const char * const usagestr[]);
+enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[]);
int parse_options_end(struct parse_opt_ctx_t *ctx);
diff --git a/path.h b/path.h
index 251c78d980..b68691a86b 100644
--- a/path.h
+++ b/path.h
@@ -181,10 +181,7 @@ struct path_cache {
const char *shallow;
};
-#define PATH_CACHE_INIT \
- { \
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
- }
+#define PATH_CACHE_INIT { 0 }
const char *git_path_squash_msg(struct repository *r);
const char *git_path_merge_msg(struct repository *r);
diff --git a/pathspec.c b/pathspec.c
index 44306fdaca..ddeeba7911 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -39,7 +39,8 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
return;
for (i = 0; i < istate->cache_nr; i++) {
const struct cache_entry *ce = istate->cache[i];
- if (sw_action == PS_IGNORE_SKIP_WORKTREE && ce_skip_worktree(ce))
+ if (sw_action == PS_IGNORE_SKIP_WORKTREE &&
+ (ce_skip_worktree(ce) || !path_in_sparse_checkout(ce->name, istate)))
continue;
ce_path_match(istate, ce, pathspec, seen);
}
@@ -70,7 +71,7 @@ char *find_pathspecs_matching_skip_worktree(const struct pathspec *pathspec)
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
- if (ce_skip_worktree(ce))
+ if (ce_skip_worktree(ce) || !path_in_sparse_checkout(ce->name, istate))
ce_path_match(istate, ce, pathspec, seen);
}
diff --git a/pkt-line.c b/pkt-line.c
index de4a94b437..2dc8ac274b 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -289,22 +289,6 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
va_end(args);
}
-void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len)
-{
- size_t orig_len, n;
-
- orig_len = buf->len;
- strbuf_addstr(buf, "0000");
- strbuf_add(buf, data, len);
- n = buf->len - orig_len;
-
- if (n > LARGE_PACKET_MAX)
- die(_("protocol error: impossibly long line"));
-
- set_packet_header(&buf->buf[orig_len], n);
- packet_trace(data, len, 1);
-}
-
int write_packetized_from_fd_no_flush(int fd_in, int fd_out)
{
char *buf = xmalloc(LARGE_PACKET_DATA_MAX);
@@ -453,38 +437,28 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
return PACKET_READ_NORMAL;
}
-int packet_read(int fd, char **src_buffer, size_t *src_len,
- char *buffer, unsigned size, int options)
+int packet_read(int fd, char *buffer, unsigned size, int options)
{
int pktlen = -1;
- packet_read_with_status(fd, src_buffer, src_len, buffer, size,
- &pktlen, options);
+ packet_read_with_status(fd, NULL, NULL, buffer, size, &pktlen,
+ options);
return pktlen;
}
-static char *packet_read_line_generic(int fd,
- char **src, size_t *src_len,
- int *dst_len)
+char *packet_read_line(int fd, int *dst_len)
{
- int len = packet_read(fd, src, src_len,
- packet_buffer, sizeof(packet_buffer),
+ int len = packet_read(fd, packet_buffer, sizeof(packet_buffer),
PACKET_READ_CHOMP_NEWLINE);
if (dst_len)
*dst_len = len;
return (len > 0) ? packet_buffer : NULL;
}
-char *packet_read_line(int fd, int *len_p)
-{
- return packet_read_line_generic(fd, NULL, NULL, len_p);
-}
-
int packet_read_line_gently(int fd, int *dst_len, char **dst_line)
{
- int len = packet_read(fd, NULL, NULL,
- packet_buffer, sizeof(packet_buffer),
+ int len = packet_read(fd, packet_buffer, sizeof(packet_buffer),
PACKET_READ_CHOMP_NEWLINE|PACKET_READ_GENTLE_ON_EOF);
if (dst_len)
*dst_len = len;
@@ -493,11 +467,6 @@ int packet_read_line_gently(int fd, int *dst_len, char **dst_line)
return len;
}
-char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
-{
- return packet_read_line_generic(-1, src, src_len, dst_len);
-}
-
ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options)
{
int packet_len;
@@ -507,7 +476,7 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options)
for (;;) {
strbuf_grow(sb_out, LARGE_PACKET_DATA_MAX);
- packet_len = packet_read(fd_in, NULL, NULL,
+ packet_len = packet_read(fd_in,
/* strbuf_grow() above always allocates one extra byte to
* store a '\0' at the end of the string. packet_read()
* writes a '\0' extra byte at the end, too. Let it know
diff --git a/pkt-line.h b/pkt-line.h
index 82b95e4bdd..467ae01357 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -29,7 +29,6 @@ void packet_buf_delim(struct strbuf *buf);
void set_packet_header(char *buf, int size);
void packet_write(int fd_out, const char *buf, size_t size);
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
-void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len);
int packet_flush_gently(int fd);
int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
int write_packetized_from_fd_no_flush(int fd_in, int fd_out);
@@ -88,8 +87,7 @@ void packet_fflush(FILE *f);
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
#define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2)
#define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
-int packet_read(int fd, char **src_buffer, size_t *src_len, char
- *buffer, unsigned size, int options);
+int packet_read(int fd, char *buffer, unsigned size, int options);
/*
* Convert a four hex digit packet line length header into its numeric
@@ -139,12 +137,6 @@ char *packet_read_line(int fd, int *size);
int packet_read_line_gently(int fd, int *size, char **dst_line);
/*
- * Same as packet_read_line, but read from a buf rather than a descriptor;
- * see packet_read for details on how src_* is used.
- */
-char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
-
-/*
* Reads a stream of variable sized packets until a flush packet is detected.
*/
ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options);
diff --git a/pretty.c b/pretty.c
index 73b5ead509..fe95107ae5 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1436,8 +1436,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
check_commit_signature(c->commit, &(c->signature_check));
switch (placeholder[1]) {
case 'G':
- if (c->signature_check.gpg_output)
- strbuf_addstr(sb, c->signature_check.gpg_output);
+ if (c->signature_check.output)
+ strbuf_addstr(sb, c->signature_check.output);
break;
case '?':
switch (c->signature_check.result) {
diff --git a/read-cache.c b/read-cache.c
index f5d4385c40..f398659662 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -738,7 +738,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
int intent_only = flags & ADD_CACHE_INTENT;
int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
- int hash_flags = HASH_WRITE_OBJECT;
+ unsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;
struct object_id oid;
if (flags & ADD_CACHE_RENORMALIZE)
@@ -849,6 +849,19 @@ struct cache_entry *make_empty_transient_cache_entry(size_t len,
return xcalloc(1, cache_entry_size(len));
}
+enum verify_path_result {
+ PATH_OK,
+ PATH_INVALID,
+ PATH_DIR_WITH_SEP,
+};
+
+static enum verify_path_result verify_path_internal(const char *, unsigned);
+
+int verify_path(const char *path, unsigned mode)
+{
+ return verify_path_internal(path, mode) == PATH_OK;
+}
+
struct cache_entry *make_cache_entry(struct index_state *istate,
unsigned int mode,
const struct object_id *oid,
@@ -859,7 +872,7 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
struct cache_entry *ce, *ret;
int len;
- if (!verify_path(path, mode)) {
+ if (verify_path_internal(path, mode) == PATH_INVALID) {
error(_("invalid path '%s'"), path);
return NULL;
}
@@ -993,60 +1006,62 @@ static int verify_dotfile(const char *rest, unsigned mode)
return 1;
}
-int verify_path(const char *path, unsigned mode)
+static enum verify_path_result verify_path_internal(const char *path,
+ unsigned mode)
{
char c = 0;
if (has_dos_drive_prefix(path))
- return 0;
+ return PATH_INVALID;
if (!is_valid_path(path))
- return 0;
+ return PATH_INVALID;
goto inside;
for (;;) {
if (!c)
- return 1;
+ return PATH_OK;
if (is_dir_sep(c)) {
inside:
if (protect_hfs) {
if (is_hfs_dotgit(path))
- return 0;
+ return PATH_INVALID;
if (S_ISLNK(mode)) {
if (is_hfs_dotgitmodules(path))
- return 0;
+ return PATH_INVALID;
}
}
if (protect_ntfs) {
#if defined GIT_WINDOWS_NATIVE || defined __CYGWIN__
if (c == '\\')
- return 0;
+ return PATH_INVALID;
#endif
if (is_ntfs_dotgit(path))
- return 0;
+ return PATH_INVALID;
if (S_ISLNK(mode)) {
if (is_ntfs_dotgitmodules(path))
- return 0;
+ return PATH_INVALID;
}
}
c = *path++;
if ((c == '.' && !verify_dotfile(path, mode)) ||
is_dir_sep(c))
- return 0;
+ return PATH_INVALID;
/*
* allow terminating directory separators for
* sparse directory entries.
*/
if (c == '\0')
- return S_ISDIR(mode);
+ return S_ISDIR(mode) ? PATH_DIR_WITH_SEP :
+ PATH_INVALID;
} else if (c == '\\' && protect_ntfs) {
if (is_ntfs_dotgit(path))
- return 0;
+ return PATH_INVALID;
if (S_ISLNK(mode)) {
if (is_ntfs_dotgitmodules(path))
- return 0;
+ return PATH_INVALID;
}
}
@@ -1349,7 +1364,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
if (!ok_to_add)
return -1;
- if (!verify_path(ce->name, ce->ce_mode))
+ if (verify_path_internal(ce->name, ce->ce_mode) == PATH_INVALID)
return error(_("invalid path '%s'"), ce->name);
if (!skip_df_check &&
@@ -1944,13 +1959,22 @@ static void tweak_untracked_cache(struct index_state *istate)
prepare_repo_settings(r);
- if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) {
+ switch (r->settings.core_untracked_cache) {
+ case UNTRACKED_CACHE_REMOVE:
remove_untracked_cache(istate);
- return;
- }
-
- if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
+ break;
+ case UNTRACKED_CACHE_WRITE:
add_untracked_cache(istate);
+ break;
+ case UNTRACKED_CACHE_KEEP:
+ /*
+ * Either an explicit "core.untrackedCache=keep", the
+ * default if "core.untrackedCache" isn't configured,
+ * or a fallback on an unknown "core.untrackedCache"
+ * value.
+ */
+ break;
+ }
}
static void tweak_split_index(struct index_state *istate)
@@ -2391,9 +2415,21 @@ int read_index_from(struct index_state *istate, const char *path,
base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
trace2_region_enter_printf("index", "shared/do_read_index",
the_repository, "%s", base_path);
- ret = do_read_index(split_index->base, base_path, 1);
+ ret = do_read_index(split_index->base, base_path, 0);
trace2_region_leave_printf("index", "shared/do_read_index",
the_repository, "%s", base_path);
+ if (!ret) {
+ char *path_copy = xstrdup(path);
+ const char *base_path2 = xstrfmt("%s/sharedindex.%s",
+ dirname(path_copy),
+ base_oid_hex);
+ free(path_copy);
+ trace2_region_enter_printf("index", "shared/do_read_index",
+ the_repository, "%s", base_path2);
+ ret = do_read_index(split_index->base, base_path2, 1);
+ trace2_region_leave_printf("index", "shared/do_read_index",
+ the_repository, "%s", base_path2);
+ }
if (!oideq(&split_index->base_oid, &split_index->base->oid))
die(_("broken index, expect %s in %s, got %s"),
base_oid_hex, base_path,
@@ -2812,11 +2848,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
}
}
- if (!istate->version) {
+ if (!istate->version)
istate->version = get_index_format_default(the_repository);
- if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0))
- init_split_index(istate);
- }
/* demote version 3 to version 2 when the latter suffices */
if (istate->version == 3 || istate->version == 2)
@@ -3243,7 +3276,7 @@ static int too_many_not_shared_entries(struct index_state *istate)
int write_locked_index(struct index_state *istate, struct lock_file *lock,
unsigned flags)
{
- int new_shared_index, ret;
+ int new_shared_index, ret, test_split_index_env;
struct split_index *si = istate->split_index;
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
@@ -3258,7 +3291,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
if (istate->fsmonitor_last_update)
fill_fsmonitor_bitmap(istate);
- if (!si || alternate_index_output ||
+ test_split_index_env = git_env_bool("GIT_TEST_SPLIT_INDEX", 0);
+
+ if ((!si && !test_split_index_env) ||
+ alternate_index_output ||
(istate->cache_changed & ~EXTMASK)) {
if (si)
oidclr(&si->base_oid);
@@ -3266,10 +3302,15 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
goto out;
}
- if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) {
- int v = si->base_oid.hash[0];
- if ((v & 15) < 6)
+ if (test_split_index_env) {
+ if (!si) {
+ si = init_split_index(istate);
istate->cache_changed |= SPLIT_INDEX_ORDERED;
+ } else {
+ int v = si->base_oid.hash[0];
+ if ((v & 15) < 6)
+ istate->cache_changed |= SPLIT_INDEX_ORDERED;
+ }
}
if (too_many_not_shared_entries(istate))
istate->cache_changed |= SPLIT_INDEX_ORDERED;
diff --git a/rebase-interactive.c b/rebase-interactive.c
index b6cbd16a17..87649d0c01 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -226,32 +226,3 @@ int todo_list_check_against_backup(struct repository *r, struct todo_list *todo_
todo_list_release(&backup);
return res;
}
-
-int check_todo_list_from_file(struct repository *r)
-{
- struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
- int res = 0;
-
- if (strbuf_read_file(&new_todo.buf, rebase_path_todo(), 0) < 0) {
- res = error(_("could not read '%s'."), rebase_path_todo());
- goto out;
- }
-
- if (strbuf_read_file(&old_todo.buf, rebase_path_todo_backup(), 0) < 0) {
- res = error(_("could not read '%s'."), rebase_path_todo_backup());
- goto out;
- }
-
- res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
- if (!res)
- res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
- if (res)
- fprintf(stderr, _(edit_todo_list_advice));
- if (!res)
- res = todo_list_check(&old_todo, &new_todo);
-out:
- todo_list_release(&old_todo);
- todo_list_release(&new_todo);
-
- return res;
-}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index dc2cf0ee12..7239c60f79 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -16,6 +16,4 @@ int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
int todo_list_check_against_backup(struct repository *r,
struct todo_list *todo_list);
-int check_todo_list_from_file(struct repository *r);
-
#endif
diff --git a/rebase.c b/rebase.c
index f8137d859b..6775cddb28 100644
--- a/rebase.c
+++ b/rebase.c
@@ -1,5 +1,6 @@
#include "rebase.h"
#include "config.h"
+#include "gettext.h"
/*
* Parses textual value for pull.rebase, branch.<name>.rebase, etc.
@@ -20,12 +21,12 @@ enum rebase_type rebase_parse_value(const char *value)
return REBASE_FALSE;
else if (v > 0)
return REBASE_TRUE;
- else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
- return REBASE_PRESERVE;
else if (!strcmp(value, "merges") || !strcmp(value, "m"))
return REBASE_MERGES;
else if (!strcmp(value, "interactive") || !strcmp(value, "i"))
return REBASE_INTERACTIVE;
+ else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
+ error(_("%s: 'preserve' superseded by 'merges'"), value);
/*
* Please update _git_config() in git-completion.bash when you
* add new rebase modes.
diff --git a/rebase.h b/rebase.h
index cc723d4748..203b437282 100644
--- a/rebase.h
+++ b/rebase.h
@@ -5,7 +5,6 @@ enum rebase_type {
REBASE_INVALID = -1,
REBASE_FALSE = 0,
REBASE_TRUE,
- REBASE_PRESERVE,
REBASE_MERGES,
REBASE_INTERACTIVE
};
diff --git a/ref-filter.c b/ref-filter.c
index 93ce2a6ef2..add429be79 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -633,7 +633,7 @@ static struct {
*/
};
-#define REF_FORMATTING_STATE_INIT { 0, NULL }
+#define REF_FORMATTING_STATE_INIT { 0 }
struct ref_formatting_stack {
struct ref_formatting_stack *prev;
@@ -2100,8 +2100,7 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
*/
static int for_each_fullref_in_pattern(struct ref_filter *filter,
each_ref_fn cb,
- void *cb_data,
- int broken)
+ void *cb_data)
{
if (!filter->match_as_path) {
/*
@@ -2109,7 +2108,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* prefixes like "refs/heads/" etc. are stripped off,
* so we have to look at everything:
*/
- return for_each_fullref_in("", cb, cb_data, broken);
+ return for_each_fullref_in("", cb, cb_data);
}
if (filter->ignore_case) {
@@ -2118,16 +2117,16 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
* so just return everything and let the caller
* sort it out.
*/
- return for_each_fullref_in("", cb, cb_data, broken);
+ return for_each_fullref_in("", cb, cb_data);
}
if (!filter->name_patterns[0]) {
/* no patterns; we have to look at everything */
- return for_each_fullref_in("", cb, cb_data, broken);
+ return for_each_fullref_in("", cb, cb_data);
}
return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
- cb, cb_data, broken);
+ cb, cb_data);
}
/*
@@ -2405,13 +2404,10 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
{
struct ref_filter_cbdata ref_cbdata;
int ret = 0;
- unsigned int broken = 0;
ref_cbdata.array = array;
ref_cbdata.filter = filter;
- if (type & FILTER_REFS_INCLUDE_BROKEN)
- broken = 1;
filter->kind = type & FILTER_REFS_KIND_MASK;
init_contains_cache(&ref_cbdata.contains_cache);
@@ -2428,13 +2424,13 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
* of filter_ref_kind().
*/
if (filter->kind == FILTER_REFS_BRANCHES)
- ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken);
+ ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata);
else if (filter->kind == FILTER_REFS_REMOTES)
- ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken);
+ ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata);
else if (filter->kind == FILTER_REFS_TAGS)
- ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken);
+ ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata);
else if (filter->kind & FILTER_REFS_ALL)
- ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken);
+ ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata);
if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
head_ref(ref_filter_handler, &ref_cbdata);
}
diff --git a/ref-filter.h b/ref-filter.h
index c15dee8d6b..b636f4389d 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -13,7 +13,6 @@
#define QUOTE_PYTHON 4
#define QUOTE_TCL 8
-#define FILTER_REFS_INCLUDE_BROKEN 0x0001
#define FILTER_REFS_TAGS 0x0002
#define FILTER_REFS_BRANCHES 0x0004
#define FILTER_REFS_REMOTES 0x0008
diff --git a/reflog-walk.c b/reflog-walk.c
index e9cd328369..8ac4b284b6 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -158,10 +158,9 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
}
reflogs = read_complete_reflog(branch);
if (!reflogs || reflogs->nr == 0) {
- struct object_id oid;
char *b;
int ret = dwim_log(branch, strlen(branch),
- &oid, &b);
+ NULL, &b);
if (ret > 1)
free(b);
else if (ret == 1) {
diff --git a/refs.c b/refs.c
index 8b9f7c3a80..d7cc0a23a3 100644
--- a/refs.c
+++ b/refs.c
@@ -10,6 +10,7 @@
#include "refs.h"
#include "refs/refs-internal.h"
#include "run-command.h"
+#include "hook.h"
#include "object-store.h"
#include "object.h"
#include "tag.h"
@@ -33,11 +34,6 @@ static struct ref_storage_be *find_ref_storage_backend(const char *name)
return NULL;
}
-int ref_storage_backend_exists(const char *name)
-{
- return find_ref_storage_backend(name) != NULL;
-}
-
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refs
@@ -255,12 +251,13 @@ int refname_is_safe(const char *refname)
* does not exist, emit a warning and return false.
*/
int ref_resolves_to_object(const char *refname,
+ struct repository *repo,
const struct object_id *oid,
unsigned int flags)
{
if (flags & REF_ISBROKEN)
return 0;
- if (!has_object_file(oid)) {
+ if (!repo_has_object_file(repo, oid)) {
error(_("%s does not point to a valid object!"), refname);
return 0;
}
@@ -698,7 +695,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
strbuf_addf(&path, *p, len, str);
ref = refs_resolve_ref_unsafe(refs, path.buf,
RESOLVE_REF_READING,
- &hash, NULL);
+ oid ? &hash : NULL, NULL);
if (!ref)
continue;
if (refs_reflog_exists(refs, path.buf))
@@ -710,7 +707,8 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
continue;
if (!logs_found++) {
*log = xstrdup(it);
- oidcpy(oid, &hash);
+ if (oid)
+ oidcpy(oid, &hash);
}
if (!warn_ambiguous_refs)
break;
@@ -1413,14 +1411,21 @@ int head_ref(each_ref_fn fn, void *cb_data)
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
- const char *prefix, int trim, int flags)
+ const char *prefix, int trim,
+ enum do_for_each_ref_flags flags)
{
struct ref_iterator *iter;
- if (ref_paranoia < 0)
- ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
- if (ref_paranoia)
- flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+ if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+ static int ref_paranoia = -1;
+
+ if (ref_paranoia < 0)
+ ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1);
+ if (ref_paranoia) {
+ flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+ flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS;
+ }
+ }
iter = refs->be->iterator_begin(refs, prefix, flags);
@@ -1479,7 +1484,8 @@ static int do_for_each_ref_helper(struct repository *r,
}
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, int trim, int flags, void *cb_data)
+ each_ref_fn fn, int trim,
+ enum do_for_each_ref_flags flags, void *cb_data)
{
struct ref_iterator *iter;
struct do_for_each_ref_help hp = { fn, cb_data };
@@ -1514,25 +1520,16 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
}
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
{
- unsigned int flag = 0;
-
- if (broken)
- flag = DO_FOR_EACH_INCLUDE_BROKEN;
return do_for_each_ref(get_main_ref_store(the_repository),
- prefix, fn, 0, flag, cb_data);
+ prefix, fn, 0, 0, cb_data);
}
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data,
- unsigned int broken)
+ each_ref_fn fn, void *cb_data)
{
- unsigned int flag = 0;
-
- if (broken)
- flag = DO_FOR_EACH_INCLUDE_BROKEN;
- return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
+ return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
}
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@@ -1624,8 +1621,7 @@ static void find_longest_prefixes(struct string_list *out,
int for_each_fullref_in_prefixes(const char *namespace,
const char **patterns,
- each_ref_fn fn, void *cb_data,
- unsigned int broken)
+ each_ref_fn fn, void *cb_data)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
@@ -1640,7 +1636,7 @@ int for_each_fullref_in_prefixes(const char *namespace,
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
- ret = for_each_fullref_in(buf.buf, fn, cb_data, broken);
+ ret = for_each_fullref_in(buf.buf, fn, cb_data);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
@@ -1681,7 +1677,7 @@ int refs_read_raw_ref(struct ref_store *ref_store,
}
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
- type);
+ type, &errno);
}
/* This function needs to return a meaningful errno on failure */
@@ -1875,7 +1871,8 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,
* Create, record, and return a ref_store instance for the specified
* gitdir.
*/
-static struct ref_store *ref_store_init(const char *gitdir,
+static struct ref_store *ref_store_init(struct repository *repo,
+ const char *gitdir,
unsigned int flags)
{
const char *be_name = "files";
@@ -1885,7 +1882,7 @@ static struct ref_store *ref_store_init(const char *gitdir,
if (!be)
BUG("reference backend %s is unknown", be_name);
- refs = be->init(gitdir, flags);
+ refs = be->init(repo, gitdir, flags);
return refs;
}
@@ -1897,7 +1894,7 @@ struct ref_store *get_main_ref_store(struct repository *r)
if (!r->gitdir)
BUG("attempting to get main_ref_store outside of repository");
- r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r, r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
@@ -1927,6 +1924,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
struct ref_store *refs;
char *to_free = NULL;
size_t len;
+ struct repository *subrepo;
if (!submodule)
return NULL;
@@ -1952,8 +1950,19 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
if (submodule_to_gitdir(&submodule_sb, submodule))
goto done;
- /* assume that add_submodule_odb() has been called */
- refs = ref_store_init(submodule_sb.buf,
+ subrepo = xmalloc(sizeof(*subrepo));
+ /*
+ * NEEDSWORK: Make get_submodule_ref_store() work with arbitrary
+ * superprojects other than the_repository. This probably should be
+ * done by making it take a struct repository * parameter instead of a
+ * submodule path.
+ */
+ if (repo_submodule_init(subrepo, the_repository, submodule,
+ null_oid())) {
+ free(subrepo);
+ goto done;
+ }
+ refs = ref_store_init(subrepo, submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
register_ref_store_map(&submodule_ref_stores, "submodule",
refs, submodule);
@@ -1979,10 +1988,12 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
return refs;
if (wt->id)
- refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
+ refs = ref_store_init(the_repository,
+ git_common_path("worktrees/%s", wt->id),
REF_STORE_ALL_CAPS);
else
- refs = ref_store_init(get_git_common_dir(),
+ refs = ref_store_init(the_repository,
+ get_git_common_dir(),
REF_STORE_ALL_CAPS);
if (refs)
@@ -2370,19 +2381,19 @@ int delete_reflog(const char *refname)
}
int refs_reflog_expire(struct ref_store *refs,
- const char *refname, const struct object_id *oid,
+ 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)
{
- return refs->be->reflog_expire(refs, refname, oid, flags,
+ return refs->be->reflog_expire(refs, refname, flags,
prepare_fn, should_prune_fn,
cleanup_fn, policy_cb_data);
}
-int reflog_expire(const char *refname, const struct object_id *oid,
+int reflog_expire(const char *refname,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
@@ -2390,7 +2401,7 @@ int reflog_expire(const char *refname, const struct object_id *oid,
void *policy_cb_data)
{
return refs_reflog_expire(get_main_ref_store(the_repository),
- refname, oid, flags,
+ refname, flags,
prepare_fn, should_prune_fn,
cleanup_fn, policy_cb_data);
}
diff --git a/refs.h b/refs.h
index 48970dfc7e..d5099d4984 100644
--- a/refs.h
+++ b/refs.h
@@ -342,10 +342,8 @@ int for_each_ref(each_ref_fn fn, void *cb_data);
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
- each_ref_fn fn, void *cb_data,
- unsigned int broken);
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
- unsigned int broken);
+ each_ref_fn fn, void *cb_data);
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
/**
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
@@ -354,8 +352,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
* callers should be prepared to ignore references that they did not ask for.
*/
int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
- each_ref_fn fn, void *cb_data,
- unsigned int broken);
+ each_ref_fn fn, void *cb_data);
/**
* iterate refs from the respective area.
*/
@@ -796,7 +793,7 @@ enum expire_reflog_flags {
* expiration policy that is desired.
*
* reflog_expiry_prepare_fn -- Called once after the reference is
- * locked.
+ * locked. Called with the OID of the locked reference.
*
* reflog_expiry_should_prune_fn -- Called once for each entry in the
* existing reflog. It should return true iff that entry should be
@@ -816,28 +813,25 @@ typedef int reflog_expiry_should_prune_fn(struct object_id *ooid,
typedef void reflog_expiry_cleanup_fn(void *cb_data);
/*
- * Expire reflog entries for the specified reference. oid is the old
- * value of the reference. flags is a combination of the constants in
+ * Expire reflog entries for the specified reference.
+ * flags is a combination of the constants in
* enum expire_reflog_flags. The three function pointers are described
* above. On success, return zero.
*/
int refs_reflog_expire(struct ref_store *refs,
const char *refname,
- const struct object_id *oid,
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);
-int reflog_expire(const char *refname, const struct object_id *oid,
+int reflog_expire(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);
-int ref_storage_backend_exists(const char *name);
-
struct ref_store *get_main_ref_store(struct repository *r);
/**
diff --git a/refs/debug.c b/refs/debug.c
index 1a7a9e11cf..8667c64023 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -239,15 +239,14 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct object_id *oid, struct strbuf *referent,
- unsigned int *type)
+ unsigned int *type, int *failure_errno)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res = 0;
oidcpy(oid, null_oid());
- errno = 0;
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
- type);
+ type, failure_errno);
if (res == 0) {
trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
@@ -255,7 +254,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
} else {
trace_printf_key(&trace_refs,
"read_raw_ref: %s: %d (errno %d)\n", refname,
- res, errno);
+ res, *failure_errno);
}
return res;
}
@@ -365,8 +364,8 @@ struct debug_reflog_expiry_should_prune {
};
static void debug_reflog_expiry_prepare(const char *refname,
- const struct object_id *oid,
- void *cb_data)
+ const struct object_id *oid,
+ void *cb_data)
{
struct debug_reflog_expiry_should_prune *prune = cb_data;
trace_printf_key(&trace_refs, "reflog_expire_prepare: %s\n", refname);
@@ -392,7 +391,7 @@ static void debug_reflog_expiry_cleanup(void *cb_data)
}
static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
- const struct object_id *oid, unsigned int flags,
+ unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
reflog_expiry_cleanup_fn cleanup_fn,
@@ -405,7 +404,7 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
.should_prune = should_prune_fn,
.cb_data = policy_cb_data,
};
- int res = drefs->refs->be->reflog_expire(drefs->refs, refname, oid,
+ int res = drefs->refs->be->reflog_expire(drefs->refs, refname,
flags, &debug_reflog_expiry_prepare,
&debug_reflog_expiry_should_prune_fn,
&debug_reflog_expiry_cleanup,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 74c0385873..151b0056fe 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -79,13 +79,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
* Create a new submodule ref cache and add it to the internal
* set of caches.
*/
-static struct ref_store *files_ref_store_create(const char *gitdir,
+static struct ref_store *files_ref_store_create(struct repository *repo,
+ const char *gitdir,
unsigned int flags)
{
struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
struct strbuf sb = STRBUF_INIT;
+ ref_store->repo = repo;
ref_store->gitdir = xstrdup(gitdir);
base_ref_store_init(ref_store, &refs_be_files);
refs->store_flags = flags;
@@ -93,7 +95,7 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
- refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+ refs->packed_ref_store = packed_ref_store_create(repo, sb.buf, flags);
strbuf_release(&sb);
chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
@@ -227,7 +229,7 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir
pos = search_ref_dir(dir, prefix, prefix_len);
if (pos >= 0)
continue;
- child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+ child_entry = create_dir_entry(dir->cache, prefix, prefix_len);
add_entry_to_dir(dir, child_entry);
}
}
@@ -278,7 +280,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_addch(&refname, '/');
add_entry_to_dir(dir,
create_dir_entry(dir->cache, refname.buf,
- refname.len, 1));
+ refname.len));
} else {
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
@@ -336,14 +338,14 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
* lazily):
*/
add_entry_to_dir(get_ref_dir(refs->loose->root),
- create_dir_entry(refs->loose, "refs/", 5, 1));
+ create_dir_entry(refs->loose, "refs/", 5));
}
return refs->loose;
}
-static int files_read_raw_ref(struct ref_store *ref_store,
- const char *refname, struct object_id *oid,
- struct strbuf *referent, unsigned int *type)
+static int files_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 files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@@ -354,7 +356,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
struct stat st;
int fd;
int ret = -1;
- int save_errno;
int remaining_retries = 3;
*type = 0;
@@ -459,10 +460,9 @@ stat_ref:
ret = parse_loose_ref_contents(buf, oid, referent, type);
out:
- save_errno = errno;
+ *failure_errno = errno;
strbuf_release(&sb_path);
strbuf_release(&sb_contents);
- errno = save_errno;
return ret;
}
@@ -531,7 +531,6 @@ static void unlock_ref(struct ref_lock *lock)
static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist,
const struct string_list *extras,
- const struct string_list *skip,
struct ref_lock **lock_p,
struct strbuf *referent,
unsigned int *type,
@@ -541,6 +540,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
struct strbuf ref_file = STRBUF_INIT;
int attempts_remaining = 3;
int ret = TRANSACTION_GENERIC_ERROR;
+ int failure_errno;
assert(err);
files_assert_main_repository(refs, "lock_raw_ref");
@@ -568,7 +568,7 @@ retry:
* reason to expect this error to be transitory.
*/
if (refs_verify_refname_available(&refs->base, refname,
- extras, skip, err)) {
+ extras, NULL, err)) {
if (mustexist) {
/*
* To the user the relevant error is
@@ -611,7 +611,9 @@ retry:
if (hold_lock_file_for_update_timeout(
&lock->lk, ref_file.buf, LOCK_NO_DEREF,
get_files_ref_lock_timeout_ms()) < 0) {
- if (errno == ENOENT && --attempts_remaining > 0) {
+ int myerr = errno;
+ errno = 0;
+ if (myerr == ENOENT && --attempts_remaining > 0) {
/*
* Maybe somebody just deleted one of the
* directories leading to ref_file. Try
@@ -619,7 +621,7 @@ retry:
*/
goto retry;
} else {
- unable_to_lock_message(ref_file.buf, errno, err);
+ unable_to_lock_message(ref_file.buf, myerr, err);
goto error_return;
}
}
@@ -629,9 +631,9 @@ retry:
* fear that its value will change.
*/
- if (files_read_raw_ref(&refs->base, refname,
- &lock->old_oid, referent, type)) {
- if (errno == ENOENT) {
+ if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
+ type, &failure_errno)) {
+ if (failure_errno == ENOENT) {
if (mustexist) {
/* Garden variety missing reference. */
strbuf_addf(err, "unable to resolve reference '%s'",
@@ -655,7 +657,7 @@ retry:
* reference named "refs/foo/bar/baz".
*/
}
- } else if (errno == EISDIR) {
+ } else if (failure_errno == EISDIR) {
/*
* There is a directory in the way. It might have
* contained references that have been deleted. If
@@ -673,7 +675,7 @@ retry:
REMOVE_DIR_EMPTY_ONLY)) {
if (refs_verify_refname_available(
&refs->base, refname,
- extras, skip, err)) {
+ extras, NULL, err)) {
/*
* The error message set by
* verify_refname_available() is OK.
@@ -693,13 +695,13 @@ retry:
goto error_return;
}
}
- } else if (errno == EINVAL && (*type & REF_ISBROKEN)) {
+ } else if (failure_errno == EINVAL && (*type & REF_ISBROKEN)) {
strbuf_addf(err, "unable to resolve reference '%s': "
"reference broken", refname);
goto error_return;
} else {
strbuf_addf(err, "unable to resolve reference '%s': %s",
- refname, strerror(errno));
+ refname, strerror(failure_errno));
goto error_return;
}
@@ -710,7 +712,7 @@ retry:
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, skip, err))
+ extras, NULL, err))
goto error_return;
}
@@ -730,6 +732,7 @@ struct files_ref_iterator {
struct ref_iterator base;
struct ref_iterator *iter0;
+ struct repository *repo;
unsigned int flags;
};
@@ -744,8 +747,14 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
continue;
+ if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
+ (iter->iter0->flags & REF_ISSYMREF) &&
+ (iter->iter0->flags & REF_ISBROKEN))
+ continue;
+
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
!ref_resolves_to_object(iter->iter0->refname,
+ iter->repo,
iter->iter0->oid,
iter->iter0->flags))
continue;
@@ -824,7 +833,7 @@ static struct ref_iterator *files_ref_iterator_begin(
*/
loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
- prefix, 1);
+ prefix, ref_store->repo, 1);
/*
* The packed-refs file might contain broken references, for
@@ -848,45 +857,119 @@ static struct ref_iterator *files_ref_iterator_begin(
base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
overlay_iter->ordered);
iter->iter0 = overlay_iter;
+ iter->repo = ref_store->repo;
iter->flags = flags;
return ref_iterator;
}
/*
- * Verify that the reference locked by lock has the value old_oid
- * (unless it is NULL). Fail if the reference doesn't exist and
- * mustexist is set. Return 0 on success. On error, write an error
- * message to err, set errno, and return a negative value.
+ * Callback function for raceproof_create_file(). This function is
+ * expected to do something that makes dirname(path) permanent despite
+ * the fact that other processes might be cleaning up empty
+ * directories at the same time. Usually it will create a file named
+ * path, but alternatively it could create another file in that
+ * directory, or even chdir() into that directory. The function should
+ * return 0 if the action was completed successfully. On error, it
+ * should return a nonzero result and set errno.
+ * raceproof_create_file() treats two errno values specially:
+ *
+ * - ENOENT -- dirname(path) does not exist. In this case,
+ * raceproof_create_file() tries creating dirname(path)
+ * (and any parent directories, if necessary) and calls
+ * the function again.
+ *
+ * - EISDIR -- the file already exists and is a directory. In this
+ * case, raceproof_create_file() removes the directory if
+ * it is empty (and recursively any empty directories that
+ * it contains) and calls the function again.
+ *
+ * Any other errno causes raceproof_create_file() to fail with the
+ * callback's return value and errno.
+ *
+ * Obviously, this function should be OK with being called again if it
+ * fails with ENOENT or EISDIR. In other scenarios it will not be
+ * called again.
+ */
+typedef int create_file_fn(const char *path, void *cb);
+
+/*
+ * Create a file in dirname(path) by calling fn, creating leading
+ * directories if necessary. Retry a few times in case we are racing
+ * with another process that is trying to clean up the directory that
+ * contains path. See the documentation for create_file_fn for more
+ * details.
+ *
+ * Return the value and set the errno that resulted from the most
+ * recent call of fn. fn is always called at least once, and will be
+ * called more than once if it returns ENOENT or EISDIR.
*/
-static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
- const struct object_id *old_oid, int mustexist,
- struct strbuf *err)
+static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
{
- assert(err);
+ /*
+ * The number of times we will try to remove empty directories
+ * in the way of path. This is only 1 because if another
+ * process is racily creating directories that conflict with
+ * us, we don't want to fight against them.
+ */
+ int remove_directories_remaining = 1;
- if (refs_read_ref_full(ref_store, lock->ref_name,
- mustexist ? RESOLVE_REF_READING : 0,
- &lock->old_oid, NULL)) {
- if (old_oid) {
- int save_errno = errno;
- strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
- errno = save_errno;
- return -1;
- } else {
- oidclr(&lock->old_oid);
- return 0;
- }
- }
- if (old_oid && !oideq(&lock->old_oid, old_oid)) {
- strbuf_addf(err, "ref '%s' is at %s but expected %s",
- lock->ref_name,
- oid_to_hex(&lock->old_oid),
- oid_to_hex(old_oid));
- errno = EBUSY;
- return -1;
+ /*
+ * The number of times that we will try to create the
+ * directories containing path. We are willing to attempt this
+ * more than once, because another process could be trying to
+ * clean up empty directories at the same time as we are
+ * trying to create them.
+ */
+ int create_directories_remaining = 3;
+
+ /* A scratch copy of path, filled lazily if we need it: */
+ struct strbuf path_copy = STRBUF_INIT;
+
+ int ret, save_errno;
+
+ /* Sanity check: */
+ assert(*path);
+
+retry_fn:
+ ret = fn(path, cb);
+ save_errno = errno;
+ if (!ret)
+ goto out;
+
+ if (errno == EISDIR && remove_directories_remaining-- > 0) {
+ /*
+ * A directory is in the way. Maybe it is empty; try
+ * to remove it:
+ */
+ if (!path_copy.len)
+ strbuf_addstr(&path_copy, path);
+
+ if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
+ goto retry_fn;
+ } else if (errno == ENOENT && create_directories_remaining-- > 0) {
+ /*
+ * Maybe the containing directory didn't exist, or
+ * maybe it was just deleted by a process that is
+ * racing with us to clean up empty directories. Try
+ * to create it:
+ */
+ enum scld_error scld_result;
+
+ if (!path_copy.len)
+ strbuf_addstr(&path_copy, path);
+
+ do {
+ scld_result = safe_create_leading_directories(path_copy.buf);
+ if (scld_result == SCLD_OK)
+ goto retry_fn;
+ } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
}
- return 0;
+
+out:
+ strbuf_release(&path_copy);
+ errno = save_errno;
+ return ret;
}
static int remove_empty_directories(struct strbuf *path)
@@ -910,64 +993,27 @@ static int create_reflock(const char *path, void *cb)
/*
* Locks a ref returning the lock on success and NULL on failure.
- * On failure errno is set to something meaningful.
*/
static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
- const char *refname,
- const struct object_id *old_oid,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int flags, int *type,
+ const char *refname, int *type,
struct strbuf *err)
{
struct strbuf ref_file = STRBUF_INIT;
struct ref_lock *lock;
- int last_errno = 0;
- int mustexist = (old_oid && !is_null_oid(old_oid));
- int resolve_flags = RESOLVE_REF_NO_RECURSE;
- int resolved;
files_assert_main_repository(refs, "lock_ref_oid_basic");
assert(err);
CALLOC_ARRAY(lock, 1);
- if (mustexist)
- resolve_flags |= RESOLVE_REF_READING;
- if (flags & REF_DELETING)
- resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
-
files_ref_path(refs, &ref_file, refname);
- resolved = !!refs_resolve_ref_unsafe(&refs->base,
- refname, resolve_flags,
- &lock->old_oid, type);
- if (!resolved && errno == EISDIR) {
- /*
- * we are trying to lock foo but we used to
- * have foo/bar which now does not exist;
- * it is normal for the empty directory 'foo'
- * to remain.
- */
- if (remove_empty_directories(&ref_file)) {
- last_errno = errno;
- if (!refs_verify_refname_available(
- &refs->base,
- refname, extras, skip, err))
- strbuf_addf(err, "there are still refs under '%s'",
- refname);
- goto error_return;
- }
- resolved = !!refs_resolve_ref_unsafe(&refs->base,
- refname, resolve_flags,
- &lock->old_oid, type);
- }
- if (!resolved) {
- last_errno = errno;
- if (last_errno != ENOTDIR ||
- !refs_verify_refname_available(&refs->base, refname,
- extras, skip, err))
+ if (!refs_resolve_ref_unsafe(&refs->base, refname,
+ RESOLVE_REF_NO_RECURSE,
+ &lock->old_oid, type)) {
+ if (!refs_verify_refname_available(&refs->base, refname,
+ NULL, NULL, err))
strbuf_addf(err, "unable to resolve reference '%s': %s",
- refname, strerror(last_errno));
+ refname, strerror(errno));
goto error_return;
}
@@ -980,23 +1026,20 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
*/
if (is_null_oid(&lock->old_oid) &&
refs_verify_refname_available(refs->packed_ref_store, refname,
- extras, skip, err)) {
- last_errno = ENOTDIR;
+ NULL, NULL, err))
goto error_return;
- }
lock->ref_name = xstrdup(refname);
if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
- last_errno = errno;
unable_to_lock_message(ref_file.buf, errno, err);
goto error_return;
}
- if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) {
- last_errno = errno;
- goto error_return;
- }
+ if (refs_read_ref_full(&refs->base, lock->ref_name,
+ 0,
+ &lock->old_oid, NULL))
+ oidclr(&lock->old_oid);
goto out;
error_return:
@@ -1005,7 +1048,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
out:
strbuf_release(&ref_file);
- errno = last_errno;
return lock;
}
@@ -1132,7 +1174,7 @@ static int should_pack_ref(const char *refname,
return 0;
/* Do not pack broken refs: */
- if (!ref_resolves_to_object(refname, oid, ref_flags))
+ if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
return 0;
return 1;
@@ -1155,7 +1197,8 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
- iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
+ iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+ the_repository, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
/*
* If the loose reference can be packed, add an entry
@@ -1415,8 +1458,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
logmoved = log;
- lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL,
- REF_NO_DEREF, NULL, &err);
+ lock = lock_ref_oid_basic(refs, newrefname, NULL, &err);
if (!lock) {
if (copy)
error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
@@ -1438,8 +1480,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
goto out;
rollback:
- lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL,
- REF_NO_DEREF, NULL, &err);
+ lock = lock_ref_oid_basic(refs, oldrefname, NULL, &err);
if (!lock) {
error("unable to lock %s for rollback: %s", oldrefname, err.buf);
strbuf_release(&err);
@@ -1846,9 +1887,7 @@ static int files_create_symref(struct ref_store *ref_store,
struct ref_lock *lock;
int ret;
- lock = lock_ref_oid_basic(refs, refname, NULL,
- NULL, NULL, REF_NO_DEREF, NULL,
- &err);
+ lock = lock_ref_oid_basic(refs, refname, NULL, &err);
if (!lock) {
error("%s", err.buf);
strbuf_release(&err);
@@ -2416,7 +2455,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
}
ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames, NULL,
+ affected_refnames,
&lock, &referent,
&update->type, err);
if (ret) {
@@ -3037,7 +3076,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
}
static int files_reflog_expire(struct ref_store *ref_store,
- const char *refname, const struct object_id *oid,
+ const char *refname,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
@@ -3054,6 +3093,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
int status = 0;
int type;
struct strbuf err = STRBUF_INIT;
+ const struct object_id *oid;
memset(&cb, 0, sizeof(cb));
cb.flags = flags;
@@ -3065,14 +3105,26 @@ static int files_reflog_expire(struct ref_store *ref_store,
* reference itself, plus we might need to update the
* reference if --updateref was specified:
*/
- lock = lock_ref_oid_basic(refs, refname, oid,
- NULL, NULL, REF_NO_DEREF,
- &type, &err);
+ lock = lock_ref_oid_basic(refs, refname, &type, &err);
if (!lock) {
error("cannot lock ref '%s': %s", refname, err.buf);
strbuf_release(&err);
return -1;
}
+ oid = &lock->old_oid;
+
+ /*
+ * When refs are deleted, their reflog is deleted before the
+ * ref itself is deleted. This is because there is no separate
+ * lock for reflog; instead we take a lock on the ref with
+ * lock_ref_oid_basic().
+ *
+ * If a race happens and the reflog doesn't exist after we've
+ * acquired the lock that's OK. We've got nothing more to do;
+ * We were asked to delete the reflog, but someone else
+ * deleted it! The caller doesn't care that we deleted it,
+ * just that it is deleted. So we can return successfully.
+ */
if (!refs_reflog_exists(ref_store, refname)) {
unlock_ref(lock);
return 0;
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index f8aa97d799..1c5211b03e 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -193,13 +193,15 @@ static int release_snapshot(struct snapshot *snapshot)
}
}
-struct ref_store *packed_ref_store_create(const char *path,
+struct ref_store *packed_ref_store_create(struct repository *repo,
+ const char *path,
unsigned int store_flags)
{
struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
struct ref_store *ref_store = (struct ref_store *)refs;
base_ref_store_init(ref_store, &refs_be_packed);
+ ref_store->repo = repo;
ref_store->gitdir = xstrdup(path);
refs->store_flags = store_flags;
@@ -724,9 +726,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs)
return refs->snapshot;
}
-static int packed_read_raw_ref(struct ref_store *ref_store,
- const char *refname, struct object_id *oid,
- struct strbuf *referent, unsigned int *type)
+static int packed_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 packed_ref_store *refs =
packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@@ -739,7 +741,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store,
if (!rec) {
/* refname is not a packed reference. */
- errno = ENOENT;
+ *failure_errno = ENOENT;
return -1;
}
@@ -776,6 +778,7 @@ struct packed_ref_iterator {
struct object_id oid, peeled;
struct strbuf refname_buf;
+ struct repository *repo;
unsigned int flags;
};
@@ -864,8 +867,8 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
continue;
if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
- !ref_resolves_to_object(iter->base.refname, &iter->oid,
- iter->flags))
+ !ref_resolves_to_object(iter->base.refname, iter->repo,
+ &iter->oid, iter->flags))
continue;
return ITER_OK;
@@ -883,6 +886,9 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
+ if (iter->repo != the_repository)
+ BUG("peeling for non-the_repository is not supported");
+
if ((iter->base.flags & REF_KNOWS_PEELED)) {
oidcpy(peeled, &iter->peeled);
return is_null_oid(&iter->peeled) ? -1 : 0;
@@ -954,6 +960,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->base.oid = &iter->oid;
+ iter->repo = ref_store->repo;
iter->flags = flags;
if (prefix && *prefix)
@@ -1600,6 +1607,7 @@ static int packed_for_each_reflog_ent(struct ref_store *ref_store,
const char *refname,
each_reflog_ent_fn fn, void *cb_data)
{
+ BUG("packed reference store does not support reflogs");
return 0;
}
@@ -1608,12 +1616,14 @@ static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store,
each_reflog_ent_fn fn,
void *cb_data)
{
+ BUG("packed reference store does not support reflogs");
return 0;
}
static int packed_reflog_exists(struct ref_store *ref_store,
const char *refname)
{
+ BUG("packed reference store does not support reflogs");
return 0;
}
@@ -1627,17 +1637,19 @@ static int packed_create_reflog(struct ref_store *ref_store,
static int packed_delete_reflog(struct ref_store *ref_store,
const char *refname)
{
+ BUG("packed reference store does not support reflogs");
return 0;
}
static int packed_reflog_expire(struct ref_store *ref_store,
- const char *refname, const struct object_id *oid,
+ 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)
{
+ BUG("packed reference store does not support reflogs");
return 0;
}
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
index a01a0aff9c..f61a73ec25 100644
--- a/refs/packed-backend.h
+++ b/refs/packed-backend.h
@@ -1,6 +1,7 @@
#ifndef REFS_PACKED_BACKEND_H
#define REFS_PACKED_BACKEND_H
+struct repository;
struct ref_transaction;
/*
@@ -12,7 +13,8 @@ struct ref_transaction;
* even among packed refs.
*/
-struct ref_store *packed_ref_store_create(const char *path,
+struct ref_store *packed_ref_store_create(struct repository *repo,
+ const char *path,
unsigned int store_flags);
/*
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 49d732f6db..be4aa5e098 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -49,7 +49,7 @@ struct ref_cache *create_ref_cache(struct ref_store *refs,
ret->ref_store = refs;
ret->fill_ref_dir = fill_ref_dir;
- ret->root = create_dir_entry(ret, "", 0, 1);
+ ret->root = create_dir_entry(ret, "", 0);
return ret;
}
@@ -86,14 +86,13 @@ static void clear_ref_dir(struct ref_dir *dir)
}
struct ref_entry *create_dir_entry(struct ref_cache *cache,
- const char *dirname, size_t len,
- int incomplete)
+ const char *dirname, size_t len)
{
struct ref_entry *direntry;
FLEX_ALLOC_MEM(direntry, name, dirname, len);
direntry->u.subdir.cache = cache;
- direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
+ direntry->flag = REF_DIR | REF_INCOMPLETE;
return direntry;
}
@@ -144,30 +143,19 @@ int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
/*
* Search for a directory entry directly within dir (without
* recursing). Sort dir if necessary. subdirname must be a directory
- * name (i.e., end in '/'). If mkdir is set, then create the
- * directory if it is missing; otherwise, return NULL if the desired
+ * name (i.e., end in '/'). Returns NULL if the desired
* directory cannot be found. dir must already be complete.
*/
static struct ref_dir *search_for_subdir(struct ref_dir *dir,
- const char *subdirname, size_t len,
- int mkdir)
+ const char *subdirname, size_t len)
{
int entry_index = search_ref_dir(dir, subdirname, len);
struct ref_entry *entry;
- if (entry_index == -1) {
- if (!mkdir)
- return NULL;
- /*
- * Since dir is complete, the absence of a subdir
- * means that the subdir really doesn't exist;
- * therefore, create an empty record for it but mark
- * the record complete.
- */
- entry = create_dir_entry(dir->cache, subdirname, len, 0);
- add_entry_to_dir(dir, entry);
- } else {
- entry = dir->entries[entry_index];
- }
+
+ if (entry_index == -1)
+ return NULL;
+
+ entry = dir->entries[entry_index];
return get_ref_dir(entry);
}
@@ -176,18 +164,17 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
* tree that should hold refname. If refname is a directory name
* (i.e., it ends in '/'), then return that ref_dir itself. dir must
* represent the top-level directory and must already be complete.
- * Sort ref_dirs and recurse into subdirectories as necessary. If
- * mkdir is set, then create any missing directories; otherwise,
+ * Sort ref_dirs and recurse into subdirectories as necessary. Will
* return NULL if the desired directory cannot be found.
*/
static struct ref_dir *find_containing_dir(struct ref_dir *dir,
- const char *refname, int mkdir)
+ const char *refname)
{
const char *slash;
for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
size_t dirnamelen = slash - refname + 1;
struct ref_dir *subdir;
- subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
+ subdir = search_for_subdir(dir, refname, dirnamelen);
if (!subdir) {
dir = NULL;
break;
@@ -202,7 +189,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname)
{
int entry_index;
struct ref_entry *entry;
- dir = find_containing_dir(dir, refname, 0);
+ dir = find_containing_dir(dir, refname);
if (!dir)
return NULL;
entry_index = search_ref_dir(dir, refname, strlen(refname));
@@ -212,50 +199,6 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname)
return (entry->flag & REF_DIR) ? NULL : entry;
}
-int remove_entry_from_dir(struct ref_dir *dir, const char *refname)
-{
- int refname_len = strlen(refname);
- int entry_index;
- struct ref_entry *entry;
- int is_dir = refname[refname_len - 1] == '/';
- if (is_dir) {
- /*
- * refname represents a reference directory. Remove
- * the trailing slash; otherwise we will get the
- * directory *representing* refname rather than the
- * one *containing* it.
- */
- char *dirname = xmemdupz(refname, refname_len - 1);
- dir = find_containing_dir(dir, dirname, 0);
- free(dirname);
- } else {
- dir = find_containing_dir(dir, refname, 0);
- }
- if (!dir)
- return -1;
- entry_index = search_ref_dir(dir, refname, refname_len);
- if (entry_index == -1)
- return -1;
- entry = dir->entries[entry_index];
-
- MOVE_ARRAY(&dir->entries[entry_index],
- &dir->entries[entry_index + 1], dir->nr - entry_index - 1);
- dir->nr--;
- if (dir->sorted > entry_index)
- dir->sorted--;
- free_ref_entry(entry);
- return dir->nr;
-}
-
-int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref)
-{
- dir = find_containing_dir(dir, ref->name, 1);
- if (!dir)
- return -1;
- add_entry_to_dir(dir, ref);
- return 0;
-}
-
/*
* Emit a warning and return true iff ref1 and ref2 have the same name
* and the same oid. Die if they have the same name but different
@@ -435,6 +378,8 @@ struct cache_ref_iterator {
* on from there.)
*/
struct cache_ref_iterator_level *levels;
+
+ struct repository *repo;
};
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -491,6 +436,11 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
+ struct cache_ref_iterator *iter =
+ (struct cache_ref_iterator *)ref_iterator;
+
+ if (iter->repo != the_repository)
+ BUG("peeling for non-the_repository is not supported");
return peel_object(ref_iterator->oid, peeled) ? -1 : 0;
}
@@ -513,6 +463,7 @@ static struct ref_iterator_vtable cache_ref_iterator_vtable = {
struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
const char *prefix,
+ struct repository *repo,
int prime_dir)
{
struct ref_dir *dir;
@@ -522,7 +473,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
dir = get_ref_dir(cache->root);
if (prefix && *prefix)
- dir = find_containing_dir(dir, prefix, 0);
+ dir = find_containing_dir(dir, prefix);
if (!dir)
/* There's nothing to iterate over. */
return empty_ref_iterator_begin();
@@ -547,5 +498,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
level->prefix_state = PREFIX_CONTAINS_DIR;
}
+ iter->repo = repo;
+
return ref_iterator;
}
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index 3bfb89d2b3..850d9d3744 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -169,8 +169,7 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry);
* "refs/heads/") or "" for the top-level directory.
*/
struct ref_entry *create_dir_entry(struct ref_cache *cache,
- const char *dirname, size_t len,
- int incomplete);
+ const char *dirname, size_t len);
struct ref_entry *create_ref_entry(const char *refname,
const struct object_id *oid, int flag);
@@ -200,29 +199,6 @@ void free_ref_cache(struct ref_cache *cache);
void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
/*
- * Remove the entry with the given name from dir, recursing into
- * subdirectories as necessary. If refname is the name of a directory
- * (i.e., ends with '/'), then remove the directory and its contents.
- * If the removal was successful, return the number of entries
- * remaining in the directory entry that contained the deleted entry.
- * If the name was not found, return -1. Please note that this
- * function only deletes the entry from the cache; it does not delete
- * it from the filesystem or ensure that other cache entries (which
- * might be symbolic references to the removed entry) are updated.
- * Nor does it remove any containing dir entries that might be made
- * empty by the removal. dir must represent the top-level directory
- * and must already be complete.
- */
-int remove_entry_from_dir(struct ref_dir *dir, const char *refname);
-
-/*
- * Add a ref_entry to the ref_dir (unsorted), recursing into
- * subdirectories as necessary. dir must represent the top-level
- * directory. Return 0 on success.
- */
-int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref);
-
-/*
* Find the value entry with the given name in dir, sorting ref_dirs
* and recursing into subdirectories as necessary. If the name is not
* found or it corresponds to a directory entry, return NULL.
@@ -238,6 +214,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname);
*/
struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
const char *prefix,
+ struct repository *repo,
int prime_dir);
#endif /* REFS_REF_CACHE_H */
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 3155708345..12224742ed 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -66,6 +66,7 @@ int refname_is_safe(const char *refname);
* referred-to object does not exist, emit a warning and return false.
*/
int ref_resolves_to_object(const char *refname,
+ struct repository *repo,
const struct object_id *oid,
unsigned int flags);
@@ -245,8 +246,36 @@ int refs_rename_ref_available(struct ref_store *refs,
/* We allow "recursive" symbolic refs. Only within reason, though */
#define SYMREF_MAXDEPTH 5
-/* Include broken references in a do_for_each_ref*() iteration: */
-#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
+/*
+ * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(),
+ * which feeds it).
+ */
+enum do_for_each_ref_flags {
+ /*
+ * Include broken references in a do_for_each_ref*() iteration, which
+ * would normally be omitted. This includes both refs that point to
+ * missing objects (a true repository corruption), ones with illegal
+ * names (which we prefer not to expose to callers), as well as
+ * dangling symbolic refs (i.e., those that point to a non-existent
+ * ref; this is not a corruption, but as they have no valid oid, we
+ * omit them from normal iteration results).
+ */
+ DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0),
+
+ /*
+ * Only include per-worktree refs in a do_for_each_ref*() iteration.
+ * Normally this will be used with a files ref_store, since that's
+ * where all reference backends will presumably store their
+ * per-worktree refs.
+ */
+ DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1),
+
+ /*
+ * Omit dangling symrefs from output; this only has an effect with
+ * INCLUDE_BROKEN, since they are otherwise not included at all.
+ */
+ DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
+};
/*
* Reference iterators
@@ -349,16 +378,12 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
* Return an iterator that goes over each reference in `refs` for
* which the refname begins with prefix. If trim is non-zero, then
* trim that many characters off the beginning of each refname.
- * The output is ordered by refname. The following flags are supported:
- *
- * DO_FOR_EACH_INCLUDE_BROKEN: include broken references in
- * the iteration.
- *
- * DO_FOR_EACH_PER_WORKTREE_ONLY: only produce REF_TYPE_PER_WORKTREE refs.
+ * The output is ordered by refname.
*/
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
- const char *prefix, int trim, int flags);
+ const char *prefix, int trim,
+ enum do_for_each_ref_flags flags);
/*
* A callback function used to instruct merge_ref_iterator how to
@@ -446,10 +471,8 @@ void base_ref_iterator_free(struct ref_iterator *iter);
/*
* backend-specific implementation of ref_iterator_advance. For symrefs, the
* function should set REF_ISSYMREF, and it should also dereference the symref
- * to provide the OID referent. If DO_FOR_EACH_INCLUDE_BROKEN is set, symrefs
- * with non-existent referents and refs pointing to non-existent object names
- * should also be returned. If DO_FOR_EACH_PER_WORKTREE_ONLY, only
- * REF_TYPE_PER_WORKTREE refs should be returned.
+ * to provide the OID referent. It should respect do_for_each_ref_flags
+ * that were passed to refs_ref_iterator_begin().
*/
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
@@ -498,14 +521,6 @@ int do_for_each_repo_ref_iterator(struct repository *r,
struct ref_iterator *iter,
each_repo_ref_fn fn, void *cb_data);
-/*
- * Only include per-worktree refs in a do_for_each_ref*() iteration.
- * Normally this will be used with a files ref_store, since that's
- * where all reference backends will presumably store their
- * per-worktree refs.
- */
-#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02
-
struct ref_store;
/* refs backends */
@@ -525,7 +540,8 @@ struct ref_store;
* should call base_ref_store_init() to initialize the shared part of
* the ref_store and to record the ref_store for later lookup.
*/
-typedef struct ref_store *ref_store_init_fn(const char *gitdir,
+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);
@@ -593,7 +609,7 @@ typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname,
int force_create, struct strbuf *err);
typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname);
typedef int reflog_expire_fn(struct ref_store *ref_store,
- const char *refname, const struct object_id *oid,
+ const char *refname,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
reflog_expiry_should_prune_fn should_prune_fn,
@@ -620,11 +636,15 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
* properly-formatted or even safe reference name. NEITHER INPUT NOR
* OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION.
*
- * Return 0 on success. If the ref doesn't exist, set errno to ENOENT
- * and return -1. If the ref exists but is neither a symbolic ref nor
- * an object ID, it is broken; set REF_ISBROKEN in type, set errno to
- * EINVAL, and return -1. If there is another error reading the ref,
- * set errno appropriately and return -1.
+ * Return 0 on success, or -1 on failure. If the ref exists but is neither a
+ * symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in
+ * type, and return -1 (failure_errno should not be ENOENT)
+ *
+ * failure_errno provides errno codes that are interpreted beyond error
+ * reporting. The following error codes have special meaning:
+ * * ENOENT: the ref doesn't exist
+ * * EISDIR: ref name is a directory
+ * * ENOTDIR: ref prefix is not a directory
*
* Backend-specific flags might be set in type as well, regardless of
* outcome.
@@ -638,9 +658,9 @@ typedef int reflog_expire_fn(struct ref_store *ref_store,
* - in all other cases, referent will be untouched, and therefore
* refname will still be valid and unchanged.
*/
-typedef int read_raw_ref_fn(struct ref_store *ref_store,
- const char *refname, struct object_id *oid,
- struct strbuf *referent, unsigned int *type);
+typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
+ struct object_id *oid, struct strbuf *referent,
+ unsigned int *type, int *failure_errno);
struct ref_storage_be {
struct ref_storage_be *next;
@@ -683,7 +703,12 @@ struct ref_store {
/* The backend describing this ref_store's storage scheme: */
const struct ref_storage_be *be;
- /* The gitdir that this ref_store applies to: */
+ struct repository *repo;
+
+ /*
+ * The gitdir that this ref_store applies to. Note that this is not
+ * necessarily repo->gitdir if the repo has multiple worktrees.
+ */
char *gitdir;
};
diff --git a/remote-curl.c b/remote-curl.c
index 598cff7cde..d69156312b 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -499,6 +499,10 @@ static struct discovery *discover_refs(const char *service, int for_push)
show_http_message(&type, &charset, &buffer);
die(_("Authentication failed for '%s'"),
transport_anonymize_url(url.buf));
+ case HTTP_NOMATCHPUBLICKEY:
+ show_http_message(&type, &charset, &buffer);
+ die(_("unable to access '%s' with http.pinnedPubkey configuration: %s"),
+ transport_anonymize_url(url.buf), curl_errorstr);
default:
show_http_message(&type, &charset, &buffer);
die(_("unable to access '%s': %s"),
@@ -1084,7 +1088,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
rpc->protocol_header = NULL;
while (!err) {
- int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
+ int n = packet_read(rpc->out, rpc->buf, rpc->alloc, 0);
if (!n)
break;
rpc->pos = 0;
@@ -1478,8 +1482,8 @@ int cmd_main(int argc, const char **argv)
options.verbosity = 1;
options.progress = !!isatty(2);
options.thin = 1;
- string_list_init(&options.deepen_not, 1);
- string_list_init(&options.push_options, 1);
+ string_list_init_dup(&options.deepen_not);
+ string_list_init_dup(&options.push_options);
/*
* Just report "remote-curl" here (folding all the various aliases
diff --git a/remote.c b/remote.c
index 31e141b01f..f958543d70 100644
--- a/remote.c
+++ b/remote.c
@@ -2403,7 +2403,7 @@ struct reflog_commit_array {
size_t nr, alloc;
};
-#define REFLOG_COMMIT_ARRAY_INIT { NULL, 0, 0 }
+#define REFLOG_COMMIT_ARRAY_INIT { 0 }
/* Append a commit to the array. */
static void append_commit(struct reflog_commit_array *arr,
diff --git a/repo-settings.c b/repo-settings.c
index 0cfe8b787d..b93e91a212 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -3,40 +3,77 @@
#include "repository.h"
#include "midx.h"
-#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } } while(0)
+static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
+ int def)
+{
+ if (repo_config_get_bool(r, key, dest))
+ *dest = def;
+}
void prepare_repo_settings(struct repository *r)
{
+ int experimental;
int value;
char *strval;
+ int manyfiles;
- if (r->settings.initialized)
+ if (r->settings.initialized++)
return;
/* Defaults */
- memset(&r->settings, -1, sizeof(r->settings));
+ r->settings.index_version = -1;
+ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
+
+ /* Booleans config or default, cascades to other settings */
+ repo_cfg_bool(r, "feature.manyfiles", &manyfiles, 0);
+ repo_cfg_bool(r, "feature.experimental", &experimental, 0);
+
+ /* Defaults modified by feature.* */
+ if (experimental) {
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+ }
+ if (manyfiles) {
+ r->settings.index_version = 4;
+ r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
+ }
- if (!repo_config_get_bool(r, "core.commitgraph", &value))
- r->settings.core_commit_graph = value;
- if (!repo_config_get_bool(r, "commitgraph.readchangedpaths", &value))
- r->settings.commit_graph_read_changed_paths = value;
- if (!repo_config_get_bool(r, "gc.writecommitgraph", &value))
- r->settings.gc_write_commit_graph = value;
- UPDATE_DEFAULT_BOOL(r->settings.core_commit_graph, 1);
- UPDATE_DEFAULT_BOOL(r->settings.commit_graph_read_changed_paths, 1);
- UPDATE_DEFAULT_BOOL(r->settings.gc_write_commit_graph, 1);
+ /* Boolean config or default, does not cascade (simple) */
+ repo_cfg_bool(r, "core.commitgraph", &r->settings.core_commit_graph, 1);
+ repo_cfg_bool(r, "commitgraph.readchangedpaths", &r->settings.commit_graph_read_changed_paths, 1);
+ repo_cfg_bool(r, "gc.writecommitgraph", &r->settings.gc_write_commit_graph, 1);
+ repo_cfg_bool(r, "fetch.writecommitgraph", &r->settings.fetch_write_commit_graph, 0);
+ repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1);
+ repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1);
+ repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
+ /*
+ * The GIT_TEST_MULTI_PACK_INDEX variable is special in that
+ * either it *or* the config sets
+ * r->settings.core_multi_pack_index if true. We don't take
+ * the environment variable if it exists (even if false) over
+ * any config, as in most other cases.
+ */
+ if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
+ r->settings.core_multi_pack_index = 1;
+
+ /*
+ * Non-boolean config
+ */
if (!repo_config_get_int(r, "index.version", &value))
r->settings.index_version = value;
- if (!repo_config_get_maybe_bool(r, "core.untrackedcache", &value)) {
- if (value == 0)
- r->settings.core_untracked_cache = UNTRACKED_CACHE_REMOVE;
- else
- r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
- } else if (!repo_config_get_string(r, "core.untrackedcache", &strval)) {
- if (!strcasecmp(strval, "keep"))
- r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+ if (!repo_config_get_string(r, "core.untrackedcache", &strval)) {
+ int v = git_parse_maybe_bool(strval);
+
+ /*
+ * If it's set to "keep", or some other non-boolean
+ * value then "v < 0". Then we do nothing and keep it
+ * at the default of UNTRACKED_CACHE_KEEP.
+ */
+ if (v >= 0)
+ r->settings.core_untracked_cache = v ?
+ UNTRACKED_CACHE_WRITE : UNTRACKED_CACHE_REMOVE;
free(strval);
}
@@ -45,39 +82,8 @@ void prepare_repo_settings(struct repository *r)
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
else if (!strcasecmp(strval, "noop"))
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_NOOP;
- else
- r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
}
- if (!repo_config_get_bool(r, "pack.usesparse", &value))
- r->settings.pack_use_sparse = value;
- UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1);
-
- value = git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0);
- if (value || !repo_config_get_bool(r, "core.multipackindex", &value))
- r->settings.core_multi_pack_index = value;
- UPDATE_DEFAULT_BOOL(r->settings.core_multi_pack_index, 1);
-
- if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
- UPDATE_DEFAULT_BOOL(r->settings.index_version, 4);
- UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
- }
-
- if (!repo_config_get_bool(r, "fetch.writecommitgraph", &value))
- r->settings.fetch_write_commit_graph = value;
- UPDATE_DEFAULT_BOOL(r->settings.fetch_write_commit_graph, 0);
-
- if (!repo_config_get_bool(r, "feature.experimental", &value) && value)
- UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
-
- /* Hack for test programs like test-dump-untracked-cache */
- if (ignore_untracked_cache_config)
- r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
- else
- UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
-
- UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
-
/*
* This setting guards all index reads to require a full index
* over a sparse index. After suitable guards are placed in the
@@ -85,11 +91,4 @@ void prepare_repo_settings(struct repository *r)
* removed.
*/
r->settings.command_requires_full_index = 1;
-
- /*
- * Initialize this as off.
- */
- r->settings.sparse_index = 0;
- if (!repo_config_get_bool(r, "index.sparse", &value) && value)
- r->settings.sparse_index = 1;
}
diff --git a/repository.c b/repository.c
index b2bf44c6fa..c5b90ba93e 100644
--- a/repository.c
+++ b/repository.c
@@ -190,19 +190,15 @@ error:
int repo_submodule_init(struct repository *subrepo,
struct repository *superproject,
- const struct submodule *sub)
+ const char *path,
+ const struct object_id *treeish_name)
{
struct strbuf gitdir = STRBUF_INIT;
struct strbuf worktree = STRBUF_INIT;
int ret = 0;
- if (!sub) {
- ret = -1;
- goto out;
- }
-
- strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", sub->path);
- strbuf_repo_worktree_path(&worktree, superproject, "%s", sub->path);
+ strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
+ strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
/*
@@ -212,9 +208,15 @@ int repo_submodule_init(struct repository *subrepo,
* in the superproject's 'modules' directory. In this case the
* submodule would not have a worktree.
*/
+ const struct submodule *sub =
+ submodule_from_path(superproject, treeish_name, path);
+ if (!sub) {
+ ret = -1;
+ goto out;
+ }
+
strbuf_reset(&gitdir);
- strbuf_repo_git_path(&gitdir, superproject,
- "modules/%s", sub->name);
+ submodule_name_to_gitdir(&gitdir, superproject, sub->name);
if (repo_init(subrepo, gitdir.buf, NULL)) {
ret = -1;
@@ -225,7 +227,7 @@ int repo_submodule_init(struct repository *subrepo,
subrepo->submodule_prefix = xstrfmt("%s%s/",
superproject->submodule_prefix ?
superproject->submodule_prefix :
- "", sub->path);
+ "", path);
out:
strbuf_release(&gitdir);
diff --git a/repository.h b/repository.h
index 3740c93bc0..a057653981 100644
--- a/repository.h
+++ b/repository.h
@@ -13,18 +13,15 @@ struct submodule_cache;
struct promisor_remote_config;
enum untracked_cache_setting {
- UNTRACKED_CACHE_UNSET = -1,
- UNTRACKED_CACHE_REMOVE = 0,
- UNTRACKED_CACHE_KEEP = 1,
- UNTRACKED_CACHE_WRITE = 2
+ UNTRACKED_CACHE_KEEP,
+ UNTRACKED_CACHE_REMOVE,
+ UNTRACKED_CACHE_WRITE,
};
enum fetch_negotiation_setting {
- FETCH_NEGOTIATION_UNSET = -1,
- FETCH_NEGOTIATION_NONE = 0,
- FETCH_NEGOTIATION_DEFAULT = 1,
- FETCH_NEGOTIATION_SKIPPING = 2,
- FETCH_NEGOTIATION_NOOP = 3,
+ FETCH_NEGOTIATION_DEFAULT,
+ FETCH_NEGOTIATION_SKIPPING,
+ FETCH_NEGOTIATION_NOOP,
};
struct repo_settings {
@@ -34,6 +31,8 @@ struct repo_settings {
int commit_graph_read_changed_paths;
int gc_write_commit_graph;
int fetch_write_commit_graph;
+ int command_requires_full_index;
+ int sparse_index;
int index_version;
enum untracked_cache_setting core_untracked_cache;
@@ -42,9 +41,6 @@ struct repo_settings {
enum fetch_negotiation_setting fetch_negotiation_algorithm;
int core_multi_pack_index;
-
- unsigned command_requires_full_index:1,
- sparse_index:1;
};
struct repository {
@@ -172,15 +168,18 @@ void initialize_the_repository(void);
int repo_init(struct repository *r, const char *gitdir, const char *worktree);
/*
- * Initialize the repository 'subrepo' as the submodule given by the
- * struct submodule 'sub' in parent repository 'superproject'.
- * Return 0 upon success and a non-zero value upon failure, which may happen
- * if the submodule is not found, or 'sub' is NULL.
+ * Initialize the repository 'subrepo' as the submodule at the given path. If
+ * the submodule's gitdir cannot be found at <path>/.git, this function calls
+ * submodule_from_path() to try to find it. treeish_name is only used if
+ * submodule_from_path() needs to be called; see its documentation for more
+ * information.
+ * Return 0 upon success and a non-zero value upon failure.
*/
-struct submodule;
+struct object_id;
int repo_submodule_init(struct repository *subrepo,
struct repository *superproject,
- const struct submodule *sub);
+ const char *path,
+ const struct object_id *treeish_name);
void repo_clear(struct repository *repo);
/*
diff --git a/reset.c b/reset.c
index 79310ae071..f214df3d96 100644
--- a/reset.c
+++ b/reset.c
@@ -56,9 +56,10 @@ int reset_head(struct repository *r, struct object_id *oid, const char *action,
unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
unpack_tree_opts.update = 1;
unpack_tree_opts.merge = 1;
+ unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL);
if (!detach_head)
- unpack_tree_opts.reset = 1;
+ unpack_tree_opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
if (repo_read_index_unmerged(r) < 0) {
ret = error(_("could not read index"));
diff --git a/revision.c b/revision.c
index 0dabb5a0bc..ab7c135804 100644
--- a/revision.c
+++ b/revision.c
@@ -249,7 +249,7 @@ struct commit_stack {
struct commit **items;
size_t nr, alloc;
};
-#define COMMIT_STACK_INIT { NULL, 0, 0 }
+#define COMMIT_STACK_INIT { 0 }
static void commit_stack_push(struct commit_stack *stack, struct commit *commit)
{
@@ -2548,7 +2548,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
struct strbuf bisect_refs = STRBUF_INIT;
int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
- status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
+ status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data);
strbuf_release(&bisect_refs);
return status;
}
@@ -2563,8 +2563,7 @@ static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void
return for_each_bisect_ref(refs, fn, cb_data, term_good);
}
-static int handle_revision_pseudo_opt(const char *submodule,
- struct rev_info *revs,
+static int handle_revision_pseudo_opt(struct rev_info *revs,
const char **argv, int *flags)
{
const char *arg = argv[0];
@@ -2572,7 +2571,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
struct ref_store *refs;
int argcount;
- if (submodule) {
+ if (revs->repo != the_repository) {
/*
* We need some something like get_submodule_worktrees()
* before we can go through all worktrees of a submodule,
@@ -2581,9 +2580,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
*/
if (!revs->single_worktree)
BUG("--single-worktree cannot be used together with submodule");
- refs = get_submodule_ref_store(submodule);
- } else
- refs = get_main_ref_store(revs->repo);
+ }
+ refs = get_main_ref_store(revs->repo);
/*
* NOTE!
@@ -2707,12 +2705,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
{
int i, flags, left, seen_dashdash, revarg_opt;
struct strvec prune_data = STRVEC_INIT;
- const char *submodule = NULL;
int seen_end_of_options = 0;
- if (opt)
- submodule = opt->submodule;
-
/* First, search for "--" */
if (opt && opt->assume_dashdash) {
seen_dashdash = 1;
@@ -2741,7 +2735,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (!seen_end_of_options && *arg == '-') {
int opts;
- opts = handle_revision_pseudo_opt(submodule,
+ opts = handle_revision_pseudo_opt(
revs, argv + i,
&flags);
if (opts > 0) {
diff --git a/revision.h b/revision.h
index 0c65a760ee..5578bb4720 100644
--- a/revision.h
+++ b/revision.h
@@ -336,7 +336,6 @@ extern volatile show_early_output_fn_t show_early_output;
struct setup_revision_opt {
const char *def;
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
- const char *submodule; /* TODO: drop this and use rev_info->repo */
unsigned int assume_dashdash:1,
allow_exclude_promisor_objects:1;
unsigned revarg_opt;
diff --git a/run-command.c b/run-command.c
index 821245672d..7ef5cc712a 100644
--- a/run-command.c
+++ b/run-command.c
@@ -9,6 +9,7 @@
#include "quote.h"
#include "config.h"
#include "packfile.h"
+#include "hook.h"
void child_process_init(struct child_process *child)
{
@@ -211,9 +212,9 @@ static char *locate_in_PATH(const char *file)
return NULL;
}
-static int exists_in_PATH(const char *file)
+int exists_in_PATH(const char *command)
{
- char *r = locate_in_PATH(file);
+ char *r = locate_in_PATH(command);
int found = r != NULL;
free(r);
return found;
@@ -1322,40 +1323,6 @@ int async_with_fork(void)
#endif
}
-const char *find_hook(const char *name)
-{
- static struct strbuf path = STRBUF_INIT;
-
- strbuf_reset(&path);
- strbuf_git_path(&path, "hooks/%s", name);
- if (access(path.buf, X_OK) < 0) {
- int err = errno;
-
-#ifdef STRIP_EXTENSION
- strbuf_addstr(&path, STRIP_EXTENSION);
- if (access(path.buf, X_OK) >= 0)
- return path.buf;
- if (errno == EACCES)
- err = errno;
-#endif
-
- if (err == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
- static struct string_list advise_given = STRING_LIST_INIT_DUP;
-
- if (!string_list_lookup(&advise_given, name)) {
- string_list_insert(&advise_given, name);
- advise(_("The '%s' hook was ignored because "
- "it's not set as executable.\n"
- "You can disable this warning with "
- "`git config advice.ignoredHook false`."),
- path.buf);
- }
- }
- return NULL;
- }
- return path.buf;
-}
-
int run_hook_ve(const char *const *env, const char *name, va_list args)
{
struct child_process hook = CHILD_PROCESS_INIT;
@@ -1907,3 +1874,132 @@ void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir)
}
strvec_pushf(env_array, "%s=%s", GIT_DIR_ENVIRONMENT, new_git_dir);
}
+
+enum start_bg_result start_bg_command(struct child_process *cmd,
+ start_bg_wait_cb *wait_cb,
+ void *cb_data,
+ unsigned int timeout_sec)
+{
+ enum start_bg_result sbgr = SBGR_ERROR;
+ int ret;
+ int wait_status;
+ pid_t pid_seen;
+ time_t time_limit;
+
+ /*
+ * We do not allow clean-on-exit because the child process
+ * should persist in the background and possibly/probably
+ * after this process exits. So we don't want to kill the
+ * child during our atexit routine.
+ */
+ if (cmd->clean_on_exit)
+ BUG("start_bg_command() does not allow non-zero clean_on_exit");
+
+ if (!cmd->trace2_child_class)
+ cmd->trace2_child_class = "background";
+
+ ret = start_command(cmd);
+ if (ret) {
+ /*
+ * We assume that if `start_command()` fails, we
+ * either get a complete `trace2_child_start() /
+ * trace2_child_exit()` pair or it fails before the
+ * `trace2_child_start()` is emitted, so we do not
+ * need to worry about it here.
+ *
+ * We also assume that `start_command()` does not add
+ * us to the cleanup list. And that it calls
+ * calls `child_process_clear()`.
+ */
+ sbgr = SBGR_ERROR;
+ goto done;
+ }
+
+ time(&time_limit);
+ time_limit += timeout_sec;
+
+wait:
+ pid_seen = waitpid(cmd->pid, &wait_status, WNOHANG);
+
+ if (!pid_seen) {
+ /*
+ * The child is currently running. Ask the callback
+ * if the child is ready to do work or whether we
+ * should keep waiting for it to boot up.
+ */
+ ret = (*wait_cb)(cmd, cb_data);
+ if (!ret) {
+ /*
+ * The child is running and "ready".
+ */
+ trace2_child_ready(cmd, "ready");
+ sbgr = SBGR_READY;
+ goto done;
+ } else if (ret > 0) {
+ /*
+ * The callback said to give it more time to boot up
+ * (subject to our timeout limit).
+ */
+ time_t now;
+
+ time(&now);
+ if (now < time_limit)
+ goto wait;
+
+ /*
+ * Our timeout has expired. We don't try to
+ * kill the child, but rather let it continue
+ * (hopefully) trying to startup.
+ */
+ trace2_child_ready(cmd, "timeout");
+ sbgr = SBGR_TIMEOUT;
+ goto done;
+ } else {
+ /*
+ * The cb gave up on this child. It is still running,
+ * but our cb got an error trying to probe it.
+ */
+ trace2_child_ready(cmd, "error");
+ sbgr = SBGR_CB_ERROR;
+ goto done;
+ }
+ }
+
+ else if (pid_seen == cmd->pid) {
+ int child_code = -1;
+
+ /*
+ * The child started, but exited or was terminated
+ * before becoming "ready".
+ *
+ * We try to match the behavior of `wait_or_whine()`
+ * WRT the handling of WIFSIGNALED() and WIFEXITED()
+ * and convert the child's status to a return code for
+ * tracing purposes and emit the `trace2_child_exit()`
+ * event.
+ *
+ * We do not want the wait_or_whine() error message
+ * because we will be called by client-side library
+ * routines.
+ */
+ if (WIFEXITED(wait_status))
+ child_code = WEXITSTATUS(wait_status);
+ else if (WIFSIGNALED(wait_status))
+ child_code = WTERMSIG(wait_status) + 128;
+ trace2_child_exit(cmd, child_code);
+
+ sbgr = SBGR_DIED;
+ goto done;
+ }
+
+ else if (pid_seen < 0 && errno == EINTR)
+ goto wait;
+
+ trace2_child_exit(cmd, -1);
+ sbgr = SBGR_ERROR;
+
+done:
+ child_process_clear(cmd);
+ invalidate_lstat_cache();
+ return sbgr;
+}
diff --git a/run-command.h b/run-command.h
index ad207daced..4987826258 100644
--- a/run-command.h
+++ b/run-command.h
@@ -191,6 +191,18 @@ void child_process_clear(struct child_process *);
int is_executable(const char *name);
/**
+ * Check if the command exists on $PATH. This emulates the path search that
+ * execvp would perform, without actually executing the command so it
+ * can be used before fork() to prepare to run a command using
+ * execve() or after execvp() to diagnose why it failed.
+ *
+ * The caller should ensure that command contains no directory separators.
+ *
+ * Returns 1 if it is found in $PATH or 0 if the command could not be found.
+ */
+int exists_in_PATH(const char *command);
+
+/**
* Start a sub-process. Takes a pointer to a `struct child_process`
* that specifies the details and returns pipe FDs (if requested).
* See below for details.
@@ -212,13 +224,6 @@ int finish_command_in_signal(struct child_process *);
*/
int run_command(struct child_process *);
-/*
- * Returns the path to the hook file, or NULL if the hook is missing
- * or disabled. Note that this points to static storage that will be
- * overwritten by further calls to find_hook and run_hook_*.
- */
-const char *find_hook(const char *name);
-
/**
* Run a hook.
* The first argument is a pathname to an index file, or NULL
@@ -505,4 +510,61 @@ int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
*/
void prepare_other_repo_env(struct strvec *env_array, const char *new_git_dir);
+/**
+ * Possible return values for start_bg_command().
+ */
+enum start_bg_result {
+ /* child process is "ready" */
+ SBGR_READY = 0,
+
+ /* child process could not be started */
+ SBGR_ERROR,
+
+ /* callback error when testing for "ready" */
+ SBGR_CB_ERROR,
+
+ /* timeout expired waiting for child to become "ready" */
+ SBGR_TIMEOUT,
+
+ /* child process exited or was signalled before becomming "ready" */
+ SBGR_DIED,
+};
+
+/**
+ * Callback used by start_bg_command() to ask whether the
+ * child process is ready or needs more time to become "ready".
+ *
+ * The callback will receive the cmd and cb_data arguments given to
+ * start_bg_command().
+ *
+ * Returns 1 is child needs more time (subject to the requested timeout).
+ * Returns 0 if child is "ready".
+ * Returns -1 on any error and cause start_bg_command() to also error out.
+ */
+typedef int(start_bg_wait_cb)(const struct child_process *cmd, void *cb_data);
+
+/**
+ * Start a command in the background. Wait long enough for the child
+ * to become "ready" (as defined by the provided callback). Capture
+ * immediate errors (like failure to start) and any immediate exit
+ * status (such as a shutdown/signal before the child became "ready")
+ * and return this like start_command().
+ *
+ * We run a custom wait loop using the provided callback to wait for
+ * the child to start and become "ready". This is limited by the given
+ * timeout value.
+ *
+ * If the child does successfully start and become "ready", we orphan
+ * it into the background.
+ *
+ * The caller must not call finish_command().
+ *
+ * The opaque cb_data argument will be forwarded to the callback for
+ * any instance data that it might require. This may be NULL.
+ */
+enum start_bg_result start_bg_command(struct child_process *cmd,
+ start_bg_wait_cb *wait_cb,
+ void *cb_data,
+ unsigned int timeout_sec);
+
#endif
diff --git a/send-pack.c b/send-pack.c
index b3a495b7b1..bc0fcdbb00 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -341,13 +341,13 @@ static int generate_push_cert(struct strbuf *req_buf,
{
const struct ref *ref;
struct string_list_item *item;
- char *signing_key = xstrdup(get_signing_key());
+ char *signing_key_id = xstrdup(get_signing_key_id());
const char *cp, *np;
struct strbuf cert = STRBUF_INIT;
int update_seen = 0;
strbuf_addstr(&cert, "certificate version 0.1\n");
- strbuf_addf(&cert, "pusher %s ", signing_key);
+ strbuf_addf(&cert, "pusher %s ", signing_key_id);
datestamp(&cert);
strbuf_addch(&cert, '\n');
if (args->url && *args->url) {
@@ -374,7 +374,7 @@ static int generate_push_cert(struct strbuf *req_buf,
if (!update_seen)
goto free_return;
- if (sign_buffer(&cert, &cert, signing_key))
+ if (sign_buffer(&cert, &cert, get_signing_key()))
die(_("failed to sign the push certificate"));
packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);
@@ -386,7 +386,7 @@ static int generate_push_cert(struct strbuf *req_buf,
packet_buf_write(req_buf, "push-cert-end\n");
free_return:
- free(signing_key);
+ free(signing_key_id);
strbuf_release(&cert);
return update_seen;
}
diff --git a/sequencer.c b/sequencer.c
index 614d56f5e2..cd2aabf1f7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -8,6 +8,7 @@
#include "sequencer.h"
#include "tag.h"
#include "run-command.h"
+#include "hook.h"
#include "exec-cmd.h"
#include "utf8.h"
#include "cache-tree.h"
@@ -1459,7 +1460,7 @@ static int try_to_commit(struct repository *r,
}
}
- if (find_hook("prepare-commit-msg")) {
+ if (hook_exists("prepare-commit-msg")) {
res = run_prepare_commit_msg_hook(r, msg, hook_commit);
if (res)
goto out;
@@ -2693,7 +2694,6 @@ static int read_populate_todo(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
{
- struct stat st;
const char *todo_file = get_todo_path(opts);
int res;
@@ -2701,11 +2701,6 @@ static int read_populate_todo(struct repository *r,
if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
return -1;
- res = stat(todo_file, &st);
- if (res)
- return error(_("could not stat '%s'"), todo_file);
- fill_stat_data(&todo_list->stat, &st);
-
res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
@@ -3650,9 +3645,9 @@ static int do_reset(struct repository *r,
struct strbuf ref_name = STRBUF_INIT;
struct object_id oid;
struct lock_file lock = LOCK_INIT;
- struct tree_desc desc;
+ struct tree_desc desc = { 0 };
struct tree *tree;
- struct unpack_trees_options unpack_tree_opts;
+ struct unpack_trees_options unpack_tree_opts = { 0 };
int ret = 0;
if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0)
@@ -3684,14 +3679,11 @@ static int do_reset(struct repository *r,
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
if (get_oid(ref_name.buf, &oid) &&
get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) {
- error(_("could not read '%s'"), ref_name.buf);
- rollback_lock_file(&lock);
- strbuf_release(&ref_name);
- return -1;
+ ret = error(_("could not read '%s'"), ref_name.buf);
+ goto cleanup;
}
}
- memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
setup_unpack_trees_porcelain(&unpack_tree_opts, "reset");
unpack_tree_opts.head_idx = 1;
unpack_tree_opts.src_index = r->index;
@@ -3699,27 +3691,22 @@ static int do_reset(struct repository *r,
unpack_tree_opts.fn = oneway_merge;
unpack_tree_opts.merge = 1;
unpack_tree_opts.update = 1;
+ unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL);
if (repo_read_index_unmerged(r)) {
- rollback_lock_file(&lock);
- strbuf_release(&ref_name);
- return error_resolve_conflict(_(action_name(opts)));
+ ret = error_resolve_conflict(_(action_name(opts)));
+ goto cleanup;
}
if (!fill_tree_descriptor(r, &desc, &oid)) {
- error(_("failed to find tree of %s"), oid_to_hex(&oid));
- rollback_lock_file(&lock);
- free((void *)desc.buffer);
- strbuf_release(&ref_name);
- return -1;
+ ret = error(_("failed to find tree of %s"), oid_to_hex(&oid));
+ goto cleanup;
}
if (unpack_trees(1, &desc, &unpack_tree_opts)) {
- rollback_lock_file(&lock);
- free((void *)desc.buffer);
- strbuf_release(&ref_name);
- return -1;
+ ret = -1;
+ goto cleanup;
}
tree = parse_tree_indirect(&oid);
@@ -3727,14 +3714,17 @@ static int do_reset(struct repository *r,
if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
ret = error(_("could not write index"));
- free((void *)desc.buffer);
if (!ret)
ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
len, name), "HEAD", &oid,
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-
+cleanup:
+ free((void *)desc.buffer);
+ if (ret < 0)
+ rollback_lock_file(&lock);
strbuf_release(&ref_name);
+ clear_unpack_trees_porcelain(&unpack_tree_opts);
return ret;
}
@@ -4284,6 +4274,30 @@ static int stopped_at_head(struct repository *r)
}
+static int reread_todo_if_changed(struct repository *r,
+ struct todo_list *todo_list,
+ struct replay_opts *opts)
+{
+ int offset;
+ struct strbuf buf = STRBUF_INIT;
+
+ if (strbuf_read_file_or_whine(&buf, get_todo_path(opts)) < 0)
+ return -1;
+ offset = get_item_line_offset(todo_list, todo_list->current + 1);
+ if (buf.len != todo_list->buf.len - offset ||
+ memcmp(buf.buf, todo_list->buf.buf + offset, buf.len)) {
+ /* Reread the todo file if it has changed. */
+ todo_list_release(todo_list);
+ if (read_populate_todo(r, todo_list, opts))
+ return -1; /* message was printed */
+ /* `current` will be incremented on return */
+ todo_list->current = -1;
+ }
+ strbuf_release(&buf);
+
+ return 0;
+}
+
static const char rescheduled_advice[] =
N_("Could not execute the todo command\n"
"\n"
@@ -4462,20 +4476,9 @@ static int pick_commits(struct repository *r,
item->commit,
arg, item->arg_len,
opts, res, 0);
- } else if (is_rebase_i(opts) && check_todo && !res) {
- struct stat st;
-
- if (stat(get_todo_path(opts), &st)) {
- res = error_errno(_("could not stat '%s'"),
- get_todo_path(opts));
- } else if (match_stat_data(&todo_list->stat, &st)) {
- /* Reread the todo file if it has changed. */
- todo_list_release(todo_list);
- if (read_populate_todo(r, todo_list, opts))
- res = -1; /* message was printed */
- /* `current` will be incremented below */
- todo_list->current = -1;
- }
+ } else if (is_rebase_i(opts) && check_todo && !res &&
+ reread_todo_if_changed(r, todo_list, opts)) {
+ return -1;
}
todo_list->current++;
@@ -5437,8 +5440,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
* Add commands after pick and (series of) squash/fixup commands
* in the todo list.
*/
-void todo_list_add_exec_commands(struct todo_list *todo_list,
- struct string_list *commands)
+static void todo_list_add_exec_commands(struct todo_list *todo_list,
+ struct string_list *commands)
{
struct strbuf *buf = &todo_list->buf;
size_t base_offset = buf->len;
diff --git a/sequencer.h b/sequencer.h
index 2088344cb3..05a7d2ba6b 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -116,10 +116,11 @@ struct todo_list {
struct todo_item *items;
int nr, alloc, current;
int done_nr, total_nr;
- struct stat_data stat;
};
-#define TODO_LIST_INIT { STRBUF_INIT }
+#define TODO_LIST_INIT { \
+ .buf = STRBUF_INIT, \
+}
int todo_list_parse_insn_buffer(struct repository *r, char *buf,
struct todo_list *todo_list);
@@ -161,8 +162,6 @@ int sequencer_remove_state(struct replay_opts *opts);
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
const char **argv, unsigned flags);
-void todo_list_add_exec_commands(struct todo_list *todo_list,
- struct string_list *commands);
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
struct commit *onto, const struct object_id *orig_head,
diff --git a/serve.c b/serve.c
index 1817edc7f5..b3fe9b5126 100644
--- a/serve.c
+++ b/serve.c
@@ -10,6 +10,7 @@
#include "upload-pack.h"
static int advertise_sid = -1;
+static int client_hash_algo = GIT_HASH_SHA1;
static int always_advertise(struct repository *r,
struct strbuf *value)
@@ -33,6 +34,17 @@ static int object_format_advertise(struct repository *r,
return 1;
}
+static void object_format_receive(struct repository *r,
+ const char *algo_name)
+{
+ if (!algo_name)
+ die("object-format capability requires an argument");
+
+ client_hash_algo = hash_algo_by_name(algo_name);
+ if (client_hash_algo == GIT_HASH_UNKNOWN)
+ die("unknown object format '%s'", algo_name);
+}
+
static int session_id_advertise(struct repository *r, struct strbuf *value)
{
if (advertise_sid == -1 &&
@@ -45,6 +57,14 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)
return 1;
}
+static void session_id_receive(struct repository *r,
+ const char *client_sid)
+{
+ if (!client_sid)
+ client_sid = "";
+ trace2_data_string("transfer", NULL, "client-sid", client_sid);
+}
+
struct protocol_capability {
/*
* The name of the capability. The server uses this name when
@@ -70,6 +90,16 @@ struct protocol_capability {
* This field should be NULL for capabilities which are not commands.
*/
int (*command)(struct repository *r, struct packet_reader *request);
+
+ /*
+ * Function called when a client requests the capability as a
+ * non-command. This may be NULL if the capability does nothing.
+ *
+ * For a capability of the form "foo=bar", the value string points to
+ * the content after the "=" (i.e., "bar"). For simple capabilities
+ * (just "foo"), it is NULL.
+ */
+ void (*receive)(struct repository *r, const char *value);
};
static struct protocol_capability capabilities[] = {
@@ -94,10 +124,12 @@ static struct protocol_capability capabilities[] = {
{
.name = "object-format",
.advertise = object_format_advertise,
+ .receive = object_format_receive,
},
{
.name = "session-id",
.advertise = session_id_advertise,
+ .receive = session_id_receive,
},
{
.name = "object-info",
@@ -139,7 +171,7 @@ void protocol_v2_advertise_capabilities(void)
strbuf_release(&value);
}
-static struct protocol_capability *get_capability(const char *key)
+static struct protocol_capability *get_capability(const char *key, const char **value)
{
int i;
@@ -149,31 +181,46 @@ static struct protocol_capability *get_capability(const char *key)
for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
struct protocol_capability *c = &capabilities[i];
const char *out;
- if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
+ if (!skip_prefix(key, c->name, &out))
+ continue;
+ if (!*out) {
+ *value = NULL;
return c;
+ }
+ if (*out++ == '=') {
+ *value = out;
+ return c;
+ }
}
return NULL;
}
-static int is_valid_capability(const char *key)
+static int receive_client_capability(const char *key)
{
- const struct protocol_capability *c = get_capability(key);
+ const char *value;
+ const struct protocol_capability *c = get_capability(key, &value);
- return c && c->advertise(the_repository, NULL);
+ if (!c || c->command || !c->advertise(the_repository, NULL))
+ return 0;
+
+ if (c->receive)
+ c->receive(the_repository, value);
+ return 1;
}
-static int is_command(const char *key, struct protocol_capability **command)
+static int parse_command(const char *key, struct protocol_capability **command)
{
const char *out;
if (skip_prefix(key, "command=", &out)) {
- struct protocol_capability *cmd = get_capability(out);
+ const char *value;
+ struct protocol_capability *cmd = get_capability(out, &value);
if (*command)
die("command '%s' requested after already requesting command '%s'",
out, (*command)->name);
- if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
+ if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command || value)
die("invalid command '%s'", out);
*command = cmd;
@@ -183,42 +230,6 @@ static int is_command(const char *key, struct protocol_capability **command)
return 0;
}
-static int has_capability(const struct strvec *keys, const char *capability,
- const char **value)
-{
- int i;
- for (i = 0; i < keys->nr; i++) {
- const char *out;
- if (skip_prefix(keys->v[i], capability, &out) &&
- (!*out || *out == '=')) {
- if (value) {
- if (*out == '=')
- out++;
- *value = out;
- }
- return 1;
- }
- }
-
- return 0;
-}
-
-static void check_algorithm(struct repository *r, struct strvec *keys)
-{
- int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
- const char *algo_name;
-
- if (has_capability(keys, "object-format", &algo_name)) {
- client = hash_algo_by_name(algo_name);
- if (client == GIT_HASH_UNKNOWN)
- die("unknown object format '%s'", algo_name);
- }
-
- if (client != server)
- die("mismatched object format: server %s; client %s\n",
- r->hash_algo->name, hash_algos[client].name);
-}
-
enum request_state {
PROCESS_REQUEST_KEYS,
PROCESS_REQUEST_DONE,
@@ -228,9 +239,8 @@ static int process_request(void)
{
enum request_state state = PROCESS_REQUEST_KEYS;
struct packet_reader reader;
- struct strvec keys = STRVEC_INIT;
+ int seen_capability_or_command = 0;
struct protocol_capability *command = NULL;
- const char *client_sid;
packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
@@ -250,10 +260,9 @@ static int process_request(void)
case PACKET_READ_EOF:
BUG("Should have already died when seeing EOF");
case PACKET_READ_NORMAL:
- /* collect request; a sequence of keys and values */
- if (is_command(reader.line, &command) ||
- is_valid_capability(reader.line))
- strvec_push(&keys, reader.line);
+ if (parse_command(reader.line, &command) ||
+ receive_client_capability(reader.line))
+ seen_capability_or_command = 1;
else
die("unknown capability '%s'", reader.line);
@@ -265,7 +274,7 @@ static int process_request(void)
* If no command and no keys were given then the client
* wanted to terminate the connection.
*/
- if (!keys.nr)
+ if (!seen_capability_or_command)
return 1;
/*
@@ -292,14 +301,13 @@ static int process_request(void)
if (!command)
die("no command requested");
- check_algorithm(the_repository, &keys);
-
- if (has_capability(&keys, "session-id", &client_sid))
- trace2_data_string("transfer", NULL, "client-sid", client_sid);
+ if (client_hash_algo != hash_algo_by_ptr(the_repository->hash_algo))
+ die("mismatched object format: server %s; client %s\n",
+ the_repository->hash_algo->name,
+ hash_algos[client_hash_algo].name);
command->command(the_repository, &reader);
- strvec_clear(&keys);
return 0;
}
diff --git a/shallow.h b/shallow.h
index 5b4a96dcd6..aba6ff5829 100644
--- a/shallow.h
+++ b/shallow.h
@@ -23,7 +23,9 @@ int is_repository_shallow(struct repository *r);
struct shallow_lock {
struct lock_file lock;
};
-#define SHALLOW_LOCK_INIT { LOCK_INIT }
+#define SHALLOW_LOCK_INIT { \
+ .lock = LOCK_INIT, \
+}
/* commit $GIT_DIR/shallow and reset stat-validity checks */
int commit_shallow_file(struct repository *r, struct shallow_lock *lk);
diff --git a/simple-ipc.h b/simple-ipc.h
index 2c48a5ee00..a849d9f841 100644
--- a/simple-ipc.h
+++ b/simple-ipc.h
@@ -5,13 +5,6 @@
* See Documentation/technical/api-simple-ipc.txt
*/
-#ifdef SUPPORTS_SIMPLE_IPC
-#include "pkt-line.h"
-
-/*
- * Simple IPC Client Side API.
- */
-
enum ipc_active_state {
/*
* The pipe/socket exists and the daemon is waiting for connections.
@@ -43,6 +36,13 @@ enum ipc_active_state {
IPC_STATE__OTHER_ERROR,
};
+#ifdef SUPPORTS_SIMPLE_IPC
+#include "pkt-line.h"
+
+/*
+ * Simple IPC Client Side API.
+ */
+
struct ipc_client_connect_options {
/*
* Spin under timeout if the server is running but can't
@@ -65,11 +65,7 @@ struct ipc_client_connect_options {
unsigned int uds_disallow_chdir:1;
};
-#define IPC_CLIENT_CONNECT_OPTIONS_INIT { \
- .wait_if_busy = 0, \
- .wait_if_not_found = 0, \
- .uds_disallow_chdir = 0, \
-}
+#define IPC_CLIENT_CONNECT_OPTIONS_INIT { 0 }
/*
* Determine if a server is listening on this named pipe or socket using
@@ -107,7 +103,8 @@ void ipc_client_close_connection(struct ipc_client_connection *connection);
*/
int ipc_client_send_command_to_connection(
struct ipc_client_connection *connection,
- const char *message, struct strbuf *answer);
+ const char *message, size_t message_len,
+ struct strbuf *answer);
/*
* Used by the client to synchronously connect and send and receive a
@@ -119,7 +116,8 @@ int ipc_client_send_command_to_connection(
*/
int ipc_client_send_command(const char *path,
const struct ipc_client_connect_options *options,
- const char *message, struct strbuf *answer);
+ const char *message, size_t message_len,
+ struct strbuf *answer);
/*
* Simple IPC Server Side API.
@@ -144,6 +142,7 @@ typedef int (ipc_server_reply_cb)(struct ipc_server_reply_data *,
*/
typedef int (ipc_server_application_cb)(void *application_data,
const char *request,
+ size_t request_len,
ipc_server_reply_cb *reply_cb,
struct ipc_server_reply_data *reply_data);
diff --git a/strbuf.c b/strbuf.c
index c8a5789694..b22e981655 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1059,15 +1059,21 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
strbuf_setlen(sb, sb->len + len);
}
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
- int abbrev_len)
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+ const struct object_id *oid, int abbrev_len)
{
int r;
strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
- r = find_unique_abbrev_r(sb->buf + sb->len, oid, abbrev_len);
+ r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
strbuf_setlen(sb, sb->len + r);
}
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+ int abbrev_len)
+{
+ strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
+}
+
/*
* Returns the length of a line, without trailing spaces.
*
diff --git a/strbuf.h b/strbuf.h
index 5b1113abf8..96512f85b3 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -70,7 +70,7 @@ struct strbuf {
};
extern char strbuf_slopbuf[];
-#define STRBUF_INIT { .alloc = 0, .len = 0, .buf = strbuf_slopbuf }
+#define STRBUF_INIT { .buf = strbuf_slopbuf }
/*
* Predeclare this here, since cache.h includes this file before it defines the
@@ -634,8 +634,10 @@ void strbuf_list_free(struct strbuf **list);
* Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
* the strbuf `sb`.
*/
-void strbuf_add_unique_abbrev(struct strbuf *sb,
- const struct object_id *oid,
+struct repository;
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+ const struct object_id *oid, int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
int abbrev_len);
/**
diff --git a/streaming.c b/streaming.c
index 5f480ad50c..fe54665d86 100644
--- a/streaming.c
+++ b/streaming.c
@@ -223,19 +223,24 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
const struct object_id *oid,
enum object_type *type)
{
+ struct object_info oi = OBJECT_INFO_INIT;
+ oi.sizep = &st->size;
+ oi.typep = type;
+
st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
if (!st->u.loose.mapped)
return -1;
- if ((unpack_loose_header(&st->z,
- st->u.loose.mapped,
- st->u.loose.mapsize,
- st->u.loose.hdr,
- sizeof(st->u.loose.hdr)) < 0) ||
- (parse_loose_header(st->u.loose.hdr, &st->size) < 0)) {
- git_inflate_end(&st->z);
- munmap(st->u.loose.mapped, st->u.loose.mapsize);
- return -1;
+ switch (unpack_loose_header(&st->z, st->u.loose.mapped,
+ st->u.loose.mapsize, st->u.loose.hdr,
+ sizeof(st->u.loose.hdr), NULL)) {
+ case ULHR_OK:
+ break;
+ case ULHR_BAD:
+ case ULHR_TOO_LONG:
+ goto error;
}
+ if (parse_loose_header(st->u.loose.hdr, &oi) < 0 || *type < 0)
+ goto error;
st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
st->u.loose.hdr_avail = st->z.total_out;
@@ -244,6 +249,10 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
st->read = read_istream_loose;
return 0;
+error:
+ git_inflate_end(&st->z);
+ munmap(st->u.loose.mapped, st->u.loose.mapsize);
+ return -1;
}
diff --git a/string-list.c b/string-list.c
index 43576ad126..549fc416d6 100644
--- a/string-list.c
+++ b/string-list.c
@@ -13,14 +13,6 @@ void string_list_init_dup(struct string_list *list)
memcpy(list, &blank, sizeof(*list));
}
-void string_list_init(struct string_list *list, int strdup_strings)
-{
- if (strdup_strings)
- string_list_init_dup(list);
- else
- string_list_init_nodup(list);
-}
-
/* if there is no exact match, point to the index where the entry could be
* inserted */
static int get_entry_index(const struct string_list *list, const char *string,
diff --git a/string-list.h b/string-list.h
index 0d6b469239..267d6e5769 100644
--- a/string-list.h
+++ b/string-list.h
@@ -104,11 +104,6 @@ struct string_list {
void string_list_init_nodup(struct string_list *list);
void string_list_init_dup(struct string_list *list);
-/**
- * TODO remove: For compatibility with any in-flight older API users
- */
-void string_list_init(struct string_list *list, int strdup_strings);
-
/** Callback function type for for_each_string_list */
typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
diff --git a/strvec.h b/strvec.h
index fdcad75b45..9f55c8766b 100644
--- a/strvec.h
+++ b/strvec.h
@@ -29,11 +29,13 @@ extern const char *empty_strvec[];
*/
struct strvec {
const char **v;
- int nr;
- int alloc;
+ size_t nr;
+ size_t alloc;
};
-#define STRVEC_INIT { empty_strvec, 0, 0 }
+#define STRVEC_INIT { \
+ .v = empty_strvec, \
+}
/**
* Initialize an array. This is no different than assigning from
diff --git a/submodule-config.h b/submodule-config.h
index c11e22cf50..65875b94ea 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -45,10 +45,6 @@ struct submodule {
struct object_id gitmodules_oid;
int recommend_shallow;
};
-
-#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \
- NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, { { 0 } }, -1 };
-
struct submodule_cache;
struct repository;
diff --git a/submodule.c b/submodule.c
index 85ea1c36dd..c689070524 100644
--- a/submodule.c
+++ b/submodule.c
@@ -201,6 +201,8 @@ int register_all_submodule_odb_as_alternates(void)
add_to_alternates_memory(added_submodule_odb_paths.items[i].string);
if (ret) {
string_list_clear(&added_submodule_odb_paths, 0);
+ trace2_data_intmax("submodule", the_repository,
+ "register_all_submodule_odb_as_alternates/registered", ret);
if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0))
BUG("register_all_submodule_odb_as_alternates() called");
}
@@ -525,9 +527,6 @@ static void prepare_submodule_repo_env_in_gitdir(struct strvec *out)
/*
* Initialize a repository struct for a submodule based on the provided 'path'.
*
- * Unlike repo_submodule_init, this tolerates submodules not present
- * in .gitmodules. This function exists only to preserve historical behavior,
- *
* Returns the repository struct on success,
* NULL when the submodule is not present.
*/
@@ -931,23 +930,33 @@ struct has_commit_data {
static int check_has_commit(const struct object_id *oid, void *data)
{
struct has_commit_data *cb = data;
+ struct repository subrepo;
+ enum object_type type;
+
+ if (repo_submodule_init(&subrepo, cb->repo, cb->path, null_oid())) {
+ cb->result = 0;
+ goto cleanup;
+ }
- enum object_type type = oid_object_info(cb->repo, oid, NULL);
+ type = oid_object_info(&subrepo, oid, NULL);
switch (type) {
case OBJ_COMMIT:
- return 0;
+ goto cleanup;
case OBJ_BAD:
/*
* Object is missing or invalid. If invalid, an error message
* has already been printed.
*/
cb->result = 0;
- return 0;
+ goto cleanup;
default:
die(_("submodule entry '%s' (%s) is a %s, not a commit"),
cb->path, oid_to_hex(oid), type_name(type));
}
+cleanup:
+ repo_clear(&subrepo);
+ return 0;
}
static int submodule_has_commits(struct repository *r,
@@ -1321,9 +1330,11 @@ struct submodule_parallel_fetch {
struct strbuf submodules_with_errors;
};
-#define SPF_INIT {0, STRVEC_INIT, NULL, NULL, 0, 0, 0, 0, \
- STRING_LIST_INIT_DUP, \
- NULL, 0, 0, STRBUF_INIT}
+#define SPF_INIT { \
+ .args = STRVEC_INIT, \
+ .changed_submodule_names = STRING_LIST_INIT_DUP, \
+ .submodules_with_errors = STRBUF_INIT, \
+}
static int get_fetch_recurse_config(const struct submodule *submodule,
struct submodule_parallel_fetch *spf)
@@ -1421,24 +1432,13 @@ static void fetch_task_release(struct fetch_task *p)
}
static struct repository *get_submodule_repo_for(struct repository *r,
- const struct submodule *sub)
+ const char *path)
{
struct repository *ret = xmalloc(sizeof(*ret));
- if (repo_submodule_init(ret, r, sub)) {
- /*
- * No entry in .gitmodules? Technically not a submodule,
- * but historically we supported repositories that happen to be
- * in-place where a gitlink is. Keep supporting them.
- */
- struct strbuf gitdir = STRBUF_INIT;
- strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path);
- if (repo_init(ret, gitdir.buf, NULL)) {
- strbuf_release(&gitdir);
- free(ret);
- return NULL;
- }
- strbuf_release(&gitdir);
+ if (repo_submodule_init(ret, r, path, null_oid())) {
+ free(ret);
+ return NULL;
}
return ret;
@@ -1480,7 +1480,7 @@ static int get_next_submodule(struct child_process *cp,
continue;
}
- task->repo = get_submodule_repo_for(spf->r, task->sub);
+ task->repo = get_submodule_repo_for(spf->r, task->sub->path);
if (task->repo) {
struct strbuf submodule_prefix = STRBUF_INIT;
child_process_init(cp);
@@ -1859,14 +1859,16 @@ out:
void submodule_unset_core_worktree(const struct submodule *sub)
{
- char *config_path = xstrfmt("%s/modules/%s/config",
- get_git_dir(), sub->name);
+ struct strbuf config_path = STRBUF_INIT;
- if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+ 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))
warning(_("Could not unset core.worktree setting in submodule '%s'"),
sub->path);
- free(config_path);
+ strbuf_release(&config_path);
}
static const char *get_super_prefix_or_empty(void)
@@ -1906,6 +1908,7 @@ static void submodule_reset_index(const char *path)
strvec_pushf(&cp.args, "--super-prefix=%s%s/",
get_super_prefix_or_empty(), path);
+ /* TODO: determine if this might overwright untracked files */
strvec_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
strvec_push(&cp.args, empty_tree_oid_hex());
@@ -1962,20 +1965,22 @@ int submodule_move_head(const char *path,
absorb_git_dir_into_superproject(path,
ABSORB_GITDIR_RECURSE_SUBMODULES);
} else {
- char *gitdir = xstrfmt("%s/modules/%s",
- get_git_dir(), sub->name);
- connect_work_tree_and_git_dir(path, gitdir, 0);
- free(gitdir);
+ struct strbuf gitdir = STRBUF_INIT;
+ submodule_name_to_gitdir(&gitdir, the_repository,
+ sub->name);
+ connect_work_tree_and_git_dir(path, gitdir.buf, 0);
+ strbuf_release(&gitdir);
/* make sure the index is clean as well */
submodule_reset_index(path);
}
if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
- char *gitdir = xstrfmt("%s/modules/%s",
- get_git_dir(), sub->name);
- connect_work_tree_and_git_dir(path, gitdir, 1);
- free(gitdir);
+ struct strbuf gitdir = STRBUF_INIT;
+ submodule_name_to_gitdir(&gitdir, the_repository,
+ sub->name);
+ connect_work_tree_and_git_dir(path, gitdir.buf, 1);
+ strbuf_release(&gitdir);
}
}
@@ -2090,7 +2095,7 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
static void relocate_single_git_dir_into_superproject(const char *path)
{
char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
- char *new_git_dir;
+ struct strbuf new_gitdir = STRBUF_INIT;
const struct submodule *sub;
if (submodule_uses_worktrees(path))
@@ -2108,14 +2113,13 @@ static void relocate_single_git_dir_into_superproject(const char *path)
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
- new_git_dir = git_pathdup("modules/%s", sub->name);
- if (validate_submodule_git_dir(new_git_dir, sub->name) < 0)
+ submodule_name_to_gitdir(&new_gitdir, the_repository, sub->name);
+ if (validate_submodule_git_dir(new_gitdir.buf, sub->name) < 0)
die(_("refusing to move '%s' into an existing git dir"),
real_old_git_dir);
- if (safe_create_leading_directories_const(new_git_dir) < 0)
- die(_("could not create directory '%s'"), new_git_dir);
- real_new_git_dir = real_pathdup(new_git_dir, 1);
- free(new_git_dir);
+ if (safe_create_leading_directories_const(new_gitdir.buf) < 0)
+ die(_("could not create directory '%s'"), new_gitdir.buf);
+ real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
get_super_prefix_or_empty(), path,
@@ -2126,6 +2130,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)
free(old_git_dir);
free(real_old_git_dir);
free(real_new_git_dir);
+ strbuf_release(&new_gitdir);
}
/*
@@ -2145,6 +2150,7 @@ void absorb_git_dir_into_superproject(const char *path,
/* Not populated? */
if (!sub_git_dir) {
const struct submodule *sub;
+ struct strbuf sub_gitdir = STRBUF_INIT;
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
/* unpopulated as expected */
@@ -2166,8 +2172,9 @@ void absorb_git_dir_into_superproject(const char *path,
sub = submodule_from_path(the_repository, null_oid(), path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
- connect_work_tree_and_git_dir(path,
- git_path("modules/%s", sub->name), 0);
+ submodule_name_to_gitdir(&sub_gitdir, the_repository, sub->name);
+ connect_work_tree_and_git_dir(path, sub_gitdir.buf, 0);
+ strbuf_release(&sub_gitdir);
} else {
/* Is it already absorbed into the superprojects git dir? */
char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
@@ -2318,9 +2325,36 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
goto cleanup;
}
strbuf_reset(buf);
- strbuf_git_path(buf, "%s/%s", "modules", sub->name);
+ submodule_name_to_gitdir(buf, the_repository, sub->name);
}
cleanup:
return ret;
}
+
+void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
+ const char *submodule_name)
+{
+ /*
+ * NEEDSWORK: The current way of mapping a submodule's name to
+ * its location in .git/modules/ has problems with some naming
+ * schemes. For example, if a submodule is named "foo" and
+ * another is named "foo/bar" (whether present in the same
+ * superproject commit or not - the problem will arise if both
+ * superproject commits have been checked out at any point in
+ * time), or if two submodule names only have different cases in
+ * a case-insensitive filesystem.
+ *
+ * There are several solutions, including encoding the path in
+ * some way, introducing a submodule.<name>.gitdir config in
+ * .git/config (not .gitmodules) that allows overriding what the
+ * gitdir of a submodule would be (and teach Git, upon noticing
+ * a clash, to automatically determine a non-clashing name and
+ * to write such a config), or introducing a
+ * submodule.<name>.gitdir config in .gitmodules that repo
+ * administrators can explicitly set. Nothing has been decided,
+ * so for now, just append the name at the end of the path.
+ */
+ strbuf_repo_git_path(buf, r, "modules/");
+ strbuf_addstr(buf, submodule_name);
+}
diff --git a/submodule.h b/submodule.h
index 17a06cc43b..6bd2c99fd9 100644
--- a/submodule.h
+++ b/submodule.h
@@ -37,7 +37,9 @@ struct submodule_update_strategy {
enum submodule_update_type type;
const char *command;
};
-#define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
+#define SUBMODULE_UPDATE_STRATEGY_INIT { \
+ .type = SM_UPDATE_UNSPECIFIED, \
+}
int is_gitmodules_unmerged(struct index_state *istate);
int is_writing_gitmodules_ok(void);
@@ -133,6 +135,13 @@ int push_unpushed_submodules(struct repository *r,
int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
/*
+ * Given a submodule name, create a path to where the submodule's gitdir lives
+ * inside of the provided repository's 'modules' directory.
+ */
+void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
+ const char *submodule_name);
+
+/*
* Make sure that no submodule's git dir is nested in a sibling submodule's.
*/
int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
diff --git a/t/.gitattributes b/t/.gitattributes
index dafa17c3e6..9930e28351 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1,6 +1,5 @@
t[0-9][0-9][0-9][0-9]/* -whitespace
/chainlint/*.expect eol=lf
-/lib-diff/* eol=lf
/t0110/url-* binary
/t3206/* eol=lf
/t3900/*.txt eol=lf
diff --git a/t/README b/t/README
index 51065d0800..29f72354bf 100644
--- a/t/README
+++ b/t/README
@@ -366,6 +366,13 @@ excluded as so much relies on it, but this might change in the future.
GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
test suite. Accept any boolean values that are accepted by git-config.
+GIT_TEST_PASSING_SANITIZE_LEAK=<boolean> when compiled with
+SANITIZE=leak will run only those tests that have whitelisted
+themselves as passing with no memory leaks. Tests can be whitelisted
+by setting "TEST_PASSES_SANITIZE_LEAK=true" before sourcing
+"test-lib.sh" itself at the top of the test script. This test mode is
+used by the "linux-leaks" CI target.
+
GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version'
default to n.
@@ -456,11 +463,8 @@ GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes
registering submodule ODBs as alternates a fatal action. Support for
this environment variable can be removed once the migration to
explicitly providing repositories when accessing submodule objects is
-complete (in which case we might want to replace this with a trace2
-call so that users can make it visible if accessing submodule objects
-without an explicit repository still happens) or needs to be abandoned
-for whatever reason (in which case the migrated codepaths still retain
-their performance benefits).
+complete or needs to be abandoned for whatever reason (in which case the
+migrated codepaths still retain their performance benefits).
Naming Tests
------------
diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c
index 134a1e9d76..ff35f5999b 100644
--- a/t/helper/test-bitmap.c
+++ b/t/helper/test-bitmap.c
@@ -7,6 +7,11 @@ static int bitmap_list_commits(void)
return test_bitmap_commits(the_repository);
}
+static int bitmap_dump_hashes(void)
+{
+ return test_bitmap_hashes(the_repository);
+}
+
int cmd__bitmap(int argc, const char **argv)
{
setup_git_directory();
@@ -16,9 +21,12 @@ int cmd__bitmap(int argc, const char **argv)
if (!strcmp(argv[1], "list-commits"))
return bitmap_list_commits();
+ if (!strcmp(argv[1], "dump-hashes"))
+ return bitmap_dump_hashes();
usage:
- usage("\ttest-tool bitmap list-commits");
+ usage("\ttest-tool bitmap list-commits\n"
+ "\ttest-tool bitmap dump-hashes");
return -1;
}
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index cf0f2c7228..99010614f6 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -45,8 +45,10 @@ int cmd__dump_untracked_cache(int ac, const char **av)
struct untracked_cache *uc;
struct strbuf base = STRBUF_INIT;
- /* Hack to avoid modifying the untracked cache when we read it */
- ignore_untracked_cache_config = 1;
+ /* Set core.untrackedCache=keep before setup_git_directory() */
+ xsetenv("GIT_CONFIG_COUNT", "1", 1);
+ xsetenv("GIT_CONFIG_KEY_0", "core.untrackedCache", 1);
+ xsetenv("GIT_CONFIG_VALUE_0", "keep", 1);
setup_git_directory();
if (read_cache() < 0)
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index c5cffaa4b7..ebf68f7de8 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -2,6 +2,12 @@
#include "cache.h"
#include "mergesort.h"
+static uint32_t minstd_rand(uint32_t *state)
+{
+ *state = (uint64_t)*state * 48271 % 2147483647;
+ return *state;
+}
+
struct line {
char *text;
struct line *next;
@@ -23,14 +29,12 @@ static int compare_strings(const void *a, const void *b)
return strcmp(x->text, y->text);
}
-int cmd__mergesort(int argc, const char **argv)
+static int sort_stdin(void)
{
struct line *line, *p = NULL, *lines = NULL;
struct strbuf sb = STRBUF_INIT;
- for (;;) {
- if (strbuf_getwholeline(&sb, stdin, '\n'))
- break;
+ while (!strbuf_getline(&sb, stdin)) {
line = xmalloc(sizeof(struct line));
line->text = strbuf_detach(&sb, NULL);
if (p) {
@@ -46,8 +50,362 @@ int cmd__mergesort(int argc, const char **argv)
lines = llist_mergesort(lines, get_next, set_next, compare_strings);
while (lines) {
- printf("%s", lines->text);
+ puts(lines->text);
lines = lines->next;
}
return 0;
}
+
+static void dist_sawtooth(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = i % m;
+}
+
+static void dist_rand(int *arr, int n, int m)
+{
+ int i;
+ uint32_t seed = 1;
+ for (i = 0; i < n; i++)
+ arr[i] = minstd_rand(&seed) % m;
+}
+
+static void dist_stagger(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = (i * m + i) % n;
+}
+
+static void dist_plateau(int *arr, int n, int m)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] = (i < m) ? i : m;
+}
+
+static void dist_shuffle(int *arr, int n, int m)
+{
+ int i, j, k;
+ uint32_t seed = 1;
+ for (i = j = 0, k = 1; i < n; i++)
+ arr[i] = minstd_rand(&seed) % m ? (j += 2) : (k += 2);
+}
+
+#define DIST(name) { #name, dist_##name }
+
+static struct dist {
+ const char *name;
+ void (*fn)(int *arr, int n, int m);
+} dist[] = {
+ DIST(sawtooth),
+ DIST(rand),
+ DIST(stagger),
+ DIST(plateau),
+ DIST(shuffle),
+};
+
+static const struct dist *get_dist_by_name(const char *name)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(dist); i++) {
+ if (!strcmp(dist[i].name, name))
+ return &dist[i];
+ }
+ return NULL;
+}
+
+static void mode_copy(int *arr, int n)
+{
+ /* nothing */
+}
+
+static void mode_reverse(int *arr, int n)
+{
+ int i, j;
+ for (i = 0, j = n - 1; i < j; i++, j--)
+ SWAP(arr[i], arr[j]);
+}
+
+static void mode_reverse_1st_half(int *arr, int n)
+{
+ mode_reverse(arr, n / 2);
+}
+
+static void mode_reverse_2nd_half(int *arr, int n)
+{
+ int half = n / 2;
+ mode_reverse(arr + half, n - half);
+}
+
+static int compare_ints(const void *av, const void *bv)
+{
+ const int *ap = av, *bp = bv;
+ int a = *ap, b = *bp;
+ return (a > b) - (a < b);
+}
+
+static void mode_sort(int *arr, int n)
+{
+ QSORT(arr, n, compare_ints);
+}
+
+static void mode_dither(int *arr, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ arr[i] += i % 5;
+}
+
+static void unriffle(int *arr, int n, int *tmp)
+{
+ int i, j;
+ COPY_ARRAY(tmp, arr, n);
+ for (i = j = 0; i < n; i += 2)
+ arr[j++] = tmp[i];
+ for (i = 1; i < n; i += 2)
+ arr[j++] = tmp[i];
+}
+
+static void unriffle_recursively(int *arr, int n, int *tmp)
+{
+ if (n > 1) {
+ int half = n / 2;
+ unriffle(arr, n, tmp);
+ unriffle_recursively(arr, half, tmp);
+ unriffle_recursively(arr + half, n - half, tmp);
+ }
+}
+
+static void mode_unriffle(int *arr, int n)
+{
+ int *tmp;
+ ALLOC_ARRAY(tmp, n);
+ unriffle_recursively(arr, n, tmp);
+ free(tmp);
+}
+
+static unsigned int prev_pow2(unsigned int n)
+{
+ unsigned int pow2 = 1;
+ while (pow2 * 2 < n)
+ pow2 *= 2;
+ return pow2;
+}
+
+static void unriffle_recursively_skewed(int *arr, int n, int *tmp)
+{
+ if (n > 1) {
+ int pow2 = prev_pow2(n);
+ int rest = n - pow2;
+ unriffle(arr + pow2 - rest, rest * 2, tmp);
+ unriffle_recursively_skewed(arr, pow2, tmp);
+ unriffle_recursively_skewed(arr + pow2, rest, tmp);
+ }
+}
+
+static void mode_unriffle_skewed(int *arr, int n)
+{
+ int *tmp;
+ ALLOC_ARRAY(tmp, n);
+ unriffle_recursively_skewed(arr, n, tmp);
+ free(tmp);
+}
+
+#define MODE(name) { #name, mode_##name }
+
+static struct mode {
+ const char *name;
+ void (*fn)(int *arr, int n);
+} mode[] = {
+ MODE(copy),
+ MODE(reverse),
+ MODE(reverse_1st_half),
+ MODE(reverse_2nd_half),
+ MODE(sort),
+ MODE(dither),
+ MODE(unriffle),
+ MODE(unriffle_skewed),
+};
+
+static const struct mode *get_mode_by_name(const char *name)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mode); i++) {
+ if (!strcmp(mode[i].name, name))
+ return &mode[i];
+ }
+ return NULL;
+}
+
+static int generate(int argc, const char **argv)
+{
+ const struct dist *dist = NULL;
+ const struct mode *mode = NULL;
+ int i, n, m, *arr;
+
+ if (argc != 4)
+ return 1;
+
+ dist = get_dist_by_name(argv[0]);
+ mode = get_mode_by_name(argv[1]);
+ n = strtol(argv[2], NULL, 10);
+ m = strtol(argv[3], NULL, 10);
+ if (!dist || !mode)
+ return 1;
+
+ ALLOC_ARRAY(arr, n);
+ dist->fn(arr, n, m);
+ mode->fn(arr, n);
+ for (i = 0; i < n; i++)
+ printf("%08x\n", arr[i]);
+ free(arr);
+ return 0;
+}
+
+static struct stats {
+ int get_next, set_next, compare;
+} stats;
+
+struct number {
+ int value, rank;
+ struct number *next;
+};
+
+static void *get_next_number(const void *a)
+{
+ stats.get_next++;
+ return ((const struct number *)a)->next;
+}
+
+static void set_next_number(void *a, void *b)
+{
+ stats.set_next++;
+ ((struct number *)a)->next = b;
+}
+
+static int compare_numbers(const void *av, const void *bv)
+{
+ const struct number *an = av, *bn = bv;
+ int a = an->value, b = bn->value;
+ stats.compare++;
+ return (a > b) - (a < b);
+}
+
+static void clear_numbers(struct number *list)
+{
+ while (list) {
+ struct number *next = list->next;
+ free(list);
+ list = next;
+ }
+}
+
+static int test(const struct dist *dist, const struct mode *mode, int n, int m)
+{
+ int *arr;
+ size_t i;
+ struct number *curr, *list, **tail;
+ int is_sorted = 1;
+ int is_stable = 1;
+ const char *verdict;
+ int result = -1;
+
+ ALLOC_ARRAY(arr, n);
+ dist->fn(arr, n, m);
+ mode->fn(arr, n);
+ for (i = 0, tail = &list; i < n; i++) {
+ curr = xmalloc(sizeof(*curr));
+ curr->value = arr[i];
+ curr->rank = i;
+ *tail = curr;
+ tail = &curr->next;
+ }
+ *tail = NULL;
+
+ stats.get_next = stats.set_next = stats.compare = 0;
+ list = llist_mergesort(list, get_next_number, set_next_number,
+ compare_numbers);
+
+ QSORT(arr, n, compare_ints);
+ for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) {
+ if (arr[i] != curr->value)
+ is_sorted = 0;
+ if (curr->next && curr->value == curr->next->value &&
+ curr->rank >= curr->next->rank)
+ is_stable = 0;
+ }
+ if (i < n) {
+ verdict = "too short";
+ } else if (curr) {
+ verdict = "too long";
+ } else if (!is_sorted) {
+ verdict = "not sorted";
+ } else if (!is_stable) {
+ verdict = "unstable";
+ } else {
+ verdict = "OK";
+ result = 0;
+ }
+
+ printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n",
+ dist->name, mode->name, n, m, stats.get_next, stats.set_next,
+ stats.compare, verdict);
+
+ clear_numbers(list);
+ free(arr);
+
+ return result;
+}
+
+/*
+ * A version of the qsort certification program from "Engineering a Sort
+ * Function" by Bentley and McIlroy, Software—Practice and Experience,
+ * Volume 23, Issue 11, 1249–1265 (November 1993).
+ */
+static int run_tests(int argc, const char **argv)
+{
+ const char *argv_default[] = { "100", "1023", "1024", "1025" };
+ if (!argc)
+ return run_tests(ARRAY_SIZE(argv_default), argv_default);
+ printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n",
+ "distribut", "mode", "n", "m", "get_next", "set_next",
+ "compare", "verdict");
+ while (argc--) {
+ int i, j, m, n = strtol(*argv++, NULL, 10);
+ for (i = 0; i < ARRAY_SIZE(dist); i++) {
+ for (j = 0; j < ARRAY_SIZE(mode); j++) {
+ for (m = 1; m < 2 * n; m *= 2) {
+ if (test(&dist[i], &mode[j], n, m))
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int cmd__mergesort(int argc, const char **argv)
+{
+ int i;
+ const char *sep;
+
+ if (argc == 6 && !strcmp(argv[1], "generate"))
+ return generate(argc - 2, argv + 2);
+ if (argc == 2 && !strcmp(argv[1], "sort"))
+ return sort_stdin();
+ if (argc > 1 && !strcmp(argv[1], "test"))
+ return run_tests(argc - 2, argv + 2);
+ fprintf(stderr, "usage: test-tool mergesort generate <distribution> <mode> <n> <m>\n");
+ fprintf(stderr, " or: test-tool mergesort sort\n");
+ fprintf(stderr, " or: test-tool mergesort test [<n>...]\n");
+ fprintf(stderr, "\n");
+ for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ")
+ fprintf(stderr, "%s%s", sep, dist[i].name);
+ fprintf(stderr, "\n");
+ for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ")
+ fprintf(stderr, "%s%s", sep, mode[i].name);
+ fprintf(stderr, "\n");
+ return 129;
+}
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index b16cd0b11b..d1324d086a 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -35,5 +35,9 @@ int cmd__oid_array(int argc, const char **argv)
else
die("unknown command: %s", line.buf);
}
+
+ strbuf_release(&line);
+ oid_array_clear(&array);
+
return 0;
}
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
index 180ee28dd9..d48a409f4e 100644
--- a/t/helper/test-oidtree.c
+++ b/t/helper/test-oidtree.c
@@ -45,5 +45,8 @@ int cmd__oidtree(int argc, const char **argv)
die("unknown command: %s", line.buf);
}
}
+
+ strbuf_release(&line);
+
return 0;
}
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 2051ce57db..48d3cf6692 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -14,7 +14,6 @@ static int dry_run = 0, quiet = 0;
static char *string = NULL;
static char *file = NULL;
static int ambiguous;
-static struct string_list list = STRING_LIST_INIT_NODUP;
static struct {
int called;
@@ -107,6 +106,8 @@ int cmd__parse_options(int argc, const char **argv)
NULL
};
struct string_list expect = STRING_LIST_INIT_NODUP;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+
struct option options[] = {
OPT_BOOL(0, "yes", &boolean, "get a boolean"),
OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
@@ -134,7 +135,6 @@ int cmd__parse_options(int argc, const char **argv)
OPT_NOOP_NOARG(0, "obsolete"),
OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
OPT_GROUP("Magic arguments"),
- OPT_ARGUMENT("quux", NULL, "means --quux"),
OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
number_callback),
{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
@@ -186,5 +186,9 @@ int cmd__parse_options(int argc, const char **argv)
for (i = 0; i < argc; i++)
show(&expect, &ret, "arg %02d: %s", i, argv[i]);
+ expect.strdup_strings = 1;
+ string_list_clear(&expect, 0);
+ string_list_clear(&list, 0);
+
return ret;
}
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
index f4028442e3..133b5e6f4a 100644
--- a/t/helper/test-prio-queue.c
+++ b/t/helper/test-prio-queue.c
@@ -46,5 +46,7 @@ int cmd__prio_queue(int argc, const char **argv)
}
}
+ clear_prio_queue(&pq);
+
return 0;
}
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index cb0d27049a..9d6fa7a377 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -3,6 +3,7 @@
#include "midx.h"
#include "repository.h"
#include "object-store.h"
+#include "pack-bitmap.h"
static int read_midx_file(const char *object_dir, int show_objects)
{
@@ -72,14 +73,40 @@ static int read_midx_checksum(const char *object_dir)
return 0;
}
+static int read_midx_preferred_pack(const char *object_dir)
+{
+ struct multi_pack_index *midx = NULL;
+ struct bitmap_index *bitmap = NULL;
+
+ setup_git_directory();
+
+ midx = load_multi_pack_index(object_dir, 1);
+ if (!midx)
+ return 1;
+
+ bitmap = prepare_bitmap_git(the_repository);
+ if (!bitmap)
+ return 1;
+ if (!bitmap_is_midx(bitmap)) {
+ free_bitmap_index(bitmap);
+ return 1;
+ }
+
+ printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
+ free_bitmap_index(bitmap);
+ return 0;
+}
+
int cmd__read_midx(int argc, const char **argv)
{
if (!(argc == 2 || argc == 3))
- usage("read-midx [--show-objects|--checksum] <object-dir>");
+ usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
if (!strcmp(argv[1], "--show-objects"))
return read_midx_file(argv[2], 1);
else if (!strcmp(argv[1], "--checksum"))
return read_midx_checksum(argv[2]);
+ else if (!strcmp(argv[1], "--preferred-pack"))
+ return read_midx_preferred_pack(argv[2]);
return read_midx_file(argv[1], 0);
}
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index 7ae03dc712..3c4fb86223 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -60,8 +60,10 @@ struct testsuite {
int next;
int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
};
-#define TESTSUITE_INIT \
- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 }
+#define TESTSUITE_INIT { \
+ .tests = STRING_LIST_INIT_DUP, \
+ .failed = STRING_LIST_INIT_DUP, \
+}
static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
void **task_cb)
@@ -142,9 +144,6 @@ static int testsuite(int argc, const char **argv)
OPT_END()
};
- memset(&suite, 0, sizeof(suite));
- suite.tests.strdup_strings = suite.failed.strdup_strings = 1;
-
argc = parse_options(argc, argv, NULL, options,
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c
index 42040ef81b..28365ff85b 100644
--- a/t/helper/test-simple-ipc.c
+++ b/t/helper/test-simple-ipc.c
@@ -9,6 +9,7 @@
#include "parse-options.h"
#include "thread-utils.h"
#include "strvec.h"
+#include "run-command.h"
#ifndef SUPPORTS_SIMPLE_IPC
int cmd__simple_ipc(int argc, const char **argv)
@@ -112,7 +113,7 @@ static int app__slow_command(ipc_server_reply_cb *reply_cb,
/*
* The client sent a command followed by a (possibly very) large buffer.
*/
-static int app__sendbytes_command(const char *received,
+static int app__sendbytes_command(const char *received, size_t received_len,
ipc_server_reply_cb *reply_cb,
struct ipc_server_reply_data *reply_data)
{
@@ -123,6 +124,13 @@ static int app__sendbytes_command(const char *received,
int errs = 0;
int ret;
+ /*
+ * The test is setup to send:
+ * "sendbytes" SP <n * char>
+ */
+ if (received_len < strlen("sendbytes "))
+ BUG("received_len is short in app__sendbytes_command");
+
if (skip_prefix(received, "sendbytes ", &p))
len_ballast = strlen(p);
@@ -160,7 +168,7 @@ static ipc_server_application_cb test_app_cb;
* by this application.
*/
static int test_app_cb(void *application_data,
- const char *command,
+ const char *command, size_t command_len,
ipc_server_reply_cb *reply_cb,
struct ipc_server_reply_data *reply_data)
{
@@ -173,7 +181,7 @@ static int test_app_cb(void *application_data,
if (application_data != (void*)&my_app_data)
BUG("application_cb: application_data pointer wrong");
- if (!strcmp(command, "quit")) {
+ if (command_len == 4 && !strncmp(command, "quit", 4)) {
/*
* The client sent a "quit" command. This is an async
* request for the server to shutdown.
@@ -193,22 +201,23 @@ static int test_app_cb(void *application_data,
return SIMPLE_IPC_QUIT;
}
- if (!strcmp(command, "ping")) {
+ if (command_len == 4 && !strncmp(command, "ping", 4)) {
const char *answer = "pong";
return reply_cb(reply_data, answer, strlen(answer));
}
- if (!strcmp(command, "big"))
+ if (command_len == 3 && !strncmp(command, "big", 3))
return app__big_command(reply_cb, reply_data);
- if (!strcmp(command, "chunk"))
+ if (command_len == 5 && !strncmp(command, "chunk", 5))
return app__chunk_command(reply_cb, reply_data);
- if (!strcmp(command, "slow"))
+ if (command_len == 4 && !strncmp(command, "slow", 4))
return app__slow_command(reply_cb, reply_data);
- if (starts_with(command, "sendbytes "))
- return app__sendbytes_command(command, reply_cb, reply_data);
+ if (command_len >= 10 && starts_with(command, "sendbytes "))
+ return app__sendbytes_command(command, command_len,
+ reply_cb, reply_data);
return app__unhandled_command(command, reply_cb, reply_data);
}
@@ -259,186 +268,72 @@ static int daemon__run_server(void)
*/
ret = ipc_server_run(cl_args.path, &opts, test_app_cb, (void*)&my_app_data);
if (ret == -2)
- error(_("socket/pipe already in use: '%s'"), cl_args.path);
+ error("socket/pipe already in use: '%s'", cl_args.path);
else if (ret == -1)
- error_errno(_("could not start server on: '%s'"), cl_args.path);
+ error_errno("could not start server on: '%s'", cl_args.path);
return ret;
}
-#ifndef GIT_WINDOWS_NATIVE
-/*
- * This is adapted from `daemonize()`. Use `fork()` to directly create and
- * run the daemon in a child process.
- */
-static int spawn_server(pid_t *pid)
-{
- struct ipc_server_opts opts = {
- .nr_threads = cl_args.nr_threads,
- };
-
- *pid = fork();
+static start_bg_wait_cb bg_wait_cb;
- switch (*pid) {
- case 0:
- if (setsid() == -1)
- error_errno(_("setsid failed"));
- close(0);
- close(1);
- close(2);
- sanitize_stdfds();
+static int bg_wait_cb(const struct child_process *cp, void *cb_data)
+{
+ int s = ipc_get_active_state(cl_args.path);
- return ipc_server_run(cl_args.path, &opts, test_app_cb,
- (void*)&my_app_data);
+ switch (s) {
+ case IPC_STATE__LISTENING:
+ /* child is "ready" */
+ return 0;
- case -1:
- return error_errno(_("could not spawn daemon in the background"));
+ case IPC_STATE__NOT_LISTENING:
+ case IPC_STATE__PATH_NOT_FOUND:
+ /* give child more time */
+ return 1;
default:
- return 0;
+ case IPC_STATE__INVALID_PATH:
+ case IPC_STATE__OTHER_ERROR:
+ /* all the time in world won't help */
+ return -1;
}
}
-#else
-/*
- * Conceptually like `daemonize()` but different because Windows does not
- * have `fork(2)`. Spawn a normal Windows child process but without the
- * limitations of `start_command()` and `finish_command()`.
- */
-static int spawn_server(pid_t *pid)
-{
- char test_tool_exe[MAX_PATH];
- struct strvec args = STRVEC_INIT;
- int in, out;
-
- GetModuleFileNameA(NULL, test_tool_exe, MAX_PATH);
-
- in = open("/dev/null", O_RDONLY);
- out = open("/dev/null", O_WRONLY);
-
- strvec_push(&args, test_tool_exe);
- strvec_push(&args, "simple-ipc");
- strvec_push(&args, "run-daemon");
- strvec_pushf(&args, "--name=%s", cl_args.path);
- strvec_pushf(&args, "--threads=%d", cl_args.nr_threads);
- *pid = mingw_spawnvpe(args.v[0], args.v, NULL, NULL, in, out, out);
- close(in);
- close(out);
-
- strvec_clear(&args);
-
- if (*pid < 0)
- return error(_("could not spawn daemon in the background"));
-
- return 0;
-}
-#endif
-
-/*
- * This is adapted from `wait_or_whine()`. Watch the child process and
- * let it get started and begin listening for requests on the socket
- * before reporting our success.
- */
-static int wait_for_server_startup(pid_t pid_child)
+static int daemon__start_server(void)
{
- int status;
- pid_t pid_seen;
- enum ipc_active_state s;
- time_t time_limit, now;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ enum start_bg_result sbgr;
- time(&time_limit);
- time_limit += cl_args.max_wait_sec;
+ strvec_push(&cp.args, "test-tool");
+ strvec_push(&cp.args, "simple-ipc");
+ strvec_push(&cp.args, "run-daemon");
+ strvec_pushf(&cp.args, "--name=%s", cl_args.path);
+ strvec_pushf(&cp.args, "--threads=%d", cl_args.nr_threads);
- for (;;) {
- pid_seen = waitpid(pid_child, &status, WNOHANG);
-
- if (pid_seen == -1)
- return error_errno(_("waitpid failed"));
-
- else if (pid_seen == 0) {
- /*
- * The child is still running (this should be
- * the normal case). Try to connect to it on
- * the socket and see if it is ready for
- * business.
- *
- * If there is another daemon already running,
- * our child will fail to start (possibly
- * after a timeout on the lock), but we don't
- * care (who responds) if the socket is live.
- */
- s = ipc_get_active_state(cl_args.path);
- if (s == IPC_STATE__LISTENING)
- return 0;
+ cp.no_stdin = 1;
+ cp.no_stdout = 1;
+ cp.no_stderr = 1;
- time(&now);
- if (now > time_limit)
- return error(_("daemon not online yet"));
+ sbgr = start_bg_command(&cp, bg_wait_cb, NULL, cl_args.max_wait_sec);
- continue;
- }
+ switch (sbgr) {
+ case SBGR_READY:
+ return 0;
- else if (pid_seen == pid_child) {
- /*
- * The new child daemon process shutdown while
- * it was starting up, so it is not listening
- * on the socket.
- *
- * Try to ping the socket in the odd chance
- * that another daemon started (or was already
- * running) while our child was starting.
- *
- * Again, we don't care who services the socket.
- */
- s = ipc_get_active_state(cl_args.path);
- if (s == IPC_STATE__LISTENING)
- return 0;
+ default:
+ case SBGR_ERROR:
+ case SBGR_CB_ERROR:
+ return error("daemon failed to start");
- /*
- * We don't care about the WEXITSTATUS() nor
- * any of the WIF*(status) values because
- * `cmd__simple_ipc()` does the `!!result`
- * trick on all function return values.
- *
- * So it is sufficient to just report the
- * early shutdown as an error.
- */
- return error(_("daemon failed to start"));
- }
+ case SBGR_TIMEOUT:
+ return error("daemon not online yet");
- else
- return error(_("waitpid is confused"));
+ case SBGR_DIED:
+ return error("daemon terminated");
}
}
/*
- * This process will start a simple-ipc server in a background process and
- * wait for it to become ready. This is like `daemonize()` but gives us
- * more control and better error reporting (and makes it easier to write
- * unit tests).
- */
-static int daemon__start_server(void)
-{
- pid_t pid_child;
- int ret;
-
- /*
- * Run the actual daemon in a background process.
- */
- ret = spawn_server(&pid_child);
- if (pid_child <= 0)
- return ret;
-
- /*
- * Let the parent wait for the child process to get started
- * and begin listening for requests on the socket.
- */
- ret = wait_for_server_startup(pid_child);
-
- return ret;
-}
-
-/*
* This process will run a quick probe to see if a simple-ipc server
* is active on this path.
*
@@ -488,7 +383,9 @@ static int client__send_ipc(void)
options.wait_if_busy = 1;
options.wait_if_not_found = 0;
- if (!ipc_client_send_command(cl_args.path, &options, command, &buf)) {
+ if (!ipc_client_send_command(cl_args.path, &options,
+ command, strlen(command),
+ &buf)) {
if (buf.len) {
printf("%s\n", buf.buf);
fflush(stdout);
@@ -538,7 +435,7 @@ static int client__stop_server(void)
time(&now);
if (now > time_limit)
- return error(_("daemon has not shutdown yet"));
+ return error("daemon has not shutdown yet");
}
}
@@ -556,7 +453,9 @@ static int do_sendbytes(int bytecount, char byte, const char *path,
strbuf_addstr(&buf_send, "sendbytes ");
strbuf_addchars(&buf_send, byte, bytecount);
- if (!ipc_client_send_command(path, options, buf_send.buf, &buf_resp)) {
+ if (!ipc_client_send_command(path, options,
+ buf_send.buf, buf_send.len,
+ &buf_resp)) {
strbuf_rtrim(&buf_resp);
printf("sent:%c%08d %s\n", byte, bytecount, buf_resp.buf);
fflush(stdout);
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index e3f11ff5a7..dc1c14bde3 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -11,15 +11,13 @@ static void die_usage(const char **argv, const char *msg)
int cmd__submodule_nested_repo_config(int argc, const char **argv)
{
struct repository subrepo;
- const struct submodule *sub;
if (argc < 3)
die_usage(argv, "Wrong number of arguments.");
setup_git_directory();
- sub = submodule_from_path(the_repository, null_oid(), argv[1]);
- if (repo_submodule_init(&subrepo, the_repository, sub)) {
+ if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid())) {
die_usage(argv, "Submodule not found.");
}
diff --git a/t/lib-diff-data.sh b/t/lib-diff-data.sh
new file mode 100644
index 0000000000..c64ec18324
--- /dev/null
+++ b/t/lib-diff-data.sh
@@ -0,0 +1,22 @@
+COPYING_test_data () {
+ cat <<\EOF
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ HOWEVER, in order to allow a migration to GPLv3 if that seems like
+ a good idea, I also ask that people involved with the project make
+ their preferences known. In particular, if you trust me to make that
+ decision, you might note so in your copyright message, ie something
+ like
+
+ This file is licensed under the GPL v2, or a later version
+ at the discretion of Linus.
+
+ might avoid issues. But we can also just decide to synchronize and
+ contact all copyright holders on record if/when the occasion arises.
+
+ Linus Torvalds
+EOF
+}
diff --git a/t/lib-diff.sh b/t/lib-diff.sh
index 2de880f7a5..c4606bd4b7 100644
--- a/t/lib-diff.sh
+++ b/t/lib-diff.sh
@@ -1,3 +1,5 @@
+. "$TEST_DIRECTORY"/lib-diff-data.sh
+
:
sanitize_diff_raw='/^:/s/ '"\($OID_REGEX\)"' '"\($OID_REGEX\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /'
diff --git a/t/lib-diff/COPYING b/t/lib-diff/COPYING
deleted file mode 100644
index 6ff87c4664..0000000000
--- a/t/lib-diff/COPYING
+++ /dev/null
@@ -1,361 +0,0 @@
-
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
-
- HOWEVER, in order to allow a migration to GPLv3 if that seems like
- a good idea, I also ask that people involved with the project make
- their preferences known. In particular, if you trust me to make that
- decision, you might note so in your copyright message, ie something
- like
-
- This file is licensed under the GPL v2, or a later version
- at the discretion of Linus.
-
- might avoid issues. But we can also just decide to synchronize and
- contact all copyright holders on record if/when the occasion arises.
-
- Linus Torvalds
-
-----------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/t/lib-diff/README b/t/lib-diff/README
deleted file mode 100644
index 548142c327..0000000000
--- a/t/lib-diff/README
+++ /dev/null
@@ -1,46 +0,0 @@
-////////////////////////////////////////////////////////////////
-
- GIT - the stupid content tracker
-
-////////////////////////////////////////////////////////////////
-
-"git" can mean anything, depending on your mood.
-
- - random three-letter combination that is pronounceable, and not
- actually used by any common UNIX command. The fact that it is a
- mispronunciation of "get" may or may not be relevant.
- - stupid. contemptible and despicable. simple. Take your pick from the
- dictionary of slang.
- - "global information tracker": you're in a good mood, and it actually
- works for you. Angels sing, and a light suddenly fills the room.
- - "goddamn idiotic truckload of sh*t": when it breaks
-
-Git is a fast, scalable, distributed revision control system with an
-unusually rich command set that provides both high-level operations
-and full access to internals.
-
-Git is an Open Source project covered by the GNU General Public License.
-It was originally written by Linus Torvalds with help of a group of
-hackers around the net. It is currently maintained by Junio C Hamano.
-
-Please read the file INSTALL for installation instructions.
-See Documentation/tutorial.txt to get started, then see
-Documentation/everyday.txt for a useful minimum set of commands,
-and "man git-commandname" for documentation of each command.
-CVS users may also want to read Documentation/cvs-migration.txt.
-
-Many Git online resources are accessible from http://git.or.cz/
-including full documentation and Git related tools.
-
-The user discussion and development of Git take place on the Git
-mailing list -- everyone is welcome to post bug reports, feature
-requests, comments and patches to git@vger.kernel.org. To subscribe
-to the list, send an email with just "subscribe git" in the body to
-majordomo@vger.kernel.org. The mailing list archives are available at
-http://marc.theaimsgroup.com/?l=git and other archival sites.
-
-The messages titled "A note from the maintainer", "What's in
-git.git (stable)" and "What's cooking in git.git (topics)" and
-the discussion following them on the mailing list give a good
-reference for project status, development direction and
-remaining tasks.
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 9fc5241228..f99ef3e859 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -87,6 +87,34 @@ test_lazy_prereq RFC1991 '
echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null
'
+GPGSSH_KEY_PRIMARY="${GNUPGHOME}/ed25519_ssh_signing_key"
+GPGSSH_KEY_SECONDARY="${GNUPGHOME}/rsa_2048_ssh_signing_key"
+GPGSSH_KEY_UNTRUSTED="${GNUPGHOME}/untrusted_ssh_signing_key"
+GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key"
+GPGSSH_KEY_PASSPHRASE="super_secret"
+GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile"
+
+GPGSSH_GOOD_SIGNATURE_TRUSTED='Good "git" signature for'
+GPGSSH_GOOD_SIGNATURE_UNTRUSTED='Good "git" signature with'
+GPGSSH_KEY_NOT_TRUSTED="No principal matched"
+GPGSSH_BAD_SIGNATURE="Signature verification failed"
+
+test_lazy_prereq GPGSSH '
+ ssh_version=$(ssh-keygen -Y find-principals -n "git" 2>&1)
+ test $? != 127 || exit 1
+ echo $ssh_version | grep -q "find-principals:missing signature file"
+ test $? = 0 || exit 1;
+ mkdir -p "${GNUPGHOME}" &&
+ chmod 0700 "${GNUPGHOME}" &&
+ ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null &&
+ echo "\"principal with number 1\" $(cat "${GPGSSH_KEY_PRIMARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+ ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null &&
+ echo "\"principal with number 2\" $(cat "${GPGSSH_KEY_SECONDARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+ ssh-keygen -t ed25519 -N "${GPGSSH_KEY_PASSPHRASE}" -C "git ed25519 encrypted key" -f "${GPGSSH_KEY_WITH_PASSPHRASE}" >/dev/null &&
+ echo "\"principal with number 3\" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+ ssh-keygen -t ed25519 -N "" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null
+'
+
sanitize_pgp() {
perl -ne '
/^-----END PGP/ and $in_pgp = 0;
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index afa91e38b0..180a41fe96 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -81,8 +81,6 @@ PassEnv GIT_TRACE
PassEnv GIT_CONFIG_NOSYSTEM
PassEnv GIT_TEST_SIDEBAND_ALL
-SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
-
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
@@ -117,6 +115,11 @@ Alias /auth/dumb/ www/auth/dumb/
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
+<LocationMatch /smart_v0/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+ SetEnv GIT_PROTOCOL
+</LocationMatch>
ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
diff --git a/t/lib-midx.sh b/t/lib-midx.sh
new file mode 100644
index 0000000000..1261994744
--- /dev/null
+++ b/t/lib-midx.sh
@@ -0,0 +1,8 @@
+# test_midx_consistent <objdir>
+test_midx_consistent () {
+ ls $1/pack/pack-*.idx | xargs -n 1 basename | sort >expect &&
+ test-tool read-midx $1 | grep ^pack-.*\.idx$ | sort >actual &&
+
+ test_cmp expect actual &&
+ git multi-pack-index --object-dir=$1 verify
+}
diff --git a/t/lib-subtest.sh b/t/lib-subtest.sh
new file mode 100644
index 0000000000..56ee927f0c
--- /dev/null
+++ b/t/lib-subtest.sh
@@ -0,0 +1,95 @@
+write_sub_test_lib_test () {
+ name="$1" # stdin is the body of the test code
+ mkdir "$name" &&
+ write_script "$name/$name.sh" "$TEST_SHELL_PATH" <<-EOF &&
+ test_description='A test of test-lib.sh itself'
+
+ # Point to the t/test-lib.sh, which isn't in ../ as usual
+ . "\$TEST_DIRECTORY"/test-lib.sh
+ EOF
+ cat >>"$name/$name.sh"
+}
+
+_run_sub_test_lib_test_common () {
+ cmp_op="$1" want_code="$2" name="$3" # stdin is the body of the test code
+ shift 3
+
+ # intercept pseudo-options at the front of the argument list that we
+ # will not pass to child script
+ skip=
+ while test $# -gt 0
+ do
+ case "$1" in
+ --skip=*)
+ skip=${1#--*=}
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ (
+ cd "$name" &&
+
+ # Pretend we're not running under a test harness, whether we
+ # are or not. The test-lib output depends on the setting of
+ # this variable, so we need a stable setting under which to run
+ # the sub-test.
+ sane_unset HARNESS_ACTIVE &&
+
+ export TEST_DIRECTORY &&
+ # The child test re-sources GIT-BUILD-OPTIONS and may thus
+ # override the test output directory. We thus pass it as an
+ # explicit override to the child.
+ TEST_OUTPUT_DIRECTORY_OVERRIDE=$(pwd) &&
+ export TEST_OUTPUT_DIRECTORY_OVERRIDE &&
+ GIT_SKIP_TESTS=$skip &&
+ export GIT_SKIP_TESTS &&
+ sane_unset GIT_TEST_FAIL_PREREQS &&
+ ./"$name.sh" "$@" >out 2>err;
+ ret=$? &&
+ test "$ret" "$cmp_op" "$want_code"
+ )
+}
+
+write_and_run_sub_test_lib_test () {
+ name="$1" descr="$2" # stdin is the body of the test code
+ write_sub_test_lib_test "$@" || return 1
+ _run_sub_test_lib_test_common -eq 0 "$@"
+}
+
+write_and_run_sub_test_lib_test_err () {
+ name="$1" descr="$2" # stdin is the body of the test code
+ write_sub_test_lib_test "$@" || return 1
+ _run_sub_test_lib_test_common -eq 1 "$@"
+}
+
+run_sub_test_lib_test () {
+ _run_sub_test_lib_test_common -eq 0 "$@"
+}
+
+run_sub_test_lib_test_err () {
+ _run_sub_test_lib_test_common -eq 1 "$@"
+}
+
+_check_sub_test_lib_test_common () {
+ name="$1" &&
+ sed -e 's/^> //' -e 's/Z$//' >"$name"/expect.out &&
+ test_cmp "$name"/expect.out "$name"/out
+}
+
+check_sub_test_lib_test () {
+ name="$1" # stdin is the expected output from the test
+ _check_sub_test_lib_test_common "$name" &&
+ test_must_be_empty "$name"/err
+}
+
+check_sub_test_lib_test_err () {
+ name="$1" # stdin is the expected output from the test
+ _check_sub_test_lib_test_common "$name" &&
+ # expected error output is in descriptor 3
+ sed -e 's/^> //' -e 's/Z$//' <&3 >"$name"/expect.err &&
+ test_cmp "$name"/expect.err "$name"/err
+}
diff --git a/t/oid-info/oid b/t/oid-info/oid
index a754970523..7547d2c790 100644
--- a/t/oid-info/oid
+++ b/t/oid-info/oid
@@ -27,3 +27,5 @@ numeric sha1:0123456789012345678901234567890123456789
numeric sha256:0123456789012345678901234567890123456789012345678901234567890123
deadbeef sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
deadbeef sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+deadbeef_short sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbee
+deadbeef_short sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index 82c0df4553..575d2000cc 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -17,8 +17,8 @@ sub get_times {
my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
return ($rt, $4, $5);
# size
- } elsif ($line =~ /^\d+$/) {
- return $&;
+ } elsif ($line =~ /^\s*(\d+)$/) {
+ return $1;
} else {
die "bad input line: $line";
}
diff --git a/t/perf/config b/t/perf/config
new file mode 100644
index 0000000000..b92768b039
--- /dev/null
+++ b/t/perf/config
@@ -0,0 +1,2 @@
+[gc]
+ auto = 0
diff --git a/t/perf/p0071-sort.sh b/t/perf/p0071-sort.sh
index 6e924f5fa3..ed366e2e12 100755
--- a/t/perf/p0071-sort.sh
+++ b/t/perf/p0071-sort.sh
@@ -11,16 +11,42 @@ test_expect_success 'setup' '
git cat-file --batch >unsorted
'
-test_perf 'sort(1)' '
- sort <unsorted >expect
+test_perf 'sort(1) unsorted' '
+ sort <unsorted >sorted
'
-test_perf 'string_list_sort()' '
- test-tool string-list sort <unsorted >actual
+test_expect_success 'reverse' '
+ sort -r <unsorted >reversed
'
-test_expect_success 'string_list_sort() sorts like sort(1)' '
- test_cmp_bin expect actual
-'
+for file in sorted reversed
+do
+ test_perf "sort(1) $file" "
+ sort <$file >actual
+ "
+done
+
+for file in unsorted sorted reversed
+do
+
+ test_perf "string_list_sort() $file" "
+ test-tool string-list sort <$file >actual
+ "
+
+ test_expect_success "string_list_sort() $file sorts like sort(1)" "
+ test_cmp_bin sorted actual
+ "
+done
+
+for file in unsorted sorted reversed
+do
+ test_perf "llist_mergesort() $file" "
+ test-tool mergesort sort <$file >actual
+ "
+
+ test_expect_success "llist_mergesort() $file sorts like sort(1)" "
+ test_cmp_bin sorted actual
+ "
+done
test_done
diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh
index 7a0bb29448..43d5a34e8c 100755
--- a/t/perf/p3400-rebase.sh
+++ b/t/perf/p3400-rebase.sh
@@ -18,7 +18,7 @@ test_expect_success 'setup rebasing on top of a lot of changes' '
test_tick &&
git commit -m commit$i unrelated-file$i &&
echo change$i >unrelated-file$i &&
- test_seq 1000 | tac >>unrelated-file$i &&
+ test_seq 1000 | sort -nr >>unrelated-file$i &&
git add unrelated-file$i &&
test_tick &&
git commit -m commit$i-reverse unrelated-file$i ||
diff --git a/t/perf/p5326-multi-pack-bitmaps.sh b/t/perf/p5326-multi-pack-bitmaps.sh
index 5845109ac7..f2fa228f16 100755
--- a/t/perf/p5326-multi-pack-bitmaps.sh
+++ b/t/perf/p5326-multi-pack-bitmaps.sh
@@ -6,15 +6,24 @@ test_description='Tests performance using midx bitmaps'
test_perf_large_repo
-test_expect_success 'enable multi-pack index' '
- git config core.multiPackIndex true
+# we need to create the tag up front such that it is covered by the repack and
+# thus by generated bitmaps.
+test_expect_success 'create tags' '
+ git tag --message="tag pointing to HEAD" perf-tag HEAD
+'
+
+test_expect_success 'start with bitmapped pack' '
+ git repack -adb
'
test_perf 'setup multi-pack index' '
- git repack -ad &&
git multi-pack-index write --bitmap
'
+test_expect_success 'drop pack bitmap' '
+ rm -f .git/objects/pack/pack-*.bitmap
+'
+
test_full_bitmap
test_expect_success 'create partial bitmap state' '
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index f5ed092ee5..780a7402d5 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -27,6 +27,10 @@ TEST_NO_MALLOC_CHECK=t
. ../test-lib.sh
+unset GIT_CONFIG_NOSYSTEM
+GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
+export GIT_CONFIG_SYSTEM
+
if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
then
error "Do not use GIT_TEST_INSTALLED with the perf tests.
@@ -230,6 +234,7 @@ test_perf_ () {
test_ok_ "$1"
fi
"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result
+ rm test_time.*
}
test_perf () {
diff --git a/t/perf/run b/t/perf/run
index d19dec258a..55219aa405 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -74,7 +74,7 @@ set_git_test_installed () {
mydir=$1
mydir_abs=$(cd $mydir && pwd)
- mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers"
+ mydir_abs_wrappers="$mydir_abs/bin-wrappers"
if test -d "$mydir_abs_wrappers"
then
GIT_TEST_INSTALLED=$mydir_abs_wrappers
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index cb87768513..b007f0efef 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -19,6 +19,7 @@ modification *should* take notice and update the test vectors here.
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-subtest.sh
try_local_xy () {
local x="local" y="alsolocal" &&
@@ -66,95 +67,8 @@ test_expect_success 'success is reported like this' '
:
'
-_run_sub_test_lib_test_common () {
- neg="$1" name="$2" descr="$3" # stdin is the body of the test code
- shift 3
-
- # intercept pseudo-options at the front of the argument list that we
- # will not pass to child script
- skip=
- while test $# -gt 0
- do
- case "$1" in
- --skip=*)
- skip=${1#--*=}
- shift
- ;;
- *)
- break
- ;;
- esac
- done
-
- mkdir "$name" &&
- (
- # Pretend we're not running under a test harness, whether we
- # are or not. The test-lib output depends on the setting of
- # this variable, so we need a stable setting under which to run
- # the sub-test.
- sane_unset HARNESS_ACTIVE &&
- cd "$name" &&
- write_script "$name.sh" "$TEST_SHELL_PATH" <<-EOF &&
- test_description='$descr (run in sub test-lib)
-
- This is run in a sub test-lib so that we do not get incorrect
- passing metrics
- '
-
- # Point to the t/test-lib.sh, which isn't in ../ as usual
- . "\$TEST_DIRECTORY"/test-lib.sh
- EOF
- cat >>"$name.sh" &&
- export TEST_DIRECTORY &&
- # The child test re-sources GIT-BUILD-OPTIONS and may thus
- # override the test output directory. We thus pass it as an
- # explicit override to the child.
- TEST_OUTPUT_DIRECTORY_OVERRIDE=$(pwd) &&
- export TEST_OUTPUT_DIRECTORY_OVERRIDE &&
- GIT_SKIP_TESTS=$skip &&
- export GIT_SKIP_TESTS &&
- sane_unset GIT_TEST_FAIL_PREREQS &&
- if test -z "$neg"
- then
- ./"$name.sh" "$@" >out 2>err
- else
- ! ./"$name.sh" "$@" >out 2>err
- fi
- )
-}
-
-run_sub_test_lib_test () {
- _run_sub_test_lib_test_common '' "$@"
-}
-
-run_sub_test_lib_test_err () {
- _run_sub_test_lib_test_common '!' "$@"
-}
-
-check_sub_test_lib_test () {
- name="$1" # stdin is the expected output from the test
- (
- cd "$name" &&
- test_must_be_empty err &&
- sed -e 's/^> //' -e 's/Z$//' >expect &&
- test_cmp expect out
- )
-}
-
-check_sub_test_lib_test_err () {
- name="$1" # stdin is the expected output from the test
- # expected error output is in descriptor 3
- (
- cd "$name" &&
- sed -e 's/^> //' -e 's/Z$//' >expect.out &&
- test_cmp expect.out out &&
- sed -e 's/^> //' -e 's/Z$//' <&3 >expect.err &&
- test_cmp expect.err err
- )
-}
-
-test_expect_success 'pretend we have a fully passing test suite' '
- run_sub_test_lib_test full-pass "3 passing tests" <<-\EOF &&
+test_expect_success 'subtest: 3 passing tests' '
+ write_and_run_sub_test_lib_test full-pass <<-\EOF &&
for i in 1 2 3
do
test_expect_success "passing test #$i" "true"
@@ -170,9 +84,8 @@ test_expect_success 'pretend we have a fully passing test suite' '
EOF
'
-test_expect_success 'pretend we have a partially passing test suite' '
- run_sub_test_lib_test_err \
- partial-pass "2/3 tests passing" <<-\EOF &&
+test_expect_success 'subtest: 2/3 tests passing' '
+ write_and_run_sub_test_lib_test_err partial-pass <<-\EOF &&
test_expect_success "passing test #1" "true"
test_expect_success "failing test #2" "false"
test_expect_success "passing test #3" "true"
@@ -188,8 +101,8 @@ test_expect_success 'pretend we have a partially passing test suite' '
EOF
'
-test_expect_success 'pretend we have a known breakage' '
- run_sub_test_lib_test failing-todo "A failing TODO test" <<-\EOF &&
+test_expect_success 'subtest: a failing TODO test' '
+ write_and_run_sub_test_lib_test failing-todo <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_failure "pretend we have a known breakage" "false"
test_done
@@ -203,8 +116,8 @@ test_expect_success 'pretend we have a known breakage' '
EOF
'
-test_expect_success 'pretend we have fixed a known breakage' '
- run_sub_test_lib_test passing-todo "A passing TODO test" <<-\EOF &&
+test_expect_success 'subtest: a passing TODO test' '
+ write_and_run_sub_test_lib_test passing-todo <<-\EOF &&
test_expect_failure "pretend we have fixed a known breakage" "true"
test_done
EOF
@@ -215,9 +128,8 @@ test_expect_success 'pretend we have fixed a known breakage' '
EOF
'
-test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' '
- run_sub_test_lib_test partially-passing-todos \
- "2 TODO tests, one passing" <<-\EOF &&
+test_expect_success 'subtest: 2 TODO tests, one passin' '
+ write_and_run_sub_test_lib_test partially-passing-todos <<-\EOF &&
test_expect_failure "pretend we have a known breakage" "false"
test_expect_success "pretend we have a passing test" "true"
test_expect_failure "pretend we have fixed another known breakage" "true"
@@ -234,9 +146,8 @@ test_expect_success 'pretend we have fixed one of two known breakages (run in su
EOF
'
-test_expect_success 'pretend we have a pass, fail, and known breakage' '
- run_sub_test_lib_test_err \
- mixed-results1 "mixed results #1" <<-\EOF &&
+test_expect_success 'subtest: mixed results: pass, failure and a TODO test' '
+ write_and_run_sub_test_lib_test_err mixed-results1 <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_success "failing test" "false"
test_expect_failure "pretend we have a known breakage" "false"
@@ -253,9 +164,8 @@ test_expect_success 'pretend we have a pass, fail, and known breakage' '
EOF
'
-test_expect_success 'pretend we have a mix of all possible results' '
- run_sub_test_lib_test_err \
- mixed-results2 "mixed results #2" <<-\EOF &&
+test_expect_success 'subtest: mixed results: a mixture of all possible results' '
+ write_and_run_sub_test_lib_test_err mixed-results2 <<-\EOF &&
test_expect_success "passing test" "true"
test_expect_success "passing test" "true"
test_expect_success "passing test" "true"
@@ -289,9 +199,8 @@ test_expect_success 'pretend we have a mix of all possible results' '
EOF
'
-test_expect_success 'test --verbose' '
- run_sub_test_lib_test_err \
- t1234-verbose "test verbose" --verbose <<-\EOF &&
+test_expect_success 'subtest: --verbose option' '
+ write_and_run_sub_test_lib_test_err t1234-verbose --verbose <<-\EOF &&
test_expect_success "passing test" true
test_expect_success "test with output" "echo foo"
test_expect_success "failing test" false
@@ -316,19 +225,14 @@ test_expect_success 'test --verbose' '
EOF
'
-test_expect_success 'test --verbose-only' '
+test_expect_success 'subtest: --verbose-only option' '
run_sub_test_lib_test_err \
- t2345-verbose-only-2 "test verbose-only=2" \
- --verbose-only=2 <<-\EOF &&
- test_expect_success "passing test" true
- test_expect_success "test with output" "echo foo"
- test_expect_success "failing test" false
- test_done
- EOF
- check_sub_test_lib_test t2345-verbose-only-2 <<-\EOF
+ t1234-verbose \
+ --verbose-only=2 &&
+ check_sub_test_lib_test t1234-verbose <<-\EOF
> ok 1 - passing test
> Z
- > expecting success of 2345.2 '\''test with output'\'': echo foo
+ > expecting success of 1234.2 '\''test with output'\'': echo foo
> foo
> ok 2 - test with output
> Z
@@ -339,18 +243,11 @@ test_expect_success 'test --verbose-only' '
EOF
'
-test_expect_success 'GIT_SKIP_TESTS' '
+test_expect_success 'subtest: skip one with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-basic \
- "GIT_SKIP_TESTS" \
- --skip="git.2" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-basic <<-\EOF
+ run_sub_test_lib_test full-pass \
+ --skip="full.2" &&
+ check_sub_test_lib_test full-pass <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
> ok 3 - passing test #3
@@ -360,10 +257,9 @@ test_expect_success 'GIT_SKIP_TESTS' '
)
'
-test_expect_success 'GIT_SKIP_TESTS several tests' '
+test_expect_success 'subtest: skip several with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-several \
- "GIT_SKIP_TESTS several tests" \
+ write_and_run_sub_test_lib_test git-skip-tests-several \
--skip="git.2 git.5" <<-\EOF &&
for i in 1 2 3 4 5 6
do
@@ -384,18 +280,11 @@ test_expect_success 'GIT_SKIP_TESTS several tests' '
)
'
-test_expect_success 'GIT_SKIP_TESTS sh pattern' '
+test_expect_success 'subtest: sh pattern skipping with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-sh-pattern \
- "GIT_SKIP_TESTS sh pattern" \
- --skip="git.[2-5]" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-sh-pattern <<-\EOF
+ run_sub_test_lib_test git-skip-tests-several \
+ --skip="git.[2-5]" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
> ok 3 # skip passing test #3 (GIT_SKIP_TESTS)
@@ -408,35 +297,23 @@ test_expect_success 'GIT_SKIP_TESTS sh pattern' '
)
'
-test_expect_success 'GIT_SKIP_TESTS entire suite' '
+test_expect_success 'subtest: skip entire test suite with GIT_SKIP_TESTS' '
(
- run_sub_test_lib_test git-skip-tests-entire-suite \
- "GIT_SKIP_TESTS entire suite" \
- --skip="git" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-entire-suite <<-\EOF
+ GIT_SKIP_TESTS="git" && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test git-skip-tests-several \
+ --skip="git" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> 1..0 # SKIP skip all tests in git
EOF
)
'
-test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' '
+test_expect_success 'subtest: GIT_SKIP_TESTS does not skip unmatched suite' '
(
- run_sub_test_lib_test git-skip-tests-unmatched-suite \
- "GIT_SKIP_TESTS does not skip unmatched suite" \
- --skip="notgit" <<-\EOF &&
- for i in 1 2 3
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test git-skip-tests-unmatched-suite <<-\EOF
+ GIT_SKIP_TESTS="notgit" && export GIT_SKIP_TESTS &&
+ run_sub_test_lib_test full-pass \
+ --skip="notfull" &&
+ check_sub_test_lib_test full-pass <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -446,16 +323,9 @@ test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' '
)
'
-test_expect_success '--run basic' '
- run_sub_test_lib_test run-basic \
- "--run basic" --run="1,3,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-basic <<-\EOF
+test_expect_success 'subtest: --run basic' '
+ run_sub_test_lib_test git-skip-tests-several --run="1,3,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -467,16 +337,10 @@ test_expect_success '--run basic' '
EOF
'
-test_expect_success '--run with a range' '
- run_sub_test_lib_test run-range \
- "--run with a range" --run="1-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range <<-\EOF
+test_expect_success 'subtest: --run with a range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -488,16 +352,10 @@ test_expect_success '--run with a range' '
EOF
'
-test_expect_success '--run with two ranges' '
- run_sub_test_lib_test run-two-ranges \
- "--run with two ranges" --run="1-2,5-6" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-two-ranges <<-\EOF
+test_expect_success 'subtest: --run with two ranges' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-2,5-6" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -509,16 +367,10 @@ test_expect_success '--run with two ranges' '
EOF
'
-test_expect_success '--run with a left open range' '
- run_sub_test_lib_test run-left-open-range \
- "--run with a left open range" --run="-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-left-open-range <<-\EOF
+test_expect_success 'subtest: --run with a left open range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 - passing test #3
@@ -530,16 +382,10 @@ test_expect_success '--run with a left open range' '
EOF
'
-test_expect_success '--run with a right open range' '
- run_sub_test_lib_test run-right-open-range \
- "--run with a right open range" --run="4-" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-right-open-range <<-\EOF
+test_expect_success 'subtest: --run with a right open range' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="4-" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 # skip passing test #2 (--run)
> ok 3 # skip passing test #3 (--run)
@@ -551,16 +397,10 @@ test_expect_success '--run with a right open range' '
EOF
'
-test_expect_success '--run with basic negation' '
- run_sub_test_lib_test run-basic-neg \
- "--run with basic negation" --run="!3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-basic-neg <<-\EOF
+test_expect_success 'subtest: --run with basic negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -572,16 +412,10 @@ test_expect_success '--run with basic negation' '
EOF
'
-test_expect_success '--run with two negations' '
- run_sub_test_lib_test run-two-neg \
- "--run with two negations" --run="!3,!6" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-two-neg <<-\EOF
+test_expect_success 'subtest: --run with two negations' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3,!6" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -593,16 +427,10 @@ test_expect_success '--run with two negations' '
EOF
'
-test_expect_success '--run a range and negation' '
- run_sub_test_lib_test run-range-and-neg \
- "--run a range and negation" --run="-4,!2" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range-and-neg <<-\EOF
+test_expect_success 'subtest: --run a range and negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="-4,!2" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -614,16 +442,10 @@ test_expect_success '--run a range and negation' '
EOF
'
-test_expect_success '--run range negation' '
- run_sub_test_lib_test run-range-neg \
- "--run range negation" --run="!1-3" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-range-neg <<-\EOF
+test_expect_success 'subtest: --run range negation' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!1-3" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 # skip passing test #2 (--run)
> ok 3 # skip passing test #3 (--run)
@@ -635,17 +457,10 @@ test_expect_success '--run range negation' '
EOF
'
-test_expect_success '--run include, exclude and include' '
- run_sub_test_lib_test run-inc-neg-inc \
- "--run include, exclude and include" \
- --run="1-5,!1-3,2" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-inc-neg-inc <<-\EOF
+test_expect_success 'subtest: --run include, exclude and include' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1-5,!1-3,2" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -657,17 +472,10 @@ test_expect_success '--run include, exclude and include' '
EOF
'
-test_expect_success '--run include, exclude and include, comma separated' '
- run_sub_test_lib_test run-inc-neg-inc-comma \
- "--run include, exclude and include, comma separated" \
- --run=1-5,!1-3,2 <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-inc-neg-inc-comma <<-\EOF
+test_expect_success 'subtest: --run include, exclude and include, comma separated' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run=1-5,!1-3,2 &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 # skip passing test #1 (--run)
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -679,17 +487,10 @@ test_expect_success '--run include, exclude and include, comma separated' '
EOF
'
-test_expect_success '--run exclude and include' '
- run_sub_test_lib_test run-neg-inc \
- "--run exclude and include" \
- --run="!3-,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-neg-inc <<-\EOF
+test_expect_success 'subtest: --run exclude and include' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="!3-,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 - passing test #2
> ok 3 # skip passing test #3 (--run)
@@ -701,17 +502,10 @@ test_expect_success '--run exclude and include' '
EOF
'
-test_expect_success '--run empty selectors' '
- run_sub_test_lib_test run-empty-sel \
- "--run empty selectors" \
- --run="1,,3,,,5" <<-\EOF &&
- for i in 1 2 3 4 5 6
- do
- test_expect_success "passing test #$i" "true"
- done
- test_done
- EOF
- check_sub_test_lib_test run-empty-sel <<-\EOF
+test_expect_success 'subtest: --run empty selectors' '
+ run_sub_test_lib_test git-skip-tests-several \
+ --run="1,,3,,,5" &&
+ check_sub_test_lib_test git-skip-tests-several <<-\EOF
> ok 1 - passing test #1
> ok 2 # skip passing test #2 (--run)
> ok 3 - passing test #3
@@ -723,9 +517,8 @@ test_expect_success '--run empty selectors' '
EOF
'
-test_expect_success '--run substring selector' '
- run_sub_test_lib_test run-substring-selector \
- "--run empty selectors" \
+test_expect_success 'subtest: --run substring selector' '
+ write_and_run_sub_test_lib_test run-substring-selector \
--run="relevant" <<-\EOF &&
test_expect_success "relevant test" "true"
for i in 1 2 3 4 5 6
@@ -747,9 +540,8 @@ test_expect_success '--run substring selector' '
EOF
'
-test_expect_success '--run keyword selection' '
- run_sub_test_lib_test_err run-inv-range-start \
- "--run invalid range start" \
+test_expect_success 'subtest: --run keyword selection' '
+ write_and_run_sub_test_lib_test_err run-inv-range-start \
--run="a-5" <<-\EOF &&
test_expect_success "passing test #1" "true"
test_done
@@ -762,14 +554,10 @@ test_expect_success '--run keyword selection' '
EOF_ERR
'
-test_expect_success '--run invalid range end' '
- run_sub_test_lib_test_err run-inv-range-end \
- "--run invalid range end" \
- --run="1-z" <<-\EOF &&
- test_expect_success "passing test #1" "true"
- test_done
- EOF
- check_sub_test_lib_test_err run-inv-range-end \
+test_expect_success 'subtest: --run invalid range end' '
+ run_sub_test_lib_test_err run-inv-range-start \
+ --run="1-z" &&
+ check_sub_test_lib_test_err run-inv-range-start \
<<-\EOF_OUT 3<<-EOF_ERR
> FATAL: Unexpected exit with code 1
EOF_OUT
@@ -777,8 +565,8 @@ test_expect_success '--run invalid range end' '
EOF_ERR
'
-test_expect_success 'tests respect prerequisites' '
- run_sub_test_lib_test prereqs "tests respect prereqs" <<-\EOF &&
+test_expect_success 'subtest: tests respect prerequisites' '
+ write_and_run_sub_test_lib_test prereqs <<-\EOF &&
test_set_prereq HAVEIT
test_expect_success HAVEIT "prereq is satisfied" "true"
@@ -807,8 +595,8 @@ test_expect_success 'tests respect prerequisites' '
EOF
'
-test_expect_success 'tests respect lazy prerequisites' '
- run_sub_test_lib_test lazy-prereqs "respect lazy prereqs" <<-\EOF &&
+test_expect_success 'subtest: tests respect lazy prerequisites' '
+ write_and_run_sub_test_lib_test lazy-prereqs <<-\EOF &&
test_lazy_prereq LAZY_TRUE true
test_expect_success LAZY_TRUE "lazy prereq is satisifed" "true"
@@ -831,8 +619,8 @@ test_expect_success 'tests respect lazy prerequisites' '
EOF
'
-test_expect_success 'nested lazy prerequisites' '
- run_sub_test_lib_test nested-lazy "nested lazy prereqs" <<-\EOF &&
+test_expect_success 'subtest: nested lazy prerequisites' '
+ write_and_run_sub_test_lib_test nested-lazy <<-\EOF &&
test_lazy_prereq NESTED_INNER "
>inner &&
@@ -857,9 +645,9 @@ test_expect_success 'nested lazy prerequisites' '
EOF
'
-test_expect_success 'lazy prereqs do not turn off tracing' '
- run_sub_test_lib_test lazy-prereq-and-tracing \
- "lazy prereqs and -x" -v -x <<-\EOF &&
+test_expect_success 'subtest: lazy prereqs do not turn off tracing' '
+ write_and_run_sub_test_lib_test lazy-prereq-and-tracing \
+ -v -x <<-\EOF &&
test_lazy_prereq LAZY true
test_expect_success lazy "test_have_prereq LAZY && echo trace"
@@ -870,8 +658,8 @@ test_expect_success 'lazy prereqs do not turn off tracing' '
grep "echo trace" lazy-prereq-and-tracing/err
'
-test_expect_success 'tests clean up after themselves' '
- run_sub_test_lib_test cleanup "test with cleanup" <<-\EOF &&
+test_expect_success 'subtest: tests clean up after themselves' '
+ write_and_run_sub_test_lib_test cleanup <<-\EOF &&
clean=no
test_expect_success "do cleanup" "
test_when_finished clean=yes
@@ -890,9 +678,9 @@ test_expect_success 'tests clean up after themselves' '
EOF
'
-test_expect_success 'tests clean up even on failures' '
- run_sub_test_lib_test_err \
- failing-cleanup "Failing tests with cleanup commands" <<-\EOF &&
+test_expect_success 'subtest: tests clean up even on failures' '
+ write_and_run_sub_test_lib_test_err \
+ failing-cleanup <<-\EOF &&
test_expect_success "tests clean up even after a failure" "
touch clean-after-failure &&
test_when_finished rm clean-after-failure &&
@@ -919,9 +707,9 @@ test_expect_success 'tests clean up even on failures' '
EOF
'
-test_expect_success 'test_atexit is run' '
- run_sub_test_lib_test_err \
- atexit-cleanup "Run atexit commands" -i <<-\EOF &&
+test_expect_success 'subtest: test_atexit is run' '
+ write_and_run_sub_test_lib_test_err \
+ atexit-cleanup -i <<-\EOF &&
test_expect_success "tests clean up even after a failure" "
> ../../clean-atexit &&
test_atexit rm ../../clean-atexit &&
@@ -1271,28 +1059,29 @@ P=$(test_oid root)
test_expect_success 'git commit-tree records the correct tree in a commit' '
commit0=$(echo NO | git commit-tree $P) &&
- tree=$(git show --pretty=raw $commit0 |
- sed -n -e "s/^tree //p" -e "/^author /q") &&
+ git show --pretty=raw $commit0 >out &&
+ tree=$(sed -n -e "s/^tree //p" -e "/^author /q" out) &&
test "z$tree" = "z$P"
'
test_expect_success 'git commit-tree records the correct parent in a commit' '
commit1=$(echo NO | git commit-tree $P -p $commit0) &&
- parent=$(git show --pretty=raw $commit1 |
- sed -n -e "s/^parent //p" -e "/^author /q") &&
+ git show --pretty=raw $commit1 >out &&
+ parent=$(sed -n -e "s/^parent //p" -e "/^author /q" out) &&
test "z$commit0" = "z$parent"
'
test_expect_success 'git commit-tree omits duplicated parent in a commit' '
commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
- parent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- sort -u) &&
+ git show --pretty=raw $commit2 >out &&
+ cat >match.sed <<-\EOF &&
+ s/^parent //p
+ /^author /q
+ EOF
+ parent=$(sed -n -f match.sed out | sort -u) &&
test "z$commit0" = "z$parent" &&
- numparent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- wc -l) &&
- test $numparent = 1
+ git show --pretty=raw $commit2 >out &&
+ test_stdout_line_count = 1 sed -n -f match.sed out
'
test_expect_success 'update-index D/F conflict' '
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index df544bb321..7603ad2f82 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -2,6 +2,7 @@
test_description='git init'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_config () {
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 8440e6add1..76052cb562 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -7,6 +7,7 @@ Verify that plumbing commands work when .git is a file
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
objpath() {
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 1e4c672b84..b9ed612af1 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -2,6 +2,7 @@
test_description=gitattributes
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
attr_check_basic () {
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
index e3137d638e..2e9d652d82 100755
--- a/t/t0004-unwritable.sh
+++ b/t/t0004-unwritable.sh
@@ -2,6 +2,7 @@
test_description='detect unwritable repository and fail correctly'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
@@ -18,27 +19,66 @@ test_expect_success setup '
test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git write-tree
+ test_must_fail git write-tree 2>out.write-tree
'
-test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' '
+test_lazy_prereq WRITE_TREE_OUT 'test -e "$TRASH_DIRECTORY"/out.write-tree'
+test_expect_success WRITE_TREE_OUT 'write-tree output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ fatal: git-write-tree: error building trees
+ EOF
+ test_cmp expect out.write-tree
+'
+
+test_expect_success POSIXPERM,SANITY,!SANITIZE_LEAK 'commit should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git commit -m second
+ test_must_fail git commit -m second 2>out.commit
+'
+
+test_lazy_prereq COMMIT_OUT 'test -e "$TRASH_DIRECTORY"/out.commit'
+test_expect_success COMMIT_OUT 'commit output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: Error building trees
+ EOF
+ test_cmp expect out.commit
'
test_expect_success POSIXPERM,SANITY 'update-index should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
echo 6O >file &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git update-index file
+ test_must_fail git update-index file 2>out.update-index
+'
+
+test_lazy_prereq UPDATE_INDEX_OUT 'test -e "$TRASH_DIRECTORY"/out.update-index'
+test_expect_success UPDATE_INDEX_OUT 'update-index output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: file: failed to insert into database
+ fatal: Unable to process path file
+ EOF
+ test_cmp expect out.update-index
'
test_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
test_when_finished "chmod 775 .git/objects .git/objects/??" &&
echo b >file &&
chmod a-w .git/objects .git/objects/?? &&
- test_must_fail git add file
+ test_must_fail git add file 2>out.add
+'
+
+test_lazy_prereq ADD_OUT 'test -e "$TRASH_DIRECTORY"/out.add'
+test_expect_success ADD_OUT 'add output on unwritable repository' '
+ cat >expect <<-\EOF &&
+ error: insufficient permission for adding an object to repository database .git/objects
+ error: file: failed to insert into database
+ error: unable to index file '\''file'\''
+ fatal: updating files failed
+ EOF
+ test_cmp expect out.add
'
test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
index 4c214bd11c..a5ec6a0315 100755
--- a/t/t0005-signals.sh
+++ b/t/t0005-signals.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='signals work as we expect'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect <<EOF
diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh
index 88b9ae8158..53af92d571 100755
--- a/t/t0007-git-var.sh
+++ b/t/t0007-git-var.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic sanity checks for git var'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'get GIT_AUTHOR_IDENT' '
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index a594b4aa7d..532637de88 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -2,6 +2,7 @@
test_description=check-ignore
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
init_vars () {
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
index 3941ad2528..eea99107a4 100755
--- a/t/t0009-prio-queue.sh
+++ b/t/t0009-prio-queue.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic tests for priority queue implementation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect <<'EOF'
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index 5657c5a87b..837c8b7228 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -2,6 +2,7 @@
test_description='racy GIT'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This test can give false success if your machine is sufficiently
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 5343ffd3f9..e094975b13 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test hashmap and string hash functions'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_hashmap() {
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index 5679e29c62..91b68c74a1 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -34,6 +34,18 @@ test_expect_success 'basic help commands' '
git help -a >/dev/null
'
+test_expect_success 'invalid usage' '
+ test_expect_code 129 git help -g add &&
+ test_expect_code 129 git help -a -c &&
+
+ test_expect_code 129 git help -g add &&
+ test_expect_code 129 git help -a -g &&
+
+ test_expect_code 129 git help -g -c &&
+ test_expect_code 129 git help --config-for-completion add &&
+ test_expect_code 129 git help --config-sections-for-completion add
+'
+
test_expect_success "works for commands and guides by default" '
configure_help &&
git help status &&
@@ -73,6 +85,59 @@ test_expect_success 'git help -g' '
test_i18ngrep "^ tutorial " help.output
'
+test_expect_success 'git help fails for non-existing html pages' '
+ configure_help &&
+ mkdir html-empty &&
+ test_must_fail git -c help.htmlpath=html-empty help status &&
+ test_must_be_empty test-browser.log
+'
+
+test_expect_success 'git help succeeds without git.html' '
+ configure_help &&
+ mkdir html-with-docs &&
+ touch html-with-docs/git-status.html &&
+ git -c help.htmlpath=html-with-docs help status &&
+ echo "html-with-docs/git-status.html" >expect &&
+ test_cmp expect test-browser.log
+'
+
+test_expect_success 'git help -c' '
+ git help -c >help.output &&
+ cat >expect <<-\EOF &&
+
+ '\''git help config'\'' for more information
+ EOF
+ grep -v -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" \
+ help.output >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git help --config-for-completion' '
+ git help -c >human &&
+ grep -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" human |
+ sed -e "s/\*.*//" -e "s/<.*//" |
+ sort -u >human.munged &&
+
+ git help --config-for-completion >vars &&
+ test_cmp human.munged vars
+'
+
+test_expect_success 'git help --config-sections-for-completion' '
+ git help -c >human &&
+ grep -E \
+ -e "^[^.]+\.[^.]+$" \
+ -e "^[^.]+\.[^.]+\.[^.]+$" human |
+ sed -e "s/\..*//" |
+ sort -u >human.munged &&
+
+ git help --config-sections-for-completion >sections &&
+ test_cmp human.munged sections
+'
+
test_expect_success 'generate builtin list' '
git --list-cmds=builtins >builtins
'
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
index 419f31a8f7..9ad76080aa 100755
--- a/t/t0013-sha1dc.sh
+++ b/t/t0013-sha1dc.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test sha1 collision detection'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
TEST_DATA="$TEST_DIRECTORY/t0013"
diff --git a/t/t0016-oidmap.sh b/t/t0016-oidmap.sh
index 31f8276ba8..0faef1f4f1 100755
--- a/t/t0016-oidmap.sh
+++ b/t/t0016-oidmap.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test oidmap'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# This purposefully is very similar to t0011-hashmap.sh
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index 4a159f99e4..2e42fba956 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -2,6 +2,7 @@
test_description='test env--helper'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index 39e5e4b34f..c13057a4ca 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -2,6 +2,7 @@
test_description='Test advise_if_enabled functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'advice should be printed when config variable is unset' '
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index 7af3fbcc7b..c1a331e9e9 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
@@ -2,6 +2,7 @@
test_description='ignore CR in CRLF sequence while computing similiarity'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
index 4e9fa3cd68..a34de56420 100755
--- a/t/t0024-crlf-archive.sh
+++ b/t/t0024-crlf-archive.sh
@@ -2,6 +2,7 @@
test_description='respect crlf in git archive'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh
index e13363ade5..81447978b7 100755
--- a/t/t0025-crlf-renormalize.sh
+++ b/t/t0025-crlf-renormalize.sh
@@ -2,6 +2,7 @@
test_description='CRLF renormalization'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
index c5203e232c..cdcafcdff7 100755
--- a/t/t0026-eol-config.sh
+++ b/t/t0026-eol-config.sh
@@ -2,6 +2,7 @@
test_description='CRLF conversion'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
has_cr() {
diff --git a/t/t0029-core-unsetenvvars.sh b/t/t0029-core-unsetenvvars.sh
index 24ce46a6ea..b138e1d9cb 100755
--- a/t/t0029-core-unsetenvvars.sh
+++ b/t/t0029-core-unsetenvvars.sh
@@ -2,6 +2,7 @@
test_description='test the Windows-only core.unsetenvvars setting'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq MINGW
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
index 0c24a0f9a3..ae1ca380c1 100755
--- a/t/t0030-stripspace.sh
+++ b/t/t0030-stripspace.sh
@@ -5,6 +5,7 @@
test_description='git stripspace'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
t40='A quick brown fox jumps over the lazy do'
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index ad4746d899..ed2fb620a9 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -5,6 +5,7 @@
test_description='our own option parser'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expect <<\EOF
@@ -37,7 +38,6 @@ String options
--list <str> add str to list
Magic arguments
- --quux means --quux
-NUM set integer to NUM
+ same as -b
--ambiguous positive ambiguity
@@ -169,9 +169,45 @@ test_expect_success 'long options' '
'
test_expect_success 'missing required value' '
- test_expect_code 129 test-tool parse-options -s &&
- test_expect_code 129 test-tool parse-options --string &&
- test_expect_code 129 test-tool parse-options --file
+ cat >expect <<-\EOF &&
+ error: switch `s'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options -s 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `string'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options --string 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `file'\'' requires a value
+ EOF
+ test_expect_code 129 test-tool parse-options --file 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: boolean' '
+ cat >expect <<-\EOF &&
+ error: option `yes'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --yes=hi 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: option `no-yes'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --no-yes=hi 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: cmdmode' '
+ cat >expect <<-\EOF &&
+ error: option `mode1'\'' takes no value
+ EOF
+ test_expect_code 129 test-tool parse-options --mode1=hi 2>actual &&
+ test_cmp expect actual
'
cat >expect <<\EOF
@@ -263,10 +299,6 @@ test_expect_success 'detect possible typos' '
test_cmp typo.err output.err
'
-test_expect_success 'keep some options as arguments' '
- test-tool parse-options --expect="arg 00: --quux" --quux
-'
-
cat >expect <<\EOF
Callback: "four", 0
boolean: 5
diff --git a/t/t0052-simple-ipc.sh b/t/t0052-simple-ipc.sh
index ff98be31a5..1a36a53574 100755
--- a/t/t0052-simple-ipc.sh
+++ b/t/t0052-simple-ipc.sh
@@ -2,6 +2,7 @@
test_description='simple command server'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test-tool simple-ipc SUPPORTS_SIMPLE_IPC || {
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
index 0c6ff567a1..6bada37022 100755
--- a/t/t0055-beyond-symlinks.sh
+++ b/t/t0055-beyond-symlinks.sh
@@ -2,6 +2,7 @@
test_description='update-index and add refuse to add beyond symlinks'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success SYMLINKS setup '
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 7d599675e3..ee281909bc 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -5,6 +5,7 @@
test_description='Test run command'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >hello-script <<-EOF
diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh
index c6ee9f66b1..46d4839194 100755
--- a/t/t0063-string-list.sh
+++ b/t/t0063-string-list.sh
@@ -5,6 +5,7 @@
test_description='Test string list functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_split () {
diff --git a/t/t0064-oid-array.sh b/t/t0064-oid-array.sh
index 2e5438ccda..88c89e8f48 100755
--- a/t/t0064-oid-array.sh
+++ b/t/t0064-oid-array.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='basic tests for the oid array implementation'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
echoid () {
diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh
index 91fa639c4a..94e34c83ed 100755
--- a/t/t0065-strcmp-offset.sh
+++ b/t/t0065-strcmp-offset.sh
@@ -2,6 +2,7 @@
test_description='Test strcmp_offset functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
while read s1 s2 expect
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh
index 92910e4e6c..63a1a45cd3 100755
--- a/t/t0066-dir-iterator.sh
+++ b/t/t0066-dir-iterator.sh
@@ -2,6 +2,7 @@
test_description='Test the dir-iterator functionality'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t0067-parse_pathspec_file.sh b/t/t0067-parse_pathspec_file.sh
index 7bab49f361..0188d0423a 100755
--- a/t/t0067-parse_pathspec_file.sh
+++ b/t/t0067-parse_pathspec_file.sh
@@ -2,6 +2,7 @@
test_description='Test parse_pathspec_file()'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'one item from stdin' '
diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh
index bfb1397d7b..74cc59bf8a 100755
--- a/t/t0069-oidtree.sh
+++ b/t/t0069-oidtree.sh
@@ -1,6 +1,7 @@
#!/bin/sh
test_description='basic tests for the oidtree implementation'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
maxhexsz=$(test_oid hexsz)
diff --git a/t/t0071-sort.sh b/t/t0071-sort.sh
new file mode 100755
index 0000000000..a8ab174879
--- /dev/null
+++ b/t/t0071-sort.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+test_description='verify sort functions'
+
+. ./test-lib.sh
+
+test_expect_success 'llist_mergesort()' '
+ test-tool mergesort test
+'
+
+test_done
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 9bf66c9e68..9067572648 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -195,6 +195,7 @@ test_expect_success 'reset --hard gives cache-tree' '
test_expect_success 'reset --hard without index gives cache-tree' '
rm -f .git/index &&
+ git clean -fd &&
git reset --hard &&
test_cache_tree
'
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 526304ff95..eeedbfa919 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -2,6 +2,7 @@
test_description='git bugreport'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Headers "[System Info]" will be followed by a non-empty line if we put some
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 0cf3a63b75..37c359bd5a 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility (normal target)'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index 6ee8ee3b67..22d0845544 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility (perf target)'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 1529155cf0..6d3374ff77 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test trace2 facility'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Turn off any inherited trace2 settings for this test.
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index ebd5fa5249..698b7159f0 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -9,6 +9,21 @@ test -z "$NO_UNIX_SOCKETS" || {
test_done
}
+uname_s=$(uname -s)
+case $uname_s in
+*MINGW*)
+ test_path_is_socket () {
+ # `test -S` cannot detect Win10's Unix sockets
+ test_path_exists "$1"
+ }
+ ;;
+*)
+ test_path_is_socket () {
+ test -S "$1"
+ }
+ ;;
+esac
+
# don't leave a stale daemon running
test_atexit 'git credential-cache exit'
@@ -21,7 +36,7 @@ test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
rmdir -p .cache/git/credential/
" &&
test_path_is_missing "$HOME/.git-credential-cache" &&
- test -S "$HOME/.cache/git/credential/socket"
+ test_path_is_socket "$HOME/.cache/git/credential/socket"
'
XDG_CACHE_HOME="$HOME/xdg"
@@ -31,7 +46,7 @@ helper_test cache
test_expect_success "use custom XDG_CACHE_HOME if set and default sockets are not created" '
test_when_finished "git credential-cache exit" &&
- test -S "$XDG_CACHE_HOME/git/credential/socket" &&
+ test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket" &&
test_path_is_missing "$HOME/.git-credential-cache/socket" &&
test_path_is_missing "$HOME/.cache/git/credential/socket"
'
@@ -48,7 +63,7 @@ test_expect_success 'credential-cache --socket option overrides default location
username=store-user
password=store-pass
EOF
- test -S "$HOME/dir/socket"
+ test_path_is_socket "$HOME/dir/socket"
'
test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
@@ -62,7 +77,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
username=store-user
password=store-pass
EOF
- test -S "$HOME/.cache/git/credential/socket" &&
+ test_path_is_socket "$HOME/.cache/git/credential/socket" &&
XDG_CACHE_HOME="$HOME/xdg" &&
export XDG_CACHE_HOME &&
check approve cache <<-\EOF &&
@@ -71,7 +86,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
username=store-user
password=store-pass
EOF
- test -S "$XDG_CACHE_HOME/git/credential/socket"
+ test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket"
'
test_expect_success 'use user socket if user directory exists' '
@@ -79,14 +94,15 @@ test_expect_success 'use user socket if user directory exists' '
git credential-cache exit &&
rmdir \"\$HOME/.git-credential-cache/\"
" &&
- mkdir -p -m 700 "$HOME/.git-credential-cache/" &&
+ mkdir -p "$HOME/.git-credential-cache/" &&
+ chmod 700 "$HOME/.git-credential-cache/" &&
check approve cache <<-\EOF &&
protocol=https
host=example.com
username=store-user
password=store-pass
EOF
- test -S "$HOME/.git-credential-cache/socket"
+ test_path_is_socket "$HOME/.git-credential-cache/socket"
'
test_expect_success SYMLINKS 'use user socket if user directory is a symlink to a directory' '
@@ -103,7 +119,7 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to
username=store-user
password=store-pass
EOF
- test -S "$HOME/.git-credential-cache/socket"
+ test_path_is_socket "$HOME/.git-credential-cache/socket"
'
helper_test_timeout cache --timeout=1
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 013c5a7bc3..0e8c0dfbbe 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -71,6 +71,8 @@ In addition:
DF: a special case, where A makes a directory and B makes a file.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 1057a96b24..d1115528cb 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -20,6 +20,8 @@ In the test, these paths are used:
rezrov - in H, deleted in M
yomin - not in H or M
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
index b6111cd150..e0db2066f3 100755
--- a/t/t1003-read-tree-prefix.sh
+++ b/t/t1003-read-tree-prefix.sh
@@ -6,6 +6,7 @@
test_description='git read-tree --prefix test.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 18b3779ccb..658628375c 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -315,39 +315,238 @@ test_expect_success '%(deltabase) reports packed delta bases' '
}
'
-bogus_type="bogus"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+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_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)
+'
+
+for arg1 in '' --allow-unknown-type
+do
+ for arg2 in -s -t -p
+ do
+ if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p"
+ then
+ continue
+ fi
+
+
+ test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" '
+ cat >expect <<-\EOF &&
+ fatal: invalid object type
+ EOF
+
+ if test "$arg1" = "--allow-unknown-type"
+ then
+ git cat-file $arg1 $arg2 $bogus_short_sha1
+ else
+ test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+ fi
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on bogus full OID" '
+ 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
+ EOF
+ else
+ cat >expect <<-EOF
+ error: header for $bogus_long_sha1 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
+ else
+ test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+ fi
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on missing short OID" '
+ cat >expect.err <<-EOF &&
+ fatal: Not a valid object name $(test_oid deadbeef_short)
+ EOF
+ test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
+ test_must_be_empty out
+ '
+
+ test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
+ if test "$arg2" = "-p"
+ then
+ cat >expect.err <<-EOF
+ fatal: Not a valid object name $(test_oid deadbeef)
+ EOF
+ else
+ cat >expect.err <<-\EOF
+ fatal: git cat-file: could not get object info
+ EOF
+ fi &&
+ test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef) >out 2>err.actual &&
+ test_must_be_empty out &&
+ test_cmp expect.err err.actual
+ '
+ done
+done
+
+test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
+ git cat-file -e $bogus_short_sha1
+'
+
+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_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_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_cmp err.expect err.actual
+'
+
+test_expect_success 'broken types combined with --batch and --batch-check' '
+ echo $bogus_short_sha1 >bogus-oid &&
+
+ cat >err.expect <<-\EOF &&
+ fatal: invalid object type
+ EOF
+
+ test_must_fail git cat-file --batch <bogus-oid 2>err.actual &&
+ test_cmp err.expect err.actual &&
+
+ test_must_fail git cat-file --batch-check <bogus-oid 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'the --batch and --batch-check options do not combine with --allow-unknown-type' '
+ test_expect_code 128 git cat-file --batch --allow-unknown-type <bogus-oid &&
+ test_expect_code 128 git cat-file --batch-check --allow-unknown-type <bogus-oid
+'
+
+test_expect_success 'the --allow-unknown-type option does not consider replacement refs' '
+ cat >expect <<-EOF &&
+ $bogus_short_type
+ EOF
+ git cat-file -t --allow-unknown-type $bogus_short_sha1 >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 "rm -rf .git/refs/replace" &&
+ mkdir -p .git/refs/replace &&
+ echo $head >.git/refs/replace/$bogus_short_sha1 &&
+
+ cat >expect <<-EOF &&
+ commit
+ EOF
+ git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+ test_cmp expect actual
+'
test_expect_success "Type of broken object is correct" '
- echo $bogus_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_short_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
test_cmp expect actual
'
test_expect_success "Size of broken object is correct" '
- echo $bogus_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_short_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
test_cmp expect actual
'
-bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success 'clean up broken object' '
+ rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
+'
test_expect_success "Type of broken object is correct when type is large" '
- echo $bogus_type >expect &&
- git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_long_type >expect &&
+ git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
test_cmp expect actual
'
test_expect_success "Size of large broken object is correct when type is large" '
- echo $bogus_size >expect &&
- git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+ echo $bogus_long_size >expect &&
+ git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
test_cmp expect actual
'
+test_expect_success 'clean up broken object' '
+ rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+'
+
+test_expect_success 'cat-file -t and -s on corrupt loose object' '
+ git init --bare corrupt-loose.git &&
+ (
+ cd corrupt-loose.git &&
+
+ # Setup and create the empty blob and its path
+ empty_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$EMPTY_BLOB")) &&
+ git hash-object -w --stdin </dev/null &&
+
+ # Create another blob and its path
+ echo other >other.blob &&
+ other_blob=$(git hash-object -w --stdin <other.blob) &&
+ other_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$other_blob")) &&
+
+ # Before the swap the size is 0
+ cat >out.expect <<-EOF &&
+ 0
+ EOF
+ git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # Swap the two to corrupt the repository
+ mv -f "$other_path" "$empty_path" &&
+ test_must_fail git fsck 2>err.fsck &&
+ grep "hash-path mismatch" err.fsck &&
+
+ # confirm that cat-file is reading the new swapped-in
+ # blob...
+ cat >out.expect <<-EOF &&
+ blob
+ EOF
+ git cat-file -t "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # ... since it has a different size now.
+ cat >out.expect <<-EOF &&
+ 6
+ EOF
+ git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+ test_must_be_empty err.actual &&
+ test_cmp out.expect out.actual &&
+
+ # So far "cat-file" has been happy to spew the found
+ # content out as-is. Try to make it zlib-invalid.
+ mv -f other.blob "$empty_path" &&
+ test_must_fail git fsck 2>err.fsck &&
+ grep "^error: inflate: data stream error (" err.fsck
+ )
+'
+
# Tests for git cat-file --follow-symlinks
test_expect_success 'prep for symlink tests' '
echo_without_newline "$hello_content" >morx &&
@@ -608,4 +807,70 @@ test_expect_success 'cat-file --batch="batman" with --batch-all-objects will wor
cmp expect actual
'
+test_expect_success 'set up replacement object' '
+ orig=$(git rev-parse HEAD) &&
+ git cat-file commit $orig >orig &&
+ {
+ cat orig &&
+ echo extra
+ } >fake &&
+ fake=$(git hash-object -t commit -w fake) &&
+ orig_size=$(git cat-file -s $orig) &&
+ fake_size=$(git cat-file -s $fake) &&
+ git replace $orig $fake
+'
+
+test_expect_success 'cat-file --batch respects replace objects' '
+ git cat-file --batch >actual <<-EOF &&
+ $orig
+ EOF
+ {
+ echo "$orig commit $fake_size" &&
+ cat fake &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cat-file --batch-check respects replace objects' '
+ git cat-file --batch-check >actual <<-EOF &&
+ $orig
+ EOF
+ echo "$orig commit $fake_size" >expect &&
+ test_cmp expect actual
+'
+
+# Pull the entry for object with oid "$1" out of the output of
+# "cat-file --batch", including its object content (which requires
+# parsing and reading a set amount of bytes, hence perl).
+extract_batch_output () {
+ perl -ne '
+ BEGIN { $oid = shift }
+ if (/^$oid \S+ (\d+)$/) {
+ print;
+ read STDIN, my $buf, $1;
+ print $buf;
+ print "\n";
+ }
+ ' "$@"
+}
+
+test_expect_success 'cat-file --batch-all-objects --batch ignores replace' '
+ git cat-file --batch-all-objects --batch >actual.raw &&
+ extract_batch_output $orig <actual.raw >actual &&
+ {
+ echo "$orig commit $orig_size" &&
+ cat orig &&
+ echo
+ } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cat-file --batch-all-objects --batch-check ignores replace' '
+ git cat-file --batch-all-objects --batch-check >actual.raw &&
+ grep ^$orig actual.raw >actual &&
+ echo "$orig commit $orig_size" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
index 2935f68f8d..fc179ac5dd 100755
--- a/t/t1009-read-tree-new-index.sh
+++ b/t/t1009-read-tree-new-index.sh
@@ -5,6 +5,7 @@ test_description='test read-tree into a fresh index file'
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/t1010-mktree.sh b/t/t1010-mktree.sh
index b946f87686..48bfad07ab 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -2,6 +2,7 @@
test_description='git mktree'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
index 57f0770df1..cde93d22cd 100755
--- a/t/t1012-read-tree-df.sh
+++ b/t/t1012-read-tree-df.sh
@@ -2,6 +2,7 @@
test_description='read-tree D/F conflict corner cases'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree.sh
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index b6df7444c0..bfc90d4cf2 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -6,7 +6,6 @@ test_description='read-tree can handle submodules'
. "$TEST_DIRECTORY"/lib-submodule-update.sh
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
test_submodule_switch_recursing_with_args "read-tree -u -m"
diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
index da3376b3bb..8ea8d36818 100755
--- a/t/t1014-read-tree-confusing.sh
+++ b/t/t1014-read-tree-confusing.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='check that read-tree rejects confusing paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create base tree' '
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 71236981e6..272ba1b566 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -206,16 +206,21 @@ test_expect_success 'sparse-checkout disable' '
'
test_expect_success 'sparse-index enabled and disabled' '
- git -C repo sparse-checkout init --cone --sparse-index &&
- test_cmp_config -C repo true index.sparse &&
- test-tool -C repo read-cache --table >cache &&
- grep " tree " cache &&
-
- git -C repo sparse-checkout disable &&
- test-tool -C repo read-cache --table >cache &&
- ! grep " tree " cache &&
- git -C repo config --list >config &&
- ! grep index.sparse config
+ (
+ sane_unset GIT_TEST_SPLIT_INDEX &&
+ git -C repo update-index --no-split-index &&
+
+ git -C repo sparse-checkout init --cone --sparse-index &&
+ test_cmp_config -C repo true index.sparse &&
+ test-tool -C repo read-cache --table >cache &&
+ grep " tree " cache &&
+
+ git -C repo sparse-checkout disable &&
+ test-tool -C repo read-cache --table >cache &&
+ ! grep " tree " cache &&
+ git -C repo config --list >config &&
+ ! grep index.sparse config
+ )
'
test_expect_success 'cone mode: init and set' '
@@ -406,7 +411,7 @@ test_expect_success 'sparse-checkout (init|set|disable) warns with unmerged stat
git -C unmerged sparse-checkout disable
'
-test_expect_success 'sparse-checkout reapply' '
+test_expect_failure 'sparse-checkout reapply' '
git clone repo tweak &&
echo dirty >tweak/deep/deeper2/a &&
@@ -438,6 +443,8 @@ test_expect_success 'sparse-checkout reapply' '
test_i18ngrep "warning.*The following paths are unmerged" err &&
test_path_is_file tweak/folder1/a &&
+ # NEEDSWORK: We are asking to update a file outside of the
+ # sparse-checkout cone, but this is no longer allowed.
git -C tweak add folder1/a &&
git -C tweak sparse-checkout reapply 2>err &&
test_must_be_empty err &&
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 886e78715f..16fbd2c6db 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -187,6 +187,16 @@ test_sparse_match () {
test_cmp sparse-checkout-err sparse-index-err
}
+test_sparse_unstaged () {
+ file=$1 &&
+ for repo in sparse-checkout sparse-index
+ do
+ # Skip "unmerged" paths
+ git -C $repo diff --staged --diff-filter=u -- "$file" >diff &&
+ test_must_be_empty diff || return 1
+ done
+}
+
test_expect_success 'sparse-index contents' '
init_repos &&
@@ -291,6 +301,20 @@ test_expect_success 'add, commit, checkout' '
test_all_match git checkout -
'
+test_expect_success 'add outside sparse cone' '
+ init_repos &&
+
+ run_on_sparse mkdir folder1 &&
+ run_on_sparse ../edit-contents folder1/a &&
+ run_on_sparse ../edit-contents folder1/newfile &&
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_sparse_match test_must_fail git add folder1/newfile &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/newfile
+'
+
test_expect_success 'commit including unstaged changes' '
init_repos &&
@@ -339,18 +363,24 @@ test_expect_success 'status/add: outside sparse cone' '
# Adding the path outside of the sparse-checkout cone should fail.
test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
test_sparse_match test_must_fail git add --refresh folder1/a &&
-
- # NEEDSWORK: Adding a newly-tracked file outside the cone succeeds
- test_sparse_match git add folder1/new &&
-
- test_all_match git add . &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_sparse_match test_must_fail git add folder1/new &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/new &&
+ test_sparse_match git add --sparse folder1/a &&
+ test_sparse_match git add --sparse folder1/new &&
+
+ test_all_match git add --sparse . &&
test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/new &&
test_all_match git rev-parse HEAD^{tree} &&
run_on_all ../edit-contents folder1/newer &&
- test_all_match git add folder1/ &&
+ test_all_match git add --sparse folder1/ &&
test_all_match git status --porcelain=v2 &&
test_all_match git commit -m folder1/newer &&
test_all_match git rev-parse HEAD^{tree}
@@ -484,7 +514,7 @@ test_expect_success 'checkout and reset (mixed) [sparse]' '
test_expect_success 'merge, cherry-pick, and rebase' '
init_repos &&
- for OPERATION in "merge -m merge" cherry-pick rebase
+ for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
do
test_all_match git checkout -B temp update-deep &&
test_all_match git $OPERATION update-folder1 &&
@@ -494,11 +524,6 @@ test_expect_success 'merge, cherry-pick, and rebase' '
done
'
-# NEEDSWORK: This test is documenting current behavior, but that
-# behavior can be confusing to users so there is desire to change it.
-# Right now, users might be using this flow to work through conflicts,
-# so any solution should present advice to users who try this sequence
-# of commands to follow whatever new method we create.
test_expect_success 'merge with conflict outside cone' '
init_repos &&
@@ -513,13 +538,19 @@ test_expect_success 'merge with conflict outside cone' '
test_all_match git status --porcelain=v2 &&
# 2. Add the file with conflict markers
- test_all_match git add folder1/a &&
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_all_match git add --sparse folder1/a &&
test_all_match git status --porcelain=v2 &&
# 3. Rename the file to another sparse filename and
# accept conflict markers as resolved content.
run_on_all mv folder2/a folder2/z &&
- test_all_match git add folder2 &&
+ test_sparse_match test_must_fail git add folder2 &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder2/z &&
+ test_all_match git add --sparse folder2 &&
test_all_match git status --porcelain=v2 &&
test_all_match git merge --continue &&
@@ -544,13 +575,25 @@ test_expect_success 'cherry-pick/rebase with conflict outside cone' '
test_all_match git status --porcelain=v2 &&
# 2. Add the file with conflict markers
- test_all_match git add folder1/a &&
+ # NEEDSWORK: Even though the merge conflict removed the
+ # SKIP_WORKTREE bit from the index entry for folder1/a, we should
+ # warn that this is a problematic add.
+ test_sparse_match test_must_fail git add folder1/a &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder1/a &&
+ test_all_match git add --sparse folder1/a &&
test_all_match git status --porcelain=v2 &&
# 3. Rename the file to another sparse filename and
# accept conflict markers as resolved content.
+ # NEEDSWORK: This mode now fails, because folder2/z is
+ # outside of the sparse-checkout cone and does not match an
+ # existing index entry with the SKIP_WORKTREE bit cleared.
run_on_all mv folder2/a folder2/z &&
- test_all_match git add folder2 &&
+ test_sparse_match test_must_fail git add folder2 &&
+ grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+ test_sparse_unstaged folder2/z &&
+ test_all_match git add --sparse folder2 &&
test_all_match git status --porcelain=v2 &&
test_all_match git $OPERATION --continue &&
@@ -626,6 +669,7 @@ test_expect_success 'clean' '
test_expect_success 'submodule handling' '
init_repos &&
+ test_sparse_match git sparse-checkout add modules &&
test_all_match mkdir modules &&
test_all_match touch modules/a &&
test_all_match git add modules &&
@@ -635,6 +679,7 @@ test_expect_success 'submodule handling' '
test_all_match git commit -m "add submodule" &&
# having a submodule prevents "modules" from collapse
+ test_sparse_match git sparse-checkout set deep/deeper1 &&
test-tool -C sparse-index read-cache --table >cache &&
grep "100644 blob .* modules/a" cache &&
grep "160000 commit $(git -C initial-repo rev-parse HEAD) modules/sub" cache
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
index ae66ba5bab..0f37a43fd3 100755
--- a/t/t1100-commit-tree-options.sh
+++ b/t/t1100-commit-tree-options.sh
@@ -12,6 +12,7 @@ Also make sure that command line parser understands the normal
"flags first and then non flag arguments" command line.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
cat >expected <<EOF
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 4506cd435b..0d4f73acaa 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1598,6 +1598,40 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
test_must_fail git show-ref --verify refs/heads/restart
'
+test_expect_success PIPE 'transaction flushes status updates' '
+ mkfifo in out &&
+ (git update-ref --stdin <in >out &) &&
+
+ exec 9>in &&
+ exec 8<out &&
+ test_when_finished "exec 9>&-" &&
+ test_when_finished "exec 8<&-" &&
+
+ echo "start" >&9 &&
+ echo "start: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual &&
+
+ echo "create refs/heads/flush $A" >&9 &&
+
+ echo prepare >&9 &&
+ echo "prepare: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual &&
+
+ # This must now fail given that we have locked the ref.
+ test_must_fail git update-ref refs/heads/flush $B 2>stderr &&
+ grep "fatal: update_ref failed for ref ${SQ}refs/heads/flush${SQ}: cannot lock ref" stderr &&
+
+ echo commit >&9 &&
+ echo "commit: ok" >expected &&
+ read line <&8 &&
+ echo "$line" >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'directory not created deleting packed ref' '
git branch d1/d2/r1 HEAD &&
git pack-refs --all &&
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index b1839e0877..4c77cf89a6 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -4,6 +4,7 @@ test_description='Test handling of ref names that check-ref-format rejects'
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 '
@@ -170,7 +171,7 @@ test_expect_success 'for-each-ref emits warnings for broken names' '
! grep -e "badname" output &&
! grep -e "broken\.\.\.symref" output &&
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
- test_i18ngrep "ignoring broken ref refs/heads/badname" error &&
+ test_i18ngrep ! "ignoring broken ref refs/heads/badname" error &&
test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error
'
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 5071ac63a5..6337236fd8 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -48,24 +48,70 @@ remove_object () {
rm "$(sha1_file "$1")"
}
-test_expect_success 'object with bad sha1' '
- sha=$(echo blob | git hash-object -w --stdin) &&
- old=$(test_oid_to_path "$sha") &&
- new=$(dirname $old)/$(test_oid ff_2) &&
- sha="$(dirname $new)$(basename $new)" &&
- mv .git/objects/$old .git/objects/$new &&
- test_when_finished "remove_object $sha" &&
- git update-index --add --cacheinfo 100644 $sha foo &&
- test_when_finished "git read-tree -u --reset HEAD" &&
- tree=$(git write-tree) &&
- test_when_finished "remove_object $tree" &&
- cmt=$(echo bogus | git commit-tree $tree) &&
- test_when_finished "remove_object $cmt" &&
- git update-ref refs/heads/bogus $cmt &&
- test_when_finished "git update-ref -d refs/heads/bogus" &&
+test_expect_success 'object with hash mismatch' '
+ git init --bare hash-mismatch &&
+ (
+ cd hash-mismatch &&
- test_must_fail git fsck 2>out &&
- test_i18ngrep "$sha.*corrupt" out
+ oid=$(echo blob | git hash-object -w --stdin) &&
+ oldoid=$oid &&
+ old=$(test_oid_to_path "$oid") &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
+ oid="$(dirname $new)$(basename $new)" &&
+
+ mv objects/$old objects/$new &&
+ git update-index --add --cacheinfo 100644 $oid foo &&
+ tree=$(git write-tree) &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ git update-ref refs/heads/bogus $cmt &&
+
+ test_must_fail git fsck 2>out &&
+ grep "$oldoid: hash-path mismatch, found at: .*$new" out
+ )
+'
+
+test_expect_success 'object with hash and type mismatch' '
+ git init --bare hash-type-mismatch &&
+ (
+ cd hash-type-mismatch &&
+
+ oid=$(echo blob | git hash-object -w --stdin -t garbage --literally) &&
+ oldoid=$oid &&
+ old=$(test_oid_to_path "$oid") &&
+ new=$(dirname $old)/$(test_oid ff_2) &&
+ oid="$(dirname $new)$(basename $new)" &&
+
+ mv objects/$old objects/$new &&
+ git update-index --add --cacheinfo 100644 $oid foo &&
+ tree=$(git write-tree) &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ git update-ref refs/heads/bogus $cmt &&
+
+
+ test_must_fail git fsck 2>out &&
+ grep "^error: $oldoid: hash-path mismatch, found at: .*$new" out &&
+ grep "^error: $oldoid: object is of unknown type '"'"'garbage'"'"'" out
+ )
+'
+
+test_expect_success POSIXPERM 'zlib corrupt loose object output ' '
+ git init --bare corrupt-loose-output &&
+ (
+ cd corrupt-loose-output &&
+ oid=$(git hash-object -w --stdin --literally </dev/null) &&
+ oidf=objects/$(test_oid_to_path "$oid") &&
+ chmod 755 $oidf &&
+ echo extra garbage >>$oidf &&
+
+ cat >expect.error <<-EOF &&
+ error: garbage at end of loose object '\''$oid'\''
+ error: unable to unpack contents of ./$oidf
+ error: $oid: object corrupt or missing: ./$oidf
+ EOF
+ test_must_fail git fsck 2>actual &&
+ grep ^error: actual >error &&
+ test_cmp expect.error error
+ )
'
test_expect_success 'branch pointing to non-commit' '
@@ -865,4 +911,21 @@ test_expect_success 'detect corrupt index file in fsck' '
test_i18ngrep "bad index file" errors
'
+test_expect_success 'fsck error and recovery on invalid object type' '
+ git init --bare garbage-type &&
+ (
+ cd garbage-type &&
+
+ garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
+
+ cat >err.expect <<-\EOF &&
+ fatal: invalid object type
+ EOF
+ test_must_fail git fsck >out 2>err &&
+ grep -e "^error" -e "^fatal" err >errors &&
+ test_line_count = 1 errors &&
+ grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
+ )
+'
+
test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index b29563fc99..284fe18e72 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -282,4 +282,58 @@ test_expect_success 'test --parseopt --stuck-long and short option with unset op
test_cmp expect output
'
+test_expect_success 'test --parseopt help output: "wrapped" options normal "or:" lines' '
+ sed -e "s/^|//" >spec <<-\EOF &&
+ |cmd [--some-option]
+ | [--another-option]
+ |cmd [--yet-another-option]
+ |--
+ |h,help show the help
+ EOF
+
+ sed -e "s/^|//" >expect <<-\END_EXPECT &&
+ |cat <<\EOF
+ |usage: cmd [--some-option]
+ | or: [--another-option]
+ | or: cmd [--yet-another-option]
+ |
+ | -h, --help show the help
+ |
+ |EOF
+ END_EXPECT
+
+ test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'test --parseopt help output: multi-line blurb after empty line' '
+ sed -e "s/^|//" >spec <<-\EOF &&
+ |cmd [--some-option]
+ | [--another-option]
+ |
+ |multi
+ |line
+ |blurb
+ |--
+ |h,help show the help
+ EOF
+
+ sed -e "s/^|//" >expect <<-\END_EXPECT &&
+ |cat <<\EOF
+ |usage: cmd [--some-option]
+ | or: [--another-option]
+ |
+ | multi
+ | line
+ | blurb
+ |
+ | -h, --help show the help
+ |
+ |EOF
+ END_EXPECT
+
+ test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
index 3d51615e42..0fafcf9dde 100755
--- a/t/t1504-ceiling-dirs.sh
+++ b/t/t1504-ceiling-dirs.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test GIT_CEILING_DIRECTORIES'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_prefix() {
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index bbfe05b8e4..591505a39c 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -43,6 +43,7 @@ A few rules for repo setup:
# This test heavily relies on the standard error of nested function calls.
test_untraceable=UnfortunatelyYes
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
here=$(pwd)
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index c9b9e718b8..46329c488b 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -4,6 +4,8 @@ test_description='index file specific tests'
. ./test-lib.sh
+sane_unset GIT_TEST_SPLIT_INDEX
+
test_expect_success 'setup' '
echo 1 >a
'
@@ -13,7 +15,8 @@ test_expect_success 'bogus GIT_INDEX_VERSION issues warning' '
rm -f .git/index &&
GIT_INDEX_VERSION=2bogus &&
export GIT_INDEX_VERSION &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: GIT_INDEX_VERSION set, but the value is invalid.
Using version Z
@@ -27,7 +30,8 @@ test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' '
rm -f .git/index &&
GIT_INDEX_VERSION=1 &&
export GIT_INDEX_VERSION &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: GIT_INDEX_VERSION set, but the value is invalid.
Using version Z
@@ -50,7 +54,8 @@ test_expect_success 'out of bounds index.version issues warning' '
sane_unset GIT_INDEX_VERSION &&
rm -f .git/index &&
git config --add index.version 1 &&
- git add a 2>&1 | sed "s/[0-9]//" >actual.err &&
+ git add a 2>err &&
+ sed "s/[0-9]//" err >actual.err &&
sed -e "s/ Z$/ /" <<-\EOF >expect.err &&
warning: index.version set, but the value is invalid.
Using version Z
@@ -79,7 +84,7 @@ test_index_version () {
else
unset GIT_INDEX_VERSION
fi &&
- git add a 2>&1 &&
+ git add a &&
echo $EXPECTED_OUTPUT_VERSION >expect &&
test-tool index-version <.git/index >actual &&
test_cmp expect actual
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 986baa612e..decd2527ed 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -510,4 +510,38 @@ test_expect_success 'do not refresh null base index' '
)
'
+test_expect_success 'reading split index at alternate location' '
+ git init reading-alternate-location &&
+ (
+ cd reading-alternate-location &&
+ >file-in-alternate &&
+ git update-index --split-index --add file-in-alternate
+ ) &&
+ echo file-in-alternate >expect &&
+
+ # Should be able to find the shared index both right next to
+ # the specified split index file ...
+ GIT_INDEX_FILE=./reading-alternate-location/.git/index \
+ git ls-files --cached >actual &&
+ test_cmp expect actual &&
+
+ # ... and, for backwards compatibility, in the current GIT_DIR
+ # as well.
+ mv -v ./reading-alternate-location/.git/sharedindex.* .git &&
+ GIT_INDEX_FILE=./reading-alternate-location/.git/index \
+ git ls-files --cached >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'GIT_TEST_SPLIT_INDEX works' '
+ git init git-test-split-index &&
+ (
+ cd git-test-split-index &&
+ >file &&
+ GIT_TEST_SPLIT_INDEX=1 git update-index --add file &&
+ ls -l .git/sharedindex.* >actual &&
+ test_line_count = 1 actual
+ )
+'
+
test_done
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
index 70361c806e..fc95cf9048 100755
--- a/t/t2002-checkout-cache-u.sh
+++ b/t/t2002-checkout-cache-u.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index -u test.
With -u flag, git checkout-index internally runs the equivalent of
git update-index --refresh on the checked out entry.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
index ff163cf675..f0fd441d81 100755
--- a/t/t2003-checkout-cache-mkdir.sh
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -10,6 +10,7 @@ also verifies that such leading path may contain symlinks, unlike
the GIT controlled paths.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
index a9352b08a8..9bb503a975 100755
--- a/t/t2004-checkout-cache-temp.sh
+++ b/t/t2004-checkout-cache-temp.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index --temp test.
With --temp flag, git checkout-index writes to temporary merge files
rather than the tracked path.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
index 9fa5610474..112682a45a 100755
--- a/t/t2005-checkout-index-symlinks.sh
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -8,6 +8,7 @@ test_description='git checkout-index on filesystem w/o symlinks test.
This tests that git checkout-index creates a symbolic link as a plain
file if core.symlinks is false.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index 70d69263e6..660132ff8d 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -48,6 +48,7 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' '
test_expect_success SYMLINKS 'the symlink remained' '
+ test_when_finished "rm a/b" &&
test -h a/b
'
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
index 21f4659a9d..1f193cde96 100755
--- a/t/t2050-git-dir-relative.sh
+++ b/t/t2050-git-dir-relative.sh
@@ -12,6 +12,7 @@ into the subdir while keeping the worktree location,
and tries commits from the top and the subdir, checking
that the commit-hook still gets called.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
COMMIT_FILE="$(pwd)/output"
diff --git a/t/t2081-parallel-checkout-collisions.sh b/t/t2081-parallel-checkout-collisions.sh
index f6fcfc0c1e..6acdb89d12 100755
--- a/t/t2081-parallel-checkout-collisions.sh
+++ b/t/t2081-parallel-checkout-collisions.sh
@@ -11,6 +11,7 @@ The tests in this file exercise parallel checkout's collision detection code in
both these mechanics.
"
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-parallel-checkout.sh"
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 45ca35d60a..94c4cb0672 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -129,12 +129,15 @@ test_expect_success 'add -n -u should not add but just report' '
echo "remove '\''top'\''"
) >expect &&
before=$(git ls-files -s check top) &&
+ git count-objects -v >objects_before &&
echo changed >>check &&
rm -f top &&
git add -n -u >actual &&
after=$(git ls-files -s check top) &&
+ git count-objects -v >objects_after &&
test "$before" = "$after" &&
+ test_cmp objects_before objects_after &&
test_cmp expect actual
'
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
index c8de6d8a19..b40eeb263f 100755
--- a/t/t2300-cd-to-toplevel.sh
+++ b/t/t2300-cd-to-toplevel.sh
@@ -2,6 +2,7 @@
test_description='cd_to_toplevel'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
EXEC_PATH="$(git --exec-path)"
diff --git a/t/t2500-untracked-overwriting.sh b/t/t2500-untracked-overwriting.sh
new file mode 100755
index 0000000000..5c0bf4d21f
--- /dev/null
+++ b/t/t2500-untracked-overwriting.sh
@@ -0,0 +1,244 @@
+#!/bin/sh
+
+test_description='Test handling of overwriting untracked files'
+
+. ./test-lib.sh
+
+test_setup_reset () {
+ git init reset_$1 &&
+ (
+ cd reset_$1 &&
+ test_commit init &&
+
+ git branch stable &&
+ git branch work &&
+
+ git checkout work &&
+ test_commit foo &&
+
+ git checkout stable
+ )
+}
+
+test_expect_success 'reset --hard will nuke untracked files/dirs' '
+ test_setup_reset hard &&
+ (
+ cd reset_hard &&
+ git ls-tree -r stable &&
+ git log --all --name-status --oneline &&
+ git ls-tree -r work &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ echo foo >expect &&
+
+ git reset --hard work &&
+
+ # check that untracked directory foo.t/ was nuked
+ test_path_is_file foo.t &&
+ test_cmp expect foo.t
+ )
+'
+
+test_expect_success 'reset --merge will preserve untracked files/dirs' '
+ test_setup_reset merge &&
+ (
+ cd reset_merge &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ cp foo.t/file expect &&
+
+ test_must_fail git reset --merge work 2>error &&
+ test_cmp expect foo.t/file &&
+ grep "Updating .foo.t. would lose untracked files" error
+ )
+'
+
+test_expect_success 'reset --keep will preserve untracked files/dirs' '
+ test_setup_reset keep &&
+ (
+ cd reset_keep &&
+
+ mkdir foo.t &&
+ echo precious >foo.t/file &&
+ cp foo.t/file expect &&
+
+ test_must_fail git reset --merge work 2>error &&
+ test_cmp expect foo.t/file &&
+ grep "Updating.*foo.t.*would lose untracked files" error
+ )
+'
+
+test_setup_checkout_m () {
+ git init checkout &&
+ (
+ cd checkout &&
+ test_commit init &&
+
+ test_write_lines file has some >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git branch stable &&
+
+ git switch -c work &&
+ echo stuff >notes.txt &&
+ test_write_lines file has some words >filler &&
+ git add notes.txt filler &&
+ git commit -m filler &&
+
+ git checkout stable
+ )
+}
+
+test_expect_success 'checkout -m does not nuke untracked file' '
+ test_setup_checkout_m &&
+ (
+ cd checkout &&
+
+ # Tweak filler
+ test_write_lines this file has some >filler &&
+ # Make an untracked file, save its contents in "expect"
+ echo precious >notes.txt &&
+ cp notes.txt expect &&
+
+ test_must_fail git checkout -m work &&
+ test_cmp expect notes.txt
+ )
+'
+
+test_setup_sequencing () {
+ git init sequencing_$1 &&
+ (
+ cd sequencing_$1 &&
+ test_commit init &&
+
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+
+ git branch simple &&
+ git branch fooey &&
+
+ git checkout fooey &&
+ git rm foo/bar/baz.t &&
+ echo stuff >>filler &&
+ git add -u &&
+ git commit -m "changes" &&
+
+ git checkout simple &&
+ echo items >>filler &&
+ echo newstuff >>newfile &&
+ git add filler newfile &&
+ git commit -m another
+ )
+}
+
+test_expect_success 'git rebase --abort and untracked files' '
+ test_setup_sequencing rebase_abort_and_untracked &&
+ (
+ cd sequencing_rebase_abort_and_untracked &&
+ git checkout fooey &&
+ test_must_fail git rebase simple &&
+
+ cat init.t &&
+ git rm init.t &&
+ echo precious >init.t &&
+ cp init.t expect &&
+ git status --porcelain &&
+ test_must_fail git rebase --abort &&
+ test_cmp expect init.t
+ )
+'
+
+test_expect_success 'git rebase fast forwarding and untracked files' '
+ test_setup_sequencing rebase_fast_forward_and_untracked &&
+ (
+ cd sequencing_rebase_fast_forward_and_untracked &&
+ git checkout init &&
+ echo precious >filler &&
+ cp filler expect &&
+ test_must_fail git rebase init simple &&
+ test_cmp expect filler
+ )
+'
+
+test_expect_failure 'git rebase --autostash and untracked files' '
+ test_setup_sequencing rebase_autostash_and_untracked &&
+ (
+ cd sequencing_rebase_autostash_and_untracked &&
+ git checkout simple &&
+ git rm filler &&
+ mkdir filler &&
+ echo precious >filler/file &&
+ cp filler/file expect &&
+ git rebase --autostash init &&
+ test_path_is_file filler/file
+ )
+'
+
+test_expect_failure 'git stash and untracked files' '
+ test_setup_sequencing stash_and_untracked_files &&
+ (
+ cd sequencing_stash_and_untracked_files &&
+ git checkout simple &&
+ git rm filler &&
+ mkdir filler &&
+ echo precious >filler/file &&
+ cp filler/file expect &&
+ git status --porcelain &&
+ git stash push &&
+ git status --porcelain &&
+ test_path_is_file filler/file
+ )
+'
+
+test_expect_success 'git am --abort and untracked dir vs. unmerged file' '
+ test_setup_sequencing am_abort_and_untracked &&
+ (
+ cd sequencing_am_abort_and_untracked &&
+ git format-patch -1 --stdout fooey >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ # Delete the conflicted file; we will stage and commit it later
+ rm filler &&
+
+ # Put an unrelated untracked directory there
+ mkdir filler &&
+ echo foo >filler/file1 &&
+ echo bar >filler/file2 &&
+
+ test_must_fail git am --abort 2>errors &&
+ test_path_is_dir filler &&
+ grep "Updating .filler. would lose untracked files in it" errors
+ )
+'
+
+test_expect_success 'git am --skip and untracked dir vs deleted file' '
+ test_setup_sequencing am_skip_and_untracked &&
+ (
+ cd sequencing_am_skip_and_untracked &&
+ git checkout fooey &&
+ git format-patch -1 --stdout simple >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ # Delete newfile
+ rm newfile &&
+
+ # Put an unrelated untracked directory there
+ mkdir newfile &&
+ echo foo >newfile/file1 &&
+ echo bar >newfile/file2 &&
+
+ # Change our mind about resolutions, just skip this patch
+ test_must_fail git am --skip 2>errors &&
+ test_path_is_dir newfile &&
+ grep "Updating .newfile. would lose untracked files in it" errors
+ )
+'
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index 740ce56eab..11af4552f7 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -15,6 +15,8 @@ filesystem.
path3/file3 - a file in a directory
path4 - an empty directory
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 516c95ea0e..48cec4e5f8 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -8,6 +8,7 @@ test_description='git ls-files --others --exclude
This test runs git ls-files --others and tests --exclude patterns.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
rm -fr one three
@@ -102,7 +103,7 @@ test_expect_success \
>output &&
test_cmp expect output'
-test_expect_success 'restore gitignore' '
+test_expect_success !SANITIZE_LEAK 'restore gitignore' '
git checkout --ignore-skip-worktree-bits $allignores &&
rm .git/index
'
@@ -125,7 +126,7 @@ cat > expect << EOF
# three/
EOF
-test_expect_success 'git status honors core.excludesfile' \
+test_expect_success !SANITIZE_LEAK 'git status honors core.excludesfile' \
'test_cmp expect output'
test_expect_success 'trailing slash in exclude allows directory match(1)' '
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 8704b04e1b..54d22a45df 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -12,6 +12,8 @@ filesystem.
-foo - a file with a funny name.
-- - another file with a funny name.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
index c41c4f046a..7933dff9b3 100755
--- a/t/t3003-ls-files-exclude.sh
+++ b/t/t3003-ls-files-exclude.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='ls-files --exclude does not affect index files'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create repo with file' '
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index 9fd5a1f188..a16e25c79b 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -6,6 +6,7 @@ This test runs git ls-files with various unusual or malformed
command-line arguments.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'ls-files in empty repository' '
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
index 727e9ae1a4..6ba8b589cd 100755
--- a/t/t3005-ls-files-relative.sh
+++ b/t/t3005-ls-files-relative.sh
@@ -5,6 +5,7 @@ test_description='ls-files tests with relative paths
This test runs git ls-files with various relative path arguments.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'prepare' '
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
index e109c3fbfb..2aaf91ebc8 100755
--- a/t/t3006-ls-files-long.sh
+++ b/t/t3006-ls-files-long.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='overly long paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh
index 85f3704958..51d3dffaa6 100755
--- a/t/t3008-ls-files-lazy-init-name-hash.sh
+++ b/t/t3008-ls-files-lazy-init-name-hash.sh
@@ -2,6 +2,7 @@
test_description='Test the lazy init name hash with various folder structures'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if test 1 -eq $(test-tool online-cpus)
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
index 124e73b8e6..2cbcbc0721 100755
--- a/t/t3020-ls-files-error-unmatch.sh
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -9,6 +9,8 @@ This test runs git ls-files --error-unmatch to ensure it correctly
returns an error when a non-existent path is provided on the command
line.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 56ea4bda13..72d5b014d8 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -2,6 +2,7 @@
test_description='wildmatch tests'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Disable expensive chain-lint tests; all of the tests in this script
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
index 18baf49a49..436de44971 100755
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -16,6 +16,8 @@ This test runs git ls-tree with the following in a tree.
The new path restriction code should do the right thing for path2 and
path2/baz. Also path0/ should snow nothing.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success \
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
index 12bf31022a..05fde64225 100755
--- a/t/t3101-ls-tree-dirname.sh
+++ b/t/t3101-ls-tree-dirname.sh
@@ -19,6 +19,8 @@ This test runs git ls-tree with the following in a tree.
Test the handling of multiple directories which have matching file
entries. Also test odd filename and missing entries handling.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh
index 1e16c6b8ea..3942db2290 100755
--- a/t/t3102-ls-tree-wildcards.sh
+++ b/t/t3102-ls-tree-wildcards.sh
@@ -2,6 +2,7 @@
test_description='ls-tree with(out) globs'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
index 14520913af..d18ba1bd84 100755
--- a/t/t3103-ls-tree-misc.sh
+++ b/t/t3103-ls-tree-misc.sh
@@ -7,6 +7,7 @@ Miscellaneous tests for git ls-tree.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
diff --git a/t/t3205-branch-color.sh b/t/t3205-branch-color.sh
index 08bd906173..6a521c1a3e 100755
--- a/t/t3205-branch-color.sh
+++ b/t/t3205-branch-color.sh
@@ -4,6 +4,7 @@ test_description='basic branch output coloring'
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 'set up some sample branches' '
diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh
index 37b9d26f4b..9cbc34fc58 100755
--- a/t/t3211-peel-ref.sh
+++ b/t/t3211-peel-ref.sh
@@ -4,6 +4,7 @@ test_description='tests for the peel_ref optimization of packed-refs'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'create annotated tag in refs/tags' '
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index f5bf16abcd..d3ac826283 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -9,6 +9,7 @@ This test tries pathnames with funny characters in the working
tree, index, and tree objects.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
HT=' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index d877872e8f..12eb226957 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -297,6 +297,7 @@ test_expect_success 'abort with error when new base cannot be checked out' '
output &&
test_i18ngrep "file1" output &&
test_path_is_missing .git/rebase-merge &&
+ rm file1 &&
git reset --hard HEAD^
'
@@ -351,82 +352,6 @@ test_expect_success 'retain authorship when squashing' '
git show HEAD | grep "^Author: Twerp Snog"
'
-test_expect_success REBASE_P '-p handles "no changes" gracefully' '
- HEAD=$(git rev-parse HEAD) &&
- git rebase -i -p HEAD^ &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test $HEAD = $(git rev-parse HEAD)
-'
-
-test_expect_failure REBASE_P 'exchange two commits with -p' '
- git checkout H &&
- (
- set_fake_editor &&
- FAKE_LINES="2 1" git rebase -i -p HEAD~2
- ) &&
- test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
- test G = $(git cat-file commit HEAD | sed -ne \$p)
-'
-
-test_expect_success REBASE_P 'preserve merges with -p' '
- git checkout -b to-be-preserved primary^ &&
- : > unrelated-file &&
- git add unrelated-file &&
- test_tick &&
- git commit -m "unrelated" &&
- git checkout -b another-branch primary &&
- echo B > file1 &&
- test_tick &&
- git commit -m J file1 &&
- test_tick &&
- git merge to-be-preserved &&
- echo C > file1 &&
- test_tick &&
- git commit -m K file1 &&
- echo D > file1 &&
- test_tick &&
- git commit -m L1 file1 &&
- git checkout HEAD^ &&
- echo 1 > unrelated-file &&
- test_tick &&
- git commit -m L2 unrelated-file &&
- test_tick &&
- git merge another-branch &&
- echo E > file1 &&
- test_tick &&
- git commit -m M file1 &&
- git checkout -b to-be-rebased &&
- test_tick &&
- git rebase -i -p --onto branch1 primary &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test_cmp_rev HEAD~6 branch1 &&
- test_cmp_rev HEAD~4^2 to-be-preserved &&
- test_cmp_rev HEAD^^2^ HEAD^^^ &&
- test $(git show HEAD~5:file1) = B &&
- test $(git show HEAD~3:file1) = C &&
- test $(git show HEAD:file1) = E &&
- test $(git show HEAD:unrelated-file) = 1
-'
-
-test_expect_success REBASE_P 'edit ancestor with -p' '
- (
- set_fake_editor &&
- FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3
- ) &&
- echo 2 > unrelated-file &&
- test_tick &&
- git commit -m L2-modified --amend unrelated-file &&
- git rebase --continue &&
- git update-index --refresh &&
- git diff-files --quiet &&
- git diff-index --quiet --cached HEAD -- &&
- test $(git show HEAD:unrelated-file) = 2
-'
-
test_expect_success '--continue tries to commit' '
git reset --hard D &&
test_tick &&
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 7c381fbc89..ebbaed147a 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -7,77 +7,77 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-### Test that we handle space characters properly
-work_dir="$(pwd)/test dir"
-
test_expect_success setup '
- mkdir -p "$work_dir" &&
- cd "$work_dir" &&
- git init &&
- echo a > a &&
- git add a &&
- git commit -m a &&
+ test_commit a a a &&
git branch to-rebase &&
- echo b > a &&
- git commit -a -m b &&
- echo c > a &&
- git commit -a -m c &&
+ test_commit --annotate b a b &&
+ test_commit --annotate c a c &&
git checkout to-rebase &&
- echo d > a &&
- git commit -a -m "merge should fail on this" &&
- echo e > a &&
- git commit -a -m "merge should fail on this, too" &&
- git branch pre-rebase
+ test_commit "merge should fail on this" a d d &&
+ test_commit --annotate "merge should fail on this, too" a e pre-rebase
'
+# Check that HEAD is equal to "pre-rebase" and the current branch is
+# "to-rebase"
+check_head() {
+ test_cmp_rev HEAD pre-rebase^{commit} &&
+ test "$(git symbolic-ref HEAD)" = refs/heads/to-rebase
+}
+
testrebase() {
type=$1
- dotest=$2
+ state_dir=$2
test_expect_success "rebase$type --abort" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ check_head &&
+ test_path_is_missing "$state_dir"
'
test_expect_success "rebase$type --abort after --skip" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
test_must_fail git rebase --skip &&
- test $(git rev-parse HEAD) = $(git rev-parse main) &&
+ test_cmp_rev HEAD main &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ check_head &&
+ test_path_is_missing "$state_dir"
'
test_expect_success "rebase$type --abort after --continue" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
- test_path_is_dir "$dotest" &&
+ test_path_is_dir "$state_dir" &&
echo c > a &&
echo d >> a &&
git add a &&
test_must_fail git rebase --continue &&
- test $(git rev-parse HEAD) != $(git rev-parse main) &&
+ test_cmp_rev ! HEAD main &&
+ git rebase --abort &&
+ check_head &&
+ test_path_is_missing "$state_dir"
+ '
+
+ test_expect_success "rebase$type --abort when checking out a tag" '
+ test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" &&
+ git reset --hard a -- &&
+ test_must_fail git rebase$type --onto b c pre-rebase &&
+ test_cmp_rev HEAD b^{commit} &&
git rebase --abort &&
- test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
- test ! -d "$dotest"
+ test_cmp_rev HEAD pre-rebase^{commit} &&
+ ! git symbolic-ref HEAD
'
test_expect_success "rebase$type --abort does not update reflog" '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
git reflog show to-rebase > reflog_before &&
@@ -89,7 +89,6 @@ testrebase() {
'
test_expect_success 'rebase --abort can not be used with other options' '
- cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
test_must_fail git rebase$type main &&
@@ -97,33 +96,21 @@ testrebase() {
test_must_fail git rebase --abort -v &&
git rebase --abort
'
+
+ test_expect_success "rebase$type --quit" '
+ test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type main &&
+ test_path_is_dir $state_dir &&
+ head_before=$(git rev-parse HEAD) &&
+ git rebase --quit &&
+ test_cmp_rev HEAD $head_before &&
+ test_path_is_missing .git/rebase-apply
+ '
}
testrebase " --apply" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --apply --quit' '
- cd "$work_dir" &&
- # Clean up the state from the previous one
- git reset --hard pre-rebase &&
- test_must_fail git rebase --apply main &&
- test_path_is_dir .git/rebase-apply &&
- head_before=$(git rev-parse HEAD) &&
- git rebase --quit &&
- test $(git rev-parse HEAD) = $head_before &&
- test ! -d .git/rebase-apply
-'
-
-test_expect_success 'rebase --merge --quit' '
- cd "$work_dir" &&
- # Clean up the state from the previous one
- git reset --hard pre-rebase &&
- test_must_fail git rebase --merge main &&
- test_path_is_dir .git/rebase-merge &&
- head_before=$(git rev-parse HEAD) &&
- git rebase --quit &&
- test $(git rev-parse HEAD) = $head_before &&
- test ! -d .git/rebase-merge
-'
-
test_done
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index ab0960e6d9..cde3562e3a 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -55,14 +55,4 @@ test_expect_success rebase '
test_cmp expect actual
'
-test_expect_success REBASE_P rebasep '
-
- git checkout side-merge &&
- git rebase -p side &&
- git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
- git cat-file commit side-merge-original | sed -e "1,/^\$/d" >expect &&
- test_cmp expect actual
-
-'
-
test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
deleted file mode 100755
index ec8062a66a..0000000000
--- a/t/t3409-rebase-preserve-merges.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/sh
-#
-# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson
-#
-test_description='git rebase -p should preserve merges
-
-Run "git rebase -p" and check that merges are properly carried along
-'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-GIT_AUTHOR_EMAIL=bogus_email_address
-export GIT_AUTHOR_EMAIL
-
-# Clone 2 (conflicting merge):
-#
-# A1--A2--B3 <-- origin/main
-# \ \
-# B1------M <-- topic
-# \
-# B2 <-- origin/topic
-#
-# Clone 3 (no-ff merge):
-#
-# A1--A2--B3 <-- origin/main
-# \
-# B1------M <-- topic
-# \ /
-# \--A3 <-- topic2
-# \
-# B2 <-- origin/topic
-#
-# Clone 4 (same as Clone 3)
-
-test_expect_success 'setup for merge-preserving rebase' \
- 'echo First > A &&
- git add A &&
- git commit -m "Add A1" &&
- git checkout -b topic &&
- echo Second > B &&
- git add B &&
- git commit -m "Add B1" &&
- git checkout -f main &&
- echo Third >> A &&
- git commit -a -m "Modify A2" &&
- echo Fifth > B &&
- git add B &&
- git commit -m "Add different B" &&
-
- git clone ./. clone2 &&
- (
- cd clone2 &&
- git checkout -b topic origin/topic &&
- test_must_fail git merge origin/main &&
- echo Resolved >B &&
- git add B &&
- git commit -m "Merge origin/main into topic"
- ) &&
-
- git clone ./. clone3 &&
- (
- cd clone3 &&
- git checkout -b topic2 origin/topic &&
- echo Sixth > A &&
- git commit -a -m "Modify A3" &&
- git checkout -b topic origin/topic &&
- git merge --no-ff topic2
- ) &&
-
- git clone ./. clone4 &&
- (
- cd clone4 &&
- git checkout -b topic2 origin/topic &&
- echo Sixth > A &&
- git commit -a -m "Modify A3" &&
- git checkout -b topic origin/topic &&
- git merge --no-ff topic2
- ) &&
-
- git checkout topic &&
- echo Fourth >> B &&
- git commit -a -m "Modify B2"
-'
-
-test_expect_success '--continue works after a conflict' '
- (
- cd clone2 &&
- git fetch &&
- test_must_fail git rebase -p origin/topic &&
- test 2 = $(git ls-files B | wc -l) &&
- echo Resolved again > B &&
- test_must_fail git rebase --continue &&
- grep "^@@@ " .git/rebase-merge/patch &&
- git add B &&
- git rebase --continue &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Add different" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge origin" | wc -l)
- )
-'
-
-test_expect_success 'rebase -p preserves no-ff merges' '
- (
- cd clone3 &&
- git fetch &&
- git rebase -p origin/topic &&
- test 3 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge branch" | wc -l)
- )
-'
-
-test_expect_success 'rebase -p ignores merge.log config' '
- (
- cd clone4 &&
- git fetch &&
- git -c merge.log=1 rebase -p origin/topic &&
- echo >expected &&
- git log --format="%b" -1 >current &&
- test_cmp expected current
- )
-'
-
-test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
deleted file mode 100755
index 2e29866993..0000000000
--- a/t/t3410-rebase-preserve-dropped-merges.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Stephen Haberman
-#
-
-test_description='git rebase preserve merges
-
-This test runs git rebase with preserve merges and ensures commits
-dropped by the --cherry-pick flag have their childrens parents
-rewritten.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-# set up two branches like this:
-#
-# A - B - C - D - E
-# \
-# F - G - H
-# \
-# I
-#
-# where B, D and G touch the same file.
-
-test_expect_success 'setup' '
- test_commit A file1 &&
- test_commit B file1 1 &&
- test_commit C file2 &&
- test_commit D file1 2 &&
- test_commit E file3 &&
- git checkout A &&
- test_commit F file4 &&
- test_commit G file1 3 &&
- test_commit H file5 &&
- git checkout F &&
- test_commit I file6
-'
-
-# A - B - C - D - E
-# \ \ \
-# F - G - H -- L \ --> L
-# \ | \
-# I -- G2 -- J -- K I -- K
-# G2 = same changes as G
-test_expect_success 'skip same-resolution merges with -p' '
- git checkout H &&
- test_must_fail git merge E &&
- test_commit L file1 23 &&
- git checkout I &&
- test_commit G2 file1 3 &&
- test_must_fail git merge E &&
- test_commit J file1 23 &&
- test_commit K file7 file7 &&
- git rebase -i -p L &&
- test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
- test "23" = "$(cat file1)" &&
- test "I" = "$(cat file6)" &&
- test "file7" = "$(cat file7)"
-'
-
-# A - B - C - D - E
-# \ \ \
-# F - G - H -- L2 \ --> L2
-# \ | \
-# I -- G3 --- J2 -- K2 I -- G3 -- K2
-# G2 = different changes as G
-test_expect_success 'keep different-resolution merges with -p' '
- git checkout H &&
- test_must_fail git merge E &&
- test_commit L2 file1 23 &&
- git checkout I &&
- test_commit G3 file1 4 &&
- test_must_fail git merge E &&
- test_commit J2 file1 24 &&
- test_commit K2 file7 file7 &&
- test_must_fail git rebase -i -p L2 &&
- echo 234 > file1 &&
- git add file1 &&
- git rebase --continue &&
- test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
- test "234" = "$(cat file1)" &&
- test "I" = "$(cat file6)" &&
- test "file7" = "$(cat file7)"
-'
-
-test_done
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
deleted file mode 100755
index fb45e7bf7b..0000000000
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2008 Stephen Haberman
-#
-
-test_description='git rebase preserve merges
-
-This test runs git rebase with -p and tries to squash a commit from after
-a merge to before the merge.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-. "$TEST_DIRECTORY"/lib-rebase.sh
-
-set_fake_editor
-
-# set up two branches like this:
-#
-# A1 - B1 - D1 - E1 - F1
-# \ /
-# -- C1 --
-
-test_expect_success 'setup' '
- test_commit A1 &&
- test_commit B1 &&
- test_commit C1 &&
- git reset --hard B1 &&
- test_commit D1 &&
- test_merge E1 C1 &&
- test_commit F1
-'
-
-# Should result in:
-#
-# A1 - B1 - D2 - E2
-# \ /
-# -- C1 --
-#
-test_expect_success 'squash F1 into D1' '
- FAKE_LINES="1 squash 4 2 3" git rebase -i -p B1 &&
- test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
- test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
- git tag E2
-'
-
-# Start with:
-#
-# A1 - B1 - D2 - E2
-# \
-# G1 ---- L1 ---- M1
-# \ /
-# H1 -- J1 -- K1
-# \ /
-# -- I1 --
-#
-# And rebase G1..M1 onto E2
-
-test_expect_success 'rebase two levels of merge' '
- git checkout A1 &&
- test_commit G1 &&
- test_commit H1 &&
- test_commit I1 &&
- git checkout -b branch3 H1 &&
- test_commit J1 &&
- test_merge K1 I1 &&
- git checkout -b branch2 G1 &&
- test_commit L1 &&
- test_merge M1 K1 &&
- GIT_EDITOR=: git rebase -i -p E2 &&
- test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
- test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
- test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)"
-'
-
-test_done
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index fda62c65bd..19c6f4acbf 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -89,17 +89,6 @@ test_expect_success 'pre-rebase got correct input (4)' '
test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4
'
-test_expect_success REBASE_P 'rebase -i -p with linear history' '
- git checkout -b work5 other &&
- git rebase -i -p --root --onto main &&
- git log --pretty=tformat:"%s" > rebased5 &&
- test_cmp expect rebased5
-'
-
-test_expect_success REBASE_P 'pre-rebase got correct input (5)' '
- test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
-'
-
test_expect_success 'set up merge history' '
git checkout other^ &&
git checkout -b side &&
@@ -123,13 +112,6 @@ commit work6~4
1
EOF
-test_expect_success REBASE_P 'rebase -i -p with merge' '
- git checkout -b work6 other &&
- git rebase -i -p --root --onto main &&
- log_with_names work6 > rebased6 &&
- test_cmp expect-side rebased6
-'
-
test_expect_success 'set up second root and merge' '
git symbolic-ref HEAD refs/heads/third &&
rm .git/index &&
@@ -158,13 +140,6 @@ commit work7~5
1
EOF
-test_expect_success REBASE_P 'rebase -i -p with two roots' '
- git checkout -b work7 other &&
- git rebase -i -p --root --onto main &&
- log_with_names work7 > rebased7 &&
- test_cmp expect-third rebased7
-'
-
test_expect_success 'setup pre-rebase hook that fails' '
mkdir -p .git/hooks &&
cat >.git/hooks/pre-rebase <<EOF &&
@@ -264,21 +239,9 @@ commit conflict3~6
1
EOF
-test_expect_success REBASE_P 'rebase -i -p --root with conflict (first part)' '
- git checkout -b conflict3 other &&
- test_must_fail git rebase -i -p --root --onto main &&
- git ls-files -u | grep "B$"
-'
-
test_expect_success 'fix the conflict' '
echo 3 > B &&
git add B
'
-test_expect_success REBASE_P 'rebase -i -p --root with conflict (second part)' '
- git rebase --continue &&
- log_with_names conflict3 >out &&
- test_cmp expect-conflict-p out
-'
-
test_done
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
deleted file mode 100755
index 72e04b5386..0000000000
--- a/t/t3414-rebase-preserve-onto.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2009 Greg Price
-#
-
-test_description='git rebase -p should respect --onto
-
-In a rebase with --onto, we should rewrite all the commits that
-aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
-'
-. ./test-lib.sh
-
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-. "$TEST_DIRECTORY"/lib-rebase.sh
-
-# Set up branches like this:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# H1
-
-test_expect_success 'setup' '
- test_commit A1 &&
- test_commit B1 &&
- test_commit C1 &&
- test_commit D1 &&
- git reset --hard B1 &&
- test_commit E1 &&
- test_commit F1 &&
- test_merge G1 D1 &&
- git reset --hard A1 &&
- test_commit H1
-'
-
-# Now rebase merge G1 from both branches' base B1, both should move:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# \
-# H1---E2---F2---G2
-# \ /
-# \--C2---D2--/
-
-test_expect_success 'rebase from B1 onto H1' '
- git checkout G1 &&
- git rebase -p --onto H1 B1 &&
- test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
-'
-
-# On the other hand if rebase from E1 which is within one branch,
-# then the other branch stays:
-# A1---B1---E1---F1---G1
-# \ \ /
-# \ \--C1---D1--/
-# \ \
-# H1-----F3-----G3
-
-test_expect_success 'rebase from E1 onto H1' '
- git checkout G1 &&
- git rebase -p --onto H1 E1 &&
- test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
-'
-
-# And the same if we rebase from a commit in the second-parent branch.
-# A1---B1---E1---F1----G1
-# \ \ \ /
-# \ \--C1---D1-\-/
-# \ \
-# H1------D3------G4
-
-test_expect_success 'rebase from C1 onto H1' '
- git checkout G1 &&
- git rev-list --first-parent --pretty=oneline C1..G1 &&
- git rebase -p --onto H1 C1 &&
- test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
- test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
-'
-
-test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 738fbae9b2..22eca73aa3 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -119,20 +119,6 @@ test_expect_success 'rebase -i --continue handles merge strategy and options' '
test -f funny.was.run
'
-test_expect_success REBASE_P 'rebase passes merge strategy options correctly' '
- rm -fr .git/rebase-* &&
- git reset --hard commit-new-file-F3-on-topic-branch &&
- test_commit theirs-to-merge &&
- git reset --hard HEAD^ &&
- test_commit some-commit &&
- test_tick &&
- git merge --no-ff theirs-to-merge &&
- FAKE_LINES="1 edit 2 3" git rebase -i -f -p -m \
- -s recursive --strategy-option=theirs HEAD~2 &&
- test_commit force-change &&
- git rebase --continue
-'
-
test_expect_success 'rebase -r passes merge strategy options correctly' '
rm -fr .git/rebase-* &&
git reset --hard commit-new-file-F3-on-topic-branch &&
@@ -268,7 +254,6 @@ test_rerere_autoupdate --apply
test_rerere_autoupdate -m
GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
test_rerere_autoupdate -i
-test_have_prereq !REBASE_P || test_rerere_autoupdate --preserve-merges
unset GIT_SEQUENCE_EDITOR
test_expect_success 'the todo command "break" works' '
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 4a9204b4b6..62d86d557d 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -29,7 +29,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_expect_success 'setup branches and remote tracking' '
git tag -l >tags &&
@@ -53,7 +52,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -70,7 +68,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -87,7 +84,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -102,7 +98,6 @@ test_run_rebase success --apply
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
# f
# /
@@ -142,7 +137,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -157,7 +151,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -172,7 +165,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -187,7 +179,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
# a---b---c---j!
# \
@@ -215,7 +206,6 @@ test_run_rebase () {
test_run_rebase failure --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -229,7 +219,6 @@ test_run_rebase () {
}
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -243,7 +232,6 @@ test_run_rebase () {
}
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
@@ -283,7 +271,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -298,7 +285,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -313,7 +299,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -329,7 +314,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -344,7 +328,6 @@ test_run_rebase () {
test_run_rebase success --apply
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -358,7 +341,6 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -373,6 +355,5 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
test_done
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index c8234062c6..eb0a3d9d48 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -63,15 +63,4 @@ test_rebase_am_only () {
test_rebase_am_only --whitespace=fix
test_rebase_am_only -C4
-test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
- git checkout B^0 &&
- test_must_fail git rebase --preserve-merges --signoff A
-'
-
-test_expect_success REBASE_P \
- '--preserve-merges incompatible with --rebase-merges' '
- git checkout B^0 &&
- test_must_fail git rebase --preserve-merges --rebase-merges A
-'
-
test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index e42faa44e7..63acc1ea4d 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -106,155 +106,4 @@ test_run_rebase success 'd n o e' --apply
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
-if ! test_have_prereq REBASE_P; then
- skip_all='skipping git rebase -p tests, as asked for'
- test_done
-fi
-
-test_expect_success "rebase -p is no-op in non-linear history" "
- reset_rebase &&
- git rebase -p d w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_success "rebase -p is no-op when base inside second parent" "
- reset_rebase &&
- git rebase -p e w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_failure "rebase -p --root on non-linear history is a no-op" "
- reset_rebase &&
- git rebase -p --root w &&
- test_cmp_rev w HEAD
-"
-
-test_expect_success "rebase -p re-creates merge from side branch" "
- reset_rebase &&
- git rebase -p z w &&
- test_cmp_rev z HEAD^ &&
- test_cmp_rev w^2 HEAD^2
-"
-
-test_expect_success "rebase -p re-creates internal merge" "
- reset_rebase &&
- git rebase -p c w &&
- test_cmp_rev c HEAD~4 &&
- test_cmp_rev HEAD^2^ HEAD~3 &&
- test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p can re-create two branches on onto" "
- reset_rebase &&
- git rebase -p --onto c d w &&
- test_cmp_rev c HEAD~3 &&
- test_cmp_rev c HEAD^2^ &&
- test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-# f
-# /
-# a---b---c---g---h
-# \
-# d---gp--i
-# \ \
-# e-------u
-#
-# gp = cherry-picked g
-# h = reverted g
-test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
- git checkout e &&
- test_merge u i
-'
-
-test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
- reset_rebase &&
- git rebase -p h u &&
- test_cmp_rev h HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
- reset_rebase &&
- git rebase -p --onto f h u &&
- test_cmp_rev f HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
- reset_rebase &&
- git rebase -p --onto h f u &&
- test_cmp_rev h HEAD~3 &&
- test_cmp_rev HEAD^2~2 HEAD~2 &&
- test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
-"
-
-# a---b---c---g---h
-# \
-# d---gp--s
-# \ \ /
-# \ X
-# \ / \
-# e---t
-#
-# gp = cherry-picked g
-# h = reverted g
-test_expect_success 'setup of non-linear-history for dropping whole side' '
- git checkout gp &&
- test_merge s e &&
- git checkout e &&
- test_merge t gp
-'
-
-test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
- reset_rebase &&
- git rebase -p h s &&
- test_cmp_rev h HEAD~2 &&
- test_linear_range 'd e' h..
-"
-
-test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
- reset_rebase &&
- git rebase -p h t &&
- test_cmp_rev h HEAD~2 &&
- test_linear_range 'd e' h..
-"
-
-# a---b---c
-# \
-# d---e
-# \ \
-# n---r
-# \
-# o
-#
-# r = tree-same with n
-test_expect_success 'setup of non-linear-history for empty commits' '
- git checkout n &&
- git merge --no-commit e &&
- git reset n . &&
- git commit -m r &&
- git reset --hard &&
- git clean -f &&
- git tag r
-'
-
-test_expect_success "rebase -p re-creates empty internal merge commit" "
- reset_rebase &&
- git rebase -p c r &&
- test_cmp_rev c HEAD~3 &&
- test_cmp_rev HEAD^2^ HEAD~2 &&
- test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
-"
-
-test_expect_success "rebase -p re-creates empty merge commit" "
- reset_rebase &&
- git rebase -p o r &&
- test_cmp_rev e HEAD^2 &&
- test_cmp_rev o HEAD^ &&
- test_revision_subjects 'r' HEAD
-"
-
test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index e78c7e3796..48b76f8232 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -36,11 +36,10 @@ commit_message() {
# where the root commit adds three files: topic_1.t, topic_2.t and topic_3.t.
#
# This commit history is then rebased onto `topic_3` with the
-# `-Xsubtree=files_subtree` option in three different ways:
+# `-Xsubtree=files_subtree` option in two different ways:
#
-# 1. using `--preserve-merges`
-# 2. using `--preserve-merges` and --keep-empty
-# 3. without specifying a rebase backend
+# 1. without specifying a rebase backend
+# 2. using the `--rebase-merges` backend
test_expect_success 'setup' '
test_commit README &&
@@ -69,25 +68,6 @@ test_expect_success 'setup' '
git commit -m "Empty commit" --allow-empty
'
-# FAILURE: Does not preserve topic_4.
-test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' '
- reset_rebase &&
- git checkout -b rebase-preserve-merges to-rebase &&
- git rebase -Xsubtree=files_subtree --preserve-merges --onto files-main main &&
- verbose test "$(commit_message HEAD~)" = "topic_4" &&
- verbose test "$(commit_message HEAD)" = "files_subtree/topic_5"
-'
-
-# FAILURE: Does not preserve topic_4.
-test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' '
- reset_rebase &&
- git checkout -b rebase-keep-empty to-rebase &&
- git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-main main &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
-'
-
test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
diff --git a/t/t3435-rebase-gpg-sign.sh b/t/t3435-rebase-gpg-sign.sh
index ec10766858..5f8ba2c739 100755
--- a/t/t3435-rebase-gpg-sign.sh
+++ b/t/t3435-rebase-gpg-sign.sh
@@ -65,6 +65,7 @@ test_rebase_gpg_sign ! true -i --gpg-sign --no-gpg-sign
test_rebase_gpg_sign false -i --no-gpg-sign --gpg-sign
test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
+ test_when_finished "git clean -f" &&
git reset --hard merged &&
git config commit.gpgsign true &&
git rebase -p --no-gpg-sign --onto=one fork-point main &&
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 49010aa946..3b0fa66c33 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -238,6 +238,7 @@ test_expect_success 'allow skipping commit but not abort for a new history' '
'
test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '
+ test_when_finished "rm unrelated" &&
pristine_detach initial &&
git rm --cached unrelated &&
git commit -m "untrack unrelated" &&
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
index 7de21f8bcf..b2a8db69af 100755
--- a/t/t3601-rm-pathspec-file.sh
+++ b/t/t3601-rm-pathspec-file.sh
@@ -2,6 +2,7 @@
test_description='rm --pathspec-from-file'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_tick
diff --git a/t/t3602-rm-sparse-checkout.sh b/t/t3602-rm-sparse-checkout.sh
index e9e9a15c74..ecce497a9c 100755
--- a/t/t3602-rm-sparse-checkout.sh
+++ b/t/t3602-rm-sparse-checkout.sh
@@ -11,12 +11,15 @@ test_expect_success 'setup' "
git commit -m files &&
cat >sparse_error_header <<-EOF &&
- The following pathspecs didn't match any eligible path, but they do match index
- entries outside the current sparse checkout:
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
EOF
cat >sparse_hint <<-EOF &&
- hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
@@ -37,9 +40,25 @@ done
test_expect_success 'recursive rm does not remove sparse entries' '
git reset --hard &&
git sparse-checkout set sub/dir &&
- git rm -r sub &&
+ test_must_fail git rm -r sub &&
+ git rm --sparse -r sub &&
git status --porcelain -uno >actual &&
- echo "D sub/dir/e" >expected &&
+ cat >expected <<-\EOF &&
+ D sub/d
+ D sub/dir/e
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'recursive rm --sparse removes sparse entries' '
+ git reset --hard &&
+ git sparse-checkout set "sub/dir" &&
+ git rm --sparse -r sub &&
+ git status --porcelain -uno >actual &&
+ cat >expected <<-\EOF &&
+ D sub/d
+ D sub/dir/e
+ EOF
test_cmp expected actual
'
@@ -75,4 +94,15 @@ test_expect_success 'do not warn about sparse entries with --ignore-unmatch' '
git ls-files --error-unmatch b
'
+test_expect_success 'refuse to rm a non-skip-worktree path outside sparse cone' '
+ git reset --hard &&
+ git sparse-checkout set a &&
+ git update-index --no-skip-worktree b &&
+ test_must_fail git rm b 2>stderr &&
+ test_cmp b_error_and_hint stderr &&
+ git rm --sparse b 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b
+'
+
test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index 4086e1ebbc..283a66955d 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -5,6 +5,7 @@
test_description='Test of git add, including the -- option.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# Test the file mode "$1" of the file "$2" in the index.
diff --git a/t/t3705-add-sparse-checkout.sh b/t/t3705-add-sparse-checkout.sh
index 2b1fd0d0ee..5b904988d4 100755
--- a/t/t3705-add-sparse-checkout.sh
+++ b/t/t3705-add-sparse-checkout.sh
@@ -19,6 +19,7 @@ setup_sparse_entry () {
fi &&
git add sparse_entry &&
git update-index --skip-worktree sparse_entry &&
+ git commit --allow-empty -m "ensure sparse_entry exists at HEAD" &&
SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry)
}
@@ -36,14 +37,22 @@ setup_gitignore () {
EOF
}
+test_sparse_entry_unstaged () {
+ git diff --staged -- sparse_entry >diff &&
+ test_must_be_empty diff
+}
+
test_expect_success 'setup' "
cat >sparse_error_header <<-EOF &&
- The following pathspecs didn't match any eligible path, but they do match index
- entries outside the current sparse checkout:
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
EOF
cat >sparse_hint <<-EOF &&
- hint: Disable or modify the sparsity rules if you intend to update such entries.
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
hint: Disable this message with \"git config advice.updateSparsePath false\"
EOF
@@ -55,6 +64,7 @@ test_expect_success 'git add does not remove sparse entries' '
setup_sparse_entry &&
rm sparse_entry &&
test_must_fail git add sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -73,6 +83,7 @@ test_expect_success 'git add . does not remove sparse entries' '
rm sparse_entry &&
setup_gitignore &&
test_must_fail git add . 2>stderr &&
+ test_sparse_entry_unstaged &&
cat sparse_error_header >expect &&
echo . >>expect &&
@@ -88,6 +99,7 @@ do
setup_sparse_entry &&
echo modified >sparse_entry &&
test_must_fail git add $opt sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -98,6 +110,7 @@ test_expect_success 'git add --refresh does not update sparse entries' '
git ls-files --debug sparse_entry | grep mtime >before &&
test-tool chmtime -60 sparse_entry &&
test_must_fail git add --refresh sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
git ls-files --debug sparse_entry | grep mtime >after &&
test_cmp before after
@@ -106,6 +119,7 @@ test_expect_success 'git add --refresh does not update sparse entries' '
test_expect_success 'git add --chmod does not update sparse entries' '
setup_sparse_entry &&
test_must_fail git add --chmod=+x sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged &&
! test -x sparse_entry
@@ -116,6 +130,7 @@ test_expect_success 'git add --renormalize does not update sparse entries' '
setup_sparse_entry "LINEONE\r\nLINETWO\r\n" &&
echo "sparse_entry text=auto" >.gitattributes &&
test_must_fail git add --renormalize sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -124,6 +139,7 @@ test_expect_success 'git add --dry-run --ignore-missing warn on sparse path' '
setup_sparse_entry &&
rm sparse_entry &&
test_must_fail git add --dry-run --ignore-missing sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp error_and_hint stderr &&
test_sparse_entry_unchanged
'
@@ -145,11 +161,57 @@ test_expect_success 'do not warn when pathspec matches dense entries' '
git ls-files --error-unmatch dense_entry
'
+test_expect_success 'git add fails outside of sparse-checkout definition' '
+ test_when_finished git sparse-checkout disable &&
+ test_commit a &&
+ git sparse-checkout init &&
+ git sparse-checkout set a &&
+ echo >>sparse_entry &&
+
+ git update-index --no-skip-worktree sparse_entry &&
+ test_must_fail git add sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ test_must_fail git add --chmod=+x sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ test_must_fail git add --renormalize sparse_entry &&
+ test_sparse_entry_unstaged &&
+
+ # Avoid munging CRLFs to avoid an error message
+ git -c core.autocrlf=input add --sparse sparse_entry 2>stderr &&
+ test_must_be_empty stderr &&
+ test-tool read-cache --table >actual &&
+ grep "^100644 blob.*sparse_entry\$" actual &&
+
+ git add --sparse --chmod=+x sparse_entry 2>stderr &&
+ test_must_be_empty stderr &&
+ test-tool read-cache --table >actual &&
+ grep "^100755 blob.*sparse_entry\$" actual &&
+
+ git reset &&
+
+ # This will print a message over stderr on Windows.
+ git add --sparse --renormalize sparse_entry &&
+ git status --porcelain >actual &&
+ grep "^M sparse_entry\$" actual
+'
+
test_expect_success 'add obeys advice.updateSparsePath' '
setup_sparse_entry &&
test_must_fail git -c advice.updateSparsePath=false add sparse_entry 2>stderr &&
+ test_sparse_entry_unstaged &&
test_cmp sparse_entry_error stderr
'
+test_expect_success 'add allows sparse entries with --sparse' '
+ git sparse-checkout set a &&
+ echo modified >sparse_entry &&
+ test_must_fail git add sparse_entry &&
+ test_sparse_entry_unchanged &&
+ git add --sparse sparse_entry 2>stderr &&
+ test_must_be_empty stderr
+'
+
test_done
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
index f528008c36..72a5a565e9 100755
--- a/t/t3902-quoted.sh
+++ b/t/t3902-quoted.sh
@@ -5,6 +5,7 @@
test_description='quoted output'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
FN='濱野'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 873aa56e35..f0a82be9de 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -1307,4 +1307,62 @@ test_expect_success 'stash -c stash.useBuiltin=false warning ' '
test_must_be_empty err
'
+test_expect_success 'git stash succeeds despite directory/file change' '
+ test_create_repo directory_file_switch_v1 &&
+ (
+ cd directory_file_switch_v1 &&
+ test_commit init &&
+
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm filler &&
+ mkdir filler &&
+ echo contents >filler/file &&
+ git stash push
+ )
+'
+
+test_expect_success 'git stash can pop file -> directory saved changes' '
+ test_create_repo directory_file_switch_v2 &&
+ (
+ cd directory_file_switch_v2 &&
+ test_commit init &&
+
+ test_write_lines this file has some words >filler &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm filler &&
+ mkdir filler &&
+ echo contents >filler/file &&
+ cp filler/file expect &&
+ git stash push --include-untracked &&
+ git stash apply --index &&
+ test_cmp expect filler/file
+ )
+'
+
+test_expect_success 'git stash can pop directory -> file saved changes' '
+ test_create_repo directory_file_switch_v3 &&
+ (
+ cd directory_file_switch_v3 &&
+ test_commit init &&
+
+ mkdir filler &&
+ test_write_lines some words >filler/file1 &&
+ test_write_lines and stuff >filler/file2 &&
+ git add filler &&
+ git commit -m filler &&
+
+ git rm -rf filler &&
+ echo contents >filler &&
+ cp filler expect &&
+ git stash push --include-untracked &&
+ git stash apply --index &&
+ test_cmp expect filler
+ )
+'
+
test_done
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index dd2cdcc114..5390eec4e3 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -422,4 +422,10 @@ test_expect_success 'stash show --{include,only}-untracked on stashes without un
test_must_be_empty actual
'
+test_expect_success 'stash -u ignores sub-repository' '
+ test_when_finished "rm -rf sub-repo" &&
+ git init sub-repo &&
+ git stash -u
+'
+
test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 6a9f010197..ea52e5b91b 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -6,6 +6,8 @@
test_description='Test diff raw-output.
'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index db07ff3eb1..f4485a87c6 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -11,7 +11,7 @@ test_description='More rename detection
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -99,7 +99,7 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
git update-index --add --remove COPYING COPYING.1'
# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 8647906132..6f1b323f97 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -9,7 +9,7 @@ test_description='Same rename detection as t4003 but testing diff-raw.'
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success 'setup reference tree' '
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ COPYING_test_data >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
tree=$(git write-tree) &&
@@ -64,7 +64,7 @@ test_expect_success 'validate output from rename/copy detection (#2)' '
# nows how to say Copy.
test_expect_success 'validate output from rename/copy detection (#3)' '
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ COPYING_test_data >COPYING &&
git update-index --add --remove COPYING COPYING.1 &&
cat <<-EOF >expected &&
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index cbb9c62f53..c634653b5b 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -11,13 +11,12 @@ test_description='Rename interaction with pathspec.
test_expect_success 'prepare reference tree' '
mkdir path0 path1 &&
- cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING &&
+ COPYING_test_data >path0/COPYING &&
git update-index --add path0/COPYING &&
tree=$(git write-tree) &&
- echo $tree
+ blob=$(git rev-parse :path0/COPYING)
'
-blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING")
test_expect_success 'prepare work tree' '
cp path0/COPYING path1/COPYING &&
git update-index --add --remove path0/COPYING path1/COPYING
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 2299f27511..562aaf3a2a 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -25,8 +25,8 @@ Further, with -B and -M together, these should turn into two renames.
. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
test_expect_success setup '
- cat "$TEST_DIRECTORY"/lib-diff/README >file0 &&
- cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 &&
+ echo some dissimilar content >file0 &&
+ COPYING_test_data >file1 &&
blob0_id=$(git hash-object file0) &&
blob1_id=$(git hash-object file1) &&
git update-index --add file0 file1 &&
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index b1da807f16..59b7f44f05 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -11,7 +11,7 @@ test_description='Same rename detection as t4003 but testing diff-raw -z.
test_expect_success \
'prepare reference tree' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
echo frotz >rezrov &&
git update-index --add COPYING rezrov &&
orig=$(git hash-object COPYING) &&
@@ -81,7 +81,7 @@ test_expect_success \
test_expect_success \
'prepare work tree once again' \
- 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+ 'COPYING_test_data >COPYING &&
git update-index --add --remove COPYING COPYING.1'
git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
index 876271d682..5a8d887683 100755
--- a/t/t4016-diff-quote.sh
+++ b/t/t4016-diff-quote.sh
@@ -6,6 +6,7 @@
test_description='Quoting paths in diff output.
'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
P0='pathname'
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index c6135c7548..c68729ac09 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -2,6 +2,7 @@
test_description='diff whitespace error detection'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 6d1c3d949c..1c89050a97 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -3,15 +3,17 @@
test_description='rewrite diff'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
test_expect_success setup '
- cat "$TEST_DIRECTORY"/../COPYING >test &&
+ COPYING_test_data >test.data &&
+ cp test.data test &&
git add test &&
tr \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
- <"$TEST_DIRECTORY"/../COPYING >test &&
+ <test.data >test &&
echo "to be deleted" >test2 &&
blob=$(git hash-object test2) &&
blob=$(git rev-parse --short $blob) &&
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 8c9823765e..47d6f35dcc 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -3,25 +3,26 @@
test_description='typechange rename detection'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
test_expect_success setup '
rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
test_ln_s_add linklink bar &&
git add foo &&
git commit -a -m Initial &&
git tag one &&
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ COPYING_test_data >bar &&
test_ln_s_add linklink foo &&
git add bar &&
git commit -a -m Second &&
git tag two &&
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
git add foo &&
git commit -a -m Third &&
git tag three &&
@@ -35,7 +36,7 @@ test_expect_success setup '
# This is purely for sanity check
git rm -f foo bar &&
- cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ COPYING_test_data >foo &&
cat "$TEST_DIRECTORY"/../Makefile >bar &&
git add foo bar &&
git commit -a -m Fifth &&
@@ -43,7 +44,7 @@ test_expect_success setup '
git rm -f foo bar &&
cat "$TEST_DIRECTORY"/../Makefile >foo &&
- cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ COPYING_test_data >bar &&
git add foo bar &&
git commit -a -m Sixth &&
git tag six
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index 35578f2bb9..6356961de4 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -2,6 +2,7 @@
test_description='diff hunk header truncation'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
N='日本語'
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
index c0b642c1ab..cc73161b46 100755
--- a/t/t4026-color.sh
+++ b/t/t4026-color.sh
@@ -4,6 +4,8 @@
#
test_description='Test diff/status color escape codes'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
ESC=$(printf '\033')
diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect
index 37d1ea2587..dc500ae092 100644
--- a/t/t4034/cpp/expect
+++ b/t/t4034/cpp/expect
@@ -1,36 +1,35 @@
<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>index a1a09b7..f1b6f3c 100644<RESET>
<BOLD>--- a/pre<RESET>
<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,19 +1,19 @@<RESET>
-Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+<CYAN>@@ -1,30 +1,30 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <RED>foo0<RESET><GREEN>bar<RESET>(x.<RED>find<RESET><GREEN>Find<RESET>); }
cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
-<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
-[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
-!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>^<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>|<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>||<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
-<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>,y
-<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
+<GREEN>(<RESET>1 <RED>-<RESET><GREEN>+<RESET>1e10 0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>2<RESET>'
+// long double<RESET>
+<RED>3.141592653e-10l<RESET><GREEN>3.141592654e+10l<RESET>
+// float<RESET>
+<RED>120E5f<RESET><GREEN>120E6f<RESET>
+// hex<RESET>
+<RED>0xdead<RESET><GREEN>0xdeaf<RESET>'1<RED>eaF<RESET><GREEN>eaf<RESET>+<RED>8ULL<RESET><GREEN>7ULL<RESET>
+// octal<RESET>
+<RED>01234567<RESET><GREEN>01234560<RESET>
+// binary<RESET>
+<RED>0b1000<RESET><GREEN>0b1100<RESET>+e1
+// expression<RESET>
+1.5-e+<RED>2<RESET><GREEN>3<RESET>+f
+// another one<RESET>
+str.e+<RED>65<RESET><GREEN>75<RESET>
+[a] b<RED>-><RESET><GREEN>->*<RESET>v d<RED>.<RESET><GREEN>.*<RESET>e
+<GREEN>~<RESET>!a <GREEN>!<RESET>~b c<RED>++<RESET><GREEN>+<RESET> d<RED>--<RESET><GREEN>-<RESET> e*<GREEN>*<RESET>f g<RED>&<RESET><GREEN>&&<RESET>h
+a<RED>*<RESET><GREEN>*=<RESET>b c<RED>/<RESET><GREEN>/=<RESET>d e<RED>%<RESET><GREEN>%=<RESET>f
+a<RED>+<RESET><GREEN>++<RESET>b c<RED>-<RESET><GREEN>--<RESET>d
+a<RED><<<RESET><GREEN><<=<RESET>b c<RED>>><RESET><GREEN>>>=<RESET>d
+a<RED><<RESET><GREEN><=<RESET>b c<RED><=<RESET><GREEN><<RESET>d e<RED>><RESET><GREEN>>=<RESET>f g<RED>>=<RESET><GREEN>><RESET>h i<RED><=<RESET><GREEN><=><RESET>j
+a<RED>==<RESET><GREEN>!=<RESET>b c<RED>!=<RESET><GREEN>=<RESET>d
+a<RED>^<RESET><GREEN>^=<RESET>b c<RED>|<RESET><GREEN>|=<RESET>d e<RED>&&<RESET><GREEN>&=<RESET>f
+a<RED>||<RESET><GREEN>|<RESET>b
+a?<GREEN>:<RESET>b
+a<RED>=<RESET><GREEN>==<RESET>b c<RED>+=<RESET><GREEN>+<RESET>d e<RED>-=<RESET><GREEN>-<RESET>f g<RED>*=<RESET><GREEN>*<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v
+a,b<RESET>
+a<RED>::<RESET><GREEN>:<RESET>b
diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post
index 7e8c026cef..f1b6f3c228 100644
--- a/t/t4034/cpp/post
+++ b/t/t4034/cpp/post
@@ -1,19 +1,30 @@
-Foo() : x(0&42) { bar(x); }
+Foo() : x(0&42) { bar(x.Find); }
cout<<"Hello World?\n"<<endl;
-(1) (-1e10) (0xabcdef) 'y'
-[x] x->y x.y
-!x ~x x++ x-- x*y x&y
-x*y x/y x%y
-x+y x-y
-x<<y x>>y
-x<y x<=y x>y x>=y
-x==y x!=y
-x&y
-x^y
-x|y
-x&&y
-x||y
-x?y:z
-x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
-x,y
-x::y
+(1 +1e10 0xabcdef) '2'
+// long double
+3.141592654e+10l
+// float
+120E6f
+// hex
+0xdeaf'1eaf+7ULL
+// octal
+01234560
+// binary
+0b1100+e1
+// expression
+1.5-e+3+f
+// another one
+str.e+75
+[a] b->*v d.*e
+~!a !~b c+ d- e**f g&&h
+a*=b c/=d e%=f
+a++b c--d
+a<<=b c>>=d
+a<=b c<d e>=f g>h i<=>j
+a!=b c=d
+a^=b c|=d e&=f
+a|b
+a?:b
+a==b c+d e-f g*h i/j k%l m<<n o>>p q&r s^t u|v
+a,b
+a:b
diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre
index 23d5c8adf5..a1a09b7712 100644
--- a/t/t4034/cpp/pre
+++ b/t/t4034/cpp/pre
@@ -1,19 +1,30 @@
-Foo():x(0&&1){}
+Foo():x(0&&1){ foo0( x.find); }
cout<<"Hello World!\n"<<endl;
1 -1e10 0xabcdef 'x'
-[a] a->b a.b
-!a ~a a++ a-- a*b a&b
-a*b a/b a%b
-a+b a-b
-a<<b a>>b
-a<b a<=b a>b a>=b
-a==b a!=b
-a&b
-a^b
-a|b
-a&&b
+// long double
+3.141592653e-10l
+// float
+120E5f
+// hex
+0xdead'1eaF+8ULL
+// octal
+01234567
+// binary
+0b1000+e1
+// expression
+1.5-e+2+f
+// another one
+str.e+65
+[a] b->v d.e
+!a ~b c++ d-- e*f g&h
+a*b c/d e%f
+a+b c-d
+a<<b c>>d
+a<b c<=d e>f g>=h i<=j
+a==b c!=d
+a^b c|d e&&f
a||b
-a?b:z
-a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
-a,y
+a?b
+a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v
+a,b
a::b
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 9d8d3c72e7..2374151662 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -23,7 +23,13 @@ test_expect_success setup '
test_tick &&
git commit -a -m $i || return 1
done &&
+ git branch changes &&
git format-patch --no-numbered initial &&
+ git checkout -b conflicting initial &&
+ echo different >>file-1 &&
+ echo whatever >new-file &&
+ git add file-1 new-file &&
+ git commit -m different &&
git checkout -b side initial &&
echo local change >file-2-expect
'
@@ -191,4 +197,37 @@ test_expect_success 'am --abort leaves index stat info alone' '
git diff-files --exit-code --quiet
'
+test_expect_success 'git am --abort return failed exit status when it fails' '
+ test_when_finished "rm -rf file-2/ && git reset --hard && git am --abort" &&
+ git checkout changes &&
+ git format-patch -1 --stdout conflicting >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ git rm file-2 &&
+ mkdir file-2 &&
+ echo precious >file-2/somefile &&
+ test_must_fail git am --abort &&
+ test_path_is_dir file-2/
+'
+
+test_expect_success 'git am --abort cleans relevant files' '
+ git checkout changes &&
+ git format-patch -1 --stdout conflicting >changes.mbox &&
+ test_must_fail git am --3way changes.mbox &&
+
+ test_path_is_file new-file &&
+ echo further changes >>file-1 &&
+ echo change other file >>file-2 &&
+
+ # Abort, and expect the files touched by am to be reverted
+ git am --abort &&
+
+ test_path_is_missing new-file &&
+
+ # Files not involved in am operation are left modified
+ git diff --name-only changes >actual &&
+ test_write_lines file-2 >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 9dfead936b..6a650dacd6 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1616,6 +1616,16 @@ test_expect_success GPGSM 'setup signed branch x509' '
git commit -S -m signed_commit
'
+test_expect_success GPGSSH 'setup sshkey signed branch' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_when_finished "git reset --hard && git checkout main" &&
+ git checkout -b signed-ssh main &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -S -m signed_commit
+'
+
test_expect_success GPGSM 'log x509 fingerprint' '
echo "F8BF62E0693D0694816377099909C779FA23FD65 | " >expect &&
git log -n1 --format="%GF | %GP" signed-x509 >actual &&
@@ -1628,6 +1638,13 @@ test_expect_success GPGSM 'log OpenPGP fingerprint' '
test_cmp expect actual
'
+test_expect_success GPGSSH 'log ssh key fingerprint' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2\" | \"}" >expect &&
+ git log -n1 --format="%GF | %GP" signed-ssh >actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG 'log --graph --show-signature' '
git log --graph --show-signature -n1 signed >actual &&
grep "^| gpg: Signature made" actual &&
@@ -1640,6 +1657,12 @@ test_expect_success GPGSM 'log --graph --show-signature x509' '
grep "^| gpgsm: Good signature" actual
'
+test_expect_success GPGSSH 'log --graph --show-signature ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git log --graph --show-signature -n1 signed-ssh >actual &&
+ grep "${GOOD_SIGNATURE_TRUSTED}" actual
+'
+
test_expect_success GPG 'log --graph --show-signature for merged tag' '
test_when_finished "git reset --hard && git checkout main" &&
git checkout -b plain main &&
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index e59601e5fe..c52c8a21fa 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -4,6 +4,8 @@
#
test_description='git merge-tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 7cabb85ca6..8ae314af58 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -291,6 +291,7 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
cat ../expected >blob &&
git add blob &&
git commit -m "second commit in third" &&
+ git clean -f && # Remove untracked left behind by deleting index
git reset --hard HEAD^
) &&
git prune --expire=now &&
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index 11423b3cb2..ea889c088a 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -7,6 +7,9 @@ if we see, for example, a ref with a bogus name, it is OK either to
bail out or to proceed using it as a reachable tip, but it is _not_
OK to proceed as if it did not exist. Otherwise we might silently
delete objects that cannot be recovered.
+
+Note that we do assert command failure in these cases, because that is
+what currently happens. If that changes, these tests should be revisited.
'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
@@ -18,39 +21,58 @@ test_expect_success 'disable reflogs' '
git reflog expire --expire=all --all
'
+create_bogus_ref () {
+ test_when_finished 'rm -f .git/refs/heads/bogus..name' &&
+ echo $bogus >.git/refs/heads/bogus..name
+}
+
test_expect_success 'create history reachable only from a bogus-named ref' '
test_tick && git commit --allow-empty -m main &&
base=$(git rev-parse HEAD) &&
test_tick && git commit --allow-empty -m bogus &&
bogus=$(git rev-parse HEAD) &&
git cat-file commit $bogus >saved &&
- echo $bogus >.git/refs/heads/bogus..name &&
git reset --hard HEAD^
'
test_expect_success 'pruning does not drop bogus object' '
test_when_finished "git hash-object -w -t commit saved" &&
- test_might_fail git prune --expire=now &&
- verbose git cat-file -e $bogus
+ create_bogus_ref &&
+ test_must_fail git prune --expire=now &&
+ git cat-file -e $bogus
'
test_expect_success 'put bogus object into pack' '
git tag reachable $bogus &&
git repack -ad &&
git tag -d reachable &&
- verbose git cat-file -e $bogus
+ git cat-file -e $bogus
+'
+
+test_expect_success 'non-destructive repack bails on bogus ref' '
+ create_bogus_ref &&
+ test_must_fail git repack -adk
'
+test_expect_success 'GIT_REF_PARANOIA=0 overrides safety' '
+ create_bogus_ref &&
+ GIT_REF_PARANOIA=0 git repack -adk
+'
+
+
test_expect_success 'destructive repack keeps packed object' '
- test_might_fail git repack -Ad --unpack-unreachable=now &&
- verbose git cat-file -e $bogus &&
- test_might_fail git repack -ad &&
- verbose git cat-file -e $bogus
+ create_bogus_ref &&
+ test_must_fail git repack -Ad --unpack-unreachable=now &&
+ git cat-file -e $bogus &&
+ test_must_fail git repack -ad &&
+ git cat-file -e $bogus
'
-# subsequent tests will have different corruptions
-test_expect_success 'clean up bogus ref' '
- rm .git/refs/heads/bogus..name
+test_expect_success 'destructive repack not confused by dangling symref' '
+ test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
+ git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
+ git repack -ad &&
+ test_must_fail git cat-file -e $bogus
'
# We create two new objects here, "one" and "two". Our
@@ -77,8 +99,8 @@ test_expect_success 'create history with missing tip commit' '
test_expect_success 'pruning with a corrupted tip does not drop history' '
test_when_finished "git hash-object -w -t commit saved" &&
- test_might_fail git prune --expire=now &&
- verbose git cat-file -e $recoverable
+ test_must_fail git prune --expire=now &&
+ git cat-file -e $recoverable
'
test_expect_success 'pack-refs does not silently delete broken loose ref' '
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index bb04f0f23b..a3c72b68f7 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -168,18 +168,33 @@ test_expect_success 'write midx with two packs' '
compare_results_with_midx "two packs"
+test_expect_success 'write midx with --stdin-packs' '
+ rm -fr $objdir/pack/multi-pack-index &&
+
+ idx="$(find $objdir/pack -name "test-2-*.idx")" &&
+ basename "$idx" >in &&
+
+ git multi-pack-index write --stdin-packs <in &&
+
+ test-tool read-midx $objdir | grep "\.idx$" >packs &&
+
+ test_cmp packs in
+'
+
+compare_results_with_midx "mixed mode (one pack + extra)"
+
test_expect_success 'write progress off for redirected stderr' '
git multi-pack-index --object-dir=$objdir write 2>err &&
test_line_count = 0 err
'
test_expect_success 'write force progress on for stderr' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress write 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'write with the --no-progress option' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --no-progress 2>err &&
test_line_count = 0 err
'
@@ -474,12 +489,12 @@ test_expect_success 'repack progress off for redirected stderr' '
'
test_expect_success 'repack force progress on for stderr' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --progress 2>err &&
test_file_not_empty err
'
test_expect_success 'repack with the --no-progress option' '
- GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --no-progress 2>err &&
test_line_count = 0 err
'
@@ -672,7 +687,7 @@ test_expect_success 'expire progress off for redirected stderr' '
test_expect_success 'expire force progress on for stderr' '
(
cd dup &&
- GIT_PROGRESS_DELAY=0 git multi-pack-index --progress expire 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index expire --progress 2>err &&
test_file_not_empty err
)
'
@@ -680,7 +695,7 @@ test_expect_success 'expire force progress on for stderr' '
test_expect_success 'expire with the --no-progress option' '
(
cd dup &&
- GIT_PROGRESS_DELAY=0 git multi-pack-index --no-progress expire 2>err &&
+ GIT_PROGRESS_DELAY=0 git multi-pack-index expire --no-progress 2>err &&
test_line_count = 0 err
)
'
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 4ad7c2c969..e187f90f29 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -283,4 +283,116 @@ test_expect_success 'pack.preferBitmapTips' '
)
'
+test_expect_success 'writing a bitmap with --refs-snapshot' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit one &&
+ test_commit two &&
+
+ git rev-parse one >snapshot &&
+
+ git repack -ad &&
+
+ # First, write a MIDX which see both refs/tags/one and
+ # refs/tags/two (causing both of those commits to receive
+ # bitmaps).
+ git multi-pack-index write --bitmap &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ grep "$(git rev-parse two)" bitmaps &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx-$(midx_checksum $objdir).rev &&
+ rm -fr $midx &&
+
+ # Then again, but with a refs snapshot which only sees
+ # refs/tags/one.
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ grep "$(git rev-parse one)" bitmaps &&
+ ! grep "$(git rev-parse two)" bitmaps
+ )
+'
+
+test_expect_success 'write a bitmap with --refs-snapshot (preferred tips)' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit_bulk --message="%s" 103 &&
+
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git multi-pack-index write --bitmap &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
+
+ (
+ grep -vf before commits.raw &&
+ # mark missing commits as preferred
+ sed "s/^/+/" before
+ ) >snapshot &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx-$(midx_checksum $objdir).rev &&
+ rm -fr $midx &&
+
+ git multi-pack-index write --bitmap --refs-snapshot=snapshot &&
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+'
+
+test_expect_success 'hash-cache values are propagated from pack bitmaps' '
+ rm -fr repo &&
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ test_commit base2 &&
+ git repack -adb &&
+
+ test-tool bitmap dump-hashes >pack.raw &&
+ test_file_not_empty pack.raw &&
+ sort pack.raw >pack.hashes &&
+
+ test_commit new &&
+ git repack &&
+ git multi-pack-index write --bitmap &&
+
+ test-tool bitmap dump-hashes >midx.raw &&
+ sort midx.raw >midx.hashes &&
+
+ # ensure that every namehash in the pack bitmap can be found in
+ # the midx bitmap (i.e., that there are no oid-namehash pairs
+ # unique to the pack bitmap).
+ comm -23 pack.hashes midx.hashes >dropped.hashes &&
+ test_must_be_empty dropped.hashes
+ )
+'
+
test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4db8edd9c8..8212ca56dc 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -662,10 +662,10 @@ test_expect_success 'push does not update local refs on failure' '
test_expect_success 'allow deleting an invalid remote ref' '
- mk_test testrepo heads/main &&
+ mk_test testrepo heads/branch &&
rm -f testrepo/.git/objects/??/* &&
- git push testrepo :refs/heads/main &&
- (cd testrepo && test_must_fail git rev-parse --verify refs/heads/main)
+ git push testrepo :refs/heads/branch &&
+ (cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch)
'
@@ -706,25 +706,26 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho
'
test_expect_success 'deleting dangling ref triggers hooks with correct args' '
- mk_test_with_hooks testrepo heads/main &&
+ mk_test_with_hooks testrepo heads/branch &&
+ orig=$(git -C testrepo rev-parse refs/heads/branch) &&
rm -f testrepo/.git/objects/??/* &&
- git push testrepo :refs/heads/main &&
+ git push testrepo :refs/heads/branch &&
(
cd testrepo/.git &&
cat >pre-receive.expect <<-EOF &&
- $ZERO_OID $ZERO_OID refs/heads/main
+ $orig $ZERO_OID refs/heads/branch
EOF
cat >update.expect <<-EOF &&
- refs/heads/main $ZERO_OID $ZERO_OID
+ refs/heads/branch $orig $ZERO_OID
EOF
cat >post-receive.expect <<-EOF &&
- $ZERO_OID $ZERO_OID refs/heads/main
+ $orig $ZERO_OID refs/heads/branch
EOF
cat >post-update.expect <<-EOF &&
- refs/heads/main
+ refs/heads/branch
EOF
test_cmp pre-receive.expect pre-receive.actual &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 672001a18b..93ecfcdd24 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -546,15 +546,6 @@ test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
test_cmp expect actual
'
-test_expect_success REBASE_P \
- 'pull.rebase=preserve rebases and merges keep-merge' '
- git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
- git pull . copy &&
- test_cmp_rev HEAD^^ copy &&
- test_cmp_rev HEAD^2 keep-merge
-'
-
test_expect_success 'pull.rebase=interactive' '
write_script "$TRASH_DIRECTORY/fake-editor" <<-\EOF &&
echo I was here >fake.out &&
@@ -598,7 +589,7 @@ test_expect_success '--rebase=false create a new merge commit' '
test_expect_success '--rebase=true rebases and flattens keep-merge' '
git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
+ test_config pull.rebase merges &&
git pull --rebase=true . copy &&
test_cmp_rev HEAD^^ copy &&
echo file3 >expect &&
@@ -606,23 +597,14 @@ test_expect_success '--rebase=true rebases and flattens keep-merge' '
test_cmp expect actual
'
-test_expect_success REBASE_P \
- '--rebase=preserve rebases and merges keep-merge' '
- git reset --hard before-preserve-rebase &&
- test_config pull.rebase true &&
- git pull --rebase=preserve . copy &&
- test_cmp_rev HEAD^^ copy &&
- test_cmp_rev HEAD^2 keep-merge
-'
-
test_expect_success '--rebase=invalid fails' '
git reset --hard before-preserve-rebase &&
test_must_fail git pull --rebase=invalid . copy
'
-test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
+test_expect_success '--rebase overrides pull.rebase=merges and flattens keep-merge' '
git reset --hard before-preserve-rebase &&
- test_config pull.rebase preserve &&
+ test_config pull.rebase merges &&
git pull --rebase . copy &&
test_cmp_rev HEAD^^ copy &&
echo file3 >expect &&
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index ed11569d8d..2dc75b80db 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -6,6 +6,9 @@ test_description='Recursive "git fetch" for submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
pwd=$(pwd)
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index d573ca496a..3f58b515ce 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -5,6 +5,9 @@ test_description='test push with submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index bba768f5de..24d374adba 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -137,6 +137,53 @@ test_expect_success GPG 'signed push sends push certificate' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSSH 'ssh signed push sends push certificate' '
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=principal with number 1
+ KEY=FINGERPRINT
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
+
+ noop=$(git rev-parse noop) &&
+ ff=$(git rev-parse ff) &&
+ noff=$(git rev-parse noff) &&
+ grep "$noop $ff refs/heads/ff" dst/push-cert &&
+ grep "$noop $noff refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
test_expect_success GPG 'inconsistent push options in signed push not allowed' '
# First, invoke receive-pack with dummy input to obtain its preamble.
prepare_dst &&
@@ -276,6 +323,60 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSSH 'fail without key and heed user.signingkey ssh' '
+ test_config gpg.format ssh &&
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ test_config user.email hasnokey@nowhere.com &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "" &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=principal with number 1
+ KEY=FINGERPRINT
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
+
+ noop=$(git rev-parse noop) &&
+ ff=$(git rev-parse ff) &&
+ noff=$(git rev-parse noff) &&
+ grep "$noop $ff refs/heads/ff" dst/push-cert &&
+ grep "$noop $noff refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
test_expect_success GPG 'failed atomic push does not execute GPG' '
prepare_dst &&
git -C dst config receive.certnonceseed sekrit &&
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 58c7add7ee..214228349a 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -5,6 +5,9 @@ test_description='pushing to a repository using push options'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
mk_repo_pair () {
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 4f87d90c5b..f92c79c132 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -196,8 +196,8 @@ test_expect_success 'GIT_TRACE_CURL redacts auth details' '
# Ensure that there is no "Basic" followed by a base64 string, but that
# the auth details are redacted
- ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace &&
- grep "Authorization: Basic <redacted>" trace
+ ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace &&
+ grep -i "Authorization: Basic <redacted>" trace
'
test_expect_success 'GIT_CURL_VERBOSE redacts auth details' '
@@ -208,8 +208,8 @@ test_expect_success 'GIT_CURL_VERBOSE redacts auth details' '
# Ensure that there is no "Basic" followed by a base64 string, but that
# the auth details are redacted
- ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace &&
- grep "Authorization: Basic <redacted>" trace
+ ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace &&
+ grep -i "Authorization: Basic <redacted>" trace
'
test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_REDACT=0' '
@@ -219,7 +219,7 @@ test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_RE
git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth &&
expect_askpass both user@host &&
- grep "Authorization: Basic [0-9a-zA-Z+/]" trace
+ grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace
'
test_expect_success 'disable dumb http on server' '
@@ -474,10 +474,10 @@ test_expect_success 'cookies are redacted by default' '
GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=<redacted>" err &&
- grep "Cookie:.*Bar=<redacted>" err &&
- ! grep "Cookie:.*Foo=1" err &&
- ! grep "Cookie:.*Bar=2" err
+ grep -i "Cookie:.*Foo=<redacted>" err &&
+ grep -i "Cookie:.*Bar=<redacted>" err &&
+ ! grep -i "Cookie:.*Foo=1" err &&
+ ! grep -i "Cookie:.*Bar=2" err
'
test_expect_success 'empty values of cookies are also redacted' '
@@ -486,7 +486,7 @@ test_expect_success 'empty values of cookies are also redacted' '
GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=<redacted>" err
+ grep -i "Cookie:.*Foo=<redacted>" err
'
test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' '
@@ -496,8 +496,8 @@ test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' '
GIT_TRACE_REDACT=0 GIT_TRACE_CURL=true \
git -c "http.cookieFile=$(pwd)/cookies" clone \
$HTTPD_URL/smart/repo.git clone 2>err &&
- grep "Cookie:.*Foo=1" err &&
- grep "Cookie:.*Bar=2" err
+ grep -i "Cookie:.*Foo=1" err &&
+ grep -i "Cookie:.*Bar=2" err
'
test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
@@ -558,4 +558,13 @@ test_expect_success 'http auth forgets bogus credentials' '
expect_askpass both user@host
'
+test_expect_success 'client falls back from v2 to v0 to match server' '
+ GIT_TRACE_PACKET=$PWD/trace \
+ GIT_TEST_PROTOCOL_VERSION=2 \
+ git clone $HTTPD_URL/smart_v0/repo.git repo-v0 &&
+ # check for v0; there the HEAD symref is communicated in the capability
+ # line; v2 uses a different syntax on each ref advertisement line
+ grep symref=HEAD:refs/heads/ trace
+'
+
test_done
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 4f92a116e1..fa6b4cca65 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -2,6 +2,9 @@
test_description='pull can handle submodules'
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
diff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh
index cd803ae8bf..cd7604fff9 100755
--- a/t/t5580-unc-paths.sh
+++ b/t/t5580-unc-paths.sh
@@ -4,6 +4,7 @@ test_description='various Windows-only path tests'
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 CYGWIN
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 5bf10261d3..34b3df4027 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -35,7 +35,9 @@ test_expect_success 'create a repo to clone' '
'
test_expect_success 'create objects in repo for later corruption' '
- test_commit -C foo file
+ test_commit -C foo file &&
+ git -C foo checkout --detach &&
+ test_commit -C foo detached
'
# source repository given to git clone should be relative to the
diff --git a/t/t5615-alternate-env.sh b/t/t5615-alternate-env.sh
index b4905b822c..83513e46a3 100755
--- a/t/t5615-alternate-env.sh
+++ b/t/t5615-alternate-env.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='handling of alternates in environment variables'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_obj () {
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 930721f053..aa1827d841 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -72,6 +72,37 @@ test_expect_success 'request invalid command' '
test_i18ngrep "invalid command" err
'
+test_expect_success 'request capability as command' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=agent
+ object-format=$(test_oid algo)
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep invalid.command.*agent err
+'
+
+test_expect_success 'request command as capability' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs
+ object-format=$(test_oid algo)
+ fetch
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep unknown.capability err
+'
+
+test_expect_success 'requested command is command=value' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs=whatever
+ object-format=$(test_oid algo)
+ 0000
+ EOF
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep invalid.command.*ls-refs=whatever err
+'
+
test_expect_success 'wrong object-format' '
test-tool pkt-line pack >in <<-EOF &&
command=fetch
@@ -116,6 +147,19 @@ test_expect_success 'basics of ls-refs' '
test_cmp expect actual
'
+test_expect_success 'ls-refs complains about unknown options' '
+ test-tool pkt-line pack >in <<-EOF &&
+ command=ls-refs
+ object-format=$(test_oid algo)
+ 0001
+ no-such-arg
+ 0000
+ EOF
+
+ test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
+ grep unexpected.line.*no-such-arg err
+'
+
test_expect_success 'basic ref-prefixes' '
test-tool pkt-line pack >in <<-EOF &&
command=ls-refs
@@ -158,6 +202,37 @@ test_expect_success 'refs/heads prefix' '
test_cmp expect actual
'
+test_expect_success 'ignore very large set of prefixes' '
+ # generate a large number of ref-prefixes that we expect
+ # to match nothing; the value here exceeds TOO_MANY_PREFIXES
+ # from ls-refs.c.
+ {
+ echo command=ls-refs &&
+ echo object-format=$(test_oid algo) &&
+ echo 0001 &&
+ perl -le "print \"ref-prefix refs/heads/\$_\" for (1..65536)" &&
+ echo 0000
+ } |
+ test-tool pkt-line pack >in &&
+
+ # and then confirm that we see unmatched prefixes anyway (i.e.,
+ # that the prefix was not applied).
+ cat >expect <<-EOF &&
+ $(git rev-parse HEAD) HEAD
+ $(git rev-parse refs/heads/dev) refs/heads/dev
+ $(git rev-parse refs/heads/main) refs/heads/main
+ $(git rev-parse refs/heads/release) refs/heads/release
+ $(git rev-parse refs/tags/annotated-tag) refs/tags/annotated-tag
+ $(git rev-parse refs/tags/one) refs/tags/one
+ $(git rev-parse refs/tags/two) refs/tags/two
+ 0000
+ EOF
+
+ test-tool serve-v2 --stateless-rpc <in >out &&
+ test-tool pkt-line unpack <out >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'peel parameter' '
test-tool pkt-line pack >in <<-EOF &&
command=ls-refs
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index d3687b1a2e..d527cf6c49 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -237,6 +237,19 @@ test_expect_success '...but not if explicitly forbidden by config' '
! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
'
+test_expect_success 'bare clone propagates empty default branch' '
+ test_when_finished "rm -rf file_empty_parent file_empty_child.git" &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c init.defaultBranch=main -c protocol.version=2 \
+ clone --bare \
+ "file://$(pwd)/file_empty_parent" file_empty_child.git &&
+ grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
+'
+
test_expect_success 'fetch with file:// using protocol v2' '
test_when_finished "rm -f log" &&
diff --git a/t/t5704-protocol-violations.sh b/t/t5704-protocol-violations.sh
index 5c941949b9..bc393d7c31 100755
--- a/t/t5704-protocol-violations.sh
+++ b/t/t5704-protocol-violations.sh
@@ -32,4 +32,19 @@ test_expect_success 'extra delim packet in v2 fetch args' '
test_i18ngrep "expected flush after fetch arguments" err
'
+test_expect_success 'bogus symref in v0 capabilities' '
+ test_commit foo &&
+ oid=$(git rev-parse HEAD) &&
+ dst=refs/heads/foo &&
+ {
+ printf "%s HEAD\0symref object-format=%s symref=HEAD:%s\n" \
+ "$oid" "$GIT_DEFAULT_HASH" "$dst" |
+ test-tool pkt-line pack-raw-stdin &&
+ printf "0000"
+ } >input &&
+ git ls-remote --symref --upload-pack="cat input; read junk;:" . >actual &&
+ printf "ref: %s\tHEAD\n%s\tHEAD\n" "$dst" "$oid" >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index a1baf4e451..1be85d064e 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -962,4 +962,22 @@ test_expect_success 'bisect handles annotated tags' '
grep "$bad is the first bad commit" output
'
+test_expect_success 'bisect run fails with exit code equals or greater than 128' '
+ write_script test_script.sh <<-\EOF &&
+ exit 128
+ EOF
+ test_must_fail git bisect run ./test_script.sh &&
+ write_script test_script.sh <<-\EOF &&
+ exit 255
+ EOF
+ test_must_fail git bisect run ./test_script.sh
+'
+
+test_expect_success 'bisect visualize with a filename with dash and space' '
+ echo "My test line" >>"./-hello 2" &&
+ git add -- "./-hello 2" &&
+ git commit --quiet -m "Add test line" -- "./-hello 2" &&
+ git bisect visualize -p -- "-hello 2"
+'
+
test_done
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index 44f55d93fe..06c5fb5615 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -81,6 +81,16 @@ test_expect_success GPG 'set up a signed tag' '
git tag -s -m signed-tag-msg signed-good-tag left
'
+test_expect_success GPGSSH 'created ssh signed commit and tag' '
+ test_config gpg.format ssh &&
+ git checkout -b signed-ssh &&
+ touch file &&
+ git add file &&
+ git commit -m "ssh signed" -S"${GPGSSH_KEY_PRIMARY}" &&
+ git tag -s -u"${GPGSSH_KEY_PRIMARY}" -m signed-ssh-tag-msg signed-good-ssh-tag left &&
+ git tag -s -u"${GPGSSH_KEY_UNTRUSTED}" -m signed-ssh-tag-msg-untrusted signed-untrusted-ssh-tag left
+'
+
test_expect_success 'message for merging local branch' '
echo "Merge branch ${apos}left${apos}" >expected &&
@@ -109,6 +119,24 @@ test_expect_success GPG 'message for merging local tag signed by unknown key' '
grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual
'
+test_expect_success GPGSSH 'message for merging local tag signed by good ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . signed-good-ssh-tag &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh key' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout main &&
+ git fetch . signed-untrusted-ssh-tag &&
+ git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
test_expect_success 'message for merging external branch' '
echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
diff --git a/t/t6415-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh
index 2ce104aca7..2655e295f5 100755
--- a/t/t6415-merge-dir-to-symlink.sh
+++ b/t/t6415-merge-dir-to-symlink.sh
@@ -25,7 +25,8 @@ test_expect_success 'checkout does not clobber untracked symlink' '
git reset --hard main &&
git rm --cached a/b &&
git commit -m "untracked symlink remains" &&
- test_must_fail git checkout start^0
+ test_must_fail git checkout start^0 &&
+ git clean -fd # Do not leave the untracked symlink in the way
'
test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
@@ -34,7 +35,8 @@ test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
git rm --cached a/b &&
git commit -m "untracked symlink remains" &&
git checkout -f start^0 &&
- test_path_is_file a/b-2/c/d
+ test_path_is_file a/b-2/c/d &&
+ git clean -fd # Do not leave the untracked symlink in the way
'
test_expect_success 'checkout should not have deleted a/b-2/c/d' '
diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh
index 5e3779ebc9..89dd544f38 100755
--- a/t/t6424-merge-unrelated-index-changes.sh
+++ b/t/t6424-merge-unrelated-index-changes.sh
@@ -132,6 +132,7 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
# Make index match B
git diff C B -- | git apply --cached &&
+ test_when_finished "git clean -fd" && # Do not leave untracked around
# Merge B & F, with B as "head"
git merge-recursive A -- B F > out &&
test_i18ngrep "Already up to date" out
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index ffcc01fe65..a0efe7cb6d 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -718,7 +718,9 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '
# merge-recursive prints in reverse order, but we do not care
sort <trees >expect &&
sed -n "s/^virtual //p" out | sort >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+
+ git clean -fd
'
test_expect_success 'merge-recursive internal merge resolves to the sameness' '
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index 84b4aacf49..c0b7bd7c3f 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -68,7 +68,8 @@ test_expect_success 'will not overwrite removed file' '
git commit -m "rm c1.c" &&
cp important c1.c &&
test_must_fail git merge c1a &&
- test_cmp important c1.c
+ test_cmp important c1.c &&
+ rm c1.c # Do not leave untracked file in way of future tests
'
test_expect_success 'will not overwrite re-added file' '
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index e5e89c2045..178413c22f 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -5,6 +5,9 @@ test_description='merging with submodules'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 25bb9bbb89..963356ba5f 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -2,10 +2,11 @@
test_description='git mv in subdirs'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
test_expect_success 'prepare reference tree' '
mkdir path0 path1 &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ COPYING_test_data >path0/COPYING &&
git add path0/COPYING &&
git commit -m add -a
'
@@ -107,7 +108,7 @@ test_expect_success 'clean up' '
'
test_expect_success 'adding another file' '
- cp "$TEST_DIRECTORY"/../README.md path0/README &&
+ COPYING_test_data | tr A-Za-z N-ZA-Mn-za-m >path0/README &&
git add path0/README &&
git commit -m add2 -a
'
diff --git a/t/t7002-mv-sparse-checkout.sh b/t/t7002-mv-sparse-checkout.sh
new file mode 100755
index 0000000000..545748949a
--- /dev/null
+++ b/t/t7002-mv-sparse-checkout.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+test_description='git mv in sparse working trees'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+ mkdir -p sub/dir sub/dir2 &&
+ touch a b c sub/d sub/dir/e sub/dir2/e &&
+ git add -A &&
+ git commit -m files &&
+
+ cat >sparse_error_header <<-EOF &&
+ The following paths and/or pathspecs matched paths that exist
+ outside of your sparse-checkout definition, so will not be
+ updated in the index:
+ EOF
+
+ cat >sparse_hint <<-EOF
+ hint: If you intend to update such entries, try one of the following:
+ hint: * Use the --sparse option.
+ hint: * Disable or modify the sparsity rules.
+ hint: Disable this message with \"git config advice.updateSparsePath false\"
+ EOF
+"
+
+test_expect_success 'mv refuses to move sparse-to-sparse' '
+ test_when_finished rm -f e &&
+ git reset --hard &&
+ git sparse-checkout set a &&
+ touch b &&
+ test_must_fail git mv b e 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'mv refuses to move sparse-to-sparse, ignores failure' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a &&
+
+ # tracked-to-untracked
+ touch b &&
+ git mv -k b e 2>stderr &&
+ test_path_exists b &&
+ test_path_is_missing e &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b &&
+ test_path_exists e &&
+
+ # tracked-to-tracked
+ git reset --hard &&
+ touch b &&
+ git mv -k b c 2>stderr &&
+ test_path_exists b &&
+ test_path_is_missing c &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ echo c >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+
+ git mv --sparse b c 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing b &&
+ test_path_exists c
+'
+
+test_expect_success 'mv refuses to move non-sparse-to-sparse' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a &&
+
+ # tracked-to-untracked
+ test_must_fail git mv a e 2>stderr &&
+ test_path_exists a &&
+ test_path_is_missing e &&
+ cat sparse_error_header >expect &&
+ echo e >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse a e 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing a &&
+ test_path_exists e &&
+
+ # tracked-to-tracked
+ rm e &&
+ git reset --hard &&
+ test_must_fail git mv a c 2>stderr &&
+ test_path_exists a &&
+ test_path_is_missing c &&
+ cat sparse_error_header >expect &&
+ echo c >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse a c 2>stderr &&
+ test_must_be_empty stderr &&
+ test_path_is_missing a &&
+ test_path_exists c
+'
+
+test_expect_success 'mv refuses to move sparse-to-non-sparse' '
+ test_when_finished rm -f b c e &&
+ git reset --hard &&
+ git sparse-checkout set a e &&
+
+ # tracked-to-untracked
+ touch b &&
+ test_must_fail git mv b e 2>stderr &&
+ cat sparse_error_header >expect &&
+ echo b >>expect &&
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse b e 2>stderr &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'recursive mv refuses to move (possible) sparse' '
+ test_when_finished rm -rf b c e sub2 &&
+ git reset --hard &&
+ # Without cone mode, "sub" and "sub2" do not match
+ git sparse-checkout set sub/dir sub2/dir &&
+
+ # Add contained contents to ensure we avoid non-existence errors
+ mkdir sub/dir2 &&
+ touch sub/d sub/dir2/e &&
+
+ test_must_fail git mv sub sub2 2>stderr &&
+ cat sparse_error_header >expect &&
+ cat >>expect <<-\EOF &&
+ sub/d
+ sub2/d
+ sub/dir/e
+ sub2/dir/e
+ sub/dir2/e
+ sub2/dir2/e
+ EOF
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse sub sub2 2>stderr &&
+ test_must_be_empty stderr &&
+ git commit -m "moved sub to sub2" &&
+ git rev-parse HEAD~1:sub >expect &&
+ git rev-parse HEAD:sub2 >actual &&
+ test_cmp expect actual &&
+ git reset --hard HEAD~1
+'
+
+test_expect_success 'recursive mv refuses to move sparse' '
+ git reset --hard &&
+ # Use cone mode so "sub/" matches the sparse-checkout patterns
+ git sparse-checkout init --cone &&
+ git sparse-checkout set sub/dir sub2/dir &&
+
+ # Add contained contents to ensure we avoid non-existence errors
+ mkdir sub/dir2 &&
+ touch sub/dir2/e &&
+
+ test_must_fail git mv sub sub2 2>stderr &&
+ cat sparse_error_header >expect &&
+ cat >>expect <<-\EOF &&
+ sub/dir2/e
+ sub2/dir2/e
+ EOF
+ cat sparse_hint >>expect &&
+ test_cmp expect stderr &&
+ git mv --sparse sub sub2 2>stderr &&
+ test_must_be_empty stderr &&
+ git commit -m "moved sub to sub2" &&
+ git rev-parse HEAD~1:sub >expect &&
+ git rev-parse HEAD:sub2 >actual &&
+ test_cmp expect actual &&
+ git reset --hard HEAD~1
+'
+
+test_done
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh
new file mode 100755
index 0000000000..06c9dd6c93
--- /dev/null
+++ b/t/t7031-verify-tag-signed-ssh.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='signed tag tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed tags ssh' '
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag -s -m initial initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -m second &&
+ git tag -s -m second second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout main &&
+ test_tick && git merge -S side &&
+ git tag -s -m merge merge &&
+
+ echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" &&
+ git tag -a -m fourth-unsigned fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag -s -m fourth fourth-signed &&
+
+ echo 5 >file && test_tick && git commit -a -m "fifth" &&
+ git tag fifth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag -a -m sixth sixth-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ &&
+ git tag -m seventh -s seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth &&
+ git tag -u"${GPGSSH_KEY_UNTRUSTED}" -m eighth eighth-signed-alt
+'
+
+test_expect_success GPGSSH 'verify and show ssh signatures' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag $tag 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ test_must_fail git verify-tag $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'detect fudged ssh signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file tag seventh-signed >raw &&
+ sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+ git hash-object -w -t tag forged1 >forged1.tag &&
+ test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
+ grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'verify ssh signatures with --raw' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for tag in initial second merge fourth-signed sixth-signed seventh-signed
+ do
+ git verify-tag --raw $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+ do
+ test_must_fail git verify-tag --raw $tag 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ ) &&
+ (
+ for tag in eighth-signed-alt
+ do
+ test_must_fail git verify-tag --raw $tag 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $tag OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git verify-tag --raw sixth-signed 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo sixth-signed OK
+'
+
+test_expect_success GPGSSH 'verify multiple tags ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ tags="seventh-signed sixth-signed" &&
+ for i in $tags
+ do
+ git verify-tag -v --raw $i || return 1
+ done >expect.stdout 2>expect.stderr.1 &&
+ grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <expect.stderr.1 >expect.stderr &&
+ git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+ grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <actual.stderr.1 >actual.stderr &&
+ test_cmp expect.stdout actual.stdout &&
+ test_cmp expect.stderr actual.stderr
+'
+
+test_expect_success GPGSSH 'verifying tag with --format - ssh' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect <<-\EOF &&
+ tagname : fourth-signed
+ EOF
+ git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'verifying a forged tag with --format should fail silently - ssh' '
+ test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
+ test_must_be_empty actual-forged
+'
+
+test_done
diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh
index bfce05ac5d..5530651eea 100755
--- a/t/t7101-reset-empty-subdirs.sh
+++ b/t/t7101-reset-empty-subdirs.sh
@@ -5,10 +5,11 @@
test_description='git reset should cull empty subdirs'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
test_expect_success 'creating initial files' '
mkdir path0 &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ COPYING_test_data >path0/COPYING &&
git add path0/COPYING &&
git commit -m add -a
'
@@ -16,10 +17,10 @@ test_expect_success 'creating initial files' '
test_expect_success 'creating second files' '
mkdir path1 &&
mkdir path1/path2 &&
- cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING COPYING &&
- cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
+ COPYING_test_data >path1/path2/COPYING &&
+ COPYING_test_data >path1/COPYING &&
+ COPYING_test_data >COPYING &&
+ COPYING_test_data >path0/COPYING-TOO &&
git add path1/path2/COPYING &&
git add path1/COPYING &&
git add COPYING &&
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec392b..cf9697eba9 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -2,6 +2,7 @@
test_description='reset --hard unmerged'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success setup '
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
index 19830d9036..a3e2413bc3 100755
--- a/t/t7112-reset-submodule.sh
+++ b/t/t7112-reset-submodule.sh
@@ -6,7 +6,6 @@ test_description='reset can handle submodules'
. "$TEST_DIRECTORY"/lib-submodule-update.sh
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
test_submodule_switch_recursing_with_args "reset --keep"
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 7f6e23a4bb..b7ba1c3268 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -585,6 +585,7 @@ test_expect_success 'checkout --conflict=diff3' '
'
test_expect_success 'failing checkout -b should not break working tree' '
+ git clean -fd && # Remove untracked files in the way
git reset --hard main &&
git symbolic-ref HEAD refs/heads/main &&
test_must_fail git checkout -b renamer side^ &&
diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh
index 3f7f271883..f87e524d6d 100755
--- a/t/t7418-submodule-sparse-gitmodules.sh
+++ b/t/t7418-submodule-sparse-gitmodules.sh
@@ -12,6 +12,9 @@ The test setup uses a sparse checkout, however the same scenario can be set up
also by committing .gitmodules and then just removing it from the filesystem.
'
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
. ./test-lib.sh
test_expect_success 'sparse checkout setup which hides .gitmodules' '
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 7a8194ce72..2a07c70867 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -250,7 +250,6 @@ test_rebase () {
}
test_rebase success
-test_have_prereq !REBASE_P || test_rebase success -p
test_expect_success 'with hook (cherry-pick)' '
test_when_finished "git checkout -f main" &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 8df5a74f1d..d65a0171f2 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -71,7 +71,25 @@ test_expect_success GPG 'create signed commits' '
git tag eleventh-signed $(cat oid) &&
echo 12 | git commit-tree --gpg-sign=B7227189 HEAD^{tree} >oid &&
test_line_count = 1 oid &&
- git tag twelfth-signed-alt $(cat oid)
+ git tag twelfth-signed-alt $(cat oid) &&
+
+ cat >keydetails <<-\EOF &&
+ Key-Type: RSA
+ Key-Length: 2048
+ Subkey-Type: RSA
+ Subkey-Length: 2048
+ Name-Real: Unknown User
+ Name-Email: unknown@git.com
+ Expire-Date: 0
+ %no-ask-passphrase
+ %no-protection
+ EOF
+ gpg --batch --gen-key keydetails &&
+ echo 13 >file && git commit -a -S"unknown@git.com" -m thirteenth &&
+ git tag thirteenth-signed &&
+ DELETE_FINGERPRINT=$(gpg -K --with-colons --fingerprint --batch unknown@git.com | grep "^fpr" | head -n 1 | awk -F ":" "{print \$10;}") &&
+ gpg --batch --yes --delete-secret-keys $DELETE_FINGERPRINT &&
+ gpg --batch --yes --delete-keys unknown@git.com
'
test_expect_success GPG 'verify and show signatures' '
@@ -110,6 +128,13 @@ test_expect_success GPG 'verify and show signatures' '
)
'
+test_expect_success GPG 'verify-commit exits failure on unknown signature' '
+ test_must_fail git verify-commit thirteenth-signed 2>actual &&
+ ! grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ grep -q -F -e "No public key" -e "public key not found" actual
+'
+
test_expect_success GPG 'verify-commit exits success on untrusted signature' '
git verify-commit eighth-signed-alt 2>actual &&
grep "Good signature from" actual &&
@@ -338,6 +363,8 @@ test_expect_success GPG 'show double signature with custom format' '
'
+# NEEDSWORK: This test relies on the test_tick commit/author dates from the first
+# 'create signed commits' test even though it creates its own
test_expect_success GPG 'verify-commit verifies multiply signed commits' '
git init multiply-signed &&
cd multiply-signed &&
diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh
index 405420ae4d..163ae80468 100755
--- a/t/t7517-per-repo-email.sh
+++ b/t/t7517-per-repo-email.sh
@@ -75,19 +75,6 @@ test_expect_success 'noop interactive rebase does not care about ident' '
git rebase -i HEAD^
'
-test_expect_success REBASE_P \
- 'fast-forward rebase does not care about ident (preserve)' '
- git checkout -B tmp side-without-commit &&
- git rebase -p main
-'
-
-test_expect_success REBASE_P \
- 'non-fast-forward rebase refuses to write commits (preserve)' '
- test_when_finished "git rebase --abort || true" &&
- git checkout -B tmp side-with-commit &&
- test_must_fail git rebase -p main
-'
-
test_expect_success 'author.name overrides user.name' '
test_config user.name user &&
test_config user.email user@example.com &&
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
index 905957bd0a..fffdb6ff2e 100755
--- a/t/t7518-ident-corner-cases.sh
+++ b/t/t7518-ident-corner-cases.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='corner cases in ident strings'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# confirm that we do not segfault _and_ that we do not say "(null)", as
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index f1463197b9..f488d930df 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -399,41 +399,45 @@ check_sparse_index_behavior () {
}
test_expect_success 'status succeeds with sparse index' '
- git clone . full &&
- git clone --sparse . sparse &&
- git -C sparse sparse-checkout init --cone --sparse-index &&
- git -C sparse sparse-checkout set dir1 dir2 &&
+ (
+ sane_unset GIT_TEST_SPLIT_INDEX &&
- write_script .git/hooks/fsmonitor-test <<-\EOF &&
- printf "last_update_token\0"
- EOF
- git -C full config core.fsmonitor ../.git/hooks/fsmonitor-test &&
- git -C sparse config core.fsmonitor ../.git/hooks/fsmonitor-test &&
- check_sparse_index_behavior ! &&
+ git clone . full &&
+ git clone --sparse . sparse &&
+ git -C sparse sparse-checkout init --cone --sparse-index &&
+ git -C sparse sparse-checkout set dir1 dir2 &&
- write_script .git/hooks/fsmonitor-test <<-\EOF &&
- printf "last_update_token\0"
- printf "dir1/modified\0"
- EOF
- check_sparse_index_behavior ! &&
-
- git -C sparse sparse-checkout add dir1a &&
+ write_script .git/hooks/fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ EOF
+ git -C full config core.fsmonitor ../.git/hooks/fsmonitor-test &&
+ git -C sparse config core.fsmonitor ../.git/hooks/fsmonitor-test &&
+ check_sparse_index_behavior ! &&
- for repo in full sparse
- do
- cp -r $repo/dir1 $repo/dir1a &&
- git -C $repo add dir1a &&
- git -C $repo commit -m "add dir1a" || return 1
- done &&
- git -C sparse sparse-checkout set dir1 dir2 &&
-
- # This one modifies outside the sparse-checkout definition
- # and hence we expect to expand the sparse-index.
- write_script .git/hooks/fsmonitor-test <<-\EOF &&
- printf "last_update_token\0"
- printf "dir1a/modified\0"
- EOF
- check_sparse_index_behavior
+ write_script .git/hooks/fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ printf "dir1/modified\0"
+ EOF
+ check_sparse_index_behavior ! &&
+
+ git -C sparse sparse-checkout add dir1a &&
+
+ for repo in full sparse
+ do
+ cp -r $repo/dir1 $repo/dir1a &&
+ git -C $repo add dir1a &&
+ git -C $repo commit -m "add dir1a" || return 1
+ done &&
+ git -C sparse sparse-checkout set dir1 dir2 &&
+
+ # This one modifies outside the sparse-checkout definition
+ # and hence we expect to expand the sparse-index.
+ write_script .git/hooks/fsmonitor-test <<-\EOF &&
+ printf "last_update_token\0"
+ printf "dir1a/modified\0"
+ EOF
+ check_sparse_index_behavior
+ )
'
test_done
diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh
new file mode 100755
index 0000000000..badf3ed320
--- /dev/null
+++ b/t/t7528-signed-commit-ssh.sh
@@ -0,0 +1,398 @@
+#!/bin/sh
+
+test_description='ssh signed commit tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed commits' '
+ test_oid_cache <<-\EOF &&
+ header sha1:gpgsig
+ header sha256:gpgsig-sha256
+ EOF
+
+ test_when_finished "test_unconfig commit.gpgsign" &&
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+ echo 1 >file && git add file &&
+ test_tick && git commit -S -m initial &&
+ git tag initial &&
+ git branch side &&
+
+ echo 2 >file && test_tick && git commit -a -S -m second &&
+ git tag second &&
+
+ git checkout side &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -m "third on side" &&
+
+ git checkout main &&
+ test_tick && git merge -S side &&
+ git tag merge &&
+
+ echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+ git tag fourth-unsigned &&
+
+ test_tick && git commit --amend -S -m "fourth signed" &&
+ git tag fourth-signed &&
+
+ git config commit.gpgsign true &&
+ echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
+ git tag fifth-signed &&
+
+ git config commit.gpgsign false &&
+ echo 6 >file && test_tick && git commit -a -m "sixth" &&
+ git tag sixth-unsigned &&
+
+ git config commit.gpgsign true &&
+ echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
+ git tag seventh-unsigned &&
+
+ test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
+ git tag seventh-signed &&
+
+ echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" &&
+ git tag eighth-signed-alt &&
+
+ # commit.gpgsign is still on but this must not be signed
+ echo 9 | git commit-tree HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag ninth-unsigned $(cat oid) &&
+ # explicit -S of course must sign.
+ echo 10 | git commit-tree -S HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag tenth-signed $(cat oid) &&
+
+ # --gpg-sign[=<key-id>] must sign.
+ echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag eleventh-signed $(cat oid) &&
+ echo 12 | git commit-tree --gpg-sign="${GPGSSH_KEY_UNTRUSTED}" HEAD^{tree} >oid &&
+ test_line_count = 1 oid &&
+ git tag twelfth-signed-alt $(cat oid)
+'
+
+test_expect_success GPGSSH 'verify and show signatures' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.mintrustlevel UNDEFINED &&
+ (
+ for commit in initial second merge fourth-signed \
+ fifth-signed sixth-signed seventh-signed tenth-signed \
+ eleventh-signed
+ do
+ git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned \
+ seventh-unsigned ninth-unsigned
+ do
+ test_must_fail git verify-commit $commit &&
+ git show --pretty=short --show-signature $commit >actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt twelfth-signed-alt
+ do
+ git show --pretty=short --show-signature $commit >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_must_fail git verify-commit eighth-signed-alt 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.minTrustLevel fully &&
+ git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with low minTrustLevel' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config gpg.minTrustLevel marginal &&
+ git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure with high minTrustLevel' '
+ test_config gpg.minTrustLevel ultimate &&
+ test_must_fail git verify-commit eighth-signed-alt
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ (
+ for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
+ do
+ git verify-commit --raw $commit 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+ do
+ test_must_fail git verify-commit --raw $commit 2>actual &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ ) &&
+ (
+ for commit in eighth-signed-alt
+ do
+ test_must_fail git verify-commit --raw $commit 2>actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+ echo $commit OK || exit 1
+ done
+ )
+'
+
+test_expect_success GPGSSH 'proper header is used for hash algorithm' '
+ git cat-file commit fourth-signed >output &&
+ grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output
+'
+
+test_expect_success GPGSSH 'show signed commit with signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git show -s initial >commit &&
+ git show -s --show-signature initial >show &&
+ git verify-commit -v initial >verify.1 2>verify.2 &&
+ git cat-file commit initial >cat &&
+ grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit &&
+ grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg &&
+ grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
+ test_cmp show.commit commit &&
+ test_cmp show.gpg verify.2 &&
+ test_cmp cat.commit verify.1
+'
+
+test_expect_success GPGSSH 'detect fudged signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file commit seventh-signed >raw &&
+ sed -e "s/^seventh/7th forged/" raw >forged1 &&
+ git hash-object -w -t commit forged1 >forged1.commit &&
+ test_must_fail git verify-commit $(cat forged1.commit) &&
+ git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
+ grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'detect fudged signature with NUL' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git cat-file commit seventh-signed >raw &&
+ cat raw >forged2 &&
+ echo Qwik | tr "Q" "\000" >>forged2 &&
+ git hash-object -w -t commit forged2 >forged2.commit &&
+ test_must_fail git verify-commit $(cat forged2.commit) &&
+ git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
+ grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
+ ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2
+'
+
+test_expect_success GPGSSH 'amending already signed commit' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ git checkout fourth-signed^0 &&
+ git commit --amend -S --no-edit &&
+ git verify-commit HEAD &&
+ git show -s --show-signature HEAD >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+ ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'show good signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ cat >expect.tmpl <<-\EOF &&
+ G
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show bad signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect <<-\EOF &&
+ B
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with custom format' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ U
+ FINGERPRINT
+
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with undefined trust level' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ undefined
+ FINGERPRINT
+
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with ultimate trust level' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ cat >expect.tmpl <<-\EOF &&
+ fully
+ FINGERPRINT
+ principal with number 1
+ FINGERPRINT
+
+ EOF
+ git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+ FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+ sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show lack of signature with custom format' '
+ cat >expect <<-\EOF &&
+ N
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'log.showsignature behaves like --show-signature' '
+ test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+ test_config log.showsignature true &&
+ git show initial >actual &&
+ grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'check config gpg.format values' '
+ test_config gpg.format ssh &&
+ test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+ test_config gpg.format ssh &&
+ git commit -S --amend -m "success" &&
+ test_config gpg.format OpEnPgP &&
+ test_must_fail git commit -S --amend -m "fail"
+'
+
+test_expect_failure GPGSSH 'detect fudged commit with double signature (TODO)' '
+ sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
+ sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
+ sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
+ gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
+ cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
+ sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
+ double-combined.asc > double-gpgsig &&
+ sed -e "/committer/r double-gpgsig" double-base >double-commit &&
+ git hash-object -w -t commit double-commit >double-commit.commit &&
+ test_must_fail git verify-commit $(cat double-commit.commit) &&
+ git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
+ grep "BAD signature from" double-actual &&
+ grep "Good signature from" double-actual
+'
+
+test_expect_failure GPGSSH 'show double signature with custom format (TODO)' '
+ cat >expect <<-\EOF &&
+ E
+
+
+
+
+ EOF
+ git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' '
+ git init multiply-signed &&
+ cd multiply-signed &&
+ test_commit first &&
+ echo 1 >second &&
+ git add second &&
+ tree=$(git write-tree) &&
+ parent=$(git rev-parse HEAD^{commit}) &&
+ git commit --gpg-sign -m second &&
+ git cat-file commit HEAD &&
+ # Avoid trailing whitespace.
+ sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
+ Qtree $tree
+ Qparent $parent
+ Qauthor A U Thor <author@example.com> 1112912653 -0700
+ Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
+ Qgpgsig -----BEGIN PGP SIGNATURE-----
+ QZ
+ Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
+ Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
+ Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
+ Q =tQ0N
+ Q -----END PGP SIGNATURE-----
+ Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+ QZ
+ Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
+ Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
+ Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
+ Q =pIwP
+ Q -----END PGP SIGNATURE-----
+ Q
+ Qsecond
+ EOF
+ head=$(git hash-object -t commit -w commit) &&
+ git reset --hard $head &&
+ git verify-commit $head 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 2ef39d3088..c773e30b3f 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -717,6 +717,7 @@ test_expect_success 'failed fast-forward merge with --autostash' '
git reset --hard c0 &&
git merge-file file file.orig file.5 &&
cp file.5 other &&
+ test_when_finished "rm other" &&
test_must_fail git merge --autostash c1 2>err &&
test_i18ngrep "Applied autostash." err &&
test_cmp file.5 file
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index cd4f9607dc..eca7555101 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -4,6 +4,7 @@ test_description='git merge
Testing merge when using a custom message for the merge commit.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
create_merge_msgs() {
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 98eda3bfeb..0260ad6f0e 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -3,6 +3,8 @@
test_description='git repack works correctly'
. ./test-lib.sh
+. "${TEST_DIRECTORY}/lib-bitmap.sh"
+. "${TEST_DIRECTORY}/lib-midx.sh"
commit_and_pack () {
test_commit "$@" 1>&2 &&
@@ -234,4 +236,140 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
test_must_be_empty actual
'
+objdir=.git/objects
+midx=$objdir/pack/multi-pack-index
+
+test_expect_success 'setup for --write-midx tests' '
+ git init midx &&
+ (
+ cd midx &&
+ git config core.multiPackIndex true &&
+
+ test_commit base
+ )
+'
+
+test_expect_success '--write-midx unchanged' '
+ (
+ cd midx &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack &&
+ test_path_is_missing $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with a new pack' '
+ (
+ cd midx &&
+ test_commit loose &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with -b' '
+ (
+ cd midx &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -mb &&
+
+ test_path_is_file $midx &&
+ test_path_is_file $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success '--write-midx with -d' '
+ (
+ cd midx &&
+ test_commit repack &&
+
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Ad --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-*.bitmap &&
+ test_midx_consistent $objdir
+ )
+'
+
+test_expect_success 'cleans up MIDX when appropriate' '
+ (
+ cd midx &&
+
+ test_commit repack-2 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
+
+ checksum=$(midx_checksum $objdir) &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$checksum.bitmap &&
+ test_path_is_file $midx-$checksum.rev &&
+
+ test_commit repack-3 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb --write-midx &&
+
+ test_path_is_file $midx &&
+ test_path_is_missing $midx-$checksum.bitmap &&
+ test_path_is_missing $midx-$checksum.rev &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+ test_path_is_file $midx-$(midx_checksum $objdir).rev &&
+
+ test_commit repack-4 &&
+ GIT_TEST_MULTI_PACK_INDEX=0 git repack -Adb &&
+
+ find $objdir/pack -type f -name "multi-pack-index*" >files &&
+ test_must_be_empty files
+ )
+'
+
+test_expect_success '--write-midx with preferred bitmap tips' '
+ git init midx-preferred-tips &&
+ test_when_finished "rm -fr midx-preferred-tips" &&
+ (
+ cd midx-preferred-tips &&
+
+ test_commit_bulk --message="%s" 103 &&
+
+ git log --format="%H" >commits.raw &&
+ sort <commits.raw >commits &&
+
+ git log --format="create refs/tags/%s/%s %H" HEAD >refs &&
+ git update-ref --stdin <refs &&
+
+ git repack --write-midx --write-bitmap-index &&
+ test_path_is_file $midx &&
+ test_path_is_file $midx-$(midx_checksum $objdir).bitmap &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >before &&
+ test_line_count = 1 before &&
+
+ rm -fr $midx-$(midx_checksum $objdir).bitmap &&
+ rm -fr $midx-$(midx_checksum $objdir).rev &&
+ rm -fr $midx &&
+
+ # instead of constructing the snapshot ourselves (c.f., the test
+ # "write a bitmap with --refs-snapshot (preferred tips)" in
+ # t5326), mark the missing commit as preferred by adding it to
+ # the pack.preferBitmapTips configuration.
+ git for-each-ref --format="%(refname:rstrip=1)" \
+ --points-at="$(cat before)" >missing &&
+ git config pack.preferBitmapTips "$(cat missing)" &&
+ git repack --write-midx --write-bitmap-index &&
+
+ test-tool bitmap list-commits | sort >bitmaps &&
+ comm -13 bitmaps commits >after &&
+
+ ! test_cmp before after
+ )
+'
+
test_done
diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh
index 5ccaa440e0..bdbbcbf1ec 100755
--- a/t/t7703-repack-geometric.sh
+++ b/t/t7703-repack-geometric.sh
@@ -15,7 +15,7 @@ test_expect_success '--geometric with no packs' '
(
cd geometric &&
- git repack --geometric 2 >out &&
+ git repack --write-midx --geometric 2 >out &&
test_i18ngrep "Nothing new to pack" out
)
'
@@ -180,4 +180,26 @@ test_expect_success '--geometric ignores kept packs' '
)
'
+test_expect_success '--geometric chooses largest MIDX preferred pack' '
+ git init geometric &&
+ test_when_finished "rm -fr geometric" &&
+ (
+ cd geometric &&
+
+ # These packs already form a geometric progression.
+ test_commit_bulk --start=1 1 && # 3 objects
+ test_commit_bulk --start=2 2 && # 6 objects
+ ls $objdir/pack/pack-*.idx >before &&
+ test_commit_bulk --start=4 4 && # 12 objects
+ ls $objdir/pack/pack-*.idx >after &&
+
+ git repack --geometric 2 -dbm &&
+
+ comm -3 before after | xargs -n 1 basename >expect &&
+ test-tool read-midx --preferred-pack $objdir >actual &&
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index a173f564bc..096456292c 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -453,6 +453,13 @@ run_dir_diff_test 'difftool --dir-diff' '
grep "^file$" output
'
+run_dir_diff_test 'difftool --dir-diff avoids repeated slashes in TMPDIR' '
+ TMPDIR="${TMPDIR:-/tmp}////" \
+ git difftool --dir-diff $symlinks --extcmd echo branch >output &&
+ grep -v // output >actual &&
+ test_line_count = 1 actual
+'
+
run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
grep "^sub$" output &&
@@ -674,7 +681,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
rm c &&
ln -s d c &&
cat >expect <<-EOF &&
- b
c
c
@@ -710,7 +716,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
# Deleted symlinks
rm -f c &&
cat >expect <<-EOF &&
- b
c
EOF
@@ -723,6 +728,71 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
test_cmp expect actual
'
+test_expect_success SYMLINKS 'difftool --dir-diff writes symlinks as raw text' '
+ # Start out on a branch called "branch-init".
+ git init -b branch-init symlink-files &&
+ (
+ cd symlink-files &&
+ # This test ensures that symlinks are written as raw text.
+ # The "cat" tools output link and file contents.
+ git config difftool.cat-left-link.cmd "cat \"\$LOCAL/link\"" &&
+ git config difftool.cat-left-a.cmd "cat \"\$LOCAL/file-a\"" &&
+ git config difftool.cat-right-link.cmd "cat \"\$REMOTE/link\"" &&
+ git config difftool.cat-right-b.cmd "cat \"\$REMOTE/file-b\"" &&
+
+ # Record the empty initial state so that we can come back here
+ # later and not have to consider the any cases where difftool
+ # will create symlinks back into the worktree.
+ test_tick &&
+ git commit --allow-empty -m init &&
+
+ # Create a file called "file-a" with a symlink pointing to it.
+ git switch -c branch-a &&
+ echo a >file-a &&
+ ln -s file-a link &&
+ git add file-a link &&
+ test_tick &&
+ git commit -m link-to-file-a &&
+
+ # Create a file called "file-b" and point the symlink to it.
+ git switch -c branch-b &&
+ echo b >file-b &&
+ rm link &&
+ ln -s file-b link &&
+ git add file-b link &&
+ git rm file-a &&
+ test_tick &&
+ git commit -m link-to-file-b &&
+
+ # Checkout the initial branch so that the --symlinks behavior is
+ # not activated. The two directories should be completely
+ # independent with no symlinks pointing back here.
+ git switch branch-init &&
+
+ # The left link must be "file-a" and "file-a" must contain "a".
+ echo file-a >expect &&
+ git difftool --symlinks --dir-diff --tool cat-left-link \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ echo a >expect &&
+ git difftool --symlinks --dir-diff --tool cat-left-a \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ # The right link must be "file-b" and "file-b" must contain "b".
+ echo file-b >expect &&
+ git difftool --symlinks --dir-diff --tool cat-right-link \
+ branch-a branch-b >actual &&
+ test_cmp expect actual &&
+
+ echo b >expect &&
+ git difftool --symlinks --dir-diff --tool cat-right-b \
+ branch-a branch-b >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'add -N and difftool -d' '
test_when_finished git reset --hard &&
diff --git a/t/t7813-grep-icase-iso.sh b/t/t7813-grep-icase-iso.sh
index 701e08a8e5..1227885737 100755
--- a/t/t7813-grep-icase-iso.sh
+++ b/t/t7813-grep-icase-iso.sh
@@ -2,6 +2,7 @@
test_description='grep icase on non-English locales'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
test_expect_success GETTEXT_ISO_LOCALE 'setup' '
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 3172f5b936..058e5d0c96 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -441,4 +441,107 @@ test_expect_success 'grep --recurse-submodules with --cached ignores worktree mo
test_must_fail git grep --recurse-submodules --cached "A modified line in submodule" >actual 2>&1 &&
test_must_be_empty actual
'
+
+test_expect_failure 'grep --textconv: superproject .gitattributes does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv: superproject .gitattributes (from index) does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >.gitattributes &&
+ git add .gitattributes &&
+ rm .gitattributes &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv: superproject .git/info/attributes does not affect submodules' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ super_attr="$(git rev-parse --git-path info/attributes)" &&
+ test_when_finished "rm -f \"$super_attr\"" &&
+ echo "a diff=d2x" >"$super_attr" &&
+
+ cat >expect <<-\EOF &&
+ a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+# Note: what currently prevents this test from passing is not that the
+# .gitattributes file from "./submodule" is being ignored, but that it is being
+# propagated to the nested "./submodule/sub" files.
+#
+test_expect_failure 'grep --textconv correctly reads submodule .gitattributes' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >submodule/.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv correctly reads submodule .gitattributes (from index)' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+ echo "a diff=d2x" >submodule/.gitattributes &&
+ git -C submodule add .gitattributes &&
+ rm submodule/.gitattributes &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep --textconv correctly reads submodule .git/info/attributes' '
+ reset_and_clean &&
+ test_config_global diff.d2x.textconv "sed -e \"s/d/x/\"" &&
+
+ submodule_attr="$(git -C submodule rev-parse --path-format=absolute --git-path info/attributes)" &&
+ test_when_finished "rm -f \"$submodule_attr\"" &&
+ echo "a diff=d2x" >"$submodule_attr" &&
+
+ cat >expect <<-\EOF &&
+ submodule/a:(1|2)x(3|4)
+ EOF
+ git grep --textconv --recurse-submodules x >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'grep saves textconv cache in the appropriate repository' '
+ reset_and_clean &&
+ test_config_global diff.d2x_cached.textconv "sed -e \"s/d/x/\"" &&
+ test_config_global diff.d2x_cached.cachetextconv true &&
+ echo "a diff=d2x_cached" >submodule/.gitattributes &&
+
+ # We only read/write to the textconv cache when grepping from an OID,
+ # as the working tree file might have modifications.
+ git grep --textconv --cached --recurse-submodules x &&
+
+ super_textconv_cache="$(git rev-parse --git-path refs/notes/textconv/d2x_cached)" &&
+ sub_textconv_cache="$(git -C submodule rev-parse \
+ --path-format=absolute --git-path refs/notes/textconv/d2x_cached)" &&
+ test_path_is_missing "$super_textconv_cache" &&
+ test_path_is_file "$sub_textconv_cache"
+'
+
test_done
diff --git a/t/t7816-grep-binary-pattern.sh b/t/t7816-grep-binary-pattern.sh
index 9d67a5fc4c..fdb2355649 100755
--- a/t/t7816-grep-binary-pattern.sh
+++ b/t/t7816-grep-binary-pattern.sh
@@ -2,6 +2,7 @@
test_description='git grep with a binary pattern files'
+TEST_PASSES_SANITIZE_LEAK=true
. ./lib-gettext.sh
nul_match_internal () {
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 36a4218745..9b9f11a8e7 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -21,8 +21,7 @@ test_xmllint () {
}
test_lazy_prereq SYSTEMD_ANALYZE '
- systemd-analyze --help >out &&
- grep verify out
+ systemd-analyze verify /lib/systemd/system/basic.target
'
test_systemd_analyze_verify () {
@@ -277,7 +276,7 @@ test_expect_success 'incremental-repack task' '
# Delete refs that have not been repacked in these packs.
git for-each-ref --format="delete %(refname)" \
- refs/prefetch refs/tags >refs &&
+ refs/prefetch refs/tags refs/remotes >refs &&
git update-ref --stdin <refs &&
# Replace the object directory with this pack layout.
@@ -286,6 +285,10 @@ test_expect_success 'incremental-repack task' '
ls $packDir/*.pack >packs-before &&
test_line_count = 3 packs-before &&
+ # make sure we do not have any broken refs that were
+ # missed in the deletion above
+ git for-each-ref &&
+
# the job repacks the two into a new pack, but does not
# delete the old ones.
git maintenance run --task=incremental-repack &&
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 2d29d486ee..17f988edd2 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -36,6 +36,13 @@ CVSWORK="$PWD/cvswork"
CVS_SERVER=git-cvsserver
export CVSROOT CVS_SERVER
+if perl -e 'exit(1) if not defined crypt("", "cv")'
+then
+ PWDHASH='lac2ItudM3.KM'
+else
+ PWDHASH='$2b$10$t8fGvE/a9eLmfOLzsZme2uOa2QtoMYwIxq9wZA6aBKtF1Yb7FJIzi'
+fi
+
rm -rf "$CVSWORK" "$SERVERDIR"
test_expect_success 'setup' '
git config push.default matching &&
@@ -54,7 +61,7 @@ test_expect_success 'setup' '
GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
- echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
+ echo "cvsuser:$PWDHASH" >"$SERVERDIR/auth.db"
'
# note that cvs doesn't accept absolute pathnames
diff --git a/t/test-lib.sh b/t/test-lib.sh
index d5ee964254..a291a5d4a2 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -534,7 +534,7 @@ SQ=\'
# when case-folding filenames
u200c=$(printf '\342\200\214')
-export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX
+export _x05 _x35 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX
# Each test should start with something like this, after copyright notices:
#
@@ -1381,14 +1381,46 @@ then
test_done
fi
+# skip non-whitelisted tests when compiled with SANITIZE=leak
+if test -n "$SANITIZE_LEAK"
+then
+ if test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+ then
+ # We need to see it in "git env--helper" (via
+ # test_bool_env)
+ export TEST_PASSES_SANITIZE_LEAK
+
+ if ! test_bool_env TEST_PASSES_SANITIZE_LEAK false
+ then
+ skip_all="skipping $this_test under GIT_TEST_PASSING_SANITIZE_LEAK=true"
+ test_done
+ fi
+ fi
+elif test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
+then
+ error "GIT_TEST_PASSING_SANITIZE_LEAK=true has no effect except when compiled with SANITIZE=leak"
+fi
+
# Last-minute variable setup
USER_HOME="$HOME"
HOME="$TRASH_DIRECTORY"
GNUPGHOME="$HOME/gnupg-home-not-used"
export HOME GNUPGHOME USER_HOME
+# "rm -rf" existing trash directory, even if a previous run left it
+# with bad permissions.
+remove_trash_directory () {
+ dir="$1"
+ if ! rm -rf "$dir" 2>/dev/null
+ then
+ chmod -R u+rwx "$dir"
+ rm -rf "$dir"
+ fi
+ ! test -d "$dir"
+}
+
# Test repository
-rm -fr "$TRASH_DIRECTORY" || {
+remove_trash_directory "$TRASH_DIRECTORY" || {
GIT_EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
exit 1
@@ -1425,10 +1457,9 @@ then
fi
# Convenience
-# A regexp to match 5, 35 and 40 hexdigits
+# A regexp to match 5 and 35 hexdigits
_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
-_x40="$_x35$_x05"
test_oid_init
@@ -1437,7 +1468,6 @@ OID_REGEX=$(echo $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
OIDPATH_REGEX=$(test_oid_to_path $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
EMPTY_TREE=$(test_oid empty_tree)
EMPTY_BLOB=$(test_oid empty_blob)
-_z40=$ZERO_OID
# Provide an implementation of the 'yes' utility; the upper bound
# limit is there to help Windows that cannot stop this loop from
@@ -1536,6 +1566,7 @@ test -z "$NO_PYTHON" && test_set_prereq PYTHON
test -n "$USE_LIBPCRE2" && test_set_prereq PCRE
test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
+test -n "$SANITIZE_LEAK" && test_set_prereq SANITIZE_LEAK
if test -z "$GIT_TEST_CHECK_CACHE_TREE"
then
@@ -1711,10 +1742,6 @@ test_lazy_prereq SHA1 '
esac
'
-test_lazy_prereq REBASE_P '
- test -z "$GIT_TEST_SKIP_REBASE_P"
-'
-
# Ensure that no test accidentally triggers a Git command
# that runs the actual maintenance scheduler, affecting a user's
# system permanently.
diff --git a/trace.h b/trace.h
index 0dbbad0e41..e25984051a 100644
--- a/trace.h
+++ b/trace.h
@@ -89,7 +89,7 @@ struct trace_key {
extern struct trace_key trace_default_key;
-#define TRACE_KEY_INIT(name) { "GIT_TRACE_" #name, 0, 0, 0 }
+#define TRACE_KEY_INIT(name) { .key = "GIT_TRACE_" #name }
extern struct trace_key trace_perf_key;
extern struct trace_key trace_setup_key;
diff --git a/trace2.c b/trace2.c
index b9b154ac44..b2d471526f 100644
--- a/trace2.c
+++ b/trace2.c
@@ -394,6 +394,37 @@ void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
us_elapsed_child);
}
+void trace2_child_ready_fl(const char *file, int line,
+ struct child_process *cmd,
+ const char *ready)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
+ uint64_t us_now;
+ uint64_t us_elapsed_absolute;
+ uint64_t us_elapsed_child;
+
+ if (!trace2_enabled)
+ return;
+
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
+ if (cmd->trace2_child_us_start)
+ us_elapsed_child = us_now - cmd->trace2_child_us_start;
+ else
+ us_elapsed_child = 0;
+
+ for_each_wanted_builtin (j, tgt_j)
+ if (tgt_j->pfn_child_ready_fl)
+ tgt_j->pfn_child_ready_fl(file, line,
+ us_elapsed_absolute,
+ cmd->trace2_child_id,
+ cmd->pid,
+ ready,
+ us_elapsed_child);
+}
+
int trace2_exec_fl(const char *file, int line, const char *exe,
const char **argv)
{
diff --git a/trace2.h b/trace2.h
index 9b7286c572..0cc7b5f531 100644
--- a/trace2.h
+++ b/trace2.h
@@ -254,6 +254,31 @@ void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
trace2_child_exit_fl(__FILE__, __LINE__, (cmd), (code))
/**
+ * Emits a "child_ready" message containing the "child-id" and a flag
+ * indicating whether the child was considered "ready" when we
+ * released it.
+ *
+ * This function should be called after starting a daemon process in
+ * the background (and after giving it sufficient time to boot
+ * up) to indicate that we no longer control or own it.
+ *
+ * The "ready" argument should contain one of { "ready", "timeout",
+ * "error" } to indicate the state of the running daemon when we
+ * released it.
+ *
+ * If the daemon process fails to start or it exits or is terminated
+ * while we are still waiting for it, the caller should emit a
+ * regular "child_exit" to report the normal process exit information.
+ *
+ */
+void trace2_child_ready_fl(const char *file, int line,
+ struct child_process *cmd,
+ const char *ready);
+
+#define trace2_child_ready(cmd, ready) \
+ trace2_child_ready_fl(__FILE__, __LINE__, (cmd), (ready))
+
+/**
* Emit an 'exec' event prior to calling one of exec(), execv(),
* execvp(), and etc. On Unix-derived systems, this will be the
* last event emitted for the current process, unless the exec
@@ -350,7 +375,7 @@ void trace2_def_repo_fl(const char *file, int line, struct repository *repo);
* being started, such as "read_recursive" or "do_read_index".
*
* The `repo` field, if set, will be used to get the "repo-id", so that
- * recursive oerations can be attributed to the correct repository.
+ * recursive operations can be attributed to the correct repository.
*/
void trace2_region_enter_fl(const char *file, int line, const char *category,
const char *label, const struct repository *repo, ...);
diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h
index 1f66fd6573..65f94e1574 100644
--- a/trace2/tr2_tgt.h
+++ b/trace2/tr2_tgt.h
@@ -45,6 +45,10 @@ typedef void(tr2_tgt_evt_child_exit_fl_t)(const char *file, int line,
uint64_t us_elapsed_absolute, int cid,
int pid, int code,
uint64_t us_elapsed_child);
+typedef void(tr2_tgt_evt_child_ready_fl_t)(const char *file, int line,
+ uint64_t us_elapsed_absolute,
+ int cid, int pid, const char *ready,
+ uint64_t us_elapsed_child);
typedef void(tr2_tgt_evt_thread_start_fl_t)(const char *file, int line,
uint64_t us_elapsed_absolute);
@@ -116,6 +120,7 @@ struct tr2_tgt {
tr2_tgt_evt_alias_fl_t *pfn_alias_fl;
tr2_tgt_evt_child_start_fl_t *pfn_child_start_fl;
tr2_tgt_evt_child_exit_fl_t *pfn_child_exit_fl;
+ tr2_tgt_evt_child_ready_fl_t *pfn_child_ready_fl;
tr2_tgt_evt_thread_start_fl_t *pfn_thread_start_fl;
tr2_tgt_evt_thread_exit_fl_t *pfn_thread_exit_fl;
tr2_tgt_evt_exec_fl_t *pfn_exec_fl;
diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c
index 578a9a5287..70cfc2f77c 100644
--- a/trace2/tr2_tgt_event.c
+++ b/trace2/tr2_tgt_event.c
@@ -383,6 +383,27 @@ static void fn_child_exit_fl(const char *file, int line,
jw_release(&jw);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_ready";
+ struct json_writer jw = JSON_WRITER_INIT;
+ double t_rel = (double)us_elapsed_child / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
+ jw_object_intmax(&jw, "child_id", cid);
+ jw_object_intmax(&jw, "pid", pid);
+ jw_object_string(&jw, "ready", ready);
+ jw_object_double(&jw, "t_rel", 6, t_rel);
+ jw_end(&jw);
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
+
+ jw_release(&jw);
+}
+
static void fn_thread_start_fl(const char *file, int line,
uint64_t us_elapsed_absolute)
{
@@ -610,6 +631,7 @@ struct tr2_tgt tr2_tgt_event = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
fn_thread_start_fl,
fn_thread_exit_fl,
fn_exec_fl,
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index a5751c8864..58d9e430f0 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -251,6 +251,19 @@ static void fn_child_exit_fl(const char *file, int line,
strbuf_release(&buf_payload);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_child / 1000000.0;
+
+ strbuf_addf(&buf_payload, "child_ready[%d] pid:%d ready:%s elapsed:%.6f",
+ cid, pid, ready, elapsed);
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
int exec_id, const char *exe, const char **argv)
{
@@ -330,6 +343,7 @@ struct tr2_tgt tr2_tgt_normal = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
NULL, /* thread_start */
NULL, /* thread_exit */
fn_exec_fl,
diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c
index af4d65a0a5..e4acca13d6 100644
--- a/trace2/tr2_tgt_perf.c
+++ b/trace2/tr2_tgt_perf.c
@@ -360,6 +360,20 @@ static void fn_child_exit_fl(const char *file, int line,
strbuf_release(&buf_payload);
}
+static void fn_child_ready_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute, int cid, int pid,
+ const char *ready, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_ready";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] pid:%d ready:%s", cid, pid, ready);
+
+ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
+ &us_elapsed_child, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
static void fn_thread_start_fl(const char *file, int line,
uint64_t us_elapsed_absolute)
{
@@ -553,6 +567,7 @@ struct tr2_tgt tr2_tgt_perf = {
fn_alias_fl,
fn_child_start_fl,
fn_child_exit_fl,
+ fn_child_ready_fl,
fn_thread_start_fl,
fn_thread_exit_fl,
fn_exec_fl,
diff --git a/transport.c b/transport.c
index b37664ba87..e4f1decae2 100644
--- a/transport.c
+++ b/transport.c
@@ -1,7 +1,7 @@
#include "cache.h"
#include "config.h"
#include "transport.h"
-#include "run-command.h"
+#include "hook.h"
#include "pkt-line.h"
#include "fetch-pack.h"
#include "remote.h"
diff --git a/transport.h b/transport.h
index 1cbab11373..8bb4c8bbc8 100644
--- a/transport.h
+++ b/transport.h
@@ -262,7 +262,9 @@ struct transport_ls_refs_options {
*/
char *unborn_head_target;
};
-#define TRANSPORT_LS_REFS_OPTIONS_INIT { STRVEC_INIT }
+#define TRANSPORT_LS_REFS_OPTIONS_INIT { \
+ .ref_prefixes = STRVEC_INIT, \
+}
/*
* Retrieve refs from a remote.
diff --git a/tree-diff.c b/tree-diff.c
index 1572615bd9..437c98a70e 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -21,7 +21,9 @@
ALLOC_ARRAY((x), nr); \
} while(0)
#define FAST_ARRAY_FREE(x, nr) do { \
- if ((nr) > 2) \
+ if ((nr) <= 2) \
+ xalloca_free((x)); \
+ else \
free((x)); \
} while(0)
diff --git a/unicode-width.h b/unicode-width.h
index b50e686bae..97c851b27d 100644
--- a/unicode-width.h
+++ b/unicode-width.h
@@ -26,7 +26,9 @@ static const struct interval zero_width[] = {
{ 0x0825, 0x0827 },
{ 0x0829, 0x082D },
{ 0x0859, 0x085B },
-{ 0x08D3, 0x0902 },
+{ 0x0890, 0x0891 },
+{ 0x0898, 0x089F },
+{ 0x08CA, 0x0902 },
{ 0x093A, 0x093A },
{ 0x093C, 0x093C },
{ 0x0941, 0x0948 },
@@ -66,6 +68,7 @@ static const struct interval zero_width[] = {
{ 0x0BCD, 0x0BCD },
{ 0x0C00, 0x0C00 },
{ 0x0C04, 0x0C04 },
+{ 0x0C3C, 0x0C3C },
{ 0x0C3E, 0x0C40 },
{ 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D },
@@ -116,7 +119,7 @@ static const struct interval zero_width[] = {
{ 0x1160, 0x11FF },
{ 0x135D, 0x135F },
{ 0x1712, 0x1714 },
-{ 0x1732, 0x1734 },
+{ 0x1732, 0x1733 },
{ 0x1752, 0x1753 },
{ 0x1772, 0x1773 },
{ 0x17B4, 0x17B5 },
@@ -124,7 +127,7 @@ static const struct interval zero_width[] = {
{ 0x17C6, 0x17C6 },
{ 0x17C9, 0x17D3 },
{ 0x17DD, 0x17DD },
-{ 0x180B, 0x180E },
+{ 0x180B, 0x180F },
{ 0x1885, 0x1886 },
{ 0x18A9, 0x18A9 },
{ 0x1920, 0x1922 },
@@ -140,7 +143,7 @@ static const struct interval zero_width[] = {
{ 0x1A65, 0x1A6C },
{ 0x1A73, 0x1A7C },
{ 0x1A7F, 0x1A7F },
-{ 0x1AB0, 0x1AC0 },
+{ 0x1AB0, 0x1ACE },
{ 0x1B00, 0x1B03 },
{ 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A },
@@ -163,8 +166,7 @@ static const struct interval zero_width[] = {
{ 0x1CED, 0x1CED },
{ 0x1CF4, 0x1CF4 },
{ 0x1CF8, 0x1CF9 },
-{ 0x1DC0, 0x1DF9 },
-{ 0x1DFB, 0x1DFF },
+{ 0x1DC0, 0x1DFF },
{ 0x200B, 0x200F },
{ 0x202A, 0x202E },
{ 0x2060, 0x2064 },
@@ -227,12 +229,16 @@ static const struct interval zero_width[] = {
{ 0x10D24, 0x10D27 },
{ 0x10EAB, 0x10EAC },
{ 0x10F46, 0x10F50 },
+{ 0x10F82, 0x10F85 },
{ 0x11001, 0x11001 },
{ 0x11038, 0x11046 },
+{ 0x11070, 0x11070 },
+{ 0x11073, 0x11074 },
{ 0x1107F, 0x11081 },
{ 0x110B3, 0x110B6 },
{ 0x110B9, 0x110BA },
{ 0x110BD, 0x110BD },
+{ 0x110C2, 0x110C2 },
{ 0x110CD, 0x110CD },
{ 0x11100, 0x11102 },
{ 0x11127, 0x1112B },
@@ -315,6 +321,8 @@ static const struct interval zero_width[] = {
{ 0x16FE4, 0x16FE4 },
{ 0x1BC9D, 0x1BC9E },
{ 0x1BCA0, 0x1BCA3 },
+{ 0x1CF00, 0x1CF2D },
+{ 0x1CF30, 0x1CF46 },
{ 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 },
{ 0x1D185, 0x1D18B },
@@ -332,6 +340,7 @@ static const struct interval zero_width[] = {
{ 0x1E023, 0x1E024 },
{ 0x1E026, 0x1E02A },
{ 0x1E130, 0x1E136 },
+{ 0x1E2AE, 0x1E2AE },
{ 0x1E2EC, 0x1E2EF },
{ 0x1E8D0, 0x1E8D6 },
{ 0x1E944, 0x1E94A },
@@ -404,7 +413,10 @@ static const struct interval double_width[] = {
{ 0x17000, 0x187F7 },
{ 0x18800, 0x18CD5 },
{ 0x18D00, 0x18D08 },
-{ 0x1B000, 0x1B11E },
+{ 0x1AFF0, 0x1AFF3 },
+{ 0x1AFF5, 0x1AFFB },
+{ 0x1AFFD, 0x1AFFE },
+{ 0x1B000, 0x1B122 },
{ 0x1B150, 0x1B152 },
{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
@@ -439,21 +451,23 @@ static const struct interval double_width[] = {
{ 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 },
{ 0x1F6D5, 0x1F6D7 },
+{ 0x1F6DD, 0x1F6DF },
{ 0x1F6EB, 0x1F6EC },
{ 0x1F6F4, 0x1F6FC },
{ 0x1F7E0, 0x1F7EB },
+{ 0x1F7F0, 0x1F7F0 },
{ 0x1F90C, 0x1F93A },
{ 0x1F93C, 0x1F945 },
-{ 0x1F947, 0x1F978 },
-{ 0x1F97A, 0x1F9CB },
-{ 0x1F9CD, 0x1F9FF },
+{ 0x1F947, 0x1F9FF },
{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7A },
+{ 0x1FA78, 0x1FA7C },
{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAA8 },
-{ 0x1FAB0, 0x1FAB6 },
-{ 0x1FAC0, 0x1FAC2 },
-{ 0x1FAD0, 0x1FAD6 },
+{ 0x1FA90, 0x1FAAC },
+{ 0x1FAB0, 0x1FABA },
+{ 0x1FAC0, 0x1FAC5 },
+{ 0x1FAD0, 0x1FAD9 },
+{ 0x1FAE0, 0x1FAE7 },
+{ 0x1FAF0, 0x1FAF6 },
{ 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD }
};
diff --git a/unpack-trees.c b/unpack-trees.c
index 8ea0a542da..89ca95ce90 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1694,9 +1694,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
static struct cache_entry *dfc;
struct pattern_list pl;
int free_pattern_list = 0;
+ struct dir_struct dir = DIR_INIT;
+
+ if (o->reset == UNPACK_RESET_INVALID)
+ BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
if (len > MAX_UNPACK_TREES)
die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
+ if (o->dir)
+ BUG("o->dir is for internal use only");
trace_performance_enter();
trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
@@ -1707,6 +1713,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
ensure_full_index(o->dst_index);
}
+ if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED &&
+ o->preserve_ignored)
+ BUG("UNPACK_RESET_OVERWRITE_UNTRACKED incompatible with preserved ignored files");
+
+ if (!o->preserve_ignored) {
+ o->dir = &dir;
+ o->dir->flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(o->dir);
+ }
+
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
if (!o->skip_sparse_checkout && !o->pl) {
@@ -1868,6 +1884,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
done:
if (free_pattern_list)
clear_pattern_list(&pl);
+ if (o->dir) {
+ dir_clear(o->dir);
+ o->dir = NULL;
+ }
trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
trace_performance_leave("unpack_trees");
return ret;
@@ -2136,9 +2156,10 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
+ dir_clear(&d);
+ free(pathbuf);
if (i)
return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
- free(pathbuf);
return cnt;
}
@@ -2158,9 +2179,15 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
}
+enum absent_checking_type {
+ COMPLETELY_ABSENT,
+ ABSENT_ANY_DIRECTORY
+};
+
static int check_ok_to_remove(const char *name, int len, int dtype,
const struct cache_entry *ce, struct stat *st,
enum unpack_trees_error_types error_type,
+ enum absent_checking_type absent_type,
struct unpack_trees_options *o)
{
const struct cache_entry *result;
@@ -2195,6 +2222,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
return 0;
}
+ /* If we only care about directories, then we can remove */
+ if (absent_type == ABSENT_ANY_DIRECTORY)
+ return 0;
+
/*
* The previous round may already have decided to
* delete this path, which is in a subdirectory that
@@ -2215,12 +2246,14 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
*/
static int verify_absent_1(const struct cache_entry *ce,
enum unpack_trees_error_types error_type,
+ enum absent_checking_type absent_type,
struct unpack_trees_options *o)
{
int len;
struct stat st;
- if (o->index_only || o->reset || !o->update)
+ if (o->index_only || !o->update ||
+ o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED)
return 0;
len = check_leading_path(ce->name, ce_namelen(ce), 0);
@@ -2240,7 +2273,8 @@ static int verify_absent_1(const struct cache_entry *ce,
NULL, o);
else
ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
- &st, error_type, o);
+ &st, error_type,
+ absent_type, o);
}
free(path);
return ret;
@@ -2255,7 +2289,7 @@ static int verify_absent_1(const struct cache_entry *ce,
return check_ok_to_remove(ce->name, ce_namelen(ce),
ce_to_dtype(ce), ce, &st,
- error_type, o);
+ error_type, absent_type, o);
}
}
@@ -2265,14 +2299,23 @@ static int verify_absent(const struct cache_entry *ce,
{
if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
return 0;
- return verify_absent_1(ce, error_type, o);
+ return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
+}
+
+static int verify_absent_if_directory(const struct cache_entry *ce,
+ enum unpack_trees_error_types error_type,
+ struct unpack_trees_options *o)
+{
+ if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
+ return 0;
+ return verify_absent_1(ce, error_type, ABSENT_ANY_DIRECTORY, o);
}
static int verify_absent_sparse(const struct cache_entry *ce,
enum unpack_trees_error_types error_type,
struct unpack_trees_options *o)
{
- return verify_absent_1(ce, error_type, o);
+ return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
}
static int merged_entry(const struct cache_entry *ce,
@@ -2346,6 +2389,12 @@ static int merged_entry(const struct cache_entry *ce,
* Previously unmerged entry left as an existence
* marker by read_index_unmerged();
*/
+ if (verify_absent_if_directory(merge,
+ ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+ discard_cache_entry(merge);
+ return -1;
+ }
+
invalidate_ce_path(old, o);
}
@@ -2363,7 +2412,10 @@ static int deleted_entry(const struct cache_entry *ce,
if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
return -1;
return 0;
+ } else if (verify_absent_if_directory(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) {
+ return -1;
}
+
if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
return -1;
add_entry(o, ce, CE_REMOVE, 0);
diff --git a/unpack-trees.h b/unpack-trees.h
index 2d88b19dca..71ffb7eeb0 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -45,10 +45,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
*/
void clear_unpack_trees_porcelain(struct unpack_trees_options *opts);
+enum unpack_trees_reset_type {
+ UNPACK_RESET_NONE = 0, /* traditional "false" value; still valid */
+ UNPACK_RESET_INVALID = 1, /* "true" no longer valid; use below values */
+ UNPACK_RESET_PROTECT_UNTRACKED,
+ UNPACK_RESET_OVERWRITE_UNTRACKED
+};
+
struct unpack_trees_options {
- unsigned int reset,
- merge,
+ unsigned int merge,
update,
+ preserve_ignored,
clone,
index_only,
nontrivial_merge,
@@ -64,9 +71,9 @@ struct unpack_trees_options {
exiting_early,
show_all_errors,
dry_run;
+ enum unpack_trees_reset_type reset;
const char *prefix;
int cache_bottom;
- struct dir_struct *dir;
struct pathspec *pathspec;
merge_fn_t fn;
const char *msgs[NB_UNPACK_TREES_WARNING_TYPES];
@@ -88,6 +95,7 @@ struct unpack_trees_options {
struct index_state result;
struct pattern_list *pl; /* for internal use */
+ struct dir_struct *dir; /* for internal use only */
struct checkout_metadata meta;
};
diff --git a/urlmatch.h b/urlmatch.h
index 6ff42f81b0..34a3ba6d19 100644
--- a/urlmatch.h
+++ b/urlmatch.h
@@ -66,6 +66,10 @@ struct urlmatch_config {
int (*fallback_match_fn)(const char *url, void *cb);
};
+#define URLMATCH_CONFIG_INIT { \
+ .vars = STRING_LIST_INIT_DUP, \
+}
+
int urlmatch_config_entry(const char *var, const char *value, void *cb);
#endif /* URL_MATCH_H */
diff --git a/userdiff.c b/userdiff.c
index af02b1878c..8578cb0d12 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -64,9 +64,15 @@ PATTERNS("cpp",
/* functions/methods, variables, and compounds at top level */
"^((::[[:space:]]*)?[A-Za-z_].*)$",
/* -- */
+ /* identifiers and keywords */
"[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+ /* decimal and octal integers as well as floatingpoint numbers */
+ "|[0-9][0-9.]*([Ee][-+]?[0-9]+)?[fFlLuU]*"
+ /* hexadecimal and binary integers */
+ "|0[xXbB][0-9a-fA-F]+[lLuU]*"
+ /* floatingpoint numbers that begin with a decimal point */
+ "|\\.[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"
diff --git a/wrapper.c b/wrapper.c
index 7c6586af32..1460d4e27b 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -145,6 +145,18 @@ void *xcalloc(size_t nmemb, size_t size)
return ret;
}
+void xsetenv(const char *name, const char *value, int overwrite)
+{
+ if (setenv(name, value, overwrite))
+ die_errno(_("could not setenv '%s'"), name ? name : "(null)");
+}
+
+void xunsetenv(const char *name)
+{
+ if (!unsetenv(name))
+ die_errno(_("could not unsetenv '%s'"), name ? name : "(null)");
+}
+
/*
* Limit size of IO chunks, because huge chunks only cause pain. OS X
* 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in