]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'da/rhel7-lacks-uncompress2-and-c99'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Jan 2022 23:15:59 +0000 (15:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Jan 2022 23:15:59 +0000 (15:15 -0800)
Adjust build on RHEL 7 to explicitly ask C99 support and use
the fallback implementation of uncompress2 we ship.

* da/rhel7-lacks-uncompress2-and-c99:
  build: centos/RHEL 7 ships with an older gcc and zlib

600 files changed:
.github/workflows/main.yml
.travis.yml [deleted file]
Documentation/CodingGuidelines
Documentation/RelNotes/2.35.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/config/branch.txt
Documentation/config/gpg.txt
Documentation/config/grep.txt
Documentation/config/merge.txt
Documentation/config/user.txt
Documentation/git-am.txt
Documentation/git-apply.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-ls-files.txt
Documentation/git-merge-file.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-repack.txt
Documentation/git-restore.txt
Documentation/git-sparse-checkout.txt
Documentation/git-switch.txt
Documentation/git-worktree.txt
Documentation/pretty-formats.txt
Documentation/technical/rerere.txt
GIT-VERSION-GEN
Makefile
README.md
add-patch.c
apply.c
apply.h
archive-tar.c
archive.c
branch.c
branch.h
builtin/add.c
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/describe.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fast-import.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/gc.c
builtin/help.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/merge-file.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rm.c
builtin/show-branch.c
builtin/sparse-checkout.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
builtin/upload-archive.c
builtin/worktree.c
cache.h
cbtree.c
cbtree.h
ci/install-dependencies.sh
ci/install-docker-dependencies.sh
ci/lib.sh
ci/print-test-failures.sh
ci/run-build-and-tests.sh
ci/run-docker-build.sh
ci/run-docker.sh
color.c
color.h
commit-graph.c
commit.c
common-main.c
compat/mingw.c
compat/win32/lazyload.h
compat/win32/trace2_win32_process_info.c
compat/winansi.c
config.c
config.mak.dev
config.mak.uname
connected.c
contrib/completion/git-completion.bash
contrib/mw-to-git/t/t9365-continuing-queries.sh
contrib/scalar/.gitignore [new file with mode: 0644]
contrib/scalar/Makefile [new file with mode: 0644]
contrib/scalar/README.md [new file with mode: 0644]
contrib/scalar/scalar.c [new file with mode: 0644]
contrib/scalar/scalar.txt [new file with mode: 0644]
contrib/scalar/t/Makefile [new file with mode: 0644]
contrib/scalar/t/t9099-scalar.sh [new file with mode: 0755]
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
daemon.c
diff.c
dir.c
dir.h
editor.c
environment.c
fetch-pack.c
fmt-merge-msg.c
fmt-merge-msg.h
git-compat-util.h
git-cvsserver.perl
git-p4.py
git.c
gpg-interface.c
gpg-interface.h
grep.c
grep.h
help.c
http-backend.c
http-fetch.c
http.c
log-tree.c
merge-ort.c
object-file.c
object-store.h
object.c
packfile.c
packfile.h
parse-options-cb.c
parse-options.c
parse-options.h
pathspec.h
perl/Git/SVN.pm
po/README.md
pretty.c
prompt.c
range-diff.c
ref-filter.c
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/packed-backend.c
refs/packed-backend.h
refs/refs-internal.h
reftable/block.h
reftable/error.c
reftable/merged_test.c
reftable/readwrite_test.c
reftable/reftable-error.h
reftable/reftable-writer.h
reftable/stack.c
reftable/stack_test.c
reftable/writer.c
remote-curl.c
repo-settings.c
repository.c
repository.h
revision.c
revision.h
run-command.c
run-command.h
sequencer.c
setup.c
strbuf.h
sub-process.c
symlinks.c
t/Makefile
t/README
t/aggregate-results.sh
t/annotate-tests.sh
t/chainlint.sed
t/chainlint/arithmetic-expansion.expect
t/chainlint/bash-array.expect
t/chainlint/blank-line.expect
t/chainlint/blank-line.test
t/chainlint/block-comment.expect [new file with mode: 0644]
t/chainlint/block-comment.test [new file with mode: 0644]
t/chainlint/block.expect
t/chainlint/block.test
t/chainlint/broken-chain.expect
t/chainlint/broken-chain.test
t/chainlint/case-comment.expect [new file with mode: 0644]
t/chainlint/case-comment.test [new file with mode: 0644]
t/chainlint/case.expect
t/chainlint/case.test
t/chainlint/close-nested-and-parent-together.expect
t/chainlint/close-subshell.expect
t/chainlint/command-substitution.expect
t/chainlint/comment.expect
t/chainlint/complex-if-in-cuddled-loop.expect
t/chainlint/complex-if-in-cuddled-loop.test
t/chainlint/cuddled-if-then-else.expect
t/chainlint/cuddled-if-then-else.test
t/chainlint/cuddled-loop.expect
t/chainlint/cuddled-loop.test
t/chainlint/cuddled.expect
t/chainlint/cuddled.test
t/chainlint/exit-loop.expect
t/chainlint/exit-subshell.expect
t/chainlint/for-loop.expect
t/chainlint/for-loop.test
t/chainlint/here-doc-close-subshell.expect
t/chainlint/here-doc-multi-line-command-subst.expect
t/chainlint/here-doc-multi-line-string.expect
t/chainlint/here-doc.expect
t/chainlint/here-doc.test
t/chainlint/if-in-loop.expect
t/chainlint/if-in-loop.test
t/chainlint/if-then-else.expect
t/chainlint/if-then-else.test
t/chainlint/incomplete-line.expect
t/chainlint/inline-comment.expect
t/chainlint/loop-in-if.expect
t/chainlint/loop-in-if.test
t/chainlint/multi-line-nested-command-substitution.expect
t/chainlint/multi-line-string.expect
t/chainlint/multi-line-string.test
t/chainlint/negated-one-liner.expect
t/chainlint/nested-cuddled-subshell.expect
t/chainlint/nested-here-doc.expect
t/chainlint/nested-subshell-comment.expect
t/chainlint/nested-subshell-comment.test
t/chainlint/nested-subshell.expect
t/chainlint/nested-subshell.test
t/chainlint/not-heredoc.expect [new file with mode: 0644]
t/chainlint/not-heredoc.test [new file with mode: 0644]
t/chainlint/one-liner.expect
t/chainlint/one-liner.test
t/chainlint/p4-filespec.expect
t/chainlint/pipe.expect
t/chainlint/pipe.test
t/chainlint/semicolon.expect
t/chainlint/semicolon.test
t/chainlint/subshell-here-doc.expect
t/chainlint/subshell-here-doc.test
t/chainlint/subshell-one-liner.expect
t/chainlint/t7900-subtree.expect
t/chainlint/t7900-subtree.test
t/chainlint/while-loop.expect
t/chainlint/while-loop.test
t/helper/test-drop-caches.c
t/helper/test-read-cache.c
t/helper/test-ref-store.c
t/helper/test-run-command.c
t/helper/test-subprocess.c
t/helper/test-trace2.c
t/lib-gpg.sh
t/lib-pager.sh
t/perf/p0005-status.sh
t/perf/p0006-read-tree-checkout.sh
t/perf/p0007-write-cache.sh
t/perf/p0100-globbing.sh
t/perf/p1400-update-ref.sh
t/perf/p1451-fsck-skip-list.sh
t/perf/p2000-sparse-operations.sh
t/perf/p3400-rebase.sh
t/perf/p4002-diff-color-moved.sh [new file with mode: 0755]
t/perf/p5302-pack-index.sh
t/perf/p5303-many-packs.sh
t/perf/p7519-fsmonitor.sh
t/perf/perf-lib.sh
t/t0001-init.sh
t/t0005-signals.sh
t/t0008-ignores.sh
t/t0011-hashmap.sh
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0026-eol-config.sh
t/t0060-path-utils.sh
t/t0069-oidtree.sh
t/t0071-sort.sh
t/t0095-bloom.sh
t/t0200-gettext-basic.sh
t/t0201-gettext-fallbacks.sh
t/t0202-gettext-perl.sh
t/t0204-gettext-reencode-sanity.sh
t/t0410-partial-clone.sh
t/t1002-read-tree-m-u-2way.sh
t/t1006-cat-file.sh
t/t1010-mktree.sh
t/t1020-subdirectory.sh
t/t1022-read-tree-partial-clone.sh
t/t1050-large.sh
t/t1091-sparse-checkout-builtin.sh
t/t1092-sparse-checkout-compatibility.sh
t/t1300-config.sh
t/t1303-wacky-config.sh
t/t1307-config-blob.sh
t/t1308-config-set.sh
t/t1309-early-config.sh
t/t1310-config-default.sh
t/t1400-update-ref.sh
t/t1403-show-ref.sh
t/t1404-update-ref-errors.sh
t/t1405-main-ref-store.sh
t/t1406-submodule-ref-store.sh
t/t1410-reflog.sh
t/t1420-lost-found.sh
t/t1430-bad-ref-name.sh
t/t1450-fsck.sh
t/t1503-rev-parse-verify.sh
t/t1506-rev-parse-diagnosis.sh
t/t1512-rev-parse-disambiguation.sh
t/t1513-rev-parse-prefix.sh
t/t1515-rev-parse-outside-repo.sh
t/t1600-index.sh
t/t1700-split-index.sh
t/t2000-conflict-when-checking-files-out.sh
t/t2004-checkout-cache-temp.sh
t/t2012-checkout-last.sh
t/t2017-checkout-orphan.sh
t/t2018-checkout-branch.sh
t/t2025-checkout-no-overlay.sh
t/t2026-checkout-pathspec-file.sh
t/t2027-checkout-track.sh
t/t2060-switch.sh
t/t2072-restore-pathspec-file.sh
t/t2100-update-cache-badpath.sh
t/t2101-update-index-reupdate.sh
t/t2102-update-index-symlinks.sh
t/t2103-update-index-ignore-missing.sh
t/t2104-update-index-skip-worktree.sh
t/t2105-update-index-gitfile.sh
t/t2200-add-update.sh
t/t2201-add-update-typechange.sh
t/t2202-add-addremove.sh
t/t2203-add-intent.sh
t/t2204-add-ignored.sh
t/t2401-worktree-prune.sh
t/t2402-worktree-list.sh
t/t2404-worktree-config.sh
t/t2406-worktree-repair.sh
t/t2501-cwd-empty.sh [new file with mode: 0755]
t/t3005-ls-files-relative.sh
t/t3070-wildmatch.sh
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3202-show-branch.sh
t/t3203-branch-output.sh
t/t3205-branch-color.sh
t/t3302-notes-index-expensive.sh
t/t3303-notes-subtrees.sh
t/t3305-notes-fanout.sh
t/t3320-notes-merge-worktrees.sh
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3409-rebase-environ.sh [new file with mode: 0755]
t/t3417-rebase-whitespace-fix.sh
t/t3431-rebase-fork-point.sh
t/t3501-revert-cherry-pick.sh
t/t3508-cherry-pick-many-commits.sh
t/t3600-rm.sh
t/t3601-rm-pathspec-file.sh
t/t3700-add.sh
t/t3702-add-edit.sh
t/t3703-add-magic-pathspec.sh
t/t3704-add-pathspec-file.sh
t/t3705-add-sparse-checkout.sh
t/t3800-mktag.sh
t/t3903-stash.sh
t/t3908-stash-in-worktree.sh
t/t3909-stash-pathspec-file.sh
t/t3920-crlf-messages.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4003-diff-rename-1.sh
t/t4004-diff-rename-symlink.sh
t/t4005-diff-rename-2.sh
t/t4006-diff-mode.sh
t/t4007-rename-3.sh
t/t4009-diff-rename-4.sh
t/t4010-diff-pathspec.sh
t/t4011-diff-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4018-diff-funcname.sh
t/t4019-diff-wserror.sh
t/t4020-diff-external.sh
t/t4023-diff-rename-typechange.sh
t/t4024-diff-optimize-common.sh
t/t4025-hunk-header.sh
t/t4026-color.sh
t/t4027-diff-submodule.sh
t/t4029-diff-trailing-space.sh
t/t4032-diff-inter-hunk-context.sh
t/t4033-diff-patience.sh
t/t4034-diff-words.sh
t/t4035-diff-quiet.sh
t/t4037-diff-r-t-dirs.sh
t/t4038-diff-combined.sh
t/t4040-whitespace-status.sh
t/t4046-diff-unmerged.sh
t/t4049-diff-stat-count.sh
t/t4050-diff-histogram.sh
t/t4052-stat-output.sh
t/t4054-diff-bogus-tree.sh
t/t4057-diff-combined-paths.sh
t/t4062-diff-pickaxe.sh
t/t4063-diff-blobs.sh
t/t4100-apply-stat.sh
t/t4101-apply-nonl.sh
t/t4102-apply-rename.sh
t/t4105-apply-fuzz.sh
t/t4106-apply-stdin.sh
t/t4108-apply-threeway.sh
t/t4109-apply-multifrag.sh
t/t4110-apply-scan.sh
t/t4112-apply-renames.sh
t/t4116-apply-reverse.sh
t/t4117-apply-reject.sh
t/t4118-apply-empty-context.sh
t/t4119-apply-config.sh
t/t4123-apply-shrink.sh
t/t4124-apply-ws-rule.sh
t/t4125-apply-ws-fuzz.sh
t/t4126-apply-empty.sh
t/t4127-apply-same-fn.sh
t/t4128-apply-root.sh
t/t4129-apply-samemode.sh
t/t4130-apply-criss-cross-rename.sh
t/t4132-apply-removal.sh
t/t4133-apply-filenames.sh
t/t4134-apply-submodule.sh
t/t4136-apply-check.sh
t/t4138-apply-ws-expansion.sh
t/t4139-apply-escape.sh
t/t4150-am.sh
t/t4151-am-abort.sh
t/t4202-log.sh
t/t4204-patch-id.sh
t/t4205-log-pretty-formats.sh
t/t4209-log-pickaxe.sh
t/t4211-line-log.sh
t/t4212-log-corrupt.sh
t/t4216-log-bloom.sh
t/t5000-tar-tree.sh
t/t5002-archive-attr-pattern.sh
t/t5003-archive-zip.sh
t/t5004-archive-corner-cases.sh
t/t5100-mailinfo.sh
t/t5200-update-server-info.sh
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5306-pack-nobase.sh
t/t5307-pack-missing-commit.sh
t/t5310-pack-bitmaps.sh
t/t5316-pack-delta-depth.sh
t/t5317-pack-objects-filter-objects.sh
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5322-pack-objects-sparse.sh
t/t5325-reverse-index.sh
t/t5500-fetch-pack.sh
t/t5502-quickfetch.sh
t/t5504-fetch-receive-strict.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5515-fetch-merge-logic.sh
t/t5516-fetch-push.sh
t/t5526-fetch-submodules.sh
t/t5540-http-push-webdav.sh
t/t5550-http-fetch-dumb.sh
t/t5552-skipping-fetch-negotiator.sh
t/t5553-set-upstream.sh
t/t5555-http-smart-common.sh
t/t5562-http-backend-content-length.sh
t/t5570-git-daemon.sh
t/t5571-pre-push-hook.sh
t/t5602-clone-remote-exec.sh
t/t5603-clone-dirname.sh
t/t5606-clone-options.sh
t/t5611-clone-config.sh
t/t5616-partial-clone.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t5704-protocol-violations.sh
t/t5705-session-id-in-capabilities.sh
t/t6005-rev-list-count.sh
t/t6009-rev-list-parent.sh
t/t6019-rev-list-ancestry-path.sh
t/t6060-merge-index.sh
t/t6101-rev-parse-parents.sh
t/t6102-rev-list-unexpected-objects.sh
t/t6112-rev-list-filters-objects.sh
t/t6120-describe.sh
t/t6132-pathspec-exclude.sh
t/t6136-pathspec-in-bare.sh
t/t6200-fmt-merge-msg.sh
t/t6300-for-each-ref.sh
t/t6302-for-each-ref-filter.sh
t/t6406-merge-attr.sh
t/t6407-merge-binary.sh
t/t6409-merge-subtree.sh
t/t6411-merge-filemode.sh
t/t6412-merge-large-rename.sh
t/t6416-recursive-corner-cases.sh
t/t6417-merge-ours-theirs.sh
t/t6418-merge-text-auto.sh
t/t6427-diff3-conflict-markers.sh
t/t6430-merge-recursive.sh
t/t6600-test-reach.sh
t/t7004-tag.sh
t/t7010-setup.sh
t/t7031-verify-tag-signed-ssh.sh
t/t7064-wtstatus-pv2.sh
t/t7101-reset-empty-subdirs.sh
t/t7103-reset-bare.sh
t/t7107-reset-pathspec-file.sh
t/t7110-reset-merge.sh
t/t7201-co.sh
t/t7500-commit-template-squash-signoff.sh
t/t7501-commit-basic-functionality.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7510-signed-commit.sh
t/t7511-status-index.sh
t/t7512-status-help.sh
t/t7513-interpret-trailers.sh
t/t7515-status-symlinks.sh
t/t7519-status-fsmonitor.sh
t/t7525-status-rename.sh
t/t7526-commit-pathspec-file.sh
t/t7528-signed-commit-ssh.sh
t/t7600-merge.sh
t/t7602-merge-octopus-many.sh
t/t7603-merge-reduce-heads.sh
t/t7700-repack.sh
t/t7810-grep.sh
t/t7812-grep-icase-non-ascii.sh
t/t8002-blame.sh
t/t8003-blame-corner-cases.sh
t/t8014-blame-ignore-fuzzy.sh
t/t9104-git-svn-follow-parent.sh
t/t9107-git-svn-migrate.sh
t/t9130-git-svn-authors-file.sh
t/t9134-git-svn-ignore-paths.sh
t/t9138-git-svn-authors-prog.sh
t/t9146-git-svn-empty-dirs.sh
t/t9147-git-svn-include-paths.sh
t/t9151-svn-mergeinfo.sh
t/t9152-svn-empty-dirs-after-gc.sh
t/t9302-fast-import-unpack-limit.sh
t/t9303-fast-import-compression.sh
t/t9304-fast-import-marks.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9603-cvsimport-patchsets.sh
t/t9800-git-p4-basic.sh
t/t9810-git-p4-rcs.sh
t/t9818-git-p4-block.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
tag.c
tmp-objdir.c
tmp-objdir.h
trace2/tr2_tgt_event.c
trace2/tr2_tgt_normal.c
trace2/tr2_tgt_perf.c
trailer.c
transport.c
transport.h
tree-diff.c
unpack-trees.c
unpack-trees.h
upload-pack.c
usage.c
worktree.c
worktree.h
write-or-die.c
wt-status.c
xdiff-interface.c
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xhistogram.c
xdiff/xmerge.c
xdiff/xprepare.c

index deda12db3a9dedb7212e6b91e7c3189d363fd9eb..c35200defb9357b6438ba3391bb4e17fed67acdc 100644 (file)
@@ -1,4 +1,4 @@
-name: CI/PR
+name: CI
 
 on: [push, pull_request]
 
@@ -7,6 +7,7 @@ env:
 
 jobs:
   ci-config:
+    name: config
     runs-on: ubuntu-latest
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
@@ -77,6 +78,7 @@ jobs:
             }
 
   windows-build:
+    name: win build
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
@@ -97,6 +99,7 @@ jobs:
         name: windows-artifacts
         path: artifacts
   windows-test:
+    name: win test
     runs-on: windows-latest
     needs: [windows-build]
     strategy:
@@ -127,6 +130,7 @@ jobs:
         name: failed-tests-windows
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   vs-build:
+    name: win+VS build
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     env:
@@ -178,6 +182,7 @@ jobs:
         name: vs-artifacts
         path: artifacts
   vs-test:
+    name: win+VS test
     runs-on: windows-latest
     needs: vs-build
     strategy:
@@ -210,6 +215,7 @@ jobs:
         name: failed-tests-windows
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   regular:
+    name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     strategy:
@@ -219,14 +225,25 @@ jobs:
           - jobname: linux-clang
             cc: clang
             pool: ubuntu-latest
+          - jobname: linux-sha256
+            cc: clang
+            os: ubuntu
+            pool: ubuntu-latest
           - jobname: linux-gcc
             cc: gcc
+            cc_package: gcc-8
+            pool: ubuntu-latest
+          - jobname: linux-TEST-vars
+            cc: gcc
+            os: ubuntu
+            cc_package: gcc-8
             pool: ubuntu-latest
           - jobname: osx-clang
             cc: clang
             pool: macos-latest
           - jobname: osx-gcc
             cc: gcc
+            cc_package: gcc-9
             pool: macos-latest
           - jobname: linux-gcc-default
             cc: gcc
@@ -236,7 +253,9 @@ jobs:
             pool: ubuntu-latest
     env:
       CC: ${{matrix.vector.cc}}
+      CC_PACKAGE: ${{matrix.vector.cc_package}}
       jobname: ${{matrix.vector.jobname}}
+      runs_on_pool: ${{matrix.vector.pool}}
     runs-on: ${{matrix.vector.pool}}
     steps:
     - uses: actions/checkout@v2
@@ -251,6 +270,7 @@ jobs:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
   dockerized:
+    name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     strategy:
@@ -259,7 +279,8 @@ jobs:
         vector:
         - jobname: linux-musl
           image: alpine
-        - jobname: Linux32
+        - jobname: linux32
+          os: ubuntu32
           image: daald/ubuntu32:xenial
         - jobname: pedantic
           image: fedora
@@ -311,6 +332,7 @@ jobs:
       run: ci/install-dependencies.sh
     - run: make sparse
   documentation:
+    name: documentation
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     env:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644 (file)
index 908330a..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-language: c
-
-cache:
-  directories:
-    - $HOME/travis-cache
-
-os:
-  - linux
-  - osx
-
-osx_image: xcode10.1
-
-compiler:
-  - clang
-  - gcc
-
-matrix:
-  include:
-    - env: jobname=linux-gcc-default
-      os: linux
-      compiler:
-      addons:
-      before_install:
-    - env: jobname=linux-gcc-4.8
-      os: linux
-      dist: trusty
-      compiler:
-    - env: jobname=Linux32
-      os: linux
-      compiler:
-      addons:
-      services:
-        - docker
-      before_install:
-      script: ci/run-docker.sh
-    - env: jobname=linux-musl
-      os: linux
-      compiler:
-      addons:
-      services:
-        - docker
-      before_install:
-      script: ci/run-docker.sh
-    - env: jobname=StaticAnalysis
-      os: linux
-      compiler:
-      script: ci/run-static-analysis.sh
-      after_failure:
-    - env: jobname=Documentation
-      os: linux
-      compiler:
-      script: ci/test-documentation.sh
-      after_failure:
-
-before_install: ci/install-dependencies.sh
-script: ci/run-build-and-tests.sh
-after_failure: ci/print-test-failures.sh
-
-notifications:
-  email: false
index 711cb9171e0920ee4ec4206d1405172248f3acce..0e27b5395d8b665fa4c474dc931640a02d4a5f6f 100644 (file)
@@ -499,6 +499,33 @@ For Python scripts:
  - Where required libraries do not restrict us to Python 2, we try to
    also be compatible with Python 3.1 and later.
 
+
+Program Output
+
+ We make a distinction between a Git command's primary output and
+ output which is merely chatty feedback (for instance, status
+ messages, running transcript, or progress display), as well as error
+ messages. Roughly speaking, a Git command's primary output is that
+ which one might want to capture to a file or send down a pipe; its
+ chatty output should not interfere with these use-cases.
+
+ As such, primary output should be sent to the standard output stream
+ (stdout), and chatty output should be sent to the standard error
+ stream (stderr). Examples of commands which produce primary output
+ include `git log`, `git show`, and `git branch --list` which generate
+ output on the stdout stream.
+
+ Not all Git commands have primary output; this is often true of
+ commands whose main function is to perform an action. Some action
+ commands are silent, whereas others are chatty. An example of a
+ chatty action commands is `git clone` with its "Cloning into
+ '<path>'..." and "Checking connectivity..." status messages which it
+ sends to the stderr stream.
+
+ Error messages from Git commands should always be sent to the stderr
+ stream.
+
+
 Error Messages
 
  - Do not end error messages with a full stop.
index 34b5ffedfdafc8e7edccfcfa4d34f0635199c504..fa5a7318a8b7f6a149ea171bb2517b2e9dad295b 100644 (file)
@@ -9,6 +9,9 @@ Backward compatibility warts
  * "_" is now treated as any other URL-valid characters in an URL when
    matching the per-URL configuration variable names.
 
+ * The color palette used by "git grep" has been updated to match that
+   of GNU grep.
+
 
 UI, Workflows & Features
 
@@ -36,6 +39,58 @@ UI, Workflows & Features
    option of commands from the "git log" family takes "human" and
    "auto" as valid values.
 
+ * "Zealous diff3" style of merge conflict presentation has been added.
+
+ * The "git log --format=%(describe)" placeholder has been extended to
+   allow passing selected command-line options to the underlying "git
+   describe" command.
+
+ * "default" and "reset" have been added to our color palette.
+
+ * The cryptographic signing using ssh keys can specify literal keys
+   for keytypes whose name do not begin with the "ssh-" prefix by
+   using the "key::" prefix mechanism (e.g. "key::ecdsa-sha2-nistp256").
+
+ * "git fetch" without the "--update-head-ok" option ought to protect
+   a checked out branch from getting updated, to prevent the working
+   tree that checks it out to go out of sync.  The code was written
+   before the use of "git worktree" got widespread, and only checked
+   the branch that was checked out in the current worktree, which has
+   been updated.
+
+ * "git name-rev" has been tweaked to give output that is shorter and
+   easier to understand.
+
+ * "git apply" has been taught to ignore a message without a patch
+   with the "--allow-empty" option.  It also learned to honor the
+   "--quiet" option given from the command line.
+
+ * The "init" and "set" subcommands in "git sparse-checkout" have been
+   unified for a better user experience and performance.
+
+ * Many git commands that deal with working tree files try to remove a
+   directory that becomes empty (i.e. "git switch" from a branch that
+   has the directory to another branch that does not would attempt
+   remove all files in the directory and the directory itself).  This
+   drops users into an unfamiliar situation if the command was run in
+   a subdirectory that becomes subject to removal due to the command.
+   The commands have been taught to keep an empty directory if it is
+   the directory they were started in to avoid surprising users.
+
+ * "git am" learns "--empty=(stop|drop|keep)" option to tweak what is
+   done to a piece of e-mail without a patch in it.
+
+ * The default merge message prepared by "git merge" records the name
+   of the current branch; the name can be overridden with a new option
+   to allow users to pretend a merge is made on a different branch.
+
+ * The way "git p4" shows file sizes in its output has been updated to
+   use human-readable units.
+
+ * "git -c branch.autosetupmerge=inherit branch new old" makes "new"
+   to have the same upstream as the "old" branch, instead of marking
+   "old" itself as its upstream.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -45,10 +100,9 @@ Performance, Internal Implementation, Development Support etc.
  * Teach and encourage first-time contributors to this project to
    state the base commit when they submit their topic.
 
- * The command line complation for "git send-email" options have been
+ * The command line completion for "git send-email" options have been
    tweaked to make it easier to keep it in sync with the command itself.
 
-
  * Ensure that the sparseness of the in-core index matches the
    index.sparse configuration specified by the repository immediately
    after the on-disk index file is read.
@@ -63,6 +117,77 @@ Performance, Internal Implementation, Development Support etc.
  * Weather balloon to break people with compilers that do not support
    C99.
 
+ * The "reftable" backend for the refs API, without integrating into
+   the refs subsystem, has been added.
+
+ * More tests are marked as leak-free.
+
+ * The test framework learns to list unsatisfied test prerequisites,
+   and optionally error out when prerequisites that are expected to be
+   satisfied are not.
+
+ * The default setting for trace2 event nesting was too low to cause
+   test failures, which is worked around by bumping it up in the test
+   framework.
+
+ * Drop support for TravisCI and update test workflows at GitHub.
+
+ * Many tests that used to need GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+   mechanism to force "git" to use 'master' as the default name for
+   the initial branch no longer need it; the use of the mechanism from
+   them have been removed.
+
+ * Allow running our tests while disabling fsync.
+
+ * Document the parameters given to the reflog entry iterator callback
+   functions.
+   (merge e6e94f34b2 jc/reflog-iterator-callback-doc later to maint).
+
+ * The test helper for refs subsystem learned to write bogus and/or
+   nonexistent object name to refs to simulate error situations we
+   want to test Git in.
+
+ * "diff --histogram" optimization.
+
+ * Weather balloon to find compilers that do not grok variable
+   declaration in the for() loop.
+
+ * diff and blame commands have been taught to work better with sparse
+   index.
+
+ * The chainlint test script linter in the test suite has been updated.
+
+ * The DEVELOPER=yes build uses -std=gnu99 now.
+
+ * "git format-patch" uses a single rev_info instance and then exits.
+   Mark the structure with UNLEAK() macro to squelch leak sanitizer.
+
+ * New interface into the tmp-objdir API to help in-core use of the
+   quarantine feature.
+
+ * Broken &&-chains in the test scripts have been corrected.
+
+ * The RCS keyword substitution in "git p4" used to be done assuming
+   that the contents are UTF-8 text, which can trigger decoding
+   errors.  We now treat the contents as a bytestring for robustness
+   and correctness.
+
+ * The conditions to choose different definitions of the FLEX_ARRAY
+   macro for vendor compilers has been simplified to make it easier to
+   maintain.
+
+ * Correctness and performance update to "diff --color-moved" feature.
+
+ * "git upload-pack" (the other side of "git fetch") used a 8kB buffer
+   but most of its payload came on 64kB "packets".  The buffer size
+   has been enlarged so that such a packet fits.
+
+ * "git fetch" and "git pull" are now declared sparse-index clean.
+   Also "git ls-files" learns the "--sparse" option to help debugging.
+
+ * Similar message templates have been consolidated so that
+   translators need to work on fewer number of messages.
+
 
 Fixes since v2.34
 -----------------
@@ -148,6 +273,107 @@ Fixes since v2.34
    'noop', which has been corrected.
    (merge cc9dcdee61 en/rebase-x-fix later to maint).
 
+ * When the "git push" command is killed while the receiving end is
+   trying to report what happened to the ref update proposals, the
+   latter used to die, due to SIGPIPE.  The code now ignores SIGPIPE
+   to increase our chances to run the post-receive hook after it
+   happens.
+   (merge d34182b9e3 rj/receive-pack-avoid-sigpipe-during-status-reporting later to maint).
+
+ * "git worktree add" showed "Preparing worktree" message to the
+   standard output stream, but when it failed, the message from die()
+   went to the standard error stream.  Depending on the order the
+   stdio streams are flushed at the program end, this resulted in
+   confusing output.  It has been corrected by sending all the chatty
+   messages to the standard error stream.
+   (merge b50252484f es/worktree-chatty-to-stderr later to maint).
+
+ * Coding guideline document has been updated to clarify what goes to
+   standard error in our system.
+   (merge e258eb4800 es/doc-stdout-vs-stderr later to maint).
+
+ * The sparse-index/sparse-checkout feature had a bug in its use of
+   the matching code to determine which path is in or outside the
+   sparse checkout patterns.
+   (merge 8c5de0d265 ds/sparse-deep-pattern-checkout-fix later to maint).
+
+ * "git rebase -x" by mistake started exporting the GIT_DIR and
+   GIT_WORK_TREE environment variables when the command was rewritten
+   in C, which has been corrected.
+   (merge 434e0636db en/rebase-x-wo-git-dir-env later to maint).
+
+ * When "git log" implicitly enabled the "decoration" processing
+   without being explicitly asked with "--decorate" option, it failed
+   to read and honor the settings given by the "--decorate-refs"
+   option.
+
+ * "git fetch --set-upstream" did not check if there is a current
+   branch, leading to a segfault when it is run on a detached HEAD,
+   which has been corrected.
+   (merge 17baeaf82d ab/fetch-set-upstream-while-detached later to maint).
+
+ * Among some code paths that ask an yes/no question, only one place
+   gave a prompt that looked different from the others, which has been
+   updated to match what the others create.
+   (merge 0fc8ed154c km/help-prompt-fix later to maint).
+
+ * "git log --invert-grep --author=<name>" used to exclude commits
+   written by the given author, but now "--invert-grep" only affects
+   the matches made by the "--grep=<pattern>" option.
+   (merge 794c000267 rs/log-invert-grep-with-headers later to maint).
+
+ * "git grep --perl-regexp" failed to match UTF-8 characters with
+   wildcard when the pattern consists only of ASCII letters, which has
+   been corrected.
+   (merge 32e3e8bc55 rs/pcre2-utf later to maint).
+
+ * Certain sparse-checkout patterns that are valid in non-cone mode
+   led to segfault in cone mode, which has been corrected.
+
+ * Use of certain "git rev-list" options with "git fast-export"
+   created nonsense results (the worst two of which being "--reverse"
+   and "--invert-grep --grep=<foo>").  The use of "--first-parent" is
+   made to behave a bit more sensible than before.
+   (merge 726a228dfb ws/fast-export-with-revision-options later to maint).
+
+ * Perf tests were run with end-user's shell, but it has been
+   corrected to use the shell specified by $TEST_SHELL_PATH.
+   (merge 9ccab75608 ja/perf-use-specified-shell later to maint).
+
+ * Fix dependency rules to generate hook-list.h header file.
+   (merge d3fd1a6667 ab/makefile-hook-list-dependency-fix later to maint).
+
+ * "git stash" by default triggers its "push" action, but its
+   implementation also made "git stash -h" to show short help only for
+   "git stash push", which has been corrected.
+   (merge ca7990cea5 ab/do-not-limit-stash-help-to-push later to maint).
+
+ * "git apply --3way" bypasses the attempt to do a three-way
+   application in more cases to address the regression caused by the
+   recent change to use direct application as a fallback.
+   (merge 34d607032c jz/apply-3-corner-cases later to maint).
+
+ * Fix performance-releated bug in "git subtree" (in contrib/).
+   (merge 3ce8888fb4 jl/subtree-check-parents-argument-passing-fix later to maint).
+
+ * Extend the guidance to choose the base commit to build your work
+   on, and hint/nudge contributors to read others' changes.
+   (merge fdfae830f8 jc/doc-submitting-patches-choice-of-base later to maint).
+
+ * A corner case bug in the ort merge strategy has been corrected.
+   (merge d30126c20d en/merge-ort-renorm-with-rename-delete-conflict-fix later to maint).
+
+ * "git stash apply" forgot to attempt restoring untracked files when
+   it failed to restore changes to tracked ones.
+   (merge 71cade5a0b en/stash-df-fix later to maint).
+
+ * Calling dynamically loaded functions on Windows has been corrected.
+   (merge 4a9b204920 ma/windows-dynload-fix later to maint).
+
+ * Some lockfile code called free() in signal-death code path, which
+   has been corrected.
+   (merge 58d4d7f1c5 ps/lockfile-cleanup-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 74db416c9c cw/protocol-v2-doc-fix later to maint).
    (merge f9b2b6684d ja/doc-cleanup later to maint).
@@ -162,3 +388,15 @@ Fixes since v2.34
    (merge 7d3fc7df70 jt/midx-doc-fix later to maint).
    (merge 7b089120d9 hn/create-reflog-simplify later to maint).
    (merge 9e12400da8 cb/mingw-gmtime-r later to maint).
+   (merge 0bf0de6cc7 tb/pack-revindex-on-disk-cleanup later to maint).
+   (merge 2c68f577fc ew/cbtree-remove-unused-and-broken-cb-unlink later to maint).
+   (merge eafd6e7e55 ab/die-with-bug later to maint).
+   (merge 91028f7659 jc/grep-patterntype-default-doc later to maint).
+   (merge 47ca93d071 ds/repack-fixlets later to maint).
+   (merge e6a9bc0c60 rs/t4202-invert-grep-test-fix later to maint).
+   (merge deb5407a42 gh/gpg-doc-markup-fix later to maint).
+   (merge 999bba3e0b rs/daemon-plug-leak later to maint).
+   (merge 786eb1ba39 js/l10n-mention-ngettext-early-in-readme later to maint).
+   (merge 2f12b31b74 ab/makefile-msgfmt-wo-stats later to maint).
+   (merge 0517f591ca fs/gpg-unknown-key-test-fix later to maint).
+   (merge 97d6fb5a1f ma/header-dup-cleanup later to maint).
index 11e03056f2e1156eb95a0ad6c2f486e60dad1cb6..92b80d94d488c2f7af6f5be2b5fbded582b65850 100644 (file)
@@ -19,8 +19,10 @@ change is relevant to.
   base your work on the tip of the topic.
 
 * A new feature should be based on `master` in general. If the new
-  feature depends on a topic that is in `seen`, but not in `master`,
-  base your work on the tip of that topic.
+  feature depends on other topics that are in `next`, but not in
+  `master`, fork a branch from the tip of `master`, merge these topics
+  to the branch, and work on that branch.  You can remind yourself of
+  how you prepared the base with `git log --first-parent master..`.
 
 * Corrections and enhancements to a topic not yet in `master` should
   be based on the tip of that topic. If the topic has not been merged
@@ -28,10 +30,10 @@ change is relevant to.
   into the series.
 
 * In the exceptional case that a new feature depends on several topics
-  not in `master`, start working on `next` or `seen` privately and send
-  out patches for discussion. Before the final merge, you may have to
-  wait until some of the dependent topics graduate to `master`, and
-  rebase your work.
+  not in `master`, start working on `next` or `seen` privately and
+  send out patches only for discussion. Once your new feature starts
+  to stabilize, you would have to rebase it (see the "depends on other
+  topics" above).
 
 * Some parts of the system have dedicated maintainers with their own
   repositories (see the section "Subsystems" below).  Changes to
@@ -71,8 +73,13 @@ Make sure that you have tests for the bug you are fixing.  See
 [[tests]]
 When adding a new feature, make sure that you have new tests to show
 the feature triggers the new behavior when it should, and to show the
-feature does not trigger when it shouldn't.  After any code change, make
-sure that the entire test suite passes.
+feature does not trigger when it shouldn't.  After any code change,
+make sure that the entire test suite passes.  When fixing a bug, make
+sure you have new tests that break if somebody else breaks what you
+fixed by accident to avoid regression.  Also, try merging your work to
+'next' and 'seen' and make sure the tests still pass; topics by others
+that are still in flight may have unexpected interactions with what
+you are trying to do in your topic.
 
 Pushing to a fork of https://github.com/git/git will use their CI
 integration to test your changes on Linux, Mac and Windows. See the
@@ -144,8 +151,21 @@ without external resources. Instead of giving a URL to a mailing list
 archive, summarize the relevant points of the discussion.
 
 [[commit-reference]]
-If you want to reference a previous commit in the history of a stable
-branch, use the format "abbreviated hash (subject, date)", like this:
+
+There are a few reasons why you may want to refer to another commit in
+the "more stable" part of the history (i.e. on branches like `maint`,
+`master`, and `next`):
+
+. A commit that introduced the root cause of a bug you are fixing.
+
+. A commit that introduced a feature that you are enhancing.
+
+. A commit that conflicts with your work when you made a trial merge
+  of your work into `next` and `seen` for testing.
+
+When you reference a commit on a more stable branch (like `master`,
+`maint` and `next`), use the format "abbreviated hash (subject,
+date)", like this:
 
 ....
        Commit f86a374 (pack-bitmap.c: fix a memleak, 2015-03-30)
@@ -259,9 +279,11 @@ Please make sure your patch does not add commented out debugging code,
 or include any extra files which do not relate to what your patch
 is trying to achieve. Make sure to review
 your patch after generating it, to ensure accuracy.  Before
-sending out, please make sure it cleanly applies to the `master`
-branch head.  If you are preparing a work based on "next" branch,
-that is fine, but please mark it as such.
+sending out, please make sure it cleanly applies to the base you
+have chosen in the "Decide what to base your work on" section,
+and unless it targets the `master` branch (which is the default),
+mark your patches as such.
+
 
 [[send-patches]]
 === Sending your patches.
@@ -365,7 +387,10 @@ Security mailing list{security-ml-ref}.
 Send your patch with "To:" set to the mailing list, with "cc:" listing
 people who are involved in the area you are touching (the `git
 contacts` command in `contrib/contacts/` can help to
-identify them), to solicit comments and reviews.
+identify them), to solicit comments and reviews.  Also, when you made
+trial merges of your topic to `next` and `seen`, you may have noticed
+work by others conflicting with your changes.  There is a good possibility
+that these people may know the area you are touching well.
 
 :current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
 :git-ml: footnote:[The mailing list: git@vger.kernel.org]
index 1167e88e341bc38c3f8b5a60ea7b476cd561e340..b168f02dc3d92c77be4d37061403a2909e6235c8 100644 (file)
@@ -262,11 +262,19 @@ color::
        colors (at most two, one for foreground and one for background)
        and attributes (as many as you want), separated by spaces.
 +
-The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
-`blue`, `magenta`, `cyan` and `white`.  The first color given is the
-foreground; the second is the background.  All the basic colors except
-`normal` have a bright variant that can be specified by prefixing the
-color with `bright`, like `brightred`.
+The basic colors accepted are `normal`, `black`, `red`, `green`,
+`yellow`, `blue`, `magenta`, `cyan`, `white` and `default`.  The first
+color given is the foreground; the second is the background.  All the
+basic colors except `normal` and `default` have a bright variant that can
+be specified by prefixing the color with `bright`, like `brightred`.
++
+The color `normal` makes no change to the color. It is the same as an
+empty string, but can be used as the foreground color when specifying a
+background color alone (for example, "normal red").
++
+The color `default` explicitly resets the color to the terminal default,
+for example to specify a cleared background. Although it varies between
+terminals, this is usually not the same as setting to "white black".
 +
 Colors may also be given as numbers between 0 and 255; these use ANSI
 256-color mode (but note that not all terminals may support this).  If
@@ -280,6 +288,11 @@ The position of any attributes with respect to the colors
 be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
 `no-ul`, etc).
 +
+The pseudo-attribute `reset` resets all colors and attributes before
+applying the specified coloring. For example, `reset green` will result
+in a green foreground and default background without any active
+attributes.
++
 An empty color string produces no color effect at all. This can be used
 to avoid coloring specific elements without disabling color entirely.
 +
index d323d7327f6ba60b0a460019057f9dd188500873..1e0c7af014beaf41534f1278cdb156dfdafdd870 100644 (file)
@@ -7,7 +7,8 @@ branch.autoSetupMerge::
        automatic setup is done; `true` -- automatic setup is done when the
        starting point is a remote-tracking branch; `always` --
        automatic setup is done when the starting point is either a
-       local branch or remote-tracking
+       local branch or remote-tracking branch; `inherit` -- if the starting point
+       has a tracking configuration, it is copied to the new
        branch. This option defaults to true.
 
 branch.autoSetupRebase::
index 4f30c7dbdd9f88480ab5ba54ba71f016c6571114..0cb189a077ca54ed83a6dfefad9be327f7176d40 100644 (file)
@@ -34,7 +34,7 @@ gpg.minTrustLevel::
 * `fully`
 * `ultimate`
 
-gpg.ssh.defaultKeyCommand:
+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
@@ -44,7 +44,7 @@ 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...
+       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.
@@ -64,6 +64,11 @@ 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.
 +
+Since OpensSSH 8.8 this file allows specifying a key lifetime using valid-after &
+valid-before options. Git will mark signatures as valid if the signing key was
+valid at the time of the signatures creation. This allows users to change a
+signing key without invalidating all previously made signatures.
++
 Using a SSH CA key with the cert-authority option
 (see ssh-keygen(1) "CERTIFICATES") is also valid.
 
index 44abe45a7cad1625e6b96a5603397ab957a873ab..182edd813a5d3f970922b23e85f1000c32f4a3df 100644 (file)
@@ -8,7 +8,8 @@ grep.patternType::
        Set the default matching behavior. Using a value of 'basic', 'extended',
        'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
        `--fixed-strings`, or `--perl-regexp` option accordingly, while the
-       value 'default' will return to the default matching behavior.
+       value 'default' will use the `grep.extendedRegexp` option to choose
+       between 'basic' and 'extended'.
 
 grep.extendedRegexp::
        If set to true, enable `--extended-regexp` option by default. This
index e27cc639447f70b97d911f43dd9e09d11114334a..99e83dd36e53e6079721a5a123fdf6392f4fc2b8 100644 (file)
@@ -4,7 +4,14 @@ merge.conflictStyle::
        shows a `<<<<<<<` conflict marker, changes made by one side,
        a `=======` marker, changes made by the other side, and then
        a `>>>>>>>` marker.  An alternate style, "diff3", adds a `|||||||`
-       marker and the original text before the `=======` marker.
+       marker and the original text before the `=======` marker.  The
+       "merge" style tends to produce smaller conflict regions than diff3,
+       both because of the exclusion of the original text, and because
+       when a subset of lines match on the two sides they are just pulled
+       out of the conflict region.  Another alternate style, "zdiff3", is
+       similar to diff3 but removes matching lines on the two sides from
+       the conflict region when those matching lines appear near either
+       the beginning or end of a conflict region.
 
 merge.defaultToUpstream::
        If merge is called without any commit argument, merge the upstream
index ad78dce9ecbfc6faf633be133f8f88218844bd9a..ec9233b060a82c41dd9a01785a6f1e748ddf0ad9 100644 (file)
@@ -36,10 +36,13 @@ 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.
+       If gpg.format is set to `ssh` this can contain the path to either
+       your private ssh key or the public key when ssh-agent is used.
+       Alternatively it can contain a public key prefixed with `key::`
+       directly (e.g.: "key::ssh-rsa XXXXXX identifier"). The private key
+       needs to be available via ssh-agent. If not set git will call
+       gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the
+       first key available. For backward compatibility, a raw key which
+       begins with "ssh-", such as "ssh-rsa XXXXXX identifier", is treated
+       as "key::ssh-rsa XXXXXX identifier", but this form is deprecated;
+       use the `key::` form instead.
index 0a4a984dfdecda8ef5d252e2a2883f6497f2cb1b..09107fb106703d14c9e695685e93f59e15362fc3 100644 (file)
@@ -16,8 +16,9 @@ SYNOPSIS
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
         [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
         [--quoted-cr=<action>]
+        [--empty=(stop|drop|keep)]
         [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -63,6 +64,14 @@ OPTIONS
 --quoted-cr=<action>::
        This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--empty=(stop|drop|keep)::
+       By default, or when the option is set to 'stop', the command
+       errors out on an input e-mail message lacking a patch
+       and stops into the middle of the current am session. When this
+       option is set to 'drop', skip such an e-mail message instead.
+       When this option is set to 'keep', create an empty commit,
+       recording the contents of the e-mail message as its log.
+
 -m::
 --message-id::
        Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
@@ -191,6 +200,11 @@ default.   You can use `--no-utf8` to override this.
        the e-mail message; if `diff`, show the diff portion only.
        Defaults to `raw`.
 
+--allow-empty::
+       After a patch failure on an input e-mail message lacking a patch,
+       create an empty commit with the contents of the e-mail message
+       as its log message.
+
 DISCUSSION
 ----------
 
index aa1ae56a25e0428cabcfa2539900ef2a09abcb7c..b6d77f420682618be63cb27a34e803cfcac8f802 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
          [--ignore-space-change | --ignore-whitespace]
          [--whitespace=(nowarn|warn|fix|error|error-all)]
          [--exclude=<path>] [--include=<path>] [--directory=<root>]
-         [--verbose] [--unsafe-paths] [<patch>...]
+         [--verbose | --quiet] [--unsafe-paths] [--allow-empty] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -228,6 +228,11 @@ behavior:
        current patch being applied will be printed. This option will cause
        additional information to be reported.
 
+-q::
+--quiet::
+       Suppress stderr output. Messages about patch status and progress
+       will not be printed.
+
 --recount::
        Do not trust the line counts in the hunk headers, but infer them
        by inspecting the patch (e.g. after editing the patch without
@@ -251,6 +256,10 @@ When `git apply` is used as a "better GNU patch", the user can pass
 the `--unsafe-paths` option to override this safety check.  This option
 has no effect when `--index` or `--cached` is in use.
 
+--allow-empty::
+       Don't return error for patches containing no diff. This includes
+       empty patches and patches with commit text only.
+
 CONFIGURATION
 -------------
 
index 8af42eff895c391994ef9e8ea1e4e7004f5d493e..2d52ae396b0c3029dc371c80853725a3a492e596 100644 (file)
@@ -16,7 +16,7 @@ SYNOPSIS
        [--points-at <object>] [--format=<format>]
        [(-r | --remotes) | (-a | --all)]
        [--list] [<pattern>...]
-'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
+'git branch' [--track [direct|inherit] | --no-track] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
 'git branch' --unset-upstream [<branchname>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -206,24 +206,34 @@ This option is only applicable in non-verbose mode.
        Display the full sha1s in the output listing rather than abbreviating them.
 
 -t::
---track::
+--track [inherit|direct]::
        When creating a new branch, set up `branch.<name>.remote` and
-       `branch.<name>.merge` configuration entries to mark the
-       start-point branch as "upstream" from the new branch. This
+       `branch.<name>.merge` configuration entries to set "upstream" tracking
+       configuration for the new branch. This
        configuration will tell git to show the relationship between the
        two branches in `git status` and `git branch -v`. Furthermore,
        it directs `git pull` without arguments to pull from the
        upstream when the new branch is checked out.
 +
-This behavior is the default when the start point is a remote-tracking branch.
+The exact upstream branch is chosen depending on the optional argument:
+`--track` or `--track direct` means to use the start-point branch itself as the
+upstream; `--track inherit` means to copy the upstream configuration of the
+start-point branch.
++
+`--track direct` is the default when the start point is a remote-tracking branch.
 Set the branch.autoSetupMerge configuration variable to `false` if you
 want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
 were given. Set it to `always` if you want this behavior when the
-start-point is either a local or remote-tracking branch.
+start-point is either a local or remote-tracking branch. Set it to
+`inherit` if you want to copy the tracking configuration from the
+branch point.
++
+See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on
+how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
 
 --no-track::
        Do not set up "upstream" configuration, even if the
-       branch.autoSetupMerge configuration variable is true.
+       branch.autoSetupMerge configuration variable is set.
 
 --set-upstream::
        As this option had confusing syntax, it is no longer supported.
index a52dc49a3dc65f06e6ad3d8b6cea96d3d439feb0..2a90ea6cd05a3f363e11d1a231caf06d2971ae2b 100644 (file)
@@ -156,7 +156,7 @@ of it").
        linkgit:git-branch[1] for details.
 
 -t::
---track::
+--track [direct|inherit]::
        When creating a new branch, set up "upstream" configuration. See
        "--track" in linkgit:git-branch[1] for details.
 +
@@ -266,8 +266,7 @@ When switching branches with `--merge`, staged changes may be lost.
        The same as `--merge` option above, but changes the way the
        conflicting hunks are presented, overriding the
        `merge.conflictStyle` configuration variable.  Possible values are
-       "merge" (default) and "diff3" (in addition to what is shown by
-       "merge" style, shows the original contents).
+       "merge" (default), "diff3", and "zdiff3".
 
 -p::
 --patch::
index 9685ea0691534dead2d393959e9055ee7b23a06a..984d194934fb84253e7f6b763256e09e2ddd8e91 100644 (file)
@@ -167,10 +167,10 @@ objects from the source repository into a pack in the cloned repository.
        configuration variables are created.
 
 --sparse::
-       Initialize the sparse-checkout file so the working
-       directory starts with only the files in the root
-       of the repository. The sparse-checkout file can be
-       modified to grow the working directory as needed.
+       Employ a sparse-checkout, with only files in the toplevel
+       directory initially being present.  The
+       linkgit:git-sparse-checkout[1] command can be used to grow the
+       working directory as needed.
 
 --filter=<filter-spec>::
        Use the partial clone feature and request that the server sends
index 6793d8fc05218f61ebc399b9ac910f27832bc58b..6f28812f38defec0047f9c47ca554231a6adfd8e 100644 (file)
@@ -9,7 +9,7 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log]
+'git fmt-merge-msg' [-m <message>] [--into-name <branch>] [--log[=<n>] | --no-log]
 'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
 
 DESCRIPTION
@@ -44,6 +44,10 @@ OPTIONS
        Use <message> instead of the branch names for the first line
        of the log message.  For use with `--log`.
 
+--into-name <branch>::
+       Prepare the merge message as if merging to the branch `<branch>`,
+       instead of the name of the real branch to which the merge is made.
+
 -F <file>::
 --file <file>::
        Take the list of merged objects from <file> instead of
index 113eabc107ca94f801eca64591421065a2bc54b4..be797d7a28f62f38e2dc38c82141e71b97ea4aae 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
                   [--in-reply-to=<message id>] [--suffix=.<sfx>]
-                  [--ignore-if-in-upstream]
+                  [--ignore-if-in-upstream] [--always]
                   [--cover-from-description=<mode>]
                   [--rfc] [--subject-prefix=<subject prefix>]
                   [(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@ will want to ensure that threading is disabled for `git send-email`.
        patches being generated, and any patch that matches is
        ignored.
 
+--always::
+       Include patches for commits that do not introduce any change,
+       which are omitted by default.
+
 --cover-from-description=<mode>::
        Controls which parts of the cover letter will be automatically
        populated using the branch's description.
index 2e3d695fa213819610982f7d02441a492b0c2c68..48cc7c0b6f4b639688f64bdfc1d7562ead1da034 100644 (file)
@@ -187,6 +187,11 @@ Both the <eolinfo> in the index ("i/<eolinfo>")
 and in the working tree ("w/<eolinfo>") are shown for regular files,
 followed by the  ("attr/<eolattr>").
 
+--sparse::
+       If the index is sparse, show the sparse directories without expanding
+       to the contained files. Sparse directories will be shown with a
+       trailing slash, such as "x/" for a sparse directory "x".
+
 \--::
        Do not interpret any more arguments as options.
 
index f85603261325f6f88b31c30b5fb2bafcded58d92..7e9093fab60d267fa795e3d3fddd26cdfacfb557 100644 (file)
@@ -70,6 +70,9 @@ OPTIONS
 --diff3::
        Show conflicts in "diff3" style.
 
+--zdiff3::
+       Show conflicts in "zdiff3" style.
+
 --ours::
 --theirs::
 --union::
index e4f3352eb584539b75235fc304431646df9415f4..3125473cc1d19140cf54f5286106b86823a9b91f 100644 (file)
@@ -12,7 +12,8 @@ SYNOPSIS
 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
        [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]allow-unrelated-histories]
-       [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
+       [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>]
+       [--into-name <branch>] [<commit>...]
 'git merge' (--continue | --abort | --quit)
 
 DESCRIPTION
@@ -76,6 +77,11 @@ The 'git fmt-merge-msg' command can be
 used to give a good default for automated 'git merge'
 invocations. The automated message can include the branch description.
 
+--into-name <branch>::
+       Prepare the default merge message as if merging to the branch
+       `<branch>`, instead of the name of the real branch to which
+       the merge is made.
+
 -F <file>::
 --file=<file>::
        Read the commit message to be used for the merge commit (in
@@ -240,7 +246,8 @@ from the RCS suite to present such a conflicted hunk, like this:
 
 ------------
 Here are lines that are either unchanged from the common
-ancestor, or cleanly resolved because only one side changed.
+ancestor, or cleanly resolved because only one side changed,
+or cleanly resolved because both sides changed the same way.
 <<<<<<< yours:sample.txt
 Conflict resolution is hard;
 let's go shopping.
@@ -261,16 +268,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the
 other side wants to claim it is easy.
 
 An alternative style can be used by setting the "merge.conflictStyle"
-configuration variable to "diff3".  In "diff3" style, the above conflict
-may look like this:
+configuration variable to either "diff3" or "zdiff3".  In "diff3"
+style, the above conflict may look like this:
+
+------------
+Here are lines that are either unchanged from the common
+ancestor, or cleanly resolved because only one side changed,
+<<<<<<< yours:sample.txt
+or cleanly resolved because both sides changed the same way.
+Conflict resolution is hard;
+let's go shopping.
+||||||| base:sample.txt
+or cleanly resolved because both sides changed identically.
+Conflict resolution is hard.
+=======
+or cleanly resolved because both sides changed the same way.
+Git makes conflict resolution easy.
+>>>>>>> theirs:sample.txt
+And here is another line that is cleanly resolved or unmodified.
+------------
+
+while in "zdiff3" style, it may look like this:
 
 ------------
 Here are lines that are either unchanged from the common
-ancestor, or cleanly resolved because only one side changed.
+ancestor, or cleanly resolved because only one side changed,
+or cleanly resolved because both sides changed the same way.
 <<<<<<< yours:sample.txt
 Conflict resolution is hard;
 let's go shopping.
-|||||||
+||||||| base:sample.txt
+or cleanly resolved because both sides changed identically.
 Conflict resolution is hard.
 =======
 Git makes conflict resolution easy.
index a1af21fcefe6098df8506c46e71a91585625281e..9da4647061cdc7e85df685169c81f2f08b9d6707 100644 (file)
@@ -714,9 +714,9 @@ information about the rebased commits and their parents (and instead
 generates new fake commits based off limited information in the
 generated patches), those commits cannot be identified; instead it has
 to fall back to a commit summary.  Also, when merge.conflictStyle is
-set to diff3, the apply backend will use "constructed merge base" to
-label the content from the merge base, and thus provide no information
-about the merge base commit whatsoever.
+set to diff3 or zdiff3, the apply backend will use "constructed merge
+base" to label the content from the merge base, and thus provide no
+information about the merge base commit whatsoever.
 
 The merge backend works with the full commits on both sides of history
 and thus has no such limitations.
index 7183fb498f4ccec69e6bd75dcef44f54974a159b..ee30edc178a4d418610612215935452b6daca567 100644 (file)
@@ -76,8 +76,9 @@ to the new separate pack will be written.
        linkgit:git-pack-objects[1].
 
 -q::
-       Pass the `-q` option to 'git pack-objects'. See
-       linkgit:git-pack-objects[1].
+--quiet::
+       Show no progress over the standard error stream and pass the `-q`
+       option to 'git pack-objects'. See linkgit:git-pack-objects[1].
 
 -n::
        Do not update the server information with
index 55bde91ef9e54be6c9ecf928f6f72c775b45809a..5964810caa4153a6628ca312669a77a90b41943a 100644 (file)
@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.
        The same as `--merge` option above, but changes the way the
        conflicting hunks are presented, overriding the
        `merge.conflictStyle` configuration variable.  Possible values
-       are "merge" (default) and "diff3" (in addition to what is
-       shown by "merge" style, shows the original contents).
+       are "merge" (default), "diff3", and "zdiff3".
 
 --ignore-unmerged::
        When restoring files on the working tree from the index, do
index 9a78dd721e8a5f6ec54e14c36854296b7038d229..b81dbe06543c169f44ffedf2e773ade6de23ecae 100644 (file)
@@ -30,28 +30,36 @@ COMMANDS
 'list'::
        Describe the patterns in the sparse-checkout file.
 
-'init'::
-       Enable the `core.sparseCheckout` setting. If the
-       sparse-checkout file does not exist, then populate it with
-       patterns that match every file in the root directory and
-       no other directories, then will remove all directories tracked
-       by Git. Add patterns to the sparse-checkout file to
-       repopulate the working directory.
+'set'::
+       Enable the necessary config settings
+       (extensions.worktreeConfig, core.sparseCheckout,
+       core.sparseCheckoutCone) if they are not already enabled, and
+       write a set of patterns to the sparse-checkout file from the
+       list of arguments following the 'set' subcommand. Update the
+       working directory to match the new patterns.
 +
-To avoid interfering with other worktrees, it first enables the
-`extensions.worktreeConfig` setting and makes sure to set the
-`core.sparseCheckout` setting in the worktree-specific config file.
+When the `--stdin` option is provided, the patterns are read from
+standard in as a newline-delimited list instead of from the arguments.
 +
-When `--cone` is provided, the `core.sparseCheckoutCone` setting is
-also set, allowing for better performance with a limited set of
-patterns (see 'CONE PATTERN SET' below).
+When `--cone` is passed or `core.sparseCheckoutCone` is enabled, the
+input list is considered a list of directories instead of
+sparse-checkout patterns.  This allows for better performance with a
+limited set of patterns (see 'CONE PATTERN SET' below).  Note that the
+set command will write patterns to the sparse-checkout file to include
+all files contained in those directories (recursively) as well as
+files that are siblings of ancestor directories. The input format
+matches the output of `git ls-tree --name-only`.  This includes
+interpreting pathnames that begin with a double quote (") as C-style
+quoted strings.  This may become the default in the future; --no-cone
+can be passed to request non-cone mode.
 +
-Use the `--[no-]sparse-index` option to toggle the use of the sparse
-index format. This reduces the size of the index to be more closely
-aligned with your sparse-checkout definition. This can have significant
-performance advantages for commands such as `git status` or `git add`.
-This feature is still experimental. Some commands might be slower with
-a sparse index until they are properly integrated with the feature.
+Use the `--[no-]sparse-index` option to use a sparse index (the
+default is to not use it).  A sparse index reduces the size of the
+index to be more closely aligned with your sparse-checkout
+definition. This can have significant performance advantages for
+commands such as `git status` or `git add`.  This feature is still
+experimental. Some commands might be slower with a sparse index until
+they are properly integrated with the feature.
 +
 **WARNING:** Using a sparse index requires modifying the index in a way
 that is not completely understood by external tools. If you have trouble
@@ -60,23 +68,6 @@ to rewrite your index to not be sparse. Older versions of Git will not
 understand the sparse directory entries index extension and may fail to
 interact with your repository until it is disabled.
 
-'set'::
-       Write a set of patterns to the sparse-checkout file, as given as
-       a list of arguments following the 'set' subcommand. Update the
-       working directory to match the new patterns. Enable the
-       core.sparseCheckout config setting if it is not already enabled.
-+
-When the `--stdin` option is provided, the patterns are read from
-standard in as a newline-delimited list instead of from the arguments.
-+
-When `core.sparseCheckoutCone` is enabled, the input list is considered a
-list of directories instead of sparse-checkout patterns. The command writes
-patterns to the sparse-checkout file to include all files contained in those
-directories (recursively) as well as files that are siblings of ancestor
-directories. The input format matches the output of `git ls-tree --name-only`.
-This includes interpreting pathnames that begin with a double quote (") as
-C-style quoted strings.
-
 'add'::
        Update the sparse-checkout file to include additional patterns.
        By default, these patterns are read from the command-line arguments,
@@ -93,12 +84,35 @@ C-style quoted strings.
        cases, it can make sense to run `git sparse-checkout reapply` later
        after cleaning up affected paths (e.g. resolving conflicts, undoing
        or committing changes, etc.).
++
+The `reapply` command can also take `--[no-]cone` and `--[no-]sparse-index`
+flags, with the same meaning as the flags from the `set` command, in order
+to change which sparsity mode you are using without needing to also respecify
+all sparsity paths.
 
 'disable'::
        Disable the `core.sparseCheckout` config setting, and restore the
-       working directory to include all files. Leaves the sparse-checkout
-       file intact so a later 'git sparse-checkout init' command may
-       return the working directory to the same state.
+       working directory to include all files.
+
+'init'::
+       Deprecated command that behaves like `set` with no specified paths.
+       May be removed in the future.
++
+Historically, `set` did not handle all the necessary config settings,
+which meant that both `init` and `set` had to be called.  Invoking
+both meant the `init` step would first remove nearly all tracked files
+(and in cone mode, ignored files too), then the `set` step would add
+many of the tracked files (but not ignored files) back.  In addition
+to the lost files, the performance and UI of this combination was
+poor.
++
+Also, historically, `init` would not actually initialize the
+sparse-checkout file if it already existed.  This meant it was
+possible to return to a sparse-checkout without remembering which
+paths to pass to a subsequent 'set' or 'add' command.  However,
+`--cone` and `--sparse-index` options would not be remembered across
+the disable command, so the easy restore of calling a plain `init`
+decreased in utility.
 
 SPARSE CHECKOUT
 ---------------
@@ -107,7 +121,7 @@ SPARSE CHECKOUT
 It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
 Git whether a file in the working directory is worth looking at. If
 the skip-worktree bit is set, then the file is ignored in the working
-directory. Git will not populate the contents of those files, which
+directory. Git will avoid populating the contents of those files, which
 makes a sparse checkout helpful when working in a repository with many
 files, but only a few are important to the current user.
 
@@ -117,10 +131,8 @@ directory, it updates the skip-worktree bits in the index based
 on this file. The files matching the patterns in the file will
 appear in the working directory, and the rest will not.
 
-To enable the sparse-checkout feature, run `git sparse-checkout init` to
-initialize a simple sparse-checkout file and enable the `core.sparseCheckout`
-config setting. Then, run `git sparse-checkout set` to modify the patterns in
-the sparse-checkout file.
+To enable the sparse-checkout feature, run `git sparse-checkout set` to
+set the patterns you want to use.
 
 To repopulate the working directory with all files, use the
 `git sparse-checkout disable` command.
index 5c438cd505875841763f0151dfe0a2c1454dfcc5..bbcbdceb459c2828e0eb46110d038137be1f6f92 100644 (file)
@@ -137,8 +137,7 @@ should result in deletion of the path).
        The same as `--merge` option above, but changes the way the
        conflicting hunks are presented, overriding the
        `merge.conflictStyle` configuration variable.  Possible values are
-       "merge" (default) and "diff3" (in addition to what is shown by
-       "merge" style, shows the original contents).
+       "merge" (default), "diff3", and "zdiff3".
 
 -q::
 --quiet::
@@ -152,7 +151,7 @@ should result in deletion of the path).
        attached to a terminal, regardless of `--quiet`.
 
 -t::
---track::
+--track [direct|inherit]::
        When creating a new branch, set up "upstream" configuration.
        `-c` is implied. See `--track` in linkgit:git-branch[1] for
        details.
index 8a7cbdd19c151e71a5de0fce27c7140f81f95724..9e862fbcf79efaa507973b5968c3ec5f592a8756 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
-'git worktree list' [--porcelain]
+'git worktree list' [-v | --porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
 'git worktree prune' [-n] [-v] [--expire <expire>]
index 23f63358871ad3732f8c4716678318237bc94870..0b4c1c8d98a4acf01c19722941095da2c2c6525d 100644 (file)
@@ -220,6 +220,12 @@ The placeholders are:
                          inconsistent when tags are added or removed at
                          the same time.
 +
+** 'tags[=<bool-value>]': Instead of only considering annotated tags,
+   consider lightweight tags as well.
+** 'abbrev=<number>': Instead of using the default number of hexadecimal digits
+   (which will vary according to the number of objects in the repository with a
+   default of 7) of the abbreviated object name, use <number> digits, or as many
+   digits as needed to form a unique object name.
 ** 'match=<pattern>': Only consider tags matching the given
    `glob(7)` pattern, excluding the "refs/tags/" prefix.
 ** 'exclude=<pattern>': Do not consider tags matching the given
@@ -273,11 +279,6 @@ endif::git-rev-list[]
                          If any option is provided multiple times the
                          last occurrence wins.
 +
-The boolean options accept an optional value `[=<value>]`. The values
-`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
-sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
-option is given with no value, it's enabled.
-+
 ** 'key=<key>': only show trailers with specified <key>. Matching is done
    case-insensitively and trailing colon is optional. If option is
    given multiple times trailer lines matching any of the keys are
@@ -313,6 +314,11 @@ insert an empty string unless we are traversing reflog entries (e.g., by
 decoration format if `--decorate` was not already provided on the command
 line.
 
+The boolean options accept an optional value `[=<bool-value>]`. The values
+`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
+sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
+option is given with no value, it's enabled.
+
 If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
 is inserted immediately before the expansion if and only if the
 placeholder expands to a non-empty string.
index af5f9fc24f9343e73aca771d310570251fe2b922..35d454143399e0593af41029d75e9ff4fd3d432e 100644 (file)
@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.
 
 Different conflict styles and branch names are normalized by stripping
 the labels from the conflict markers, and removing the common ancestor
-version from the `diff3` conflict style. Branches that are merged
-in different order are normalized by sorting the conflict hunks.  More
-on each of those steps in the following sections.
+version from the `diff3` or `zdiff3` conflict styles.  Branches that
+are merged in different order are normalized by sorting the conflict
+hunks.  More on each of those steps in the following sections.
 
 Once these two normalization operations are applied, a conflict ID is
 calculated based on the normalized conflict, which is later used by
@@ -42,8 +42,8 @@ get a conflict like the following:
     >>>>>>> AC
 
 Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB
-and then merging branch AC2 into it), using the diff3 conflict style,
-we get a conflict like the following:
+and then merging branch AC2 into it), using the diff3 or zdiff3
+conflict style, we get a conflict like the following:
 
     <<<<<<< HEAD
     B
index 8d2297c33b6fe7fc99b0f0942ef5d743ecc95d55..e9c1ad960f6ae5761c46105be76dc1c0bfbfa1a5 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.34.GIT
+DEF_VER=v2.35.0-rc1
 
 LF='
 '
index 9c00a793e4701287f6e7b1007fc75111b09715a7..5580859afdb45b44459798fa490f9b53e426079b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1206,6 +1206,7 @@ endif
 # Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be
 # tweaked by config.* below as well as the command-line, both of
 # which'll override these defaults.
+# Older versions of GCC may require adding "-std=gnu99" at the end.
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
 CC_LD_DYNPATH = -Wl,-rpath,
@@ -1880,7 +1881,7 @@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
 endif
 
 ifndef NO_MSGFMT_EXTENDED_OPTIONS
-       MSGFMT += --check --statistics
+       MSGFMT += --check
 endif
 
 ifdef HAVE_CLOCK_GETTIME
@@ -2111,11 +2112,6 @@ ifdef DEFAULT_HELP_FORMAT
 BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
 endif
 
-PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
-PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
-PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
-BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
-
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
@@ -2222,14 +2218,20 @@ 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/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h
 
-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: config-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)"' \
        '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
 
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+pager.sp pager.s pager.o: EXTRA_CPPFLAGS = \
+       -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
 version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
 version.sp version.s version.o: EXTRA_CPPFLAGS = \
        '-DGIT_VERSION="$(GIT_VERSION)"' \
@@ -2491,6 +2493,11 @@ OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
 ifndef NO_CURL
        OBJECTS += http.o http-walker.o remote-curl.o
 endif
+
+SCALAR_SOURCES := contrib/scalar/scalar.c
+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)
+OBJECTS += $(SCALAR_OBJECTS)
+
 .PHONY: objects
 objects: $(OBJECTS)
 
@@ -2624,6 +2631,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+               $(filter %.o,$^) $(LIBS)
+
 $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
index eb8115e6b04814f0c37146bbe3dbc35f3e8992e0..f6f43e78debd8e4dac7e483068c1f9c764beb0f5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build status](https://github.com/git/git/workflows/CI/PR/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
+[![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
 
 Git - fast, scalable, distributed revision control system
 =========================================================
index 8c41cdfe39be041e7d119737a83d754a06733649..573eef0cc4a86642bc92fba74763aaa913af1b2f 100644 (file)
@@ -413,7 +413,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                strvec_push(&args, ps->items[i].original);
 
        setup_child_process(s, &cp, NULL);
-       cp.argv = args.v;
+       strvec_pushv(&cp.args, args.v);
        res = capture_command(&cp, plain, 0);
        if (res) {
                strvec_clear(&args);
@@ -431,7 +431,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 
                setup_child_process(s, &colored_cp, NULL);
                xsnprintf((char *)args.v[color_arg_index], 8, "--color");
-               colored_cp.argv = args.v;
+               strvec_pushv(&colored_cp.args, args.v);
                colored = &s->colored;
                res = capture_command(&colored_cp, colored, 0);
                strvec_clear(&args);
diff --git a/apply.c b/apply.c
index 43a0aebf4eec85fa6b0a69f655c0224aa14f29e5..7ffadc3b17a314095e213ad31ceda83a9915afb2 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -133,10 +133,10 @@ int check_apply_state(struct apply_state *state, int force_apply)
        int is_not_gitdir = !startup_info->have_repository;
 
        if (state->apply_with_reject && state->threeway)
-               return error(_("--reject and --3way cannot be used together."));
+               return error(_("options '%s' and '%s' cannot be used together"), "--reject", "--3way");
        if (state->threeway) {
                if (is_not_gitdir)
-                       return error(_("--3way outside a repository"));
+                       return error(_("'%s' outside a repository"), "--3way");
                state->check_index = 1;
        }
        if (state->apply_with_reject) {
@@ -147,10 +147,10 @@ int check_apply_state(struct apply_state *state, int force_apply)
        if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
                state->apply = 0;
        if (state->check_index && is_not_gitdir)
-               return error(_("--index outside a repository"));
+               return error(_("'%s' outside a repository"), "--index");
        if (state->cached) {
                if (is_not_gitdir)
-                       return error(_("--cached outside a repository"));
+                       return error(_("'%s' outside a repository"), "--cached");
                state->check_index = 1;
        }
        if (state->ita_only && (state->check_index || is_not_gitdir))
@@ -3582,7 +3582,9 @@ static int try_threeway(struct apply_state *state,
 
        /* No point falling back to 3-way merge in these cases */
        if (patch->is_delete ||
-           S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
+           S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode) ||
+           (patch->is_new && !patch->direct_to_threeway) ||
+           (patch->is_rename && !patch->lines_added && !patch->lines_deleted))
                return -1;
 
        /* Preimage the patch was prepared for */
@@ -4752,8 +4754,10 @@ static int apply_patch(struct apply_state *state,
        }
 
        if (!list && !skipped_patch) {
-               error(_("unrecognized input"));
-               res = -128;
+               if (!state->allow_empty) {
+                       error(_("No valid patches in input (allow with \"--allow-empty\")"));
+                       res = -128;
+               }
                goto end;
        }
 
@@ -5071,7 +5075,7 @@ int apply_parse_options(int argc, const char **argv,
                        N_("leave the rejected hunks in corresponding *.rej files")),
                OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
                        N_("allow overlapping hunks")),
-               OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")),
+               OPT__VERBOSITY(&state->apply_verbosity),
                OPT_BIT(0, "inaccurate-eof", options,
                        N_("tolerate incorrectly detected missing new-line at the end of file"),
                        APPLY_OPT_INACCURATE_EOF),
@@ -5081,6 +5085,8 @@ int apply_parse_options(int argc, const char **argv,
                OPT_CALLBACK(0, "directory", state, N_("root"),
                        N_("prepend <root> to all filenames"),
                        apply_option_parse_directory),
+               OPT_BOOL(0, "allow-empty", &state->allow_empty,
+                       N_("don't return error for empty patches")),
                OPT_END()
        };
 
diff --git a/apply.h b/apply.h
index da3d95fa50989bdd18289eec3a79b955c0d59443..16202da16026f8ef127e406418229d1f47802b53 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -66,6 +66,7 @@ struct apply_state {
        int threeway;
        int unidiff_zero;
        int unsafe_paths;
+       int allow_empty;
 
        /* Other non boolean parameters */
        struct repository *repo;
index 05d2455870d7fa95f2575a35a81c3cbe7363e625..3c74db174687d7c95abf2ef8f042ef2e49146b4e 100644 (file)
@@ -430,7 +430,6 @@ static int write_tar_filter_archive(const struct archiver *ar,
 {
        struct strbuf cmd = STRBUF_INIT;
        struct child_process filter = CHILD_PROCESS_INIT;
-       const char *argv[2];
        int r;
 
        if (!ar->data)
@@ -440,14 +439,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
        if (args->compression_level >= 0)
                strbuf_addf(&cmd, " -%d", args->compression_level);
 
-       argv[0] = cmd.buf;
-       argv[1] = NULL;
-       filter.argv = argv;
+       strvec_push(&filter.args, cmd.buf);
        filter.use_shell = 1;
        filter.in = -1;
 
        if (start_command(&filter) < 0)
-               die_errno(_("unable to start '%s' filter"), argv[0]);
+               die_errno(_("unable to start '%s' filter"), cmd.buf);
        close(1);
        if (dup2(filter.in, 1) < 0)
                die_errno(_("unable to redirect descriptor"));
@@ -457,7 +454,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
 
        close(1);
        if (finish_command(&filter) != 0)
-               die(_("'%s' filter reported error"), argv[0]);
+               die(_("'%s' filter reported error"), cmd.buf);
 
        strbuf_release(&cmd);
        return r;
index a3bbb091256dd7077e257f20f532d47516e4a960..d571249cf393396ca9815cf805f39b3d3d82c2d6 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -185,7 +185,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
 
        buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size);
        if (!buffer)
-               return error(_("cannot read %s"), oid_to_hex(oid));
+               return error(_("cannot read '%s'"), oid_to_hex(oid));
        err = write_entry(args, oid, path.buf, path.len, mode, buffer, size);
        free(buffer);
        return err;
@@ -338,7 +338,7 @@ int write_archive_entries(struct archiver_args *args,
 
                strbuf_reset(&content);
                if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
-                       err = error_errno(_("could not read '%s'"), path);
+                       err = error_errno(_("cannot read '%s'"), path);
                else
                        err = write_entry(args, &fake_oid, path_in_archive.buf,
                                          path_in_archive.len,
@@ -577,11 +577,11 @@ static int parse_archive_args(int argc, const char **argv,
        if (remote)
                die(_("Unexpected option --remote"));
        if (exec)
-               die(_("Option --exec can only be used together with --remote"));
+               die(_("the option '%s' requires '%s'"), "--exec", "--remote");
        if (output)
                die(_("Unexpected option --output"));
        if (is_remote && args->extra_files.nr)
-               die(_("Options --add-file and --remote cannot be used together"));
+               die(_("options '%s' and '%s' cannot be used together"), "--add-file", "--remote");
 
        if (!base)
                base = "";
index 07a46430b3846f0fa4595f9ea0f98c2523e8d5f1..5d20a2e8484b22f321a60e01d63cb4c4bd8e88fe 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -11,7 +11,7 @@
 
 struct tracking {
        struct refspec_item spec;
-       char *src;
+       struct string_list *srcs;
        const char *remote;
        int matches;
 };
@@ -22,11 +22,11 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 
        if (!remote_find_tracking(remote, &tracking->spec)) {
                if (++tracking->matches == 1) {
-                       tracking->src = tracking->spec.src;
+                       string_list_append(tracking->srcs, tracking->spec.src);
                        tracking->remote = remote->name;
                } else {
                        free(tracking->spec.src);
-                       FREE_AND_NULL(tracking->src);
+                       string_list_clear(tracking->srcs, 0);
                }
                tracking->spec.src = NULL;
        }
@@ -49,25 +49,46 @@ static int should_setup_rebase(const char *origin)
        return 0;
 }
 
-static const char tracking_advice[] =
-N_("\n"
-"After fixing the error cause you may try to fix up\n"
-"the remote tracking information by invoking\n"
-"\"git branch --set-upstream-to=%s%s%s\".");
-
-int install_branch_config(int flag, const char *local, const char *origin, const char *remote)
+/**
+ * Install upstream tracking configuration for a branch; specifically, add
+ * `branch.<name>.remote` and `branch.<name>.merge` entries.
+ *
+ * `flag` contains integer flags for options; currently only
+ * BRANCH_CONFIG_VERBOSE is checked.
+ *
+ * `local` is the name of the branch whose configuration we're installing.
+ *
+ * `origin` is the name of the remote owning the upstream branches. NULL means
+ * the upstream branches are local to this repo.
+ *
+ * `remotes` is a list of refs that are upstream of local
+ */
+static int install_branch_config_multiple_remotes(int flag, const char *local,
+               const char *origin, struct string_list *remotes)
 {
        const char *shortname = NULL;
        struct strbuf key = STRBUF_INIT;
+       struct string_list_item *item;
        int rebasing = should_setup_rebase(origin);
 
-       if (skip_prefix(remote, "refs/heads/", &shortname)
-           && !strcmp(local, shortname)
-           && !origin) {
-               warning(_("Not setting branch %s as its own upstream."),
-                       local);
-               return 0;
-       }
+       if (!remotes->nr)
+               BUG("must provide at least one remote for branch config");
+       if (rebasing && remotes->nr > 1)
+               die(_("cannot inherit upstream tracking configuration of "
+                     "multiple refs when rebasing is requested"));
+
+       /*
+        * If the new branch is trying to track itself, something has gone
+        * wrong. Warn the user and don't proceed any further.
+        */
+       if (!origin)
+               for_each_string_list_item(item, remotes)
+                       if (skip_prefix(item->string, "refs/heads/", &shortname)
+                           && !strcmp(local, shortname)) {
+                               warning(_("not setting branch '%s' as its own upstream"),
+                                       local);
+                               return 0;
+                       }
 
        strbuf_addf(&key, "branch.%s.remote", local);
        if (git_config_set_gently(key.buf, origin ? origin : ".") < 0)
@@ -75,8 +96,17 @@ int install_branch_config(int flag, const char *local, const char *origin, const
 
        strbuf_reset(&key);
        strbuf_addf(&key, "branch.%s.merge", local);
-       if (git_config_set_gently(key.buf, remote) < 0)
+       /*
+        * We want to overwrite any existing config with all the branches in
+        * "remotes". Override any existing config, then write our branches. If
+        * more than one is provided, use CONFIG_REGEX_NONE to preserve what
+        * we've written so far.
+        */
+       if (git_config_set_gently(key.buf, NULL) < 0)
                goto out_err;
+       for_each_string_list_item(item, remotes)
+               if (git_config_set_multivar_gently(key.buf, item->string, CONFIG_REGEX_NONE, 0) < 0)
+                       goto out_err;
 
        if (rebasing) {
                strbuf_reset(&key);
@@ -87,45 +117,106 @@ int install_branch_config(int flag, const char *local, const char *origin, const
        strbuf_release(&key);
 
        if (flag & BRANCH_CONFIG_VERBOSE) {
-               if (shortname) {
-                       if (origin)
-                               printf_ln(rebasing ?
-                                         _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
-                                         _("Branch '%s' set up to track remote branch '%s' from '%s'."),
-                                         local, shortname, origin);
-                       else
-                               printf_ln(rebasing ?
-                                         _("Branch '%s' set up to track local branch '%s' by rebasing.") :
-                                         _("Branch '%s' set up to track local branch '%s'."),
-                                         local, shortname);
+               struct strbuf tmp_ref_name = STRBUF_INIT;
+               struct string_list friendly_ref_names = STRING_LIST_INIT_DUP;
+
+               for_each_string_list_item(item, remotes) {
+                       shortname = item->string;
+                       skip_prefix(shortname, "refs/heads/", &shortname);
+                       if (origin) {
+                               strbuf_addf(&tmp_ref_name, "%s/%s",
+                                           origin, shortname);
+                               string_list_append_nodup(
+                                       &friendly_ref_names,
+                                       strbuf_detach(&tmp_ref_name, NULL));
+                       } else {
+                               string_list_append(
+                                       &friendly_ref_names, shortname);
+                       }
+               }
+
+               if (remotes->nr == 1) {
+                       /*
+                        * Rebasing is only allowed in the case of a single
+                        * upstream branch.
+                        */
+                       printf_ln(rebasing ?
+                               _("branch '%s' set up to track '%s' by rebasing.") :
+                               _("branch '%s' set up to track '%s'."),
+                               local, friendly_ref_names.items[0].string);
                } else {
-                       if (origin)
-                               printf_ln(rebasing ?
-                                         _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
-                                         _("Branch '%s' set up to track remote ref '%s'."),
-                                         local, remote);
-                       else
-                               printf_ln(rebasing ?
-                                         _("Branch '%s' set up to track local ref '%s' by rebasing.") :
-                                         _("Branch '%s' set up to track local ref '%s'."),
-                                         local, remote);
+                       printf_ln(_("branch '%s' set up to track:"), local);
+                       for_each_string_list_item(item, &friendly_ref_names)
+                               printf_ln("  %s", item->string);
                }
+
+               string_list_clear(&friendly_ref_names, 0);
        }
 
        return 0;
 
 out_err:
        strbuf_release(&key);
-       error(_("Unable to write upstream branch configuration"));
-
-       advise(_(tracking_advice),
-              origin ? origin : "",
-              origin ? "/" : "",
-              shortname ? shortname : remote);
+       error(_("unable to write upstream branch configuration"));
+
+       advise(_("\nAfter fixing the error cause you may try to fix up\n"
+               "the remote tracking information by invoking:"));
+       if (remotes->nr == 1)
+               advise("  git branch --set-upstream-to=%s%s%s",
+                       origin ? origin : "",
+                       origin ? "/" : "",
+                       remotes->items[0].string);
+       else {
+               advise("  git config --add branch.\"%s\".remote %s",
+                       local, origin ? origin : ".");
+               for_each_string_list_item(item, remotes)
+                       advise("  git config --add branch.\"%s\".merge %s",
+                               local, item->string);
+       }
 
        return -1;
 }
 
+int install_branch_config(int flag, const char *local, const char *origin,
+               const char *remote)
+{
+       int ret;
+       struct string_list remotes = STRING_LIST_INIT_DUP;
+
+       string_list_append(&remotes, remote);
+       ret = install_branch_config_multiple_remotes(flag, local, origin, &remotes);
+       string_list_clear(&remotes, 0);
+       return ret;
+}
+
+static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
+{
+       const char *bare_ref;
+       struct branch *branch;
+       int i;
+
+       bare_ref = orig_ref;
+       skip_prefix(orig_ref, "refs/heads/", &bare_ref);
+
+       branch = branch_get(bare_ref);
+       if (!branch->remote_name) {
+               warning(_("asked to inherit tracking from '%s', but no remote is set"),
+                       bare_ref);
+               return -1;
+       }
+
+       if (branch->merge_nr < 1 || !branch->merge_name || !branch->merge_name[0]) {
+               warning(_("asked to inherit tracking from '%s', but no merge configuration is set"),
+                       bare_ref);
+               return -1;
+       }
+
+       tracking->remote = xstrdup(branch->remote_name);
+       for (i = 0; i < branch->merge_nr; i++)
+               string_list_append(tracking->srcs, branch->merge_name[i]);
+       return 0;
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -135,11 +226,15 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
                           enum branch_track track, int quiet)
 {
        struct tracking tracking;
+       struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
        int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
 
        memset(&tracking, 0, sizeof(tracking));
        tracking.spec.dst = (char *)orig_ref;
-       if (for_each_remote(find_tracked_branch, &tracking))
+       tracking.srcs = &tracking_srcs;
+       if (track != BRANCH_TRACK_INHERIT)
+               for_each_remote(find_tracked_branch, &tracking);
+       else if (inherit_tracking(&tracking, orig_ref))
                return;
 
        if (!tracking.matches)
@@ -147,20 +242,23 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
                case BRANCH_TRACK_ALWAYS:
                case BRANCH_TRACK_EXPLICIT:
                case BRANCH_TRACK_OVERRIDE:
+               case BRANCH_TRACK_INHERIT:
                        break;
                default:
                        return;
                }
 
        if (tracking.matches > 1)
-               die(_("Not tracking: ambiguous information for ref %s"),
+               die(_("not tracking: ambiguous information for ref %s"),
                    orig_ref);
 
-       if (install_branch_config(config_flags, new_ref, tracking.remote,
-                             tracking.src ? tracking.src : orig_ref) < 0)
+       if (tracking.srcs->nr < 1)
+               string_list_append(tracking.srcs, orig_ref);
+       if (install_branch_config_multiple_remotes(config_flags, new_ref,
+                               tracking.remote, tracking.srcs) < 0)
                exit(-1);
 
-       free(tracking.src);
+       string_list_clear(tracking.srcs, 0);
 }
 
 int read_branch_desc(struct strbuf *buf, const char *branch_name)
@@ -186,7 +284,7 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
 int validate_branchname(const char *name, struct strbuf *ref)
 {
        if (strbuf_check_branch_ref(ref, name))
-               die(_("'%s' is not a valid branch name."), name);
+               die(_("'%s' is not a valid branch name"), name);
 
        return ref_exists(ref->buf);
 }
@@ -199,18 +297,23 @@ int validate_branchname(const char *name, struct strbuf *ref)
  */
 int validate_new_branchname(const char *name, struct strbuf *ref, int force)
 {
-       const char *head;
+       struct worktree **worktrees;
+       const struct worktree *wt;
 
        if (!validate_branchname(name, ref))
                return 0;
 
        if (!force)
-               die(_("A branch named '%s' already exists."),
+               die(_("a branch named '%s' already exists"),
                    ref->buf + strlen("refs/heads/"));
 
-       head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-       if (!is_bare_repository() && head && !strcmp(head, ref->buf))
-               die(_("Cannot force update the current branch."));
+       worktrees = get_worktrees();
+       wt = find_shared_symref(worktrees, "HEAD", ref->buf);
+       if (wt && !wt->is_bare)
+               die(_("cannot force update the branch '%s' "
+                     "checked out at '%s'"),
+                   ref->buf + strlen("refs/heads/"), wt->path);
+       free_worktrees(worktrees);
 
        return 1;
 }
@@ -230,7 +333,7 @@ static int validate_remote_tracking_branch(char *ref)
 }
 
 static const char upstream_not_branch[] =
-N_("Cannot setup tracking information; starting point '%s' is not a branch.");
+N_("cannot set up tracking information; starting point '%s' is not a branch");
 static const char upstream_missing[] =
 N_("the requested upstream branch '%s' does not exist");
 static const char upstream_advice[] =
@@ -278,7 +381,7 @@ void create_branch(struct repository *r,
                        }
                        die(_(upstream_missing), start_name);
                }
-               die(_("Not a valid object name: '%s'."), start_name);
+               die(_("not a valid object name: '%s'"), start_name);
        }
 
        switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
@@ -298,12 +401,12 @@ void create_branch(struct repository *r,
                }
                break;
        default:
-               die(_("Ambiguous object name: '%s'."), start_name);
+               die(_("ambiguous object name: '%s'"), start_name);
                break;
        }
 
        if ((commit = lookup_commit_reference(r, &oid)) == NULL)
-               die(_("Not a valid branch point: '%s'."), start_name);
+               die(_("not a valid branch point: '%s'"), start_name);
        oidcpy(&oid, &commit->object.oid);
 
        if (reflog)
@@ -357,14 +460,16 @@ void remove_branch_state(struct repository *r, int verbose)
 
 void die_if_checked_out(const char *branch, int ignore_current_worktree)
 {
+       struct worktree **worktrees = get_worktrees();
        const struct worktree *wt;
 
-       wt = find_shared_symref("HEAD", branch);
-       if (!wt || (ignore_current_worktree && wt->is_current))
-               return;
-       skip_prefix(branch, "refs/heads/", &branch);
-       die(_("'%s' is already checked out at '%s'"),
-           branch, wt->path);
+       wt = find_shared_symref(worktrees, "HEAD", branch);
+       if (wt && (!ignore_current_worktree || !wt->is_current)) {
+               skip_prefix(branch, "refs/heads/", &branch);
+               die(_("'%s' is already checked out at '%s'"), branch, wt->path);
+       }
+
+       free_worktrees(worktrees);
 }
 
 int replace_each_worktree_head_symref(const char *oldref, const char *newref,
index df0be61506fd36a0bfb63a911ba532b5db495767..815dcd40c0761104eba38215d04232e7f601ab1d 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -10,7 +10,8 @@ enum branch_track {
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
-       BRANCH_TRACK_OVERRIDE
+       BRANCH_TRACK_OVERRIDE,
+       BRANCH_TRACK_INHERIT,
 };
 
 extern enum branch_track git_branch_track;
index ef6b619c45ed9e44266a04f425f6523d4d1da657..84dff3e796918ada3777fd7c01114ff5c9521fec 100644 (file)
@@ -302,15 +302,11 @@ int interactive_add(const char **argv, const char *prefix, int patch)
 static int edit_patch(int argc, const char **argv, const char *prefix)
 {
        char *file = git_pathdup("ADD_EDIT.patch");
-       const char *apply_argv[] = { "apply", "--recount", "--cached",
-               NULL, NULL };
        struct child_process child = CHILD_PROCESS_INIT;
        struct rev_info rev;
        int out;
        struct stat st;
 
-       apply_argv[3] = file;
-
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
        if (read_cache() < 0)
@@ -338,7 +334,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
                die(_("Empty patch. Aborted."));
 
        child.git_cmd = 1;
-       child.argv = apply_argv;
+       strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
+                    NULL);
        if (run_command(&child))
                die(_("Could not apply '%s'"), file);
 
@@ -510,9 +507,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                add_interactive = 1;
        if (add_interactive) {
                if (show_only)
-                       die(_("--dry-run is incompatible with --interactive/--patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
                if (pathspec_from_file)
-                       die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
                exit(interactive_add(argv + 1, prefix, patch_interactive));
        }
        if (legacy_stash_p) {
@@ -529,7 +526,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (edit_interactive) {
                if (pathspec_from_file)
-                       die(_("--pathspec-from-file is incompatible with --edit"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--edit");
                return(edit_patch(argc, argv, prefix));
        }
        argc--;
@@ -541,10 +538,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                addremove = 0; /* "-u" was given but not "-A" */
 
        if (addremove && take_worktree_changes)
-               die(_("-A and -u are mutually incompatible"));
+               die(_("options '%s' and '%s' cannot be used together"), "-A", "-u");
 
        if (!show_only && ignore_missing)
-               die(_("Option --ignore-missing can only be used together with --dry-run"));
+               die(_("the option '%s' requires '%s'"), "--ignore-missing", "--dry-run");
 
        if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
                          chmod_arg[1] != 'x' || chmod_arg[2]))
@@ -569,14 +566,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (pathspec_from_file) {
                if (pathspec.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
                                    PATHSPEC_PREFER_FULL |
                                    PATHSPEC_SYMLINK_LEADING_PATH,
                                    prefix, pathspec_from_file, pathspec_file_nul);
        } else if (pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        if (require_pathspec && pathspec.nr == 0) {
index 8677ea2348ab5b8017fc82cd1f8b79e4a7d20122..b6be1f1cb11e47dcb1012e1afe60fbae654f25cd 100644 (file)
@@ -87,6 +87,12 @@ enum show_patch_type {
        SHOW_PATCH_DIFF = 1,
 };
 
+enum empty_action {
+       STOP_ON_EMPTY_COMMIT = 0,  /* output errors and stop in the middle of an am session */
+       DROP_EMPTY_COMMIT,         /* skip with a notice message, unless "--quiet" has been passed */
+       KEEP_EMPTY_COMMIT,         /* keep recording as empty commits */
+};
+
 struct am_state {
        /* state directory path */
        char *dir;
@@ -118,6 +124,7 @@ struct am_state {
        int message_id;
        int scissors; /* enum scissors_type */
        int quoted_cr; /* enum quoted_cr_action */
+       int empty_type; /* enum empty_action */
        struct strvec git_apply_opts;
        const char *resolvemsg;
        int committer_date_is_author_date;
@@ -178,6 +185,25 @@ static int am_option_parse_quoted_cr(const struct option *opt,
        return 0;
 }
 
+static int am_option_parse_empty(const struct option *opt,
+                                    const char *arg, int unset)
+{
+       int *opt_value = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+
+       if (!strcmp(arg, "stop"))
+               *opt_value = STOP_ON_EMPTY_COMMIT;
+       else if (!strcmp(arg, "drop"))
+               *opt_value = DROP_EMPTY_COMMIT;
+       else if (!strcmp(arg, "keep"))
+               *opt_value = KEEP_EMPTY_COMMIT;
+       else
+               return error(_("Invalid value for --empty: %s"), arg);
+
+       return 0;
+}
+
 /**
  * Returns path relative to the am_state directory.
  */
@@ -1126,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
 
                printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
                printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+
+               if (advice_enabled(ADVICE_AM_WORK_DIR) &&
+                   is_empty_or_missing_file(am_path(state, "patch")) &&
+                   !repo_index_has_changes(the_repository, NULL, NULL))
+                       printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+
                printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
        }
 
@@ -1248,11 +1280,6 @@ static int parse_mail(struct am_state *state, const char *mail)
                goto finish;
        }
 
-       if (is_empty_or_missing_file(am_path(state, "patch"))) {
-               printf_ln(_("Patch is empty."));
-               die_user_resolve(state);
-       }
-
        strbuf_addstr(&msg, "\n\n");
        strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
@@ -1763,6 +1790,7 @@ static void am_run(struct am_state *state, int resume)
        while (state->cur <= state->last) {
                const char *mail = am_path(state, msgnum(state));
                int apply_status;
+               int to_keep;
 
                reset_ident_date();
 
@@ -1792,8 +1820,29 @@ static void am_run(struct am_state *state, int resume)
                if (state->interactive && do_interactive(state))
                        goto next;
 
+               to_keep = 0;
+               if (is_empty_or_missing_file(am_path(state, "patch"))) {
+                       switch (state->empty_type) {
+                       case DROP_EMPTY_COMMIT:
+                               say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+                               goto next;
+                               break;
+                       case KEEP_EMPTY_COMMIT:
+                               to_keep = 1;
+                               say(state, stdout, _("Creating an empty commit: %.*s"),
+                                       linelen(state->msg), state->msg);
+                               break;
+                       case STOP_ON_EMPTY_COMMIT:
+                               printf_ln(_("Patch is empty."));
+                               die_user_resolve(state);
+                               break;
+                       }
+               }
+
                if (run_applypatch_msg_hook(state))
                        exit(1);
+               if (to_keep)
+                       goto commit;
 
                say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
@@ -1827,6 +1876,7 @@ static void am_run(struct am_state *state, int resume)
                        die_user_resolve(state);
                }
 
+commit:
                do_commit(state);
 
 next:
@@ -1856,19 +1906,24 @@ next:
 /**
  * Resume the current am session after patch application failure. The user did
  * all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
  */
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
 {
        validate_resume_state(state);
 
        say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
 
        if (!repo_index_has_changes(the_repository, NULL, NULL)) {
-               printf_ln(_("No changes - did you forget to use 'git add'?\n"
-                       "If there is nothing left to stage, chances are that something else\n"
-                       "already introduced the same changes; you might want to skip this patch."));
-               die_user_resolve(state);
+               if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
+                       printf_ln(_("No changes - recorded it as an empty commit."));
+               } else {
+                       printf_ln(_("No changes - did you forget to use 'git add'?\n"
+                                   "If there is nothing left to stage, chances are that something else\n"
+                                   "already introduced the same changes; you might want to skip this patch."));
+                       die_user_resolve(state);
+               }
        }
 
        if (unmerged_cache()) {
@@ -2195,7 +2250,8 @@ enum resume_type {
        RESUME_SKIP,
        RESUME_ABORT,
        RESUME_QUIT,
-       RESUME_SHOW_PATCH
+       RESUME_SHOW_PATCH,
+       RESUME_ALLOW_EMPTY,
 };
 
 struct resume_mode {
@@ -2230,9 +2286,9 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar
        }
 
        if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
-               return error(_("--show-current-patch=%s is incompatible with "
-                              "--show-current-patch=%s"),
-                            arg, valid_modes[resume->sub_mode]);
+               return error(_("options '%s=%s' and '%s=%s' "
+                                          "cannot be used together"),
+                                        "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]);
 
        resume->mode = RESUME_SHOW_PATCH;
        resume->sub_mode = new_value;
@@ -2348,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                  N_("show the patch being applied"),
                  PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
                  parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+               OPT_CMDMODE(0, "allow-empty", &resume.mode,
+                       N_("record the empty patch as an empty commit"),
+                       RESUME_ALLOW_EMPTY),
                OPT_BOOL(0, "committer-date-is-author-date",
                        &state.committer_date_is_author_date,
                        N_("lie about committer date")),
@@ -2357,6 +2416,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
                  N_("GPG-sign commits"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+                 N_("how to handle empty patches"),
+                 PARSE_OPT_NONEG, am_option_parse_empty),
                OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
                        N_("(internal use for git-rebase)")),
                OPT_END()
@@ -2453,7 +2515,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                am_run(&state, 1);
                break;
        case RESUME_RESOLVED:
-               am_resolve(&state);
+       case RESUME_ALLOW_EMPTY:
+               am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
                break;
        case RESUME_SKIP:
                am_skip(&state);
index f9ee3f8c688d472804f63d0702e92747ab39067f..7fafeac408141bb89e5c69512c3a7f174729e4dd 100644 (file)
@@ -939,6 +939,9 @@ parse_done:
        revs.diffopt.flags.follow_renames = 0;
        argc = parse_options_end(&ctx);
 
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
        if (incremental || (output_option & OUTPUT_PORCELAIN)) {
                if (show_progress > 0)
                        die(_("--progress can't be used with --incremental or porcelain formats"));
index 81b5c111cb7b4b5e2700e963dd41678e2dafc493..2251e6a54f01f4665ccf5fa970e68d63ba2c4a10 100644 (file)
@@ -192,6 +192,7 @@ static void delete_branch_config(const char *branchname)
 static int delete_branches(int argc, const char **argv, int force, int kinds,
                           int quiet)
 {
+       struct worktree **worktrees;
        struct commit *head_rev = NULL;
        struct object_id oid;
        char *name = NULL;
@@ -228,6 +229,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                if (!head_rev)
                        die(_("Couldn't look up commit object for HEAD"));
        }
+
+       worktrees = get_worktrees();
+
        for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
                char *target = NULL;
                int flags = 0;
@@ -238,7 +242,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 
                if (kinds == FILTER_REFS_BRANCHES) {
                        const struct worktree *wt =
-                               find_shared_symref("HEAD", name);
+                               find_shared_symref(worktrees, "HEAD", name);
                        if (wt) {
                                error(_("Cannot delete branch '%s' "
                                        "checked out at '%s'"),
@@ -299,6 +303,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 
        free(name);
        strbuf_release(&bname);
+       free_worktrees(worktrees);
 
        return ret;
 }
@@ -633,8 +638,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&filter.verbose,
                        N_("show hash and subject, give twice for upstream branch")),
                OPT__QUIET(&quiet, N_("suppress informational messages")),
-               OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"),
-                       BRANCH_TRACK_EXPLICIT),
+               OPT_CALLBACK_F('t', "track",  &track, "direct|inherit",
+                       N_("set branch tracking configuration"),
+                       PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
+                       parse_opt_tracking_mode),
                OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
                        BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
                OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
@@ -717,7 +724,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        finalize_colopts(&colopts, -1);
        if (filter.verbose) {
                if (explicitly_enable_column(colopts))
-                       die(_("--column and --verbose are incompatible"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--column", "--verbose");
                colopts = 0;
        }
 
index 86fc03242b87c36784bb31a88eb21f6c727da893..d94050e6c188ff4594a065da87695c67c560ac94 100644 (file)
@@ -729,7 +729,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        }
 
        if (force_path && batch.enabled) {
-               error("--path=<path> incompatible with --batch");
+               error("options '--path=<path>' and '--batch' cannot be used together");
                usage_with_options(cat_file_usage, options);
        }
 
index 3fe87a632e23fe84d2eb6abafb2e44fcc08090c2..94814c37b4321ca891927d9b325ee6806c494595 100644 (file)
@@ -464,10 +464,10 @@ static int checkout_paths(const struct checkout_opts *opts,
                die(_("'%s' cannot be used with updating paths"), "--detach");
 
        if (opts->merge && opts->patch_mode)
-               die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+               die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch");
 
        if (opts->ignore_unmerged && opts->merge)
-               die(_("'%s' cannot be used with %s"),
+               die(_("options '%s' and '%s' cannot be used together"),
                    opts->ignore_unmerged_opt, "-m");
 
        if (opts->new_branch)
@@ -1536,7 +1536,7 @@ static struct option *add_common_options(struct checkout_opts *opts,
                OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
                OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
                OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
-                          N_("conflict style (merge or diff3)")),
+                          N_("conflict style (merge, diff3, or zdiff3)")),
                OPT_END()
        };
        struct option *newopts = parse_options_concat(prevopts, options);
@@ -1549,8 +1549,10 @@ static struct option *add_common_switch_branch_options(
 {
        struct option options[] = {
                OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
-               OPT_SET_INT('t', "track",  &opts->track, N_("set upstream info for new branch"),
-                       BRANCH_TRACK_EXPLICIT),
+               OPT_CALLBACK_F('t', "track",  &opts->track, "direct|inherit",
+                       N_("set up tracking mode (see git-pull(1))"),
+                       PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
+                       parse_opt_tracking_mode),
                OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
                           PARSE_OPT_NOCOMPLETE),
                OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
@@ -1635,11 +1637,11 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        }
 
        if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
-               die(_("-%c, -%c and --orphan are mutually exclusive"),
-                               cb_option, toupper(cb_option));
+               die(_("options '-%c', '-%c', and '%s' cannot be used together"),
+                       cb_option, toupper(cb_option), "--orphan");
 
        if (opts->overlay_mode == 1 && opts->patch_mode)
-               die(_("-p and --overlay are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "-p", "--overlay");
 
        if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
                if (opts->checkout_index < 0)
@@ -1746,19 +1748,19 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 
        if (opts->pathspec_from_file) {
                if (opts->pathspec.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                if (opts->force_detach)
-                       die(_("--pathspec-from-file is incompatible with --detach"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--detach");
 
                if (opts->patch_mode)
-                       die(_("--pathspec-from-file is incompatible with --patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
 
                parse_pathspec_file(&opts->pathspec, 0,
                                    0,
                                    prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
        } else if (opts->pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        opts->pathspec.recursive = 1;
index 98a2860409bb4820af393ea9e50e58124fd7873e..3ff02bbbffeb7ed041b8a9af7f0b6f6d26c88492 100644 (file)
@@ -36,6 +36,8 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
 static const char *msg_warn_remove_failed = N_("failed to remove %s");
 static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
+static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
+static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
 
 enum color_clean {
        CLEAN_COLOR_RESET = 0,
@@ -153,6 +155,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
 {
        DIR *dir;
        struct strbuf quoted = STRBUF_INIT;
+       struct strbuf realpath = STRBUF_INIT;
+       struct strbuf real_ocwd = STRBUF_INIT;
        struct dirent *e;
        int res = 0, ret = 0, gone = 1, original_len = path->len, len;
        struct string_list dels = STRING_LIST_INIT_DUP;
@@ -231,16 +235,36 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        strbuf_setlen(path, original_len);
 
        if (*dir_gone) {
-               res = dry_run ? 0 : rmdir(path->buf);
-               if (!res)
-                       *dir_gone = 1;
-               else {
-                       int saved_errno = errno;
-                       quote_path(path->buf, prefix, &quoted, 0);
-                       errno = saved_errno;
-                       warning_errno(_(msg_warn_remove_failed), quoted.buf);
+               /*
+                * Normalize path components in path->buf, e.g. change '\' to
+                * '/' on Windows.
+                */
+               strbuf_realpath(&realpath, path->buf, 1);
+
+               /*
+                * path and realpath are absolute; for comparison, we would
+                * like to transform startup_info->original_cwd to an absolute
+                * path too.
+                */
+                if (startup_info->original_cwd)
+                        strbuf_realpath(&real_ocwd,
+                                        startup_info->original_cwd, 1);
+
+               if (!strbuf_cmp(&realpath, &real_ocwd)) {
+                       printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
                        *dir_gone = 0;
-                       ret = 1;
+               } else {
+                       res = dry_run ? 0 : rmdir(path->buf);
+                       if (!res)
+                               *dir_gone = 1;
+                       else {
+                               int saved_errno = errno;
+                               quote_path(path->buf, prefix, &quoted, 0);
+                               errno = saved_errno;
+                               warning_errno(_(msg_warn_remove_failed), quoted.buf);
+                               *dir_gone = 0;
+                               ret = 1;
+                       }
                }
        }
 
@@ -250,6 +274,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                        printf(dry_run ?  _(msg_would_remove) : _(msg_remove), dels.items[i].string);
        }
 out:
+       strbuf_release(&realpath);
+       strbuf_release(&real_ocwd);
        strbuf_release(&quoted);
        string_list_clear(&dels, 0);
        return ret;
index fb377b27657c4048a04c85ddb82e15a950db5a7d..727e16e0aea435a1fe244c381d8bb7670e8e7a5a 100644 (file)
@@ -633,7 +633,7 @@ static int git_sparse_checkout_init(const char *repo)
 {
        struct strvec argv = STRVEC_INIT;
        int result = 0;
-       strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+       strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
 
        /*
         * We must apply the setting in the current process
@@ -900,10 +900,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        if (option_bare) {
                if (option_origin)
-                       die(_("--bare and --origin %s options are incompatible."),
-                           option_origin);
+                       die(_("options '%s' and '%s %s' cannot be used together"),
+                           "--bare", "--origin", option_origin);
                if (real_git_dir)
-                       die(_("--bare and --separate-git-dir are incompatible."));
+                       die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir");
                option_no_checkout = 1;
        }
 
@@ -1290,7 +1290,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
         */
        submodule_progress = transport->progress;
 
-       transport_unlock_pack(transport);
+       transport_unlock_pack(transport, 0);
        transport_disconnect(transport);
 
        if (option_dissociate) {
index 883c16256c87f75c8367573ba42a8ee8e04346a7..b9ed0374e301ae958906435fa6592e9d3bbe52fa 100644 (file)
@@ -355,19 +355,19 @@ static const char *prepare_index(const char **argv, const char *prefix,
 
        if (pathspec_from_file) {
                if (interactive)
-                       die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
 
                if (all)
-                       die(_("--pathspec-from-file with -a does not make sense"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "-a");
 
                if (pathspec.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                parse_pathspec_file(&pathspec, 0,
                                    PATHSPEC_PREFER_FULL,
                                    prefix, pathspec_from_file, pathspec_file_nul);
        } else if (pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        if (!pathspec.nr && (also || (only && !allow_empty &&
@@ -799,7 +799,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
                if (!strcmp(fixup_prefix, "amend")) {
                        if (have_option_m)
-                               die(_("cannot combine -m with --fixup:%s"), fixup_message);
+                               die(_("options '%s' and '%s:%s' cannot be used together"), "-m", "--fixup", fixup_message);
                        prepare_amend_commit(commit, &sb, &ctx);
                }
        } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
@@ -1193,7 +1193,7 @@ static void finalize_deferred_config(struct wt_status *s)
                    status_format == STATUS_FORMAT_UNSPECIFIED)
                        status_format = STATUS_FORMAT_PORCELAIN;
                else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--long", "-z");
        }
 
        if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
@@ -1229,9 +1229,10 @@ static void check_fixup_reword_options(int argc, const char *argv[]) {
                        die(_("You are in the middle of a cherry-pick -- cannot reword."));
        }
        if (argc)
-               die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
+               die(_("reword option of '%s' and path '%s' cannot be used together"), "--fixup", *argv);
        if (patch_interactive || interactive || all || also || only)
-               die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
+               die(_("reword option of '%s' and '%s' cannot be used together"),
+                       "--fixup", "--patch/--interactive/--all/--include/--only");
 }
 
 static int parse_and_validate_options(int argc, const char *argv[],
index e912ba50d7bac4fbd562774e00cf454e17651baf..42159cd26bd80797ab082d4d2509cf17ec8fc113 100644 (file)
@@ -590,7 +590,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
 
        if (longformat && abbrev == 0)
-               die(_("--long is incompatible with --abbrev=0"));
+               die(_("options '%s' and '%s' cannot be used together"), "--long", "--abbrev=0");
 
        if (contains) {
                struct string_list_item *item;
@@ -670,9 +670,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                }
                describe("HEAD", 1);
        } else if (dirty) {
-               die(_("--dirty is incompatible with commit-ishes"));
+               die(_("option '%s' and commit-ishes cannot be used together"), "--dirty");
        } else if (broken) {
-               die(_("--broken is incompatible with commit-ishes"));
+               die(_("option '%s' and commit-ishes cannot be used together"), "--broken");
        } else {
                while (argc-- > 0)
                        describe(*argv++, argc == 0);
index f33d30d57bff2ef8923ea9649a5afd4d49eb0574..0e0ac1f1670f3b21f01c4fbbc602c53392331a9c 100644 (file)
@@ -152,7 +152,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        }
 
        if (read_stdin && merge_base)
-               die(_("--stdin and --merge-base are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base");
        if (merge_base && opt->pending.nr != 2)
                die(_("--merge-base only works with two commits"));
 
index dd8ce688ba7054e7ee76ca4764b1a17b84a3044a..fa4683377ebbe51ee42d964d29a30011f12f1261 100644 (file)
@@ -437,6 +437,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 
        prefix = setup_git_directory_gently(&nongit);
 
+       if (!nongit) {
+               prepare_repo_settings(the_repository);
+               the_repository->settings.command_requires_full_index = 0;
+       }
+
        if (!no_index) {
                /*
                 * Treat git diff with at least one path outside of the
index 4931c108451721ebd49a3009adb431dbe41eb662..c79fbbf67e5ee0d4e7d09c7997f1fa04dc1400bc 100644 (file)
@@ -202,15 +202,10 @@ static void changed_files(struct hashmap *result, const char *index_path,
 {
        struct child_process update_index = CHILD_PROCESS_INIT;
        struct child_process diff_files = CHILD_PROCESS_INIT;
-       struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
-       const char *git_dir = absolute_path(get_git_dir()), *env[] = {
-               NULL, NULL
-       };
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir = absolute_path(get_git_dir());
        FILE *fp;
 
-       strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
-       env[0] = index_env.buf;
-
        strvec_pushl(&update_index.args,
                     "--git-dir", git_dir, "--work-tree", workdir,
                     "update-index", "--really-refresh", "-q",
@@ -222,7 +217,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
        update_index.use_shell = 0;
        update_index.clean_on_exit = 1;
        update_index.dir = workdir;
-       update_index.env = env;
+       strvec_pushf(&update_index.env_array, "GIT_INDEX_FILE=%s", index_path);
        /* Ignore any errors of update-index */
        run_command(&update_index);
 
@@ -235,7 +230,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
        diff_files.clean_on_exit = 1;
        diff_files.out = -1;
        diff_files.dir = workdir;
-       diff_files.env = env;
+       strvec_pushf(&diff_files.env_array, "GIT_INDEX_FILE=%s", index_path);
        if (start_command(&diff_files))
                die("could not obtain raw diff");
        fp = xfdopen(diff_files.out, "r");
@@ -248,7 +243,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
        fclose(fp);
        if (finish_command(&diff_files))
                die("diff-files did not exit properly");
-       strbuf_release(&index_env);
        strbuf_release(&buf);
 }
 
@@ -736,10 +730,10 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
                setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
                setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
        } else if (dir_diff)
-               die(_("--dir-diff is incompatible with --no-index"));
+               die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
 
        if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
-               die(_("--gui, --tool and --extcmd are mutually exclusive"));
+               die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd");
 
        if (use_gui_tool)
                setenv("GIT_MERGETOOL_GUI", "true", 1);
index 8e2caf7281970cfb1bd3cf3bad07b488e61b6f07..9f1c730e5876d5a0a8d6e015d8a9978b90e890e6 100644 (file)
@@ -107,18 +107,6 @@ static int parse_opt_reencode_mode(const struct option *opt,
 
 static struct decoration idnums;
 static uint32_t last_idnum;
-
-static int has_unshown_parent(struct commit *commit)
-{
-       struct commit_list *parent;
-
-       for (parent = commit->parents; parent; parent = parent->next)
-               if (!(parent->item->object.flags & SHOWN) &&
-                   !(parent->item->object.flags & UNINTERESTING))
-                       return 1;
-       return 0;
-}
-
 struct anonymized_entry {
        struct hashmap_entry hash;
        const char *anon;
@@ -752,20 +740,6 @@ static char *anonymize_tag(void *data)
        return strbuf_detach(&out, NULL);
 }
 
-static void handle_tail(struct object_array *commits, struct rev_info *revs,
-                       struct string_list *paths_of_changed_objects)
-{
-       struct commit *commit;
-       while (commits->nr) {
-               commit = (struct commit *)object_array_pop(commits);
-               if (has_unshown_parent(commit)) {
-                       /* Queue again, to be handled later */
-                       add_object_array(&commit->object, NULL, commits);
-                       return;
-               }
-               handle_commit(commit, revs, paths_of_changed_objects);
-       }
-}
 
 static void handle_tag(const char *name, struct tag *tag)
 {
@@ -1185,7 +1159,6 @@ static int parse_opt_anonymize_map(const struct option *opt,
 int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
-       struct object_array commits = OBJECT_ARRAY_INIT;
        struct commit *commit;
        char *export_filename = NULL,
             *import_filename = NULL,
@@ -1254,7 +1227,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                usage_with_options (fast_export_usage, options);
 
        if (anonymized_seeds.cmpfn && !anonymize)
-               die(_("--anonymize-map without --anonymize does not make sense"));
+               die(_("the option '%s' requires '%s'"), "--anonymize-map", "--anonymize");
 
        if (refspecs_list.nr) {
                int i;
@@ -1269,7 +1242,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                printf("feature done\n");
 
        if (import_filename && import_filename_if_exists)
-               die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
+               die(_("options '%s' and '%s' cannot be used together"), "--import-marks", "--import-marks-if-exists");
        if (import_filename)
                import_marks(import_filename, 0);
        else if (import_filename_if_exists)
@@ -1283,18 +1256,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
+
+       revs.reverse = 1;
        revs.diffopt.format_callback = show_filemodify;
        revs.diffopt.format_callback_data = &paths_of_changed_objects;
        revs.diffopt.flags.recursive = 1;
-       while ((commit = get_revision(&revs))) {
-               if (has_unshown_parent(commit)) {
-                       add_object_array(&commit->object, NULL, &commits);
-               }
-               else {
-                       handle_commit(commit, &revs, &paths_of_changed_objects);
-                       handle_tail(&commits, &revs, &paths_of_changed_objects);
-               }
-       }
+       while ((commit = get_revision(&revs)))
+               handle_commit(commit, &revs, &paths_of_changed_objects);
 
        handle_tags_and_duplicates(&extra_refs);
        handle_tags_and_duplicates(&tag_refs);
index 20406f677542c0c1e34b623fc3f4b2b349700525..2b2e28bad79cbaef25b4eb8fea4c0f800ba4a89d 100644 (file)
@@ -401,16 +401,18 @@ static void dump_marks(void);
 
 static NORETURN void die_nicely(const char *err, va_list params)
 {
+       va_list cp;
        static int zombie;
-       char message[2 * PATH_MAX];
+       report_fn die_message_fn = get_die_message_routine();
 
-       vsnprintf(message, sizeof(message), err, params);
-       fputs("fatal: ", stderr);
-       fputs(message, stderr);
-       fputc('\n', stderr);
+       va_copy(cp, params);
+       die_message_fn(err, params);
 
        if (!zombie) {
+               char message[2 * PATH_MAX];
+
                zombie = 1;
+               vsnprintf(message, sizeof(message), err, cp);
                write_crash_report(message);
                end_packfile();
                unkeep_all_packs();
index f7abbc31ff1414d46ff87cbace917b96181dac13..5f06b21f8e97c5459fdb558302c5b9b95e99eee8 100644 (file)
@@ -28,6 +28,7 @@
 #include "promisor-remote.h"
 #include "commit-graph.h"
 #include "shallow.h"
+#include "worktree.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 
@@ -222,17 +223,22 @@ static struct option builtin_fetch_options[] = {
        OPT_END()
 };
 
-static void unlock_pack(void)
+static void unlock_pack(unsigned int flags)
 {
        if (gtransport)
-               transport_unlock_pack(gtransport);
+               transport_unlock_pack(gtransport, flags);
        if (gsecondary)
-               transport_unlock_pack(gsecondary);
+               transport_unlock_pack(gsecondary, flags);
+}
+
+static void unlock_pack_atexit(void)
+{
+       unlock_pack(0);
 }
 
 static void unlock_pack_on_signal(int signo)
 {
-       unlock_pack();
+       unlock_pack(TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
        sigchain_pop(signo);
        raise(signo);
 }
@@ -552,7 +558,7 @@ static struct ref *get_ref_map(struct remote *remote,
                for (i = 0; i < fetch_refspec->nr; i++)
                        get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
        } else if (refmap.nr) {
-               die("--refmap option is only meaningful with command-line refspec(s).");
+               die("--refmap option is only meaningful with command-line refspec(s)");
        } else {
                /* Use the defaults */
                struct branch *branch = branch_get(NULL);
@@ -583,7 +589,7 @@ static struct ref *get_ref_map(struct remote *remote,
                } else if (!prefetch) {
                        ref_map = get_remote_ref(remote_refs, "HEAD");
                        if (!ref_map)
-                               die(_("Couldn't find remote ref HEAD"));
+                               die(_("couldn't find remote ref HEAD"));
                        ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        tail = &ref_map->next;
                }
@@ -848,13 +854,12 @@ static void format_display(struct strbuf *display, char code,
 
 static int update_local_ref(struct ref *ref,
                            struct ref_transaction *transaction,
-                           const char *remote,
-                           const struct ref *remote_ref,
-                           struct strbuf *display,
-                           int summary_width)
+                           const char *remote, const struct ref *remote_ref,
+                           struct strbuf *display, int summary_width,
+                           struct worktree **worktrees)
 {
        struct commit *current = NULL, *updated;
-       struct branch *current_branch = branch_get(NULL);
+       const struct worktree *wt;
        const char *pretty_ref = prettify_refname(ref->name);
        int fast_forward = 0;
 
@@ -868,16 +873,17 @@ static int update_local_ref(struct ref *ref,
                return 0;
        }
 
-       if (current_branch &&
-           !strcmp(ref->name, current_branch->name) &&
-           !(update_head_ok || is_bare_repository()) &&
-           !is_null_oid(&ref->old_oid)) {
+       if (!update_head_ok &&
+           (wt = find_shared_symref(worktrees, "HEAD", ref->name)) &&
+           !wt->is_bare && !is_null_oid(&ref->old_oid)) {
                /*
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
                 */
                format_display(display, '!', _("[rejected]"),
-                              _("can't fetch in current branch"),
+                              wt->is_current ?
+                                      _("can't fetch in current branch") :
+                                      _("checked out in another worktree"),
                               remote, pretty_ref, summary_width);
                return 1;
        }
@@ -995,7 +1001,7 @@ static int open_fetch_head(struct fetch_head *fetch_head)
        if (write_fetch_head) {
                fetch_head->fp = fopen(filename, "a");
                if (!fetch_head->fp)
-                       return error_errno(_("cannot open %s"), filename);
+                       return error_errno(_("cannot open '%s'"), filename);
                strbuf_init(&fetch_head->buf, 0);
        } else {
                fetch_head->fp = NULL;
@@ -1067,16 +1073,17 @@ static void close_fetch_head(struct fetch_head *fetch_head)
 }
 
 static const char warn_show_forced_updates[] =
-N_("Fetch normally indicates which branches had a forced update,\n"
-   "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
-   "flag or run 'git config fetch.showForcedUpdates true'.");
+N_("fetch normally indicates which branches had a forced update,\n"
+   "but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+   "flag or run 'git config fetch.showForcedUpdates true'");
 static const char warn_time_show_forced_updates[] =
-N_("It took %.2f seconds to check forced updates. You can use\n"
+N_("it took %.2f seconds to check forced updates; you can use\n"
    "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
-   " to avoid this check.\n");
+   "to avoid this check\n");
 
 static int store_updated_refs(const char *raw_url, const char *remote_name,
-                             int connectivity_checked, struct ref *ref_map)
+                             int connectivity_checked, struct ref *ref_map,
+                             struct worktree **worktrees)
 {
        struct fetch_head fetch_head;
        int url_len, i, rc = 0;
@@ -1205,7 +1212,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        strbuf_reset(&note);
                        if (ref) {
                                rc |= update_local_ref(ref, transaction, what,
-                                                      rm, &note, summary_width);
+                                                      rm, &note, summary_width,
+                                                      worktrees);
                                free(ref);
                        } else if (write_fetch_head || dry_run) {
                                /*
@@ -1298,7 +1306,9 @@ static int check_exist_and_connected(struct ref *ref_map)
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_and_consume_refs(struct transport *transport,
+                                 struct ref *ref_map,
+                                 struct worktree **worktrees)
 {
        int connectivity_checked = 1;
        int ret;
@@ -1319,14 +1329,12 @@ static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_m
        }
 
        trace2_region_enter("fetch", "consume_refs", the_repository);
-       ret = store_updated_refs(transport->url,
-                                transport->remote->name,
-                                connectivity_checked,
-                                ref_map);
+       ret = store_updated_refs(transport->url, transport->remote->name,
+                                connectivity_checked, ref_map, worktrees);
        trace2_region_leave("fetch", "consume_refs", the_repository);
 
 out:
-       transport_unlock_pack(transport);
+       transport_unlock_pack(transport, 0);
        return ret;
 }
 
@@ -1385,18 +1393,18 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
        return result;
 }
 
-static void check_not_current_branch(struct ref *ref_map)
+static void check_not_current_branch(struct ref *ref_map,
+                                    struct worktree **worktrees)
 {
-       struct branch *current_branch = branch_get(NULL);
-
-       if (is_bare_repository() || !current_branch)
-               return;
-
+       const struct worktree *wt;
        for (; ref_map; ref_map = ref_map->next)
-               if (ref_map->peer_ref && !strcmp(current_branch->refname,
-                                       ref_map->peer_ref->name))
-                       die(_("Refusing to fetch into current branch %s "
-                           "of non-bare repository"), current_branch->refname);
+               if (ref_map->peer_ref &&
+                   (wt = find_shared_symref(worktrees, "HEAD",
+                                            ref_map->peer_ref->name)) &&
+                   !wt->is_bare)
+                       die(_("refusing to fetch into branch '%s' "
+                             "checked out at '%s'"),
+                           ref_map->peer_ref->name, wt->path);
 }
 
 static int truncate_fetch_head(void)
@@ -1405,7 +1413,7 @@ static int truncate_fetch_head(void)
        FILE *fp = fopen_for_writing(filename);
 
        if (!fp)
-               return error_errno(_("cannot open %s"), filename);
+               return error_errno(_("cannot open '%s'"), filename);
        fclose(fp);
        return 0;
 }
@@ -1414,10 +1422,10 @@ static void set_option(struct transport *transport, const char *name, const char
 {
        int r = transport_set_option(transport, name, value);
        if (r < 0)
-               die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+               die(_("option \"%s\" value \"%s\" is not valid for %s"),
                    name, value, transport->url);
        if (r > 0)
-               warning(_("Option \"%s\" is ignored for %s\n"),
+               warning(_("option \"%s\" is ignored for %s\n"),
                        name, transport->url);
 }
 
@@ -1451,7 +1459,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
                old_nr = oids->nr;
                for_each_glob_ref(add_oid, s, oids);
                if (old_nr == oids->nr)
-                       warning("Ignoring --negotiation-tip=%s because it does not match any refs",
+                       warning("ignoring --negotiation-tip=%s because it does not match any refs",
                                s);
        }
        smart_options->negotiation_tips = oids;
@@ -1489,12 +1497,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
                if (transport->smart_options)
                        add_negotiation_tips(transport->smart_options);
                else
-                       warning("Ignoring --negotiation-tip because the protocol does not support it.");
+                       warning("ignoring --negotiation-tip because the protocol does not support it");
        }
        return transport;
 }
 
-static void backfill_tags(struct transport *transport, struct ref *ref_map)
+static void backfill_tags(struct transport *transport, struct ref *ref_map,
+                         struct worktree **worktrees)
 {
        int cannot_reuse;
 
@@ -1515,7 +1524,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       fetch_and_consume_refs(transport, ref_map);
+       fetch_and_consume_refs(transport, ref_map, worktrees);
 
        if (gsecondary) {
                transport_disconnect(gsecondary);
@@ -1533,6 +1542,7 @@ static int do_fetch(struct transport *transport,
        struct transport_ls_refs_options transport_ls_refs_options =
                TRANSPORT_LS_REFS_OPTIONS_INIT;
        int must_list_refs = 1;
+       struct worktree **worktrees = get_worktrees();
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1588,7 +1598,7 @@ static int do_fetch(struct transport *transport,
        ref_map = get_ref_map(transport->remote, remote_refs, rs,
                              tags, &autotags);
        if (!update_head_ok)
-               check_not_current_branch(ref_map);
+               check_not_current_branch(ref_map, worktrees);
 
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
@@ -1606,7 +1616,7 @@ static int do_fetch(struct transport *transport,
                                   transport->url);
                }
        }
-       if (fetch_and_consume_refs(transport, ref_map)) {
+       if (fetch_and_consume_refs(transport, ref_map, worktrees)) {
                free_refs(ref_map);
                retcode = 1;
                goto cleanup;
@@ -1638,6 +1648,16 @@ static int do_fetch(struct transport *transport,
                        }
                }
                if (source_ref) {
+                       if (!branch) {
+                               const char *shortname = source_ref->name;
+                               skip_prefix(shortname, "refs/heads/", &shortname);
+
+                               warning(_("could not set upstream of HEAD to '%s' from '%s' when "
+                                         "it does not point to any branch."),
+                                       shortname, transport->remote->name);
+                               goto skip;
+                       }
+
                        if (!strcmp(source_ref->name, "HEAD") ||
                            starts_with(source_ref->name, "refs/heads/"))
                                install_branch_config(0,
@@ -1651,11 +1671,11 @@ static int do_fetch(struct transport *transport,
                        else
                                warning(_("unknown branch type"));
                } else {
-                       warning(_("no source branch found.\n"
-                               "you need to specify exactly one branch with the --set-upstream option."));
+                       warning(_("no source branch found;\n"
+                                 "you need to specify exactly one branch with the --set-upstream option"));
                }
        }
- skip:
+skip:
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -1665,11 +1685,12 @@ static int do_fetch(struct transport *transport,
                ref_map = NULL;
                find_non_local_tags(remote_refs, &ref_map, &tail);
                if (ref_map)
-                       backfill_tags(transport, ref_map);
+                       backfill_tags(transport, ref_map, worktrees);
                free_refs(ref_map);
        }
 
- cleanup:
+cleanup:
+       free_worktrees(worktrees);
        return retcode;
 }
 
@@ -1790,7 +1811,7 @@ static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
        struct parallel_fetch_state *state = cb;
        const char *remote = task_cb;
 
-       state->result = error(_("Could not fetch %s"), remote);
+       state->result = error(_("could not fetch %s"), remote);
 
        return 0;
 }
@@ -1845,7 +1866,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
                        if (verbosity >= 0)
                                printf(_("Fetching %s\n"), name);
                        if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
-                               error(_("Could not fetch %s"), name);
+                               error(_("could not fetch %s"), name);
                                result = 1;
                        }
                        strvec_pop(&argv);
@@ -1906,8 +1927,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
        int remote_via_config = remote_is_configured(remote, 0);
 
        if (!remote)
-               die(_("No remote repository specified.  Please, specify either a URL or a\n"
-                   "remote name from which new revisions should be fetched."));
+               die(_("no remote repository specified; please specify either a URL or a\n"
+                     "remote name from which new revisions should be fetched"));
 
        gtransport = prepare_transport(remote, 1);
 
@@ -1942,7 +1963,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
                if (!strcmp(argv[i], "tag")) {
                        i++;
                        if (i >= argc)
-                               die(_("You need to specify a tag name."));
+                               die(_("you need to specify a tag name"));
 
                        refspec_appendf(&rs, "refs/tags/%s:refs/tags/%s",
                                        argv[i], argv[i]);
@@ -1962,7 +1983,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
                gtransport->server_options = &server_options;
 
        sigchain_push_common(unlock_pack_on_signal);
-       atexit(unlock_pack);
+       atexit(unlock_pack_atexit);
        sigchain_push(SIGPIPE, SIG_IGN);
        exit_code = do_fetch(gtransport, &rs);
        sigchain_pop(SIGPIPE);
@@ -1993,6 +2014,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        }
 
        git_config(git_fetch_config, NULL);
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
 
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
@@ -2010,14 +2033,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        if (deepen_relative) {
                if (deepen_relative < 0)
-                       die(_("Negative depth in --deepen is not supported"));
+                       die(_("negative depth in --deepen is not supported"));
                if (depth)
-                       die(_("--deepen and --depth are mutually exclusive"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--deepen", "--depth");
                depth = xstrfmt("%d", deepen_relative);
        }
        if (unshallow) {
                if (depth)
-                       die(_("--depth and --unshallow cannot be used together"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--depth", "--unshallow");
                else if (!is_repository_shallow(the_repository))
                        die(_("--unshallow on a complete repository does not make sense"));
                else
@@ -2047,14 +2070,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                /* All arguments are assumed to be remotes or groups */
                for (i = 0; i < argc; i++)
                        if (!add_remote_or_group(argv[i], &list))
-                               die(_("No such remote or remote group: %s"), argv[i]);
+                               die(_("no such remote or remote group: %s"),
+                                   argv[i]);
        } else {
                /* Single remote or group */
                (void) add_remote_or_group(argv[0], &list);
                if (list.nr > 1) {
                        /* More than one remote */
                        if (argc > 1)
-                               die(_("Fetching a group and specifying refspecs does not make sense"));
+                               die(_("fetching a group and specifying refspecs does not make sense"));
                } else {
                        /* Zero or one remotes */
                        remote = remote_get(argv[0]);
@@ -2075,7 +2099,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                if (gtransport->smart_options) {
                        gtransport->smart_options->acked_commits = &acked_commits;
                } else {
-                       warning(_("Protocol does not support --negotiate-only, exiting."));
+                       warning(_("protocol does not support --negotiate-only, exiting"));
                        return 1;
                }
                if (server_options.nr)
index 48a8699de728a950053b182fae23e73b14fba6a6..8d8fd393f8925c93155091db4f0959b7f03c31bb 100644 (file)
@@ -12,6 +12,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 {
        const char *inpath = NULL;
        const char *message = NULL;
+       char *into_name = NULL;
        int shortlog_len = -1;
        struct option options[] = {
                { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
@@ -23,6 +24,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
                  DEFAULT_MERGE_LOG_LEN },
                OPT_STRING('m', "message", &message, N_("text"),
                        N_("use <text> as start of message")),
+               OPT_STRING(0, "into-name", &into_name, N_("name"),
+                          N_("use <name> instead of the real target branch")),
                OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
                OPT_END()
        };
@@ -56,6 +59,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        opts.add_title = !message;
        opts.credit_people = 1;
        opts.shortlog_len = shortlog_len;
+       opts.into_name = into_name;
 
        ret = fmt_merge_msg(&input, &output, &opts);
        if (ret)
index 27b9e78094d9816141e81dff359393b81b458d82..9e54892311d5a698d51abb66ac73892c739732a1 100644 (file)
@@ -944,15 +944,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        if (the_repository->settings.core_commit_graph) {
                struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
-               const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
 
                prepare_alt_odb(the_repository);
                for (odb = the_repository->objects->odb; odb; odb = odb->next) {
                        child_process_init(&commit_graph_verify);
-                       commit_graph_verify.argv = verify_argv;
                        commit_graph_verify.git_cmd = 1;
-                       verify_argv[2] = "--object-dir";
-                       verify_argv[3] = odb->path;
+                       strvec_pushl(&commit_graph_verify.args, "commit-graph",
+                                    "verify", "--object-dir", odb->path, NULL);
                        if (run_command(&commit_graph_verify))
                                errors_found |= ERROR_COMMIT_GRAPH;
                }
@@ -960,15 +958,13 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        if (the_repository->settings.core_multi_pack_index) {
                struct child_process midx_verify = CHILD_PROCESS_INIT;
-               const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
 
                prepare_alt_odb(the_repository);
                for (odb = the_repository->objects->odb; odb; odb = odb->next) {
                        child_process_init(&midx_verify);
-                       midx_verify.argv = midx_argv;
                        midx_verify.git_cmd = 1;
-                       midx_argv[2] = "--object-dir";
-                       midx_argv[3] = odb->path;
+                       strvec_pushl(&midx_verify.args, "multi-pack-index",
+                                    "verify", "--object-dir", odb->path, NULL);
                        if (run_command(&midx_verify))
                                errors_found |= ERROR_MULTI_PACK_INDEX;
                }
index bcef6a4c8da71f54d923bbccc7a152a068e3b8c4..8e60ef1eaba426eae66c99d15d5dfdcbc26efac4 100644 (file)
@@ -470,7 +470,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 /*
  * Returns 0 if there was no previous error and gc can proceed, 1 if
  * gc should not proceed due to an error in the last run. Prints a
- * message and returns -1 if an error occurred while reading gc.log
+ * message and returns with a non-[01] status code if an error occurred
+ * while reading gc.log
  */
 static int report_last_gc_error(void)
 {
@@ -484,7 +485,7 @@ static int report_last_gc_error(void)
                if (errno == ENOENT)
                        goto done;
 
-               ret = error_errno(_("cannot stat '%s'"), gc_log_path);
+               ret = die_message_errno(_("cannot stat '%s'"), gc_log_path);
                goto done;
        }
 
@@ -493,7 +494,7 @@ static int report_last_gc_error(void)
 
        len = strbuf_read_file(&sb, gc_log_path, 0);
        if (len < 0)
-               ret = error_errno(_("cannot read '%s'"), gc_log_path);
+               ret = die_message_errno(_("cannot read '%s'"), gc_log_path);
        else if (len > 0) {
                /*
                 * A previous gc failed.  Report the error, and don't
@@ -611,12 +612,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                }
                if (detach_auto) {
                        int ret = report_last_gc_error();
-                       if (ret < 0)
-                               /* an I/O error occurred, already reported */
-                               exit(128);
+
                        if (ret == 1)
                                /* Last gc --auto failed. Skip this one. */
                                return 0;
+                       else if (ret)
+                               /* an I/O error occurred, already reported */
+                               return ret;
 
                        if (lock_repo_for_gc(force, &pid))
                                return 0;
index 75cd2fb407f6ebd3ce24b8e3588cf384d7b48699..d387131dd836c4eed62aa243ddbc1789934db8e0 100644 (file)
@@ -212,11 +212,10 @@ static int check_emacsclient_version(void)
 {
        struct strbuf buffer = STRBUF_INIT;
        struct child_process ec_process = CHILD_PROCESS_INIT;
-       const char *argv_ec[] = { "emacsclient", "--version", NULL };
        int version;
 
        /* emacsclient prints its version number on stderr */
-       ec_process.argv = argv_ec;
+       strvec_pushl(&ec_process.args, "emacsclient", "--version", NULL);
        ec_process.err = -1;
        ec_process.stdout_to_stderr = 1;
        if (start_command(&ec_process))
index c23d01de7dca4240bca05e7a087adceaaef3e78f..3c2e6aee3cc67be5df9bc1b4b62f5cd8a5c26b86 100644 (file)
@@ -1845,11 +1845,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (!pack_name && !from_stdin)
                usage(index_pack_usage);
        if (fix_thin_pack && !from_stdin)
-               die(_("--fix-thin cannot be used without --stdin"));
+               die(_("the option '%s' requires '%s'"), "--fix-thin", "--stdin");
        if (from_stdin && !startup_info->have_repository)
                die(_("--stdin requires a git repository"));
        if (from_stdin && hash_algo)
-               die(_("--object-format cannot be used with --stdin"));
+               die(_("options '%s' and '%s' cannot be used together"), "--object-format", "--stdin");
        if (!index_name && pack_name)
                index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
 
index 2167796ff2aad00762c2011b399305c94daa2a40..546f9c595e7d8c04127d3d1a3216be4673d7e6c9 100644 (file)
@@ -557,7 +557,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
 
        if (real_git_dir && is_bare_repository_cfg == 1)
-               die(_("--separate-git-dir and --bare are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare");
 
        if (real_git_dir && !is_absolute_path(real_git_dir))
                real_git_dir = real_pathdup(real_git_dir, 1);
index f75d87e8d7fea489639a8e38b7a4704bc75ba0cd..4b493408cc5d1253e84fd17dddec58340d38aa7e 100644 (file)
@@ -245,10 +245,24 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                        rev->abbrev_commit = 0;
        }
 
-       if (rev->commit_format == CMIT_FMT_USERFORMAT && !w.decorate)
-               decoration_style = 0;
+       if (rev->commit_format == CMIT_FMT_USERFORMAT) {
+               if (!w.decorate) {
+                       /*
+                        * Disable decoration loading if the format will not
+                        * show them anyway.
+                        */
+                       decoration_style = 0;
+               } else if (!decoration_style) {
+                       /*
+                        * If we are going to show them, make sure we do load
+                        * them here, but taking care not to override a
+                        * specific style set by config or --decorate.
+                        */
+                       decoration_style = DECORATE_SHORT_REFS;
+               }
+       }
 
-       if (decoration_style) {
+       if (decoration_style || rev->simplify_by_decoration) {
                const struct string_list *config_exclude =
                        repo_config_get_value_multi(the_repository,
                                                    "log.excludeDecoration");
@@ -260,7 +274,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                                                   item->string);
                }
 
-               rev->show_decorations = 1;
+               if (decoration_style)
+                       rev->show_decorations = 1;
 
                load_ref_decorations(&decoration_filter, decoration_style);
        }
@@ -1928,9 +1943,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                numbered = 0;
 
        if (numbered && keep_subject)
-               die(_("-n and -k are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
        if (keep_subject && subject_prefix)
-               die(_("--subject-prefix/--rfc and -k are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
        rev.preserve_subject = keep_subject;
 
        argc = setup_revisions(argc, argv, &rev, &s_r_opt);
@@ -1964,7 +1979,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                load_display_notes(&rev.notes_opt);
 
        if (use_stdout + rev.diffopt.close_file + !!output_directory > 1)
-               die(_("--stdout, --output, and --output-directory are mutually exclusive"));
+               die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory");
 
        if (use_stdout) {
                setup_pager();
@@ -2097,7 +2112,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (creation_factor < 0)
                creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
        else if (!rdiff_prev)
-               die(_("--creation-factor requires --range-diff"));
+               die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
 
        if (rdiff_prev) {
                if (!cover_letter && total != 1)
@@ -2241,6 +2256,7 @@ done:
        strbuf_release(&rdiff1);
        strbuf_release(&rdiff2);
        strbuf_release(&rdiff_title);
+       UNLEAK(rev);
        return 0;
 }
 
index 031fef1bcaa9d08bdad41b56be6e81c18eae485d..f7ea56cc63820b27efe7bec00ab9ac8660e06766 100644 (file)
@@ -37,6 +37,7 @@ static int debug_mode;
 static int show_eol;
 static int recurse_submodules;
 static int skipping_duplicates;
+static int show_sparse_dirs;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -315,8 +316,10 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
 
        if (!(show_cached || show_stage || show_deleted || show_modified))
                return;
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(repo->index);
+
+       if (!show_sparse_dirs)
+               ensure_full_index(repo->index);
+
        for (i = 0; i < repo->index->cache_nr; i++) {
                const struct cache_entry *ce = repo->index->cache[i];
                struct stat st;
@@ -670,6 +673,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
                OPT_BOOL(0, "deduplicate", &skipping_duplicates,
                         N_("suppress duplicate entries")),
+               OPT_BOOL(0, "sparse", &show_sparse_dirs,
+                        N_("show sparse directories in the presence of a sparse index")),
                OPT_END()
        };
        int ret = 0;
@@ -677,6 +682,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(ls_files_usage, builtin_ls_files_options);
 
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
        prefix = cmd_prefix;
        if (prefix)
                prefix_len = strlen(prefix);
@@ -767,7 +775,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                 * would not make any sense with this option.
                 */
                if (show_stage || show_unmerged)
-                       die("ls-files --with-tree is incompatible with -s or -u");
+                       die(_("options '%s' and '%s' cannot be used together"), "ls-files --with-tree", "-s/-u");
                overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
        }
 
index 06a2f90c4875f2a8ff82a927071ac584ad929f8b..e695867ee54894dceb5b0460cc9e0670d0f50607 100644 (file)
@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
                OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+               OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
+                               XDL_MERGE_ZEALOUS_DIFF3),
                OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
                            XDL_MERGE_FAVOR_OURS),
                OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
index ea3112e0c0b3acc9f4439430fb19e596a9fc9093..74e53cf20a776e23efaa36702dffec408e4d29fa 100644 (file)
@@ -87,6 +87,7 @@ static int signoff;
 static const char *sign_commit;
 static int autostash;
 static int no_verify;
+static char *into_name;
 
 static struct strategy all_strategy[] = {
        { "recursive",  NO_TRIVIAL },
@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {
        { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
                N_("read message from file"), PARSE_OPT_NONEG,
                NULL, 0, option_read_message },
+       OPT_STRING(0, "into-name", &into_name, N_("name"),
+                  N_("use <name> instead of the real target")),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
@@ -310,10 +313,9 @@ static int save_state(struct object_id *stash)
        int len;
        struct child_process cp = CHILD_PROCESS_INIT;
        struct strbuf buffer = STRBUF_INIT;
-       const char *argv[] = {"stash", "create", NULL};
        int rc = -1;
 
-       cp.argv = argv;
+       strvec_pushl(&cp.args, "stash", "create", NULL);
        cp.out = -1;
        cp.git_cmd = 1;
 
@@ -1122,6 +1124,7 @@ static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *mer
        opts.add_title = !have_message;
        opts.shortlog_len = shortlog_len;
        opts.credit_people = (0 < option_edit);
+       opts.into_name = into_name;
 
        fmt_merge_msg(merge_names, merge_msg, &opts);
        if (merge_msg->len)
@@ -1397,9 +1400,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        if (squash) {
                if (fast_forward == FF_NO)
-                       die(_("You cannot combine --squash with --no-ff."));
+                       die(_("options '%s' and '%s' cannot be used together"), "--squash", "--no-ff.");
                if (option_commit > 0)
-                       die(_("You cannot combine --squash with --commit."));
+                       die(_("options '%s' and '%s' cannot be used together"), "--squash", "--commit.");
                /*
                 * squash can now silently disable option_commit - this is not
                 * a problem as it is only overriding the default, not a user
index b221d300147fcdc1966b6415e1a1549a149d10ac..27f60153a6c732c9a47ed47eb79f4e736f714841 100644 (file)
@@ -44,11 +44,20 @@ static struct rev_name *get_commit_rev_name(const struct commit *commit)
        return is_valid_rev_name(name) ? name : NULL;
 }
 
+static int effective_distance(int distance, int generation)
+{
+       return distance + (generation > 0 ? MERGE_TRAVERSAL_WEIGHT : 0);
+}
+
 static int is_better_name(struct rev_name *name,
                          timestamp_t taggerdate,
+                         int generation,
                          int distance,
                          int from_tag)
 {
+       int name_distance = effective_distance(name->distance, name->generation);
+       int new_distance = effective_distance(distance, generation);
+
        /*
         * When comparing names based on tags, prefer names
         * based on the older tag, even if it is farther away.
@@ -56,7 +65,7 @@ static int is_better_name(struct rev_name *name,
        if (from_tag && name->from_tag)
                return (name->taggerdate > taggerdate ||
                        (name->taggerdate == taggerdate &&
-                        name->distance > distance));
+                        name_distance > new_distance));
 
        /*
         * We know that at least one of them is a non-tag at this point.
@@ -69,8 +78,8 @@ static int is_better_name(struct rev_name *name,
         * We are now looking at two non-tags.  Tiebreak to favor
         * shorter hops.
         */
-       if (name->distance != distance)
-               return name->distance > distance;
+       if (name_distance != new_distance)
+               return name_distance > new_distance;
 
        /* ... or tiebreak to favor older date */
        if (name->taggerdate != taggerdate)
@@ -88,7 +97,7 @@ static struct rev_name *create_or_update_name(struct commit *commit,
        struct rev_name *name = commit_rev_name_at(&rev_names, commit);
 
        if (is_valid_rev_name(name)) {
-               if (!is_better_name(name, taggerdate, distance, from_tag))
+               if (!is_better_name(name, taggerdate, generation, distance, from_tag))
                        return NULL;
 
                /*
index 71c59583a17f8da6eb948248e19a262781091f28..05d60483e8261d86a1295512c46a7e8cebe42469 100644 (file)
@@ -134,14 +134,13 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid)
 
 static void write_commented_object(int fd, const struct object_id *object)
 {
-       const char *show_args[5] =
-               {"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
        struct child_process show = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf cbuf = STRBUF_INIT;
 
        /* Invoke "git show --stat --no-notes $object" */
-       show.argv = show_args;
+       strvec_pushl(&show.args, "show", "--stat", "--no-notes",
+                    oid_to_hex(object), NULL);
        show.no_stdin = 1;
        show.out = -1;
        show.err = 0;
@@ -201,11 +200,12 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
 static void write_note_data(struct note_data *d, struct object_id *oid)
 {
        if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
-               error(_("unable to write note object"));
+               int status = die_message(_("unable to write note object"));
+
                if (d->edit_path)
-                       error(_("the note contents have been left in %s"),
-                               d->edit_path);
-               exit(128);
+                       die_message(_("the note contents have been left in %s"),
+                                   d->edit_path);
+               exit(status);
        }
 }
 
@@ -861,15 +861,19 @@ static int merge(int argc, const char **argv, const char *prefix)
                update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
+               struct worktree **worktrees;
                const struct worktree *wt;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
-               wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+               worktrees = get_worktrees();
+               wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
+                                       default_notes_ref());
                if (wt)
                        die(_("a notes merge into %s is already in-progress at %s"),
                            default_notes_ref(), wt->path);
+               free_worktrees(worktrees);
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die(_("failed to store link to current notes ref (%s)"),
                            default_notes_ref());
index 857be7826f3aba2c22c9fbe97e29f786ae8ef3c4..ba2006f2212bea19b202ba3db155b345bf7fbb89 100644 (file)
@@ -3397,7 +3397,7 @@ static void read_object_list_from_stdin(void)
                        if (feof(stdin))
                                break;
                        if (!ferror(stdin))
-                               die("BUG: fgets returned NULL, not EOF, not error!");
+                               BUG("fgets returned NULL, not EOF, not error!");
                        if (errno != EINTR)
                                die_errno("fgets");
                        clearerr(stdin);
@@ -4070,7 +4070,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                die(_("--thin cannot be used to build an indexable pack"));
 
        if (keep_unreachable && unpack_unreachable)
-               die(_("--keep-unreachable and --unpack-unreachable are incompatible"));
+               die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "--unpack-unreachable");
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
 
index 485c9a3c56ff96aa59d9d9b4bfec1ea894ac32dd..c2bcdc07db46a76559ecbccda756642f7cd5b6b4 100644 (file)
@@ -26,10 +26,22 @@ static int prune_tmp_file(const char *fullpath)
                return error("Could not stat '%s'", fullpath);
        if (st.st_mtime > expire)
                return 0;
-       if (show_only || verbose)
-               printf("Removing stale temporary file %s\n", fullpath);
-       if (!show_only)
-               unlink_or_warn(fullpath);
+       if (S_ISDIR(st.st_mode)) {
+               if (show_only || verbose)
+                       printf("Removing stale temporary directory %s\n", fullpath);
+               if (!show_only) {
+                       struct strbuf remove_dir_buf = STRBUF_INIT;
+
+                       strbuf_addstr(&remove_dir_buf, fullpath);
+                       remove_dir_recursively(&remove_dir_buf, 0);
+                       strbuf_release(&remove_dir_buf);
+               }
+       } else {
+               if (show_only || verbose)
+                       printf("Removing stale temporary file %s\n", fullpath);
+               if (!show_only)
+                       unlink_or_warn(fullpath);
+       }
        return 0;
 }
 
index c8457619d865c57c2a3a228d64acad3a253c181f..100cbf9fb85b59fee6a8f9f90ced02c9abdaac6e 100644 (file)
@@ -994,6 +994,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                set_reflog_message(argc, argv);
 
        git_config(git_pull_config, NULL);
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
 
        argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 
index 4b026ce6c6a90aea7bf50e8f193a6420471c2df3..359db90321c31ea27aa9160f08d21b89179ca488 100644 (file)
@@ -589,7 +589,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        set_push_cert_flags(&flags, push_cert);
 
        if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
-               die(_("--delete is incompatible with --all, --mirror and --tags"));
+               die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
        if (deleterefs && argc < 2)
                die(_("--delete doesn't make sense without any refs"));
 
@@ -627,18 +627,18 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 
        if (flags & TRANSPORT_PUSH_ALL) {
                if (tags)
-                       die(_("--all and --tags are incompatible"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags");
                if (argc >= 2)
                        die(_("--all can't be combined with refspecs"));
        }
        if (flags & TRANSPORT_PUSH_MIRROR) {
                if (tags)
-                       die(_("--mirror and --tags are incompatible"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags");
                if (argc >= 2)
                        die(_("--mirror can't be combined with refspecs"));
        }
        if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
-               die(_("--all and --mirror are incompatible"));
+               die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror");
 
        if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
                cas.use_force_if_includes = 1;
index 34b4744e5f3b7ccd5c4a215f477a622cfa06e4a0..36490d06c8ac6ad4c10596b7e2a4556ec7aaa8c8 100644 (file)
@@ -1190,13 +1190,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
        if (keep_base) {
                if (options.onto_name)
-                       die(_("cannot combine '--keep-base' with '--onto'"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
                if (options.root)
-                       die(_("cannot combine '--keep-base' with '--root'"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
        }
 
        if (options.root && options.fork_point > 0)
-               die(_("cannot combine '--root' with '--fork-point'"));
+               die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
 
        if (action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
@@ -1460,8 +1460,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
                if (i >= 0) {
                        if (is_merge(&options))
-                               die(_("cannot combine apply options with "
-                                     "merge options"));
+                               die(_("apply options and merge options "
+                                         "cannot be used together"));
                        else
                                options.type = REBASE_APPLY;
                }
index 49b846d960522ad1a5f29f7394bca4fa24e5b622..9f4a0b816cf9b6acd077a10a728a83e4048b9b5e 100644 (file)
@@ -175,7 +175,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                        strbuf_addf(&fsck_msg_types, "%c%s=%s",
                                fsck_msg_types.len ? ',' : '=', var, value);
                else
-                       warning("Skipping unknown msg id '%s'", var);
+                       warning("skipping unknown msg id '%s'", var);
                return 0;
        }
 
@@ -769,8 +769,10 @@ static void prepare_push_cert_sha1(struct child_process *proc)
                memset(&sigcheck, '\0', sizeof(sigcheck));
 
                bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
-               check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
-                               push_cert.len - bogs, &sigcheck);
+               sigcheck.payload = xmemdupz(push_cert.buf, bogs);
+               sigcheck.payload_len = bogs;
+               check_signature(&sigcheck, push_cert.buf + bogs,
+                               push_cert.len - bogs);
 
                nonce_status = check_nonce(push_cert.buf, bogs);
        }
@@ -812,16 +814,13 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        struct async muxer;
-       const char *argv[2];
        int code;
+       const char *hook_path = find_hook(hook_name);
 
-       argv[0] = find_hook(hook_name);
-       if (!argv[0])
+       if (!hook_path)
                return 0;
 
-       argv[1] = NULL;
-
-       proc.argv = argv;
+       strvec_push(&proc.args, hook_path);
        proc.in = -1;
        proc.stdout_to_stderr = 1;
        proc.trace2_hook_name = hook_name;
@@ -943,23 +942,21 @@ static int run_receive_hook(struct command *commands,
 
 static int run_update_hook(struct command *cmd)
 {
-       const char *argv[5];
        struct child_process proc = CHILD_PROCESS_INIT;
        int code;
+       const char *hook_path = find_hook("update");
 
-       argv[0] = find_hook("update");
-       if (!argv[0])
+       if (!hook_path)
                return 0;
 
-       argv[1] = cmd->ref_name;
-       argv[2] = oid_to_hex(&cmd->old_oid);
-       argv[3] = oid_to_hex(&cmd->new_oid);
-       argv[4] = NULL;
+       strvec_push(&proc.args, hook_path);
+       strvec_push(&proc.args, cmd->ref_name);
+       strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
+       strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
 
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
-       proc.argv = argv;
        proc.trace2_hook_name = "update";
 
        code = start_command(&proc);
@@ -1117,22 +1114,20 @@ static int run_proc_receive_hook(struct command *commands,
        struct child_process proc = CHILD_PROCESS_INIT;
        struct async muxer;
        struct command *cmd;
-       const char *argv[2];
        struct packet_reader reader;
        struct strbuf cap = STRBUF_INIT;
        struct strbuf errmsg = STRBUF_INIT;
        int hook_use_push_options = 0;
        int version = 0;
        int code;
+       const char *hook_path = find_hook("proc-receive");
 
-       argv[0] = find_hook("proc-receive");
-       if (!argv[0]) {
+       if (!hook_path) {
                rp_error("cannot find hook 'proc-receive'");
                return -1;
        }
-       argv[1] = NULL;
 
-       proc.argv = argv;
+       strvec_push(&proc.args, hook_path);
        proc.in = -1;
        proc.out = -1;
        proc.trace2_hook_name = "proc-receive";
@@ -1370,23 +1365,11 @@ static const char *push_to_deploy(unsigned char *sha1,
                                  struct strvec *env,
                                  const char *work_tree)
 {
-       const char *update_refresh[] = {
-               "update-index", "-q", "--ignore-submodules", "--refresh", NULL
-       };
-       const char *diff_files[] = {
-               "diff-files", "--quiet", "--ignore-submodules", "--", NULL
-       };
-       const char *diff_index[] = {
-               "diff-index", "--quiet", "--cached", "--ignore-submodules",
-               NULL, "--", NULL
-       };
-       const char *read_tree[] = {
-               "read-tree", "-u", "-m", NULL, NULL
-       };
        struct child_process child = CHILD_PROCESS_INIT;
 
-       child.argv = update_refresh;
-       child.env = env->v;
+       strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
+                    "--refresh", NULL);
+       strvec_pushv(&child.env_array, env->v);
        child.dir = work_tree;
        child.no_stdin = 1;
        child.stdout_to_stderr = 1;
@@ -1396,8 +1379,9 @@ static const char *push_to_deploy(unsigned char *sha1,
 
        /* run_command() does not clean up completely; reinitialize */
        child_process_init(&child);
-       child.argv = diff_files;
-       child.env = env->v;
+       strvec_pushl(&child.args, "diff-files", "--quiet",
+                    "--ignore-submodules", "--", NULL);
+       strvec_pushv(&child.env_array, env->v);
        child.dir = work_tree;
        child.no_stdin = 1;
        child.stdout_to_stderr = 1;
@@ -1405,12 +1389,13 @@ static const char *push_to_deploy(unsigned char *sha1,
        if (run_command(&child))
                return "Working directory has unstaged changes";
 
-       /* diff-index with either HEAD or an empty tree */
-       diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
-
        child_process_init(&child);
-       child.argv = diff_index;
-       child.env = env->v;
+       strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
+                    "--ignore-submodules",
+                    /* diff-index with either HEAD or an empty tree */
+                    head_has_history() ? "HEAD" : empty_tree_oid_hex(),
+                    "--", NULL);
+       strvec_pushv(&child.env_array, env->v);
        child.no_stdin = 1;
        child.no_stdout = 1;
        child.stdout_to_stderr = 0;
@@ -1418,10 +1403,10 @@ static const char *push_to_deploy(unsigned char *sha1,
        if (run_command(&child))
                return "Working directory has staged changes";
 
-       read_tree[3] = hash_to_hex(sha1);
        child_process_init(&child);
-       child.argv = read_tree;
-       child.env = env->v;
+       strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
+                    NULL);
+       strvec_pushv(&child.env_array, env->v);
        child.dir = work_tree;
        child.no_stdin = 1;
        child.no_stdout = 1;
@@ -1449,29 +1434,22 @@ static const char *push_to_checkout(unsigned char *hash,
 
 static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
 {
-       const char *retval, *work_tree, *git_dir = NULL;
+       const char *retval, *git_dir;
        struct strvec env = STRVEC_INIT;
 
-       if (worktree && worktree->path)
-               work_tree = worktree->path;
-       else if (git_work_tree_cfg)
-               work_tree = git_work_tree_cfg;
-       else
-               work_tree = "..";
+       if (!worktree || !worktree->path)
+               BUG("worktree->path must be non-NULL");
 
-       if (is_bare_repository())
+       if (worktree->is_bare)
                return "denyCurrentBranch = updateInstead needs a worktree";
-       if (worktree)
-               git_dir = get_worktree_git_dir(worktree);
-       if (!git_dir)
-               git_dir = get_git_dir();
+       git_dir = get_worktree_git_dir(worktree);
 
        strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
 
        if (!hook_exists(push_to_checkout_hook))
-               retval = push_to_deploy(sha1, &env, work_tree);
+               retval = push_to_deploy(sha1, &env, worktree->path);
        else
-               retval = push_to_checkout(sha1, &env, work_tree);
+               retval = push_to_checkout(sha1, &env, worktree->path);
 
        strvec_clear(&env);
        return retval;
@@ -1486,19 +1464,22 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
        int do_update_worktree = 0;
-       const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
+       struct worktree **worktrees = get_worktrees();
+       const struct worktree *worktree =
+               find_shared_symref(worktrees, "HEAD", name);
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
                rp_error("refusing to create funny ref '%s' remotely", name);
-               return "funny refname";
+               ret = "funny refname";
+               goto out;
        }
 
        strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
        free(namespaced_name);
        namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
 
-       if (worktree) {
+       if (worktree && !worktree->is_bare) {
                switch (deny_current_branch) {
                case DENY_IGNORE:
                        break;
@@ -1510,7 +1491,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                        rp_error("refusing to update checked out branch: %s", name);
                        if (deny_current_branch == DENY_UNCONFIGURED)
                                refuse_unconfigured_deny();
-                       return "branch is currently checked out";
+                       ret = "branch is currently checked out";
+                       goto out;
                case DENY_UPDATE_INSTEAD:
                        /* pass -- let other checks intervene first */
                        do_update_worktree = 1;
@@ -1521,13 +1503,15 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
                error("unpack should have generated %s, "
                      "but I can't find it!", oid_to_hex(new_oid));
-               return "bad pack";
+               ret = "bad pack";
+               goto out;
        }
 
        if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
                if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
-                       return "deletion prohibited";
+                       ret = "deletion prohibited";
+                       goto out;
                }
 
                if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
@@ -1543,9 +1527,11 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                if (deny_delete_current == DENY_UNCONFIGURED)
                                        refuse_unconfigured_deny_delete_current();
                                rp_error("refusing to delete the current branch: %s", name);
-                               return "deletion of the current branch prohibited";
+                               ret = "deletion of the current branch prohibited";
+                               goto out;
                        default:
-                               return "Invalid denyDeleteCurrent setting";
+                               ret = "Invalid denyDeleteCurrent setting";
+                               goto out;
                        }
                }
        }
@@ -1563,25 +1549,28 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                    old_object->type != OBJ_COMMIT ||
                    new_object->type != OBJ_COMMIT) {
                        error("bad sha1 objects for %s", name);
-                       return "bad ref";
+                       ret = "bad ref";
+                       goto out;
                }
                old_commit = (struct commit *)old_object;
                new_commit = (struct commit *)new_object;
                if (!in_merge_bases(old_commit, new_commit)) {
                        rp_error("denying non-fast-forward %s"
                                 " (you should pull first)", name);
-                       return "non-fast-forward";
+                       ret = "non-fast-forward";
+                       goto out;
                }
        }
        if (run_update_hook(cmd)) {
                rp_error("hook declined to update %s", name);
-               return "hook declined";
+               ret = "hook declined";
+               goto out;
        }
 
        if (do_update_worktree) {
-               ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
+               ret = update_worktree(new_oid->hash, worktree);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        if (is_null_oid(new_oid)) {
@@ -1589,9 +1578,9 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                if (!parse_object(the_repository, old_oid)) {
                        old_oid = NULL;
                        if (ref_exists(name)) {
-                               rp_warning("Allowing deletion of corrupt ref.");
+                               rp_warning("allowing deletion of corrupt ref");
                        } else {
-                               rp_warning("Deleting a non-existent ref.");
+                               rp_warning("deleting a non-existent ref");
                                cmd->did_not_exist = 1;
                        }
                }
@@ -1600,17 +1589,19 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                           old_oid,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
-                       strbuf_release(&err);
-                       return "failed to delete";
+                       ret = "failed to delete";
+               } else {
+                       ret = NULL; /* good */
                }
                strbuf_release(&err);
-               return NULL; /* good */
        }
        else {
                struct strbuf err = STRBUF_INIT;
                if (shallow_update && si->shallow_ref[cmd->index] &&
-                   update_shallow_ref(cmd, si))
-                       return "shallow error";
+                   update_shallow_ref(cmd, si)) {
+                       ret = "shallow error";
+                       goto out;
+               }
 
                if (ref_transaction_update(transaction,
                                           namespaced_name,
@@ -1618,14 +1609,16 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
-                       strbuf_release(&err);
-
-                       return "failed to update ref";
+                       ret = "failed to update ref";
+               } else {
+                       ret = NULL; /* good */
                }
                strbuf_release(&err);
-
-               return NULL; /* good */
        }
+
+out:
+       free_worktrees(worktrees);
+       return ret;
 }
 
 static void run_update_post_hook(struct command *commands)
@@ -2213,13 +2206,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                strvec_push(&child.args, alt_shallow_file);
        }
 
-       tmp_objdir = tmp_objdir_create();
+       tmp_objdir = tmp_objdir_create("incoming");
        if (!tmp_objdir) {
                if (err_fd > 0)
                        close(err_fd);
                return "unable to create temporary object directory";
        }
-       child.env = tmp_objdir_env(tmp_objdir);
+       if (tmp_objdir)
+               strvec_pushv(&child.env_array, tmp_objdir_env(tmp_objdir));
 
        /*
         * Normally we just pass the tmp_objdir environment to the child
@@ -2490,9 +2484,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
 
        if (argc > 1)
-               usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+               usage_msg_opt(_("too many arguments"), receive_pack_usage, options);
        if (argc == 0)
-               usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
+               usage_msg_opt(_("you must specify a directory"), receive_pack_usage, options);
 
        service_dir = argv[0];
 
@@ -2566,25 +2560,25 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                                 &push_options);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
+               sigchain_push(SIGPIPE, SIG_IGN);
                if (report_status_v2)
                        report_v2(commands, unpack_status);
                else if (report_status)
                        report(commands, unpack_status);
+               sigchain_pop(SIGPIPE);
                run_receive_hook(commands, "post-receive", 1,
                                 &push_options);
                run_update_post_hook(commands);
                string_list_clear(&push_options, 0);
                if (auto_gc) {
-                       const char *argv_gc_auto[] = {
-                               "gc", "--auto", "--quiet", NULL,
-                       };
                        struct child_process proc = CHILD_PROCESS_INIT;
 
                        proc.no_stdin = 1;
                        proc.stdout_to_stderr = 1;
                        proc.err = use_sideband ? -1 : 0;
                        proc.git_cmd = proc.close_object_store = 1;
-                       proc.argv = argv_gc_auto;
+                       strvec_pushl(&proc.args, "gc", "--auto", "--quiet",
+                                    NULL);
 
                        if (!start_command(&proc)) {
                                if (use_sideband)
index 175c83e7cc2804ae841e72ea487cb64fe3d537ae..a4b1dd27e13c93c670489f11c7c20b980952ba2a 100644 (file)
@@ -28,7 +28,6 @@ static timestamp_t default_reflog_expire;
 static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
-       struct rev_info revs;
        int stalefix;
        timestamp_t expire_total;
        timestamp_t expire_unreachable;
@@ -46,18 +45,12 @@ struct expire_reflog_policy_cb {
        struct cmd_reflog_expire_cb cmd;
        struct commit *tip_commit;
        struct commit_list *tips;
+       unsigned int dry_run:1;
 };
 
-struct collected_reflog {
-       struct object_id oid;
-       char reflog[FLEX_ARRAY];
-};
-
-struct collect_reflog_cb {
-       struct collected_reflog **e;
-       int alloc;
-       int nr;
-       struct worktree *wt;
+struct worktree_reflogs {
+       struct worktree *worktree;
+       struct string_list reflogs;
 };
 
 /* Remember to update object flag allocation in object.h */
@@ -310,10 +303,15 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no
                return 1;
 
        if (timestamp < cb->cmd.expire_unreachable) {
-               if (cb->unreachable_expire_kind == UE_ALWAYS)
-                       return 1;
-               if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+               switch (cb->unreachable_expire_kind) {
+               case UE_ALWAYS:
                        return 1;
+               case UE_NORMAL:
+               case UE_HEAD:
+                       if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+                               return 1;
+                       break;
+               }
        }
 
        if (cb->cmd.recno && --(cb->cmd.recno) == 0)
@@ -322,6 +320,28 @@ static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *no
        return 0;
 }
 
+static int should_expire_reflog_ent_verbose(struct object_id *ooid,
+                                           struct object_id *noid,
+                                           const char *email,
+                                           timestamp_t timestamp, int tz,
+                                           const char *message, void *cb_data)
+{
+       struct expire_reflog_policy_cb *cb = cb_data;
+       int expire;
+
+       expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
+                                         message, cb);
+
+       if (!expire)
+               printf("keep %s", message);
+       else if (cb->dry_run)
+               printf("would prune %s", message);
+       else
+               printf("prune %s", message);
+
+       return expire;
+}
+
 static int push_tip_to_list(const char *refname, const struct object_id *oid,
                            int flags, void *cb_data)
 {
@@ -355,75 +375,71 @@ static void reflog_expiry_prepare(const char *refname,
                                  void *cb_data)
 {
        struct expire_reflog_policy_cb *cb = cb_data;
+       struct commit_list *elem;
+       struct commit *commit = NULL;
 
        if (!cb->cmd.expire_unreachable || is_head(refname)) {
-               cb->tip_commit = NULL;
                cb->unreachable_expire_kind = UE_HEAD;
        } else {
-               cb->tip_commit = lookup_commit_reference_gently(the_repository,
-                                                               oid, 1);
-               if (!cb->tip_commit)
-                       cb->unreachable_expire_kind = UE_ALWAYS;
-               else
-                       cb->unreachable_expire_kind = UE_NORMAL;
+               commit = lookup_commit(the_repository, oid);
+               cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
        }
 
        if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
                cb->unreachable_expire_kind = UE_ALWAYS;
 
-       cb->mark_list = NULL;
-       cb->tips = NULL;
-       if (cb->unreachable_expire_kind != UE_ALWAYS) {
-               if (cb->unreachable_expire_kind == UE_HEAD) {
-                       struct commit_list *elem;
-
-                       for_each_ref(push_tip_to_list, &cb->tips);
-                       for (elem = cb->tips; elem; elem = elem->next)
-                               commit_list_insert(elem->item, &cb->mark_list);
-               } else {
-                       commit_list_insert(cb->tip_commit, &cb->mark_list);
-               }
-               cb->mark_limit = cb->cmd.expire_total;
-               mark_reachable(cb);
+       switch (cb->unreachable_expire_kind) {
+       case UE_ALWAYS:
+               return;
+       case UE_HEAD:
+               for_each_ref(push_tip_to_list, &cb->tips);
+               for (elem = cb->tips; elem; elem = elem->next)
+                       commit_list_insert(elem->item, &cb->mark_list);
+               break;
+       case UE_NORMAL:
+               commit_list_insert(commit, &cb->mark_list);
+               /* For reflog_expiry_cleanup() below */
+               cb->tip_commit = commit;
        }
+       cb->mark_limit = cb->cmd.expire_total;
+       mark_reachable(cb);
 }
 
 static void reflog_expiry_cleanup(void *cb_data)
 {
        struct expire_reflog_policy_cb *cb = cb_data;
+       struct commit_list *elem;
 
-       if (cb->unreachable_expire_kind != UE_ALWAYS) {
-               if (cb->unreachable_expire_kind == UE_HEAD) {
-                       struct commit_list *elem;
-                       for (elem = cb->tips; elem; elem = elem->next)
-                               clear_commit_marks(elem->item, REACHABLE);
-                       free_commit_list(cb->tips);
-               } else {
-                       clear_commit_marks(cb->tip_commit, REACHABLE);
-               }
+       switch (cb->unreachable_expire_kind) {
+       case UE_ALWAYS:
+               return;
+       case UE_HEAD:
+               for (elem = cb->tips; elem; elem = elem->next)
+                       clear_commit_marks(elem->item, REACHABLE);
+               free_commit_list(cb->tips);
+               break;
+       case UE_NORMAL:
+               clear_commit_marks(cb->tip_commit, REACHABLE);
+               break;
        }
 }
 
 static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
 {
-       struct collected_reflog *e;
-       struct collect_reflog_cb *cb = cb_data;
+       struct worktree_reflogs *cb = cb_data;
+       struct worktree *worktree = cb->worktree;
        struct strbuf newref = STRBUF_INIT;
 
        /*
         * Avoid collecting the same shared ref multiple times because
         * they are available via all worktrees.
         */
-       if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+       if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
                return 0;
 
-       strbuf_worktree_ref(cb->wt, &newref, ref);
-       FLEX_ALLOC_STR(e, reflog, newref.buf);
-       strbuf_release(&newref);
+       strbuf_worktree_ref(worktree, &newref, ref);
+       string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
 
-       oidcpy(&e->oid, oid);
-       ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
-       cb->e[cb->nr++] = e;
        return 0;
 }
 
@@ -541,11 +557,13 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
-       struct expire_reflog_policy_cb cb;
+       struct cmd_reflog_expire_cb cmd = { 0 };
        timestamp_t now = time(NULL);
        int i, status, do_all, all_worktrees = 1;
        int explicit_expiry = 0;
        unsigned int flags = 0;
+       int verbose = 0;
+       reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 
        default_reflog_expire_unreachable = now - 30 * 24 * 3600;
        default_reflog_expire = now - 90 * 24 * 3600;
@@ -553,10 +571,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 
        save_commit_buffer = 0;
        do_all = status = 0;
-       memset(&cb, 0, sizeof(cb));
 
-       cb.cmd.expire_total = default_reflog_expire;
-       cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
+       cmd.expire_total = default_reflog_expire;
+       cmd.expire_unreachable = default_reflog_expire_unreachable;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -564,17 +581,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        flags |= EXPIRE_REFLOGS_DRY_RUN;
                else if (skip_prefix(arg, "--expire=", &arg)) {
-                       if (parse_expiry_date(arg, &cb.cmd.expire_total))
+                       if (parse_expiry_date(arg, &cmd.expire_total))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_TOTAL;
                }
                else if (skip_prefix(arg, "--expire-unreachable=", &arg)) {
-                       if (parse_expiry_date(arg, &cb.cmd.expire_unreachable))
+                       if (parse_expiry_date(arg, &cmd.expire_unreachable))
                                die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_UNREACH;
                }
                else if (!strcmp(arg, "--stale-fix"))
-                       cb.cmd.stalefix = 1;
+                       cmd.stalefix = 1;
                else if (!strcmp(arg, "--rewrite"))
                        flags |= EXPIRE_REFLOGS_REWRITE;
                else if (!strcmp(arg, "--updateref"))
@@ -584,7 +601,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                else if (!strcmp(arg, "--single-worktree"))
                        all_worktrees = 0;
                else if (!strcmp(arg, "--verbose"))
-                       flags |= EXPIRE_REFLOGS_VERBOSE;
+                       verbose = 1;
                else if (!strcmp(arg, "--")) {
                        i++;
                        break;
@@ -595,54 +612,65 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                        break;
        }
 
+       if (verbose)
+               should_prune_fn = should_expire_reflog_ent_verbose;
+
        /*
         * We can trust the commits and objects reachable from refs
         * even in older repository.  We cannot trust what's reachable
         * from reflog if the repository was pruned with older git.
         */
-       if (cb.cmd.stalefix) {
-               repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
-               cb.cmd.revs.do_not_die_on_missing_tree = 1;
-               cb.cmd.revs.ignore_missing = 1;
-               cb.cmd.revs.ignore_missing_links = 1;
-               if (flags & EXPIRE_REFLOGS_VERBOSE)
+       if (cmd.stalefix) {
+               struct rev_info revs;
+
+               repo_init_revisions(the_repository, &revs, prefix);
+               revs.do_not_die_on_missing_tree = 1;
+               revs.ignore_missing = 1;
+               revs.ignore_missing_links = 1;
+               if (verbose)
                        printf(_("Marking reachable objects..."));
-               mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
-               if (flags & EXPIRE_REFLOGS_VERBOSE)
+               mark_reachable_objects(&revs, 0, 0, NULL);
+               if (verbose)
                        putchar('\n');
        }
 
        if (do_all) {
-               struct collect_reflog_cb collected;
+               struct worktree_reflogs collected = {
+                       .reflogs = STRING_LIST_INIT_DUP,
+               };
+               struct string_list_item *item;
                struct worktree **worktrees, **p;
-               int i;
 
-               memset(&collected, 0, sizeof(collected));
                worktrees = get_worktrees();
                for (p = worktrees; *p; p++) {
                        if (!all_worktrees && !(*p)->is_current)
                                continue;
-                       collected.wt = *p;
+                       collected.worktree = *p;
                        refs_for_each_reflog(get_worktree_ref_store(*p),
                                             collect_reflog, &collected);
                }
                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, flags,
+               for_each_string_list_item(item, &collected.reflogs) {
+                       struct expire_reflog_policy_cb cb = {
+                               .cmd = cmd,
+                               .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+                       };
+
+                       set_reflog_expiry_param(&cb.cmd, explicit_expiry, item->string);
+                       status |= reflog_expire(item->string, flags,
                                                reflog_expiry_prepare,
-                                               should_expire_reflog_ent,
+                                               should_prune_fn,
                                                reflog_expiry_cleanup,
                                                &cb);
-                       free(e);
                }
-               free(collected.e);
+               string_list_clear(&collected.reflogs, 0);
        }
 
        for (; i < argc; i++) {
                char *ref;
+               struct expire_reflog_policy_cb cb = { .cmd = cmd };
+
                if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
                        status |= error(_("%s points nowhere!"), argv[i]);
                        continue;
@@ -650,7 +678,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
                status |= reflog_expire(ref, flags,
                                        reflog_expiry_prepare,
-                                       should_expire_reflog_ent,
+                                       should_prune_fn,
                                        reflog_expiry_cleanup,
                                        &cb);
                free(ref);
@@ -662,19 +690,19 @@ static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
 {
-       struct expire_reflog_policy_cb *cb = cb_data;
-       if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
-               cb->cmd.recno++;
+       struct cmd_reflog_expire_cb *cb = cb_data;
+       if (!cb->expire_total || timestamp < cb->expire_total)
+               cb->recno++;
        return 0;
 }
 
 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 {
-       struct expire_reflog_policy_cb cb;
+       struct cmd_reflog_expire_cb cmd = { 0 };
        int i, status = 0;
        unsigned int flags = 0;
-
-       memset(&cb, 0, sizeof(cb));
+       int verbose = 0;
+       reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -685,7 +713,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                else if (!strcmp(arg, "--updateref"))
                        flags |= EXPIRE_REFLOGS_UPDATE_REF;
                else if (!strcmp(arg, "--verbose"))
-                       flags |= EXPIRE_REFLOGS_VERBOSE;
+                       verbose = 1;
                else if (!strcmp(arg, "--")) {
                        i++;
                        break;
@@ -696,6 +724,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                        break;
        }
 
+       if (verbose)
+               should_prune_fn = should_expire_reflog_ent_verbose;
+
        if (argc - i < 1)
                return error(_("no reflog specified to delete"));
 
@@ -703,6 +734,9 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
                const char *spec = strstr(argv[i], "@{");
                char *ep, *ref;
                int recno;
+               struct expire_reflog_policy_cb cb = {
+                       .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+               };
 
                if (!spec) {
                        status |= error(_("not a reflog: %s"), argv[i]);
@@ -716,17 +750,18 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 
                recno = strtoul(spec + 2, &ep, 10);
                if (*ep == '}') {
-                       cb.cmd.recno = -recno;
-                       for_each_reflog_ent(ref, count_reflog_ent, &cb);
+                       cmd.recno = -recno;
+                       for_each_reflog_ent(ref, count_reflog_ent, &cmd);
                } else {
-                       cb.cmd.expire_total = approxidate(spec + 2);
-                       for_each_reflog_ent(ref, count_reflog_ent, &cb);
-                       cb.cmd.expire_total = 0;
+                       cmd.expire_total = approxidate(spec + 2);
+                       for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+                       cmd.expire_total = 0;
                }
 
+               cb.cmd = cmd;
                status |= reflog_expire(ref, flags,
                                        reflog_expiry_prepare,
-                                       should_expire_reflog_ent,
+                                       should_prune_fn,
                                        reflog_expiry_cleanup,
                                        &cb);
                free(ref);
index 9b0be6a6ab318ecdc6700f1b86da7dfbf89a468e..da1e364a756b9f8c74f38f8ec596798cf3ec38ad 100644 (file)
@@ -612,7 +612,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct tempfile *refs_snapshot = NULL;
        int i, ext, ret;
        FILE *out;
-       int show_progress = isatty(2);
+       int show_progress;
 
        /* variables to be filled by option parsing */
        int pack_everything = 0;
@@ -681,7 +681,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        if (keep_unreachable &&
            (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
-               die(_("--keep-unreachable and -A are incompatible"));
+               die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
 
        if (write_bitmaps < 0) {
                if (!write_midx &&
@@ -693,7 +693,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                write_bitmaps = 0;
        }
        if (pack_kept_objects < 0)
-               pack_kept_objects = write_bitmaps > 0;
+               pack_kept_objects = write_bitmaps > 0 && !write_midx;
 
        if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
                die(_(incremental_bitmap_conflict_error));
@@ -712,7 +712,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        if (geometric_factor) {
                if (pack_everything)
-                       die(_("--geometric is incompatible with -A, -a"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
                init_pack_geometry(&geometry);
                split_pack_geometry(geometry, geometric_factor);
        }
@@ -725,6 +725,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        prepare_pack_objects(&cmd, &po_args);
 
+       show_progress = !po_args.quiet && isatty(2);
+
        strvec_push(&cmd.args, "--keep-true-parents");
        if (!pack_kept_objects)
                strvec_push(&cmd.args, "--honor-pack-keep");
@@ -926,7 +928,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                        }
                        strbuf_release(&buf);
                }
-               if (!po_args.quiet && show_progress)
+               if (show_progress)
                        opts |= PRUNE_PACKED_VERBOSE;
                prune_packed_objects(opts);
 
index 946938d011ee8ea69040b931c98acd270bbcd46d..6ff1734d5879d36c994d0ab4b82b95e60a348750 100644 (file)
@@ -258,11 +258,10 @@ static int import_object(struct object_id *oid, enum object_type type,
                return error_errno(_("unable to open %s for reading"), filename);
 
        if (!raw && type == OBJ_TREE) {
-               const char *argv[] = { "mktree", NULL };
                struct child_process cmd = CHILD_PROCESS_INIT;
                struct strbuf result = STRBUF_INIT;
 
-               cmd.argv = argv;
+               strvec_push(&cmd.args, "mktree");
                cmd.git_cmd = 1;
                cmd.in = fd;
                cmd.out = -1;
index b1ff699b43a33b6ddb3301c5fbf6a7dd314b0f6a..b97745ee94e5a30a25a477f432a275d6ae953ee2 100644 (file)
@@ -423,16 +423,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (pathspec_from_file) {
                if (patch_mode)
-                       die(_("--pathspec-from-file is incompatible with --patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
 
                if (pathspec.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                parse_pathspec_file(&pathspec, 0,
                                    PATHSPEC_PREFER_FULL,
                                    prefix, pathspec_from_file, pathspec_file_nul);
        } else if (pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
@@ -459,7 +459,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (patch_mode) {
                if (reset_type != NONE)
-                       die(_("--patch is incompatible with --{hard,mixed,soft}"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
                trace2_cmd_mode("patch-interactive");
                return run_add_interactive(rev, "--patch=reset", &pathspec);
        }
@@ -490,7 +490,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                    _(reset_type_names[reset_type]));
 
        if (intent_to_add && reset_type != MIXED)
-               die(_("-N can only be used with --mixed"));
+               die(_("the option '%s' requires '%s'"), "-N", "--mixed");
 
        prepare_repo_settings(the_repository);
        the_repository->settings.command_requires_full_index = 0;
index 36cb909ebaa51940024dce982dd53e5109c653eb..777558e9b067ebbf7e91ba4e65570a86bd25a105 100644 (file)
@@ -538,7 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
                if (skip_prefix(arg, "--missing=", &arg)) {
                        if (revs.exclude_promisor_objects)
-                               die(_("cannot combine --exclude-promisor-objects and --missing"));
+                               die(_("options '%s' and '%s' cannot be used together"), "--exclude-promisor-objects", "--missing");
                        if (parse_missing_action_value(arg))
                                break;
                }
@@ -676,7 +676,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (revs.count &&
            (revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
            (revs.left_right || revs.cherry_mark))
-               die(_("marked counting is incompatible with --objects"));
+               die(_("marked counting and '%s' cannot be used together"), "--objects");
 
        save_commit_buffer = (revs.verbose_header ||
                              revs.grep_filter.pattern_list ||
index 3d0967cdc11308b966e6225d6b95dd7ae90ab364..84a935a16e8be447d0bc95ad2a1e5d133452e635 100644 (file)
@@ -272,13 +272,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
        if (pathspec_from_file) {
                if (pathspec.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                parse_pathspec_file(&pathspec, 0,
                                    PATHSPEC_PREFER_CWD,
                                    prefix, pathspec_from_file, pathspec_file_nul);
        } else if (pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        if (!pathspec.nr)
@@ -399,12 +399,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only) {
                int removed = 0, gitmodules_modified = 0;
                struct strbuf buf = STRBUF_INIT;
+               int flag = force ? REMOVE_DIR_PURGE_ORIGINAL_CWD : 0;
                for (i = 0; i < list.nr; i++) {
                        const char *path = list.entry[i].name;
                        if (list.entry[i].is_submodule) {
                                strbuf_reset(&buf);
                                strbuf_addstr(&buf, path);
-                               if (remove_dir_recursively(&buf, 0))
+                               if (remove_dir_recursively(&buf, flag))
                                        die(_("could not remove '%s'"), path);
 
                                removed = 1;
index 082449293b56388456520cbc67ef873ed3dc767a..e12c5e80e3e4425831b9803c334245eaedf3bd9a 100644 (file)
@@ -707,8 +707,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                         *
                         * Also --all and --remotes do not make sense either.
                         */
-                       die(_("--reflog is incompatible with --all, --remotes, "
-                             "--independent or --merge-base"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--reflog",
+                               "--all/--remotes/--independent/--merge-base");
        }
 
        /* If nothing is specified, show all branches by default */
@@ -761,6 +761,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                        char *logmsg;
                        char *nth_desc;
                        const char *msg;
+                       char *end;
                        timestamp_t timestamp;
                        int tz;
 
@@ -770,11 +771,12 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                reflog = i;
                                break;
                        }
-                       msg = strchr(logmsg, '\t');
-                       if (!msg)
-                               msg = "(none)";
-                       else
-                               msg++;
+
+                       end = strchr(logmsg, '\n');
+                       if (end)
+                               *end = '\0';
+
+                       msg = (*logmsg == '\0') ? "(none)" : logmsg;
                        reflog_msg[i] = xstrfmt("(%s) %s",
                                                show_date(timestamp, tz,
                                                          DATE_MODE(RELATIVE)),
index d0f5c4702be69d0c9fade08ffbf5183c5f3dbe7e..679c10703684044aa02555227b5cdcdf92591d9b 100644 (file)
@@ -56,6 +56,9 @@ static int sparse_checkout_list(int argc, const char **argv)
        char *sparse_filename;
        int res;
 
+       if (!core_apply_sparse_checkout)
+               die(_("this worktree is not sparse"));
+
        argc = parse_options(argc, argv, NULL,
                             builtin_sparse_checkout_list_options,
                             builtin_sparse_checkout_list_usage, 0);
@@ -380,6 +383,41 @@ static int set_config(enum sparse_checkout_mode mode)
        return 0;
 }
 
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+       int mode, record_mode;
+
+       /* Determine if we need to record the mode; ensure sparse checkout on */
+       record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+       /* If not specified, use previous definition of cone mode */
+       if (*cone_mode == -1 && core_apply_sparse_checkout)
+               *cone_mode = core_sparse_checkout_cone;
+
+       /* Set cone/non-cone mode appropriately */
+       core_apply_sparse_checkout = 1;
+       if (*cone_mode == 1) {
+               mode = MODE_CONE_PATTERNS;
+               core_sparse_checkout_cone = 1;
+       } else {
+               mode = MODE_ALL_PATTERNS;
+       }
+       if (record_mode && set_config(mode))
+               return 1;
+
+       /* Set sparse-index/non-sparse-index mode if specified */
+       if (*sparse_index >= 0) {
+               if (set_sparse_index_config(the_repository, *sparse_index) < 0)
+                       die(_("failed to modify sparse-index config"));
+
+               /* force an index rewrite */
+               repo_read_index(the_repository);
+               the_repository->index->updated_workdir = 1;
+       }
+
+       return 0;
+}
+
 static char const * const builtin_sparse_checkout_init_usage[] = {
        N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"),
        NULL
@@ -396,7 +434,6 @@ static int sparse_checkout_init(int argc, const char **argv)
        char *sparse_filename;
        int res;
        struct object_id oid;
-       int mode;
        struct strbuf pattern = STRBUF_INIT;
 
        static struct option builtin_sparse_checkout_init_options[] = {
@@ -409,19 +446,14 @@ static int sparse_checkout_init(int argc, const char **argv)
 
        repo_read_index(the_repository);
 
+       init_opts.cone_mode = -1;
        init_opts.sparse_index = -1;
 
        argc = parse_options(argc, argv, NULL,
                             builtin_sparse_checkout_init_options,
                             builtin_sparse_checkout_init_usage, 0);
 
-       if (init_opts.cone_mode) {
-               mode = MODE_CONE_PATTERNS;
-               core_sparse_checkout_cone = 1;
-       } else
-               mode = MODE_ALL_PATTERNS;
-
-       if (set_config(mode))
+       if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index))
                return 1;
 
        memset(&pl, 0, sizeof(pl));
@@ -429,17 +461,6 @@ static int sparse_checkout_init(int argc, const char **argv)
        sparse_filename = get_sparse_checkout_filename();
        res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
 
-       if (init_opts.sparse_index >= 0) {
-               if (set_sparse_index_config(the_repository, init_opts.sparse_index) < 0)
-                       die(_("failed to modify sparse-index config"));
-
-               /* force an index rewrite */
-               repo_read_index(the_repository);
-               the_repository->index->updated_workdir = 1;
-       }
-
-       core_apply_sparse_checkout = 1;
-
        /* If we already have a sparse-checkout file, use it. */
        if (res >= 0) {
                free(sparse_filename);
@@ -483,7 +504,7 @@ static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *pat
                char *oldpattern = e->pattern;
                size_t newlen;
 
-               if (slash == e->pattern)
+               if (!slash || slash == e->pattern)
                        break;
 
                newlen = slash - e->pattern;
@@ -515,17 +536,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
        insert_recursive_pattern(pl, line);
 }
 
-static char const * const builtin_sparse_checkout_set_usage[] = {
-       N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
-       NULL
-};
-
-static struct sparse_checkout_set_opts {
-       int use_stdin;
-} set_opts;
-
 static void add_patterns_from_input(struct pattern_list *pl,
-                                   int argc, const char **argv)
+                                   int argc, const char **argv,
+                                   int use_stdin)
 {
        int i;
        if (core_sparse_checkout_cone) {
@@ -535,7 +548,7 @@ static void add_patterns_from_input(struct pattern_list *pl,
                hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
                pl->use_cone_patterns = 1;
 
-               if (set_opts.use_stdin) {
+               if (use_stdin) {
                        struct strbuf unquoted = STRBUF_INIT;
                        while (!strbuf_getline(&line, stdin)) {
                                if (line.buf[0] == '"') {
@@ -559,7 +572,7 @@ static void add_patterns_from_input(struct pattern_list *pl,
                        }
                }
        } else {
-               if (set_opts.use_stdin) {
+               if (use_stdin) {
                        struct strbuf line = STRBUF_INIT;
 
                        while (!strbuf_getline(&line, stdin)) {
@@ -580,7 +593,8 @@ enum modify_type {
 };
 
 static void add_patterns_cone_mode(int argc, const char **argv,
-                                  struct pattern_list *pl)
+                                  struct pattern_list *pl,
+                                  int use_stdin)
 {
        struct strbuf buffer = STRBUF_INIT;
        struct pattern_entry *pe;
@@ -588,7 +602,7 @@ static void add_patterns_cone_mode(int argc, const char **argv,
        struct pattern_list existing;
        char *sparse_filename = get_sparse_checkout_filename();
 
-       add_patterns_from_input(pl, argc, argv);
+       add_patterns_from_input(pl, argc, argv, use_stdin);
 
        memset(&existing, 0, sizeof(existing));
        existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -598,6 +612,9 @@ static void add_patterns_cone_mode(int argc, const char **argv,
                die(_("unable to load existing sparse-checkout patterns"));
        free(sparse_filename);
 
+       if (!existing.use_cone_patterns)
+               die(_("existing sparse-checkout patterns do not use cone mode"));
+
        hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
                if (!hashmap_contains_parent(&pl->recursive_hashmap,
                                        pe->pattern, &buffer) ||
@@ -614,17 +631,19 @@ static void add_patterns_cone_mode(int argc, const char **argv,
 }
 
 static void add_patterns_literal(int argc, const char **argv,
-                                struct pattern_list *pl)
+                                struct pattern_list *pl,
+                                int use_stdin)
 {
        char *sparse_filename = get_sparse_checkout_filename();
        if (add_patterns_from_file_to_list(sparse_filename, "", 0,
                                           pl, NULL, 0))
                die(_("unable to load existing sparse-checkout patterns"));
        free(sparse_filename);
-       add_patterns_from_input(pl, argc, argv);
+       add_patterns_from_input(pl, argc, argv, use_stdin);
 }
 
-static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
+static int modify_pattern_list(int argc, const char **argv, int use_stdin,
+                              enum modify_type m)
 {
        int result;
        int changed_config = 0;
@@ -633,13 +652,13 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
        switch (m) {
        case ADD:
                if (core_sparse_checkout_cone)
-                       add_patterns_cone_mode(argc, argv, pl);
+                       add_patterns_cone_mode(argc, argv, pl, use_stdin);
                else
-                       add_patterns_literal(argc, argv, pl);
+                       add_patterns_literal(argc, argv, pl, use_stdin);
                break;
 
        case REPLACE:
-               add_patterns_from_input(pl, argc, argv);
+               add_patterns_from_input(pl, argc, argv, use_stdin);
                break;
        }
 
@@ -659,41 +678,124 @@ static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
        return result;
 }
 
-static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
-                              enum modify_type m)
+static char const * const builtin_sparse_checkout_add_usage[] = {
+       N_("git sparse-checkout add (--stdin | <patterns>)"),
+       NULL
+};
+
+static struct sparse_checkout_add_opts {
+       int use_stdin;
+} add_opts;
+
+static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
 {
-       static struct option builtin_sparse_checkout_set_options[] = {
-               OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+       static struct option builtin_sparse_checkout_add_options[] = {
+               OPT_BOOL(0, "stdin", &add_opts.use_stdin,
                         N_("read patterns from standard in")),
                OPT_END(),
        };
 
+       if (!core_apply_sparse_checkout)
+               die(_("no sparse-checkout to add to"));
+
        repo_read_index(the_repository);
 
+       argc = parse_options(argc, argv, prefix,
+                            builtin_sparse_checkout_add_options,
+                            builtin_sparse_checkout_add_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+
+       return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+       N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"),
+       NULL
+};
+
+static struct sparse_checkout_set_opts {
+       int cone_mode;
+       int sparse_index;
+       int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+       int default_patterns_nr = 2;
+       const char *default_patterns[] = {"/*", "!/*/", NULL};
+
+       static struct option builtin_sparse_checkout_set_options[] = {
+               OPT_BOOL(0, "cone", &set_opts.cone_mode,
+                        N_("initialize the sparse-checkout in cone mode")),
+               OPT_BOOL(0, "sparse-index", &set_opts.sparse_index,
+                        N_("toggle the use of a sparse index")),
+               OPT_BOOL_F(0, "stdin", &set_opts.use_stdin,
+                          N_("read patterns from standard in"),
+                          PARSE_OPT_NONEG),
+               OPT_END(),
+       };
+
+       repo_read_index(the_repository);
+
+       set_opts.cone_mode = -1;
+       set_opts.sparse_index = -1;
+
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_set_options,
                             builtin_sparse_checkout_set_usage,
                             PARSE_OPT_KEEP_UNKNOWN);
 
-       return modify_pattern_list(argc, argv, m);
+       if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
+               return 1;
+
+       /*
+        * Cone mode automatically specifies the toplevel directory.  For
+        * non-cone mode, if nothing is specified, manually select just the
+        * top-level directory (much as 'init' would do).
+        */
+       if (!core_sparse_checkout_cone && argc == 0) {
+               argv = default_patterns;
+               argc = default_patterns_nr;
+       }
+
+       return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
 }
 
 static char const * const builtin_sparse_checkout_reapply_usage[] = {
-       N_("git sparse-checkout reapply"),
+       N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"),
        NULL
 };
 
+static struct sparse_checkout_reapply_opts {
+       int cone_mode;
+       int sparse_index;
+} reapply_opts;
+
 static int sparse_checkout_reapply(int argc, const char **argv)
 {
        static struct option builtin_sparse_checkout_reapply_options[] = {
+               OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
+                        N_("initialize the sparse-checkout in cone mode")),
+               OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index,
+                        N_("toggle the use of a sparse index")),
                OPT_END(),
        };
 
+       if (!core_apply_sparse_checkout)
+               die(_("must be in a sparse-checkout to reapply sparsity patterns"));
+
        argc = parse_options(argc, argv, NULL,
                             builtin_sparse_checkout_reapply_options,
                             builtin_sparse_checkout_reapply_usage, 0);
 
        repo_read_index(the_repository);
+
+       reapply_opts.cone_mode = -1;
+       reapply_opts.sparse_index = -1;
+
+       if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
+               return 1;
+
        return update_working_directory(NULL);
 }
 
@@ -710,6 +812,17 @@ static int sparse_checkout_disable(int argc, const char **argv)
        struct pattern_list pl;
        struct strbuf match_all = STRBUF_INIT;
 
+       /*
+        * We do not exit early if !core_apply_sparse_checkout; due to the
+        * ability for users to manually muck things up between
+        *   direct editing of .git/info/sparse-checkout
+        *   running read-tree -m u HEAD or update-index --skip-worktree
+        *   direct toggling of config options
+        * users might end up with an index with SKIP_WORKTREE bit set on
+        * some files and not know how to undo it.  So, here we just
+        * forcibly return to a dense checkout regardless of initial state.
+        */
+
        argc = parse_options(argc, argv, NULL,
                             builtin_sparse_checkout_disable_options,
                             builtin_sparse_checkout_disable_usage, 0);
@@ -758,9 +871,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
                if (!strcmp(argv[0], "init"))
                        return sparse_checkout_init(argc, argv);
                if (!strcmp(argv[0], "set"))
-                       return sparse_checkout_set(argc, argv, prefix, REPLACE);
+                       return sparse_checkout_set(argc, argv, prefix);
                if (!strcmp(argv[0], "add"))
-                       return sparse_checkout_set(argc, argv, prefix, ADD);
+                       return sparse_checkout_add(argc, argv, prefix);
                if (!strcmp(argv[0], "reapply"))
                        return sparse_checkout_reapply(argc, argv);
                if (!strcmp(argv[0], "disable"))
index 18c812bbe032cc24e57bd3083822bb4753dfa1ca..1ef2017c595dc09d09457affb3f5e3c3a0e352ab 100644 (file)
@@ -561,18 +561,19 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                if (index)
                        fprintf_ln(stderr, _("Index was not unstashed."));
 
-               return ret;
+               goto restore_untracked;
        }
 
        if (has_index) {
                if (reset_tree(&index_tree, 0, 0))
-                       return -1;
+                       ret = -1;
        } else {
                unstage_changes_unless_new(&c_tree);
        }
 
+restore_untracked:
        if (info->has_u && restore_untracked(&info->u_tree))
-               return error(_("could not restore untracked files from stash"));
+               ret = error(_("could not restore untracked files from stash"));
 
        if (!quiet) {
                struct child_process cp = CHILD_PROCESS_INIT;
@@ -592,7 +593,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
                run_command(&cp);
        }
 
-       return 0;
+       return ret;
 }
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
@@ -1538,8 +1539,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
                        struct child_process cp = CHILD_PROCESS_INIT;
 
                        cp.git_cmd = 1;
+                       if (startup_info->original_cwd)
+                               cp.dir = startup_info->original_cwd;
                        strvec_pushl(&cp.args, "clean", "--force",
-                                    "--quiet", "-d", NULL);
+                                    "--quiet", "-d", ":/", NULL);
                        if (include_untracked == INCLUDE_ALL_FILES)
                                strvec_push(&cp.args, "-x");
                        if (run_command(&cp)) {
@@ -1681,6 +1684,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
        if (argc) {
                force_assume = !strcmp(argv[0], "-p");
                argc = parse_options(argc, argv, prefix, options,
+                                    push_assumed ? git_stash_usage :
                                     git_stash_push_usage,
                                     PARSE_OPT_KEEP_DASHDASH);
        }
@@ -1700,19 +1704,19 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 
        if (pathspec_from_file) {
                if (patch_mode)
-                       die(_("--pathspec-from-file is incompatible with --patch"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
 
                if (only_staged)
-                       die(_("--pathspec-from-file is incompatible with --staged"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--staged");
 
                if (ps.nr)
-                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+                       die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
                parse_pathspec_file(&ps, 0,
                                    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
                                    prefix, pathspec_from_file, pathspec_file_nul);
        } else if (pathspec_file_nul) {
-               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+               die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
        }
 
        return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
index 9b25a508e6a7bb6116b48b36ab4b15e8f670552b..c5d3fc3817f5990fec156496839ec29686e4b9e6 100644 (file)
@@ -1313,7 +1313,7 @@ static int module_summary(int argc, const char **argv, const char *prefix)
 
        if (files) {
                if (cached)
-                       die(_("--cached and --files are mutually exclusive"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--cached", "--files");
                diff_cmd = DIFF_FILES;
        }
 
@@ -2972,7 +2972,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
                die(_("--branch or --default required"));
 
        if (opt_branch && opt_default)
-               die(_("--branch and --default are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "--branch", "--default");
 
        if (argc != 1 || !(path = argv[0]))
                usage_with_options(usage, options);
index 41863c5ab7771a2495fe5845c884d99e83aa1537..134b3f1edf06246aed1eed1e5890a27fff22a3bf 100644 (file)
@@ -483,6 +483,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
        int ret = 0;
+       const char *only_in_list = NULL;
 
        setup_ref_filter_porcelain_msg();
 
@@ -522,7 +523,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        finalize_colopts(&colopts, -1);
        if (cmdmode == 'l' && filter.lines != -1) {
                if (explicitly_enable_column(colopts))
-                       die(_("--column and -n are incompatible"));
+                       die(_("options '%s' and '%s' cannot be used together"), "--column", "-n");
                colopts = 0;
        }
        sorting = ref_sorting_options(&sorting_options);
@@ -542,15 +543,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                goto cleanup;
        }
        if (filter.lines != -1)
-               die(_("-n option is only allowed in list mode"));
-       if (filter.with_commit)
-               die(_("--contains option is only allowed in list mode"));
-       if (filter.no_commit)
-               die(_("--no-contains option is only allowed in list mode"));
-       if (filter.points_at.nr)
-               die(_("--points-at option is only allowed in list mode"));
-       if (filter.reachable_from || filter.unreachable_from)
-               die(_("--merged and --no-merged options are only allowed in list mode"));
+               only_in_list = "-n";
+       else if (filter.with_commit)
+               only_in_list = "--contains";
+       else if (filter.no_commit)
+               only_in_list = "--no-contains";
+       else if (filter.points_at.nr)
+               only_in_list = "--points-at";
+       else if (filter.reachable_from)
+               only_in_list = "--merged";
+       else if (filter.unreachable_from)
+               only_in_list = "--no-merged";
+       if (only_in_list)
+               die(_("the '%s' option is only allowed in list mode"), only_in_list);
        if (cmdmode == 'd') {
                ret = delete_tags(argv);
                goto cleanup;
@@ -564,7 +569,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        if (msg.given || msgfile) {
                if (msg.given && msgfile)
-                       die(_("only one -F or -m option is allowed."));
+                       die(_("options '%s' and '%s' cannot be used together"), "-F", "-m");
                if (msg.given)
                        strbuf_addbuf(&buf, &(msg.buf));
                else {
index 24654b4c9bf0664e4a2f41b42d3224062199ca81..98d028dae679080af9785d57bc83525d5cd2872f 100644 (file)
@@ -77,7 +77,7 @@ static ssize_t process_input(int child_fd, int band)
 
 int cmd_upload_archive(int argc, const char **argv, const char *prefix)
 {
-       struct child_process writer = { argv };
+       struct child_process writer = CHILD_PROCESS_INIT;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(upload_archive_usage);
@@ -89,9 +89,10 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
         * multiplexed out to our fd#1.  If the child dies, we tell the other
         * end over channel #3.
         */
-       argv[0] = "upload-archive--writer";
        writer.out = writer.err = -1;
        writer.git_cmd = 1;
+       strvec_push(&writer.args, "upload-archive--writer");
+       strvec_pushv(&writer.args, argv + 1);
        if (start_command(&writer)) {
                int err = errno;
                packet_write_fmt(1, "NACK unable to spawn subprocess\n");
index d22ece93e1a80597e2a14950d7362c37b10c2945..2838254f7f2e10f780ea8608d591c6fd8e8f5521 100644 (file)
@@ -72,7 +72,7 @@ static void delete_worktrees_dir_if_empty(void)
 static void prune_worktree(const char *id, const char *reason)
 {
        if (show_only || verbose)
-               printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
+               fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
        if (!show_only)
                delete_git_dir(id);
 }
@@ -349,18 +349,18 @@ static int add_worktree(const char *path, const char *refname,
                        strvec_push(&cp.args, "--quiet");
        }
 
-       cp.env = child_env.v;
+       strvec_pushv(&cp.env_array, child_env.v);
        ret = run_command(&cp);
        if (ret)
                goto done;
 
        if (opts->checkout) {
-               cp.argv = NULL;
-               strvec_clear(&cp.args);
+               struct child_process cp = CHILD_PROCESS_INIT;
+               cp.git_cmd = 1;
                strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
                if (opts->quiet)
                        strvec_push(&cp.args, "--quiet");
-               cp.env = child_env.v;
+               strvec_pushv(&cp.env_array, child_env.v);
                ret = run_command(&cp);
                if (ret)
                        goto done;
@@ -385,12 +385,11 @@ done:
                const char *hook = find_hook("post-checkout");
                if (hook) {
                        const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
-                       cp.git_cmd = 0;
+                       struct child_process cp = CHILD_PROCESS_INIT;
                        cp.no_stdin = 1;
                        cp.stdout_to_stderr = 1;
                        cp.dir = path;
-                       cp.env = env;
-                       cp.argv = NULL;
+                       strvec_pushv(&cp.env_array, env);
                        cp.trace2_hook_name = "post-checkout";
                        strvec_pushl(&cp.args, absolute_path(hook),
                                     oid_to_hex(null_oid()),
@@ -418,24 +417,24 @@ static void print_preparing_worktree_line(int detach,
        if (force_new_branch) {
                struct commit *commit = lookup_commit_reference_by_name(new_branch);
                if (!commit)
-                       printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+                       fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
                else
-                       printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
+                       fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
                                  new_branch,
                                  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
        } else if (new_branch) {
-               printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+               fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
        } else {
                struct strbuf s = STRBUF_INIT;
                if (!detach && !strbuf_check_branch_ref(&s, branch) &&
                    ref_exists(s.buf))
-                       printf_ln(_("Preparing worktree (checking out '%s')"),
+                       fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
                                  branch);
                else {
                        struct commit *commit = lookup_commit_reference_by_name(branch);
                        if (!commit)
                                die(_("invalid reference: %s"), branch);
-                       printf_ln(_("Preparing worktree (detached HEAD %s)"),
+                       fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
                                  find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
                }
                strbuf_release(&s);
@@ -504,9 +503,9 @@ static int add(int ac, const char **av, const char *prefix)
        opts.checkout = 1;
        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
        if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
-               die(_("-b, -B, and --detach are mutually exclusive"));
+               die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
        if (lock_reason && !keep_locked)
-               die(_("--reason requires --lock"));
+               die(_("the option '%s' requires '%s'"), "--reason", "--lock");
        if (lock_reason)
                opts.keep_locked = lock_reason;
        else if (keep_locked)
@@ -700,7 +699,7 @@ static int list(int ac, const char **av, const char *prefix)
        if (ac)
                usage_with_options(worktree_usage, options);
        else if (verbose && porcelain)
-               die(_("--verbose and --porcelain are mutually exclusive"));
+               die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
        else {
                struct worktree **worktrees = get_worktrees();
                int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
@@ -1006,7 +1005,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
 static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
 {
        if (!iserr) {
-               printf_ln(_("repair: %s: %s"), msg, path);
+               fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
        } else {
                int *exit_status = (int *)cb_data;
                fprintf_ln(stderr, _("error: %s: %s"), msg, path);
diff --git a/cache.h b/cache.h
index d5cafba17d4ff37eadd65c0e1d5d9716a2266659..281f00ab1b161dc71d0bcdae91222e9429ba0342 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -350,8 +350,6 @@ void add_name_hash(struct index_state *istate, struct cache_entry *ce);
 void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
 void free_name_hash(struct index_state *istate);
 
-void ensure_full_index(struct index_state *istate);
-
 /* Cache entry creation and cleanup */
 
 /*
@@ -996,6 +994,7 @@ extern int read_replace_refs;
 extern char *git_replace_ref_base;
 
 extern int fsync_object_files;
+extern int use_fsync;
 extern int core_preload_index;
 extern int precomposed_unicode;
 extern int protect_hfs;
@@ -1845,8 +1844,10 @@ void overlay_tree_on_index(struct index_state *istate,
 struct startup_info {
        int have_repository;
        const char *prefix;
+       const char *original_cwd;
 };
 extern struct startup_info *startup_info;
+extern const char *tmp_original_cwd;
 
 /* merge.c */
 struct commit_list;
index b0c65d810f5e5432a8e04270d6c80deefab6d052..336e46dbba5a06e7d9b5fdb5dd6f3c7bdd971186 100644 (file)
--- a/cbtree.c
+++ b/cbtree.c
@@ -95,38 +95,6 @@ struct cb_node *cb_lookup(struct cb_tree *t, const uint8_t *k, size_t klen)
        return p && !memcmp(p->k, k, klen) ? p : NULL;
 }
 
-struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen)
-{
-       struct cb_node **wherep = &t->root;
-       struct cb_node **whereq = NULL;
-       struct cb_node *q = NULL;
-       size_t direction = 0;
-       uint8_t c;
-       struct cb_node *p = t->root;
-
-       if (!p) return NULL;    /* empty tree, nothing to delete */
-
-       /* traverse to find best match, keeping link to parent */
-       while (1 & (uintptr_t)p) {
-               whereq = wherep;
-               q = cb_node_of(p);
-               c = q->byte < klen ? k[q->byte] : 0;
-               direction = (1 + (q->otherbits | c)) >> 8;
-               wherep = q->child + direction;
-               p = *wherep;
-       }
-
-       if (memcmp(p->k, k, klen))
-               return NULL;            /* no match, nothing unlinked */
-
-       /* found an exact match */
-       if (whereq)     /* update parent */
-               *whereq = q->child[1 - direction];
-       else
-               t->root = NULL;
-       return p;
-}
-
 static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg)
 {
        if (1 & (uintptr_t)p) {
index dedbb8e2a459c1a704ce9198ca44c13c24a25251..0be14fb7ee4276dff338eb27b35c68973dd2c4b2 100644 (file)
--- a/cbtree.h
+++ b/cbtree.h
@@ -47,7 +47,6 @@ static inline void cb_init(struct cb_tree *t)
 
 struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
 struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen);
-struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen);
 
 typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg);
 
index 1d0e48f451558e685602ac362270d169b42caa92..dbcebad2fb293303f9271850094a29c3a249bda6 100755 (executable)
@@ -11,18 +11,11 @@ UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
  tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl
  libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
 
-case "$jobname" in
-linux-clang|linux-gcc|linux-leaks)
-       sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
+case "$runs_on_pool" in
+ubuntu-latest)
        sudo apt-get -q update
        sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
-               $UBUNTU_COMMON_PKGS
-       case "$jobname" in
-       linux-gcc)
-               sudo apt-get -q -y install gcc-8
-               ;;
-       esac
-
+               $UBUNTU_COMMON_PKGS $CC_PACKAGE
        mkdir --parents "$P4_PATH"
        pushd "$P4_PATH"
                wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
@@ -37,7 +30,7 @@ linux-clang|linux-gcc|linux-leaks)
                cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
        popd
        ;;
-osx-clang|osx-gcc)
+macos-latest)
        export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
        # Uncomment this if you want to run perf tests:
        # brew install gnu-time
@@ -51,15 +44,17 @@ osx-clang|osx-gcc)
                brew install --cask --no-quarantine perforce
        } ||
        brew install homebrew/cask/perforce
-       case "$jobname" in
-       osx-gcc)
-               brew install gcc@9
-               # Just in case the image is updated to contain gcc@9
-               # pre-installed but not linked.
-               brew link gcc@9
-               ;;
-       esac
+
+       if test -n "$CC_PACKAGE"
+       then
+               BREW_PACKAGE=${CC_PACKAGE/-/@}
+               brew install "$BREW_PACKAGE"
+               brew link "$BREW_PACKAGE"
+       fi
        ;;
+esac
+
+case "$jobname" in
 StaticAnalysis)
        sudo apt-get -q update
        sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
@@ -77,7 +72,7 @@ Documentation)
        test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
        sudo gem install --version 1.5.8 asciidoctor
        ;;
-linux-gcc-default|linux-gcc-4.8)
+linux-gcc-default)
        sudo apt-get -q update
        sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
        ;;
index 07a8c6b199d39cc1df0e1a2bd739c49820741fd0..78b7e326da6d8b6b3d1923007d3f22dea9814b8d 100755 (executable)
@@ -4,7 +4,7 @@
 #
 
 case "$jobname" in
-Linux32)
+linux32)
        linux32 --32bit i386 sh -c '
                apt update >/dev/null &&
                apt install -y build-essential libcurl4-openssl-dev \
index 994050f7e7b4c6166ca101a1dbbf7bb65378d00c..9d28ab50fb4462a1b064e8c89cd5f13518fd86cd 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -34,7 +34,7 @@ save_good_tree () {
 # successfully before (e.g. because the branch got rebased, changing only
 # the commit messages).
 skip_good_tree () {
-       if test "$TRAVIS_DEBUG_MODE" = true || test true = "$GITHUB_ACTIONS"
+       if test true = "$GITHUB_ACTIONS"
        then
                return
        fi
@@ -60,7 +60,7 @@ skip_good_tree () {
                        cat <<-EOF
                        $(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
                        This commit's tree has already been built and tested successfully in build job $prev_good_job_number for commit $prev_good_commit.
-                       The log of that build job is available at $(url_for_job_id $prev_good_job_id)
+                       The log of that build job is available at $SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$prev_good_job_id
                        To force a re-build delete the branch's cache and then hit 'Restart job'.
                        EOF
                fi
@@ -91,29 +91,7 @@ export MAKEFLAGS=
 # and installing dependencies.
 set -ex
 
-if test true = "$TRAVIS"
-then
-       CI_TYPE=travis
-       # When building a PR, TRAVIS_BRANCH refers to the *target* branch. Not
-       # what we want here. We want the source branch instead.
-       CI_BRANCH="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
-       CI_COMMIT="$TRAVIS_COMMIT"
-       CI_JOB_ID="$TRAVIS_JOB_ID"
-       CI_JOB_NUMBER="$TRAVIS_JOB_NUMBER"
-       CI_OS_NAME="$TRAVIS_OS_NAME"
-       CI_REPO_SLUG="$TRAVIS_REPO_SLUG"
-
-       cache_dir="$HOME/travis-cache"
-
-       url_for_job_id () {
-               echo "https://travis-ci.org/$CI_REPO_SLUG/jobs/$1"
-       }
-
-       BREW_INSTALL_PACKAGES="git-lfs gettext"
-       export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
-       export GIT_TEST_OPTS="--verbose-log -x --immediate"
-       MAKEFLAGS="$MAKEFLAGS --jobs=2"
-elif test -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI"
+if test -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI"
 then
        CI_TYPE=azure-pipelines
        # We are running in Azure Pipelines
@@ -130,10 +108,6 @@ then
        # among *all* phases)
        cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
 
-       url_for_job_id () {
-               echo "$SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$1"
-       }
-
        export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
        export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
        MAKEFLAGS="$MAKEFLAGS --jobs=10"
@@ -182,11 +156,15 @@ export DEFAULT_TEST_TARGET=prove
 export GIT_TEST_CLONE_2GB=true
 export SKIP_DASHED_BUILT_INS=YesPlease
 
-case "$jobname" in
-linux-clang|linux-gcc|linux-leaks)
+case "$runs_on_pool" in
+ubuntu-latest)
+       if test "$jobname" = "linux-gcc-default"
+       then
+               break
+       fi
+
        if [ "$jobname" = linux-gcc ]
        then
-               export CC=gcc-8
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3"
        else
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python2"
@@ -206,23 +184,18 @@ linux-clang|linux-gcc|linux-leaks)
        GIT_LFS_PATH="$HOME/custom/git-lfs"
        export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
        ;;
-osx-clang|osx-gcc)
+macos-latest)
        if [ "$jobname" = osx-gcc ]
        then
-               export CC=gcc-9
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
        else
                MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
        fi
-
-       # t9810 occasionally fails on Travis CI OS X
-       # t9816 occasionally fails with "TAP out of sequence errors" on
-       # Travis CI OS X
-       export GIT_SKIP_TESTS="t9810 t9816"
-       ;;
-linux-gcc-default)
        ;;
-Linux32)
+esac
+
+case "$jobname" in
+linux32)
        CC=gcc
        MAKEFLAGS="$MAKEFLAGS NO_UNCOMPRESS2=1"
        ;;
@@ -232,9 +205,6 @@ linux-musl)
        MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes"
        MAKEFLAGS="$MAKEFLAGS GIT_TEST_UTF8_LOCALE=C.UTF-8"
        ;;
-esac
-
-case "$jobname" in
 linux-leaks)
        export SANITIZE=leak
        export GIT_TEST_PASSING_SANITIZE_LEAK=true
index c70d6cdbf243db05f9e5718070895abd837d617a..57277eefcd0c8b6117fd71e9adb3a526baa05ef1 100755 (executable)
@@ -39,8 +39,6 @@ do
                test_name="${test_name##*/}"
                trash_dir="trash directory.$test_name"
                case "$CI_TYPE" in
-               travis)
-                       ;;
                azure-pipelines)
                        mkdir -p failed-test-artifacts
                        mv "$trash_dir" failed-test-artifacts
@@ -88,11 +86,3 @@ do
                fi
        fi
 done
-
-if [ $combined_trash_size -gt 0 ]
-then
-       echo "------------------------------------------------------------------------"
-       echo "Trash directories embedded in this log can be extracted by running:"
-       echo
-       echo "  curl https://api.travis-ci.org/v3/job/$TRAVIS_JOB_ID/log.txt |./ci/util/extract-trash-dirs.sh"
-fi
index cc62616d8063cb5eadc18636d561b52a71253d90..280dda7d285674e121ac98ed444e72380168dd13 100755 (executable)
@@ -10,16 +10,13 @@ windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";;
 *) ln -s "$cache_dir/.prove" t/.prove;;
 esac
 
-if test "$jobname" = "pedantic"
-then
-       export DEVOPTS=pedantic
-fi
+export MAKE_TARGETS="all test"
 
-make
 case "$jobname" in
 linux-gcc)
        export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-       make test
+       ;;
+linux-TEST-vars)
        export GIT_TEST_SPLIT_INDEX=yes
        export GIT_TEST_MERGE_ALGORITHM=recursive
        export GIT_TEST_FULL_IN_PACK_ARRAY=true
@@ -33,23 +30,25 @@ linux-gcc)
        export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
        export GIT_TEST_WRITE_REV_INDEX=1
        export GIT_TEST_CHECKOUT_WORKERS=2
-       make test
        ;;
 linux-clang)
        export GIT_TEST_DEFAULT_HASH=sha1
-       make test
+       ;;
+linux-sha256)
        export GIT_TEST_DEFAULT_HASH=sha256
-       make test
        ;;
-linux-gcc-4.8|pedantic)
+pedantic)
        # Don't run the tests; we only care about whether Git can be
-       # built with GCC 4.8 or with pedantic
-       ;;
-*)
-       make test
+       # built.
+       export DEVOPTS=pedantic
+       export MAKE_TARGETS=all
        ;;
 esac
 
+# Any new "test" targets should not go after this "make", but should
+# adjust $MAKE_TARGETS. Otherwise compilation-only targets above will
+# start running tests.
+make $MAKE_TARGETS
 check_unignored_build_artifacts
 
 save_good_tree
index 8d47a5fda3b1c929a68662cfca3625e63e122194..6cd832efb9cb59687fac4cf95bf471bc87923a96 100755 (executable)
@@ -15,7 +15,7 @@ then
 fi
 
 case "$jobname" in
-Linux32)
+linux32)
        switch_cmd="linux32 --32bit i386"
        ;;
 linux-musl)
@@ -47,15 +47,6 @@ else
        else
                useradd -u $HOST_UID $CI_USER
        fi
-
-       # Due to a bug the test suite was run as root in the past, so
-       # a prove state file created back then is only accessible by
-       # root.  Now that bug is fixed, the test suite is run as a
-       # regular user, but the prove state file coming from Travis
-       # CI's cache might still be owned by root.
-       # Make sure that this user has rights to any cached files,
-       # including an existing prove state file.
-       test -n "$cache_dir" && chown -R $HOST_UID:$HOST_UID "$cache_dir"
 fi
 
 # Build and test
index 37fa372052ddb8a9aa9fb6a42ed3866a316a1227..af89d1624a41cbe3e51e6c10d65f72082266c490 100755 (executable)
@@ -6,7 +6,7 @@
 . ${0%/*}/lib.sh
 
 case "$jobname" in
-Linux32)
+linux32)
        CI_CONTAINER="daald/ubuntu32:xenial"
        ;;
 linux-musl)
@@ -25,7 +25,7 @@ docker pull "$CI_CONTAINER"
 # root@container:/# export jobname=<jobname>
 # root@container:/# /usr/src/git/ci/run-docker-build.sh <host-user-id>
 
-container_cache_dir=/tmp/travis-cache
+container_cache_dir=/tmp/container-cache
 
 docker run \
        --interactive \
diff --git a/color.c b/color.c
index 64f52a4f93a21c4abe5e40e8957ec6115ca46c2a..4f884c6b3dc1d98c99d3b5e5be0e990f58434d8e 100644 (file)
--- a/color.c
+++ b/color.c
@@ -40,7 +40,7 @@ struct color {
        enum {
                COLOR_UNSPECIFIED = 0,
                COLOR_NORMAL,
-               COLOR_ANSI, /* basic 0-7 ANSI colors */
+               COLOR_ANSI, /* basic 0-7 ANSI colors + "default" (value = 9) */
                COLOR_256,
                COLOR_RGB
        } type;
@@ -83,6 +83,27 @@ static int parse_ansi_color(struct color *out, const char *name, int len)
        int i;
        int color_offset = COLOR_FOREGROUND_ANSI;
 
+       if (match_word(name, len, "default")) {
+               /*
+                * Restores to the terminal's default color, which may not be
+                * the same as explicitly setting "white" or "black".
+                *
+                * ECMA-48 - Control Functions \
+                *  for Coded Character Sets, 5th edition (June 1991):
+                * > 39 default display colour (implementation-defined)
+                * > 49 default background colour (implementation-defined)
+                *
+                * Although not supported /everywhere/--according to terminfo,
+                * some terminals define "op" (original pair) as a blunt
+                * "set to white on black", or even "send full SGR reset"--
+                * it's standard and well-supported enough that if a user
+                * asks for it in their config this will do the right thing.
+                */
+               out->type = COLOR_ANSI;
+               out->value = 9 + color_offset;
+               return 0;
+       }
+
        if (strncasecmp(name, "bright", 6) == 0) {
                color_offset = COLOR_FOREGROUND_BRIGHT_ANSI;
                name += 6;
@@ -234,6 +255,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
        const char *ptr = value;
        int len = value_len;
        char *end = dst + COLOR_MAXLEN;
+       unsigned int has_reset = 0;
        unsigned int attr = 0;
        struct color fg = { COLOR_UNSPECIFIED };
        struct color bg = { COLOR_UNSPECIFIED };
@@ -248,12 +270,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                return 0;
        }
 
-       if (!strncasecmp(ptr, "reset", len)) {
-               xsnprintf(dst, end - dst, GIT_COLOR_RESET);
-               return 0;
-       }
-
-       /* [fg [bg]] [attr]... */
+       /* [reset] [fg [bg]] [attr]... */
        while (len > 0) {
                const char *word = ptr;
                struct color c = { COLOR_UNSPECIFIED };
@@ -270,6 +287,11 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                        len--;
                }
 
+               if (match_word(word, wordlen, "reset")) {
+                       has_reset = 1;
+                       continue;
+               }
+
                if (!parse_color(&c, word, wordlen)) {
                        if (fg.type == COLOR_UNSPECIFIED) {
                                fg = c;
@@ -295,13 +317,16 @@ int color_parse_mem(const char *value, int value_len, char *dst)
        *dst++ = (x); \
 } while(0)
 
-       if (attr || !color_empty(&fg) || !color_empty(&bg)) {
+       if (has_reset || attr || !color_empty(&fg) || !color_empty(&bg)) {
                int sep = 0;
                int i;
 
                OUT('\033');
                OUT('[');
 
+               if (has_reset)
+                       sep++;
+
                for (i = 0; attr; i++) {
                        unsigned bit = (1 << i);
                        if (!(attr & bit))
diff --git a/color.h b/color.h
index 98894d6a17563d7005a2ba3a1fb6070c06cbcefc..cfc8f841b237c72a0a2ea8b7e42d58dc1cab5f9f 100644 (file)
--- a/color.h
+++ b/color.h
@@ -6,6 +6,7 @@ struct strbuf;
 /*
  * The maximum length of ANSI color sequence we would generate:
  * - leading ESC '['            2
+ * - reset ';' .................1
  * - attr + ';'                 2 * num_attr (e.g. "1;")
  * - no-attr + ';'              3 * num_attr (e.g. "22;")
  * - fg color + ';'             17 (e.g. "38;2;255;255;255;")
@@ -24,30 +25,42 @@ struct strbuf;
 #define GIT_COLOR_NORMAL       ""
 #define GIT_COLOR_RESET                "\033[m"
 #define GIT_COLOR_BOLD         "\033[1m"
+#define GIT_COLOR_BLACK                "\033[30m"
 #define GIT_COLOR_RED          "\033[31m"
 #define GIT_COLOR_GREEN                "\033[32m"
 #define GIT_COLOR_YELLOW       "\033[33m"
 #define GIT_COLOR_BLUE         "\033[34m"
 #define GIT_COLOR_MAGENTA      "\033[35m"
 #define GIT_COLOR_CYAN         "\033[36m"
+#define GIT_COLOR_WHITE                "\033[37m"
+#define GIT_COLOR_DEFAULT      "\033[39m"
+#define GIT_COLOR_BOLD_BLACK   "\033[1;30m"
 #define GIT_COLOR_BOLD_RED     "\033[1;31m"
 #define GIT_COLOR_BOLD_GREEN   "\033[1;32m"
 #define GIT_COLOR_BOLD_YELLOW  "\033[1;33m"
 #define GIT_COLOR_BOLD_BLUE    "\033[1;34m"
 #define GIT_COLOR_BOLD_MAGENTA "\033[1;35m"
 #define GIT_COLOR_BOLD_CYAN    "\033[1;36m"
+#define GIT_COLOR_BOLD_WHITE   "\033[1;37m"
+#define GIT_COLOR_BOLD_DEFAULT "\033[1;39m"
+#define GIT_COLOR_FAINT_BLACK  "\033[2;30m"
 #define GIT_COLOR_FAINT_RED    "\033[2;31m"
 #define GIT_COLOR_FAINT_GREEN  "\033[2;32m"
 #define GIT_COLOR_FAINT_YELLOW "\033[2;33m"
 #define GIT_COLOR_FAINT_BLUE   "\033[2;34m"
 #define GIT_COLOR_FAINT_MAGENTA        "\033[2;35m"
 #define GIT_COLOR_FAINT_CYAN   "\033[2;36m"
+#define GIT_COLOR_FAINT_WHITE  "\033[2;37m"
+#define GIT_COLOR_FAINT_DEFAULT        "\033[2;39m"
+#define GIT_COLOR_BG_BLACK     "\033[40m"
 #define GIT_COLOR_BG_RED       "\033[41m"
 #define GIT_COLOR_BG_GREEN     "\033[42m"
 #define GIT_COLOR_BG_YELLOW    "\033[43m"
 #define GIT_COLOR_BG_BLUE      "\033[44m"
 #define GIT_COLOR_BG_MAGENTA   "\033[45m"
 #define GIT_COLOR_BG_CYAN      "\033[46m"
+#define GIT_COLOR_BG_WHITE     "\033[47m"
+#define GIT_COLOR_BG_DEFAULT   "\033[49m"
 #define GIT_COLOR_FAINT                "\033[2m"
 #define GIT_COLOR_FAINT_ITALIC "\033[2;3m"
 #define GIT_COLOR_REVERSE      "\033[7m"
index 2706683acfe1c1fc982b1c3119b3e895cac57006..265c010122e8edefc141f1dec8f506078bbbee19 100644 (file)
@@ -632,10 +632,13 @@ static int prepare_commit_graph(struct repository *r)
        struct object_directory *odb;
 
        /*
+        * Early return if there is no git dir or if the commit graph is
+        * disabled.
+        *
         * This must come before the "already attempted?" check below, because
         * we want to disable even an already-loaded graph file.
         */
-       if (r->commit_graph_disabled)
+       if (!r->gitdir || r->commit_graph_disabled)
                return 0;
 
        if (r->objects->commit_graph_attempted)
index 551de4903c1f4f5d21a4e30bc012109c593054c8..a348f085b2b853d2f14d6848f3ff57edf85601b8 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1212,8 +1212,10 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
 
        if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
-       ret = check_signature(payload.buf, payload.len, signature.buf,
-               signature.len, sigc);
+
+       sigc->payload_type = SIGNATURE_PAYLOAD_COMMIT;
+       sigc->payload = strbuf_detach(&payload, &sigc->payload_len);
+       ret = check_signature(sigc, signature.buf, signature.len);
 
  out:
        strbuf_release(&payload);
index 71e21dd20a3b141bed0d37512cdc9196321dc315..29fb7452f8a0b78cf7784ee4a1af7b26ebb53379 100644 (file)
@@ -26,6 +26,7 @@ static void restore_sigpipe_to_default(void)
 int main(int argc, const char **argv)
 {
        int result;
+       struct strbuf tmp = STRBUF_INIT;
 
        trace2_initialize_clock();
 
@@ -49,9 +50,15 @@ int main(int argc, const char **argv)
        trace2_cmd_start(argv);
        trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
 
-       result = cmd_main(argc, argv);
+       if (!strbuf_getcwd(&tmp))
+               tmp_original_cwd = strbuf_detach(&tmp, NULL);
 
-       trace2_cmd_exit(result);
+       result = cmd_main(argc, argv);
 
-       return result;
+       /*
+        * We define exit() to call trace2_cmd_exit_fl() in
+        * git-compat-util.h. Whether we reach this or exit()
+        * elsewhere we'll always run our trace2 exit handler.
+        */
+       exit(result);
 }
index e14f2d5f77ce7c5f7790f6f1f9143cc3fdae67b0..640dcb11de0dd6421c66a70a660a7fbc8bae4530 100644 (file)
@@ -8,6 +8,8 @@
 #include "win32/lazyload.h"
 #include "../config.h"
 #include "dir.h"
+#define SECURITY_WIN32
+#include <sspi.h>
 
 #define HCAST(type, handle) ((type)(intptr_t)handle)
 
@@ -1008,7 +1010,7 @@ size_t mingw_strftime(char *s, size_t max,
        /* a pointer to the original strftime in case we can't find the UCRT version */
        static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime;
        size_t ret;
-       DECLARE_PROC_ADDR(ucrtbase.dll, size_t, strftime, char *, size_t,
+       DECLARE_PROC_ADDR(ucrtbase.dll, size_t, __cdecl, strftime, char *, size_t,
                const char *, const struct tm *);
 
        if (INIT_PROC_ADDR(strftime))
@@ -2185,7 +2187,7 @@ enum EXTENDED_NAME_FORMAT {
 
 static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
 {
-       DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+       DECLARE_PROC_ADDR(secur32.dll, BOOL, SEC_ENTRY, GetUserNameExW,
                enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
        static wchar_t wbuffer[1024];
        DWORD len;
index 2b3637135f68a3e7c2acadf0971e143cf689db02..f2bb96c89c735c67ab9915e6ced1e9feabb043b4 100644 (file)
@@ -4,7 +4,7 @@
 /*
  * A pair of macros to simplify loading of DLL functions. Example:
  *
- *   DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW,
+ *   DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI, CreateHardLinkW,
  *                     LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
  *
  *   if (!INIT_PROC_ADDR(CreateHardLinkW))
@@ -25,10 +25,10 @@ struct proc_addr {
 };
 
 /* Declares a function to be loaded dynamically from a DLL. */
-#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
+#define DECLARE_PROC_ADDR(dll, rettype, convention, function, ...) \
        static struct proc_addr proc_addr_##function = \
        { #dll, #function, NULL, 0 }; \
-       typedef rettype (WINAPI *proc_type_##function)(__VA_ARGS__); \
+       typedef rettype (convention *proc_type_##function)(__VA_ARGS__); \
        static proc_type_##function function
 
 /*
index 8ccbd1c2c6f82d68e0c39f8ec77aa2bd4a899f2e..a53fd924340f7d6da04e5197363fe831b9d19bf3 100644 (file)
@@ -143,8 +143,8 @@ static void get_is_being_debugged(void)
  */
 static void get_peak_memory_info(void)
 {
-       DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
-                         PPROCESS_MEMORY_COUNTERS, DWORD);
+       DECLARE_PROC_ADDR(psapi.dll, BOOL, WINAPI, GetProcessMemoryInfo,
+                         HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
 
        if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
                PROCESS_MEMORY_COUNTERS pmc;
index c27b20a79d91cf7877d795c96e64cbadafdad7fd..4fceecf14ce599b585801bcb1ebc0b4e2c22af06 100644 (file)
@@ -45,8 +45,9 @@ typedef struct _CONSOLE_FONT_INFOEX {
 static void warn_if_raster_font(void)
 {
        DWORD fontFamily = 0;
-       DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
-                       HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
+       DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI,
+                       GetCurrentConsoleFontEx, HANDLE, BOOL,
+                       PCONSOLE_FONT_INFOEX);
 
        /* don't bother if output was ascii only */
        if (!non_ascii_used)
index c5873f3a70643a9c03feeec4981a06a439e78118..2bffa8d4a01ba1f281d6e6fd95f35bf133cbd9c5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1559,9 +1559,12 @@ static int git_default_i18n_config(const char *var, const char *value)
 static int git_default_branch_config(const char *var, const char *value)
 {
        if (!strcmp(var, "branch.autosetupmerge")) {
-               if (value && !strcasecmp(value, "always")) {
+               if (value && !strcmp(value, "always")) {
                        git_branch_track = BRANCH_TRACK_ALWAYS;
                        return 0;
+               } else if (value && !strcmp(value, "inherit")) {
+                       git_branch_track = BRANCH_TRACK_INHERIT;
+                       return 0;
                }
                git_branch_track = git_config_bool(var, value);
                return 0;
@@ -2555,11 +2558,12 @@ void git_die_config(const char *key, const char *err, ...)
 {
        const struct string_list *values;
        struct key_value_info *kv_info;
+       report_fn error_fn = get_error_routine();
 
        if (err) {
                va_list params;
                va_start(params, err);
-               vreportf("error: ", err, params);
+               error_fn(err, params);
                va_end(params);
        }
        values = git_config_get_value_multi(key);
index 7673fed11425409c9a7fd584fb4387e79678d498..d4afac6b51fa9100e9f751ec0006cae5a259adc4 100644 (file)
@@ -19,6 +19,11 @@ endif
 endif
 endif
 endif
+
+ifneq ($(or $(filter gcc6,$(COMPILER_FEATURES)),$(filter clang7,$(COMPILER_FEATURES))),)
+DEVELOPER_CFLAGS += -std=gnu99
+endif
+
 DEVELOPER_CFLAGS += -Wdeclaration-after-statement
 DEVELOPER_CFLAGS += -Wformat-security
 DEVELOPER_CFLAGS += -Wold-style-definition
index 5ed1d8755fd07dea221e5c3a8c564f0b34c49e9b..c48db45106c8dc0e4138ccb8ecff016f3f7d0faa 100644 (file)
@@ -581,6 +581,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
        NO_SETENV = YesPlease
        NO_UNSETENV = YesPlease
        NO_MKDTEMP = YesPlease
+       NO_UNCOMPRESS2 = YesPlease
        # Currently libiconv-1.9.1.
        OLD_ICONV = UnfortunatelyYes
        NO_REGEX = NeedsStartEnd
index 35bd4a26382a445302aa6239c8661b223b478781..ed3025e7a2a7cffdc6607aa9408d097f6d537768 100644 (file)
@@ -109,7 +109,8 @@ no_promisor_pack_found:
                             _("Checking connectivity"));
 
        rev_list.git_cmd = 1;
-       rev_list.env = opt->env;
+       if (opt->env)
+               strvec_pushv(&rev_list.env_array, opt->env);
        rev_list.in = -1;
        rev_list.no_stdout = 1;
        if (opt->err_fd)
index 63b3a6b22e65612f1425d4a091c6a5040c0ccc31..377d6c5494ac9643ce3435cf270cd8a2420c0287 100644 (file)
@@ -1566,7 +1566,7 @@ _git_checkout ()
 
        case "$cur" in
        --conflict=*)
-               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
                ;;
        --*)
                __gitcomp_builtin checkout
@@ -2437,7 +2437,7 @@ _git_switch ()
 
        case "$cur" in
        --conflict=*)
-               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
                ;;
        --*)
                __gitcomp_builtin switch
@@ -2877,7 +2877,7 @@ _git_restore ()
 
        case "$cur" in
        --conflict=*)
-               __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+               __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
                ;;
        --source=*)
                __git_complete_refs --cur="${cur##--source=}"
index 016454749f8dfa8539c7af1ca7fe4338414ab661..d3e73126595df72b5d422b6afe708f16f15e9894 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'creating page w/ >500 revisions' '
        for i in $(test_seq 501)
        do
                echo "creating revision $i" &&
-               wiki_editpage foo "revision $i<br/>" true
+               wiki_editpage foo "revision $i<br/>" true || return 1
        done
 '
 
diff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore
new file mode 100644 (file)
index 0000000..ff3d47e
--- /dev/null
@@ -0,0 +1,2 @@
+/*.exe
+/scalar
diff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile
new file mode 100644 (file)
index 0000000..231b1ee
--- /dev/null
@@ -0,0 +1,45 @@
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring s,$(MAKEFLAGS)),s)
+ifndef V
+       QUIET_GEN      = @echo '   ' GEN $@;
+       QUIET_SUBDIR0  = +@subdir=
+       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+                        $(MAKE) $(PRINT_DIR) -C $$subdir
+else
+       export V
+endif
+endif
+
+all:
+
+include ../../config.mak.uname
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+TARGETS = scalar$(X) scalar.o
+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a
+
+all: scalar$(X) ../../bin-wrappers/scalar
+
+$(GITLIBS):
+       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)
+
+$(TARGETS): $(GITLIBS) scalar.c
+       $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)
+
+clean:
+       $(RM) $(TARGETS) ../../bin-wrappers/scalar
+
+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile
+       @mkdir -p ../../bin-wrappers
+       $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+            -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \
+            -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \
+       chmod +x $@
+
+test: all
+       $(MAKE) -C t
+
+.PHONY: $(GITLIBS) all clean test FORCE
diff --git a/contrib/scalar/README.md b/contrib/scalar/README.md
new file mode 100644 (file)
index 0000000..634b577
--- /dev/null
@@ -0,0 +1,82 @@
+# Scalar - an opinionated repository management tool
+
+Scalar is an add-on to Git that helps users take advantage of advanced
+performance features in Git. Originally implemented in C# using .NET Core,
+based on the learnings from the VFS for Git project, most of the techniques
+developed by the Scalar project have been integrated into core Git already:
+
+* partial clone,
+* commit graphs,
+* multi-pack index,
+* sparse checkout (cone mode),
+* scheduled background maintenance,
+* etc
+
+This directory contains the remaining parts of Scalar that are not (yet) in
+core Git.
+
+## Roadmap
+
+The idea is to populate this directory via incremental patch series and
+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The
+current plan involves the following patch series:
+
+- `scalar-the-beginning`: The initial patch series which sets up
+  `contrib/scalar/` and populates it with a minimal `scalar` command that
+  demonstrates the fundamental ideas.
+
+- `scalar-c-and-C`: The `scalar` command learns about two options that can be
+  specified before the command, `-c <key>=<value>` and `-C <directory>`.
+
+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.
+
+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar
+  register` and in `scalar clone`, for an enormous performance boost when
+  working in large worktrees. This patch series necessarily depends on Jeff
+  Hostetler's FSMonitor patch series to be integrated into Git.
+
+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the
+  user's Git config. This usually does not represent any problem because it is
+  rare for a user to register an enlistment. However, in Scalar's functional
+  tests, Scalar enlistments are created galore, and in parallel, which can lead
+  to lock contention. This patch series works around that problem by re-trying
+  to lock the config file in a gentle fashion.
+
+- `scalar-extra-docs`: Add some extensive documentation that has been written
+  in the original Scalar project (all subject to discussion, of course).
+
+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)
+  complete and is verified in CI builds, let's offer to install it.
+
+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to
+  `gitk-git/` and to `git-gui/`, making it a top-level command.
+
+The following two patch series exist in Microsoft's fork of Git and are
+publicly available. There is no current plan to upstream them, not because I
+want to withhold these patches, but because I don't think the Git community is
+interested in these patches.
+
+There are some interesting ideas there, but the implementation is too specific
+to Azure Repos and/or VFS for Git to be of much help in general (and also: my
+colleagues tried to upstream some patches already and the enthusiasm for
+integrating things related to Azure Repos and VFS for Git can be summarized in
+very, very few words).
+
+These still exist mainly because the GVFS protocol is what Azure Repos has
+instead of partial clone, while Git is focused on improving partial clone:
+
+- `scalar-with-gvfs`: The primary purpose of this patch series is to support
+  existing Scalar users whose repositories are hosted in Azure Repos (which
+  does not support Git's partial clones, but supports its predecessor, the GVFS
+  protocol, which is used by Scalar to emulate the partial clone).
+
+  Since the GVFS protocol will never be supported by core Git, this patch
+  series will remain in Microsoft's fork of Git.
+
+- `run-scalar-functional-tests`: The Scalar project developed a quite
+  comprehensive set of integration tests (or, "Functional Tests"). They are the
+  sole remaining part of the original C#-based Scalar project, and this patch
+  adds a GitHub workflow that runs them all.
+
+  Since the tests partially depend on features that are only provided in the
+  `scalar-with-gvfs` patch series, this patch cannot be upstreamed.
diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c
new file mode 100644 (file)
index 0000000..1ce9c2b
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * The Scalar command-line interface.
+ */
+
+#include "cache.h"
+#include "gettext.h"
+#include "parse-options.h"
+#include "config.h"
+#include "run-command.h"
+#include "refs.h"
+#include "dir.h"
+#include "packfile.h"
+#include "help.h"
+
+/*
+ * Remove the deepest subdirectory in the provided path string. Path must not
+ * include a trailing path separator. Returns 1 if parent directory found,
+ * otherwise 0.
+ */
+static int strbuf_parent_directory(struct strbuf *buf)
+{
+       size_t len = buf->len;
+       size_t offset = offset_1st_component(buf->buf);
+       char *path_sep = find_last_dir_sep(buf->buf + offset);
+       strbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);
+
+       return buf->len < len;
+}
+
+static void setup_enlistment_directory(int argc, const char **argv,
+                                      const char * const *usagestr,
+                                      const struct option *options,
+                                      struct strbuf *enlistment_root)
+{
+       struct strbuf path = STRBUF_INIT;
+       char *root;
+       int enlistment_found = 0;
+
+       if (startup_info->have_repository)
+               BUG("gitdir already set up?!?");
+
+       if (argc > 1)
+               usage_with_options(usagestr, options);
+
+       /* find the worktree, determine its corresponding root */
+       if (argc == 1)
+               strbuf_add_absolute_path(&path, argv[0]);
+       else if (strbuf_getcwd(&path) < 0)
+               die(_("need a working directory"));
+
+       strbuf_trim_trailing_dir_sep(&path);
+       do {
+               const size_t len = path.len;
+
+               /* check if currently in enlistment root with src/ workdir */
+               strbuf_addstr(&path, "/src");
+               if (is_nonbare_repository_dir(&path)) {
+                       if (enlistment_root)
+                               strbuf_add(enlistment_root, path.buf, len);
+
+                       enlistment_found = 1;
+                       break;
+               }
+
+               /* reset to original path */
+               strbuf_setlen(&path, len);
+
+               /* check if currently in workdir */
+               if (is_nonbare_repository_dir(&path)) {
+                       if (enlistment_root) {
+                               /*
+                                * If the worktree's directory's name is `src`, the enlistment is the
+                                * parent directory, otherwise it is identical to the worktree.
+                                */
+                               root = strip_path_suffix(path.buf, "src");
+                               strbuf_addstr(enlistment_root, root ? root : path.buf);
+                               free(root);
+                       }
+
+                       enlistment_found = 1;
+                       break;
+               }
+       } while (strbuf_parent_directory(&path));
+
+       if (!enlistment_found)
+               die(_("could not find enlistment root"));
+
+       if (chdir(path.buf) < 0)
+               die_errno(_("could not switch to '%s'"), path.buf);
+
+       strbuf_release(&path);
+       setup_git_directory();
+}
+
+static int run_git(const char *arg, ...)
+{
+       struct strvec argv = STRVEC_INIT;
+       va_list args;
+       const char *p;
+       int res;
+
+       va_start(args, arg);
+       strvec_push(&argv, arg);
+       while ((p = va_arg(args, const char *)))
+               strvec_push(&argv, p);
+       va_end(args);
+
+       res = run_command_v_opt(argv.v, RUN_GIT_CMD);
+
+       strvec_clear(&argv);
+       return res;
+}
+
+static int set_recommended_config(int reconfigure)
+{
+       struct {
+               const char *key;
+               const char *value;
+               int overwrite_on_reconfigure;
+       } config[] = {
+               /* Required */
+               { "am.keepCR", "true", 1 },
+               { "core.FSCache", "true", 1 },
+               { "core.multiPackIndex", "true", 1 },
+               { "core.preloadIndex", "true", 1 },
+#ifndef WIN32
+               { "core.untrackedCache", "true", 1 },
+#else
+               /*
+                * Unfortunately, Scalar's Functional Tests demonstrated
+                * that the untracked cache feature is unreliable on Windows
+                * (which is a bummer because that platform would benefit the
+                * most from it). For some reason, freshly created files seem
+                * not to update the directory's `lastModified` time
+                * immediately, but the untracked cache would need to rely on
+                * that.
+                *
+                * Therefore, with a sad heart, we disable this very useful
+                * feature on Windows.
+                */
+               { "core.untrackedCache", "false", 1 },
+#endif
+               { "core.logAllRefUpdates", "true", 1 },
+               { "credential.https://dev.azure.com.useHttpPath", "true", 1 },
+               { "credential.validate", "false", 1 }, /* GCM4W-only */
+               { "gc.auto", "0", 1 },
+               { "gui.GCWarning", "false", 1 },
+               { "index.threads", "true", 1 },
+               { "index.version", "4", 1 },
+               { "merge.stat", "false", 1 },
+               { "merge.renames", "true", 1 },
+               { "pack.useBitmaps", "false", 1 },
+               { "pack.useSparse", "true", 1 },
+               { "receive.autoGC", "false", 1 },
+               { "reset.quiet", "true", 1 },
+               { "feature.manyFiles", "false", 1 },
+               { "feature.experimental", "false", 1 },
+               { "fetch.unpackLimit", "1", 1 },
+               { "fetch.writeCommitGraph", "false", 1 },
+#ifdef WIN32
+               { "http.sslBackend", "schannel", 1 },
+#endif
+               /* Optional */
+               { "status.aheadBehind", "false" },
+               { "commitGraph.generationVersion", "1" },
+               { "core.autoCRLF", "false" },
+               { "core.safeCRLF", "false" },
+               { "fetch.showForcedUpdates", "false" },
+               { NULL, NULL },
+       };
+       int i;
+       char *value;
+
+       for (i = 0; config[i].key; i++) {
+               if ((reconfigure && config[i].overwrite_on_reconfigure) ||
+                   git_config_get_string(config[i].key, &value)) {
+                       trace2_data_string("scalar", the_repository, config[i].key, "created");
+                       if (git_config_set_gently(config[i].key,
+                                                 config[i].value) < 0)
+                               return error(_("could not configure %s=%s"),
+                                            config[i].key, config[i].value);
+               } else {
+                       trace2_data_string("scalar", the_repository, config[i].key, "exists");
+                       free(value);
+               }
+       }
+
+       /*
+        * The `log.excludeDecoration` setting is special because it allows
+        * for multiple values.
+        */
+       if (git_config_get_string("log.excludeDecoration", &value)) {
+               trace2_data_string("scalar", the_repository,
+                                  "log.excludeDecoration", "created");
+               if (git_config_set_multivar_gently("log.excludeDecoration",
+                                                  "refs/prefetch/*",
+                                                  CONFIG_REGEX_NONE, 0))
+                       return error(_("could not configure "
+                                      "log.excludeDecoration"));
+       } else {
+               trace2_data_string("scalar", the_repository,
+                                  "log.excludeDecoration", "exists");
+               free(value);
+       }
+
+       return 0;
+}
+
+static int toggle_maintenance(int enable)
+{
+       return run_git("maintenance", enable ? "start" : "unregister", NULL);
+}
+
+static int add_or_remove_enlistment(int add)
+{
+       int res;
+
+       if (!the_repository->worktree)
+               die(_("Scalar enlistments require a worktree"));
+
+       res = run_git("config", "--global", "--get", "--fixed-value",
+                     "scalar.repo", the_repository->worktree, NULL);
+
+       /*
+        * If we want to add and the setting is already there, then do nothing.
+        * If we want to remove and the setting is not there, then do nothing.
+        */
+       if ((add && !res) || (!add && res))
+               return 0;
+
+       return run_git("config", "--global", add ? "--add" : "--unset",
+                      add ? "--no-fixed-value" : "--fixed-value",
+                      "scalar.repo", the_repository->worktree, NULL);
+}
+
+static int register_dir(void)
+{
+       int res = add_or_remove_enlistment(1);
+
+       if (!res)
+               res = set_recommended_config(0);
+
+       if (!res)
+               res = toggle_maintenance(1);
+
+       return res;
+}
+
+static int unregister_dir(void)
+{
+       int res = 0;
+
+       if (toggle_maintenance(0) < 0)
+               res = -1;
+
+       if (add_or_remove_enlistment(0) < 0)
+               res = -1;
+
+       return res;
+}
+
+/* printf-style interface, expects `<key>=<value>` argument */
+static int set_config(const char *fmt, ...)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char *value;
+       int res;
+       va_list args;
+
+       va_start(args, fmt);
+       strbuf_vaddf(&buf, fmt, args);
+       va_end(args);
+
+       value = strchr(buf.buf, '=');
+       if (value)
+               *(value++) = '\0';
+       res = git_config_set_gently(buf.buf, value);
+       strbuf_release(&buf);
+
+       return res;
+}
+
+static char *remote_default_branch(const char *url)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf out = STRBUF_INIT;
+
+       cp.git_cmd = 1;
+       strvec_pushl(&cp.args, "ls-remote", "--symref", url, "HEAD", NULL);
+       if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
+               const char *line = out.buf;
+
+               while (*line) {
+                       const char *eol = strchrnul(line, '\n'), *p;
+                       size_t len = eol - line;
+                       char *branch;
+
+                       if (!skip_prefix(line, "ref: ", &p) ||
+                           !strip_suffix_mem(line, &len, "\tHEAD")) {
+                               line = eol + (*eol == '\n');
+                               continue;
+                       }
+
+                       eol = line + len;
+                       if (skip_prefix(p, "refs/heads/", &p)) {
+                               branch = xstrndup(p, eol - p);
+                               strbuf_release(&out);
+                               return branch;
+                       }
+
+                       error(_("remote HEAD is not a branch: '%.*s'"),
+                             (int)(eol - p), p);
+                       strbuf_release(&out);
+                       return NULL;
+               }
+       }
+       warning(_("failed to get default branch name from remote; "
+                 "using local default"));
+       strbuf_reset(&out);
+
+       child_process_init(&cp);
+       cp.git_cmd = 1;
+       strvec_pushl(&cp.args, "symbolic-ref", "--short", "HEAD", NULL);
+       if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
+               strbuf_trim(&out);
+               return strbuf_detach(&out, NULL);
+       }
+
+       strbuf_release(&out);
+       error(_("failed to get default branch name"));
+       return NULL;
+}
+
+static int delete_enlistment(struct strbuf *enlistment)
+{
+#ifdef WIN32
+       struct strbuf parent = STRBUF_INIT;
+#endif
+
+       if (unregister_dir())
+               die(_("failed to unregister repository"));
+
+#ifdef WIN32
+       /*
+        * Change the current directory to one outside of the enlistment so
+        * that we may delete everything underneath it.
+        */
+       strbuf_addbuf(&parent, enlistment);
+       strbuf_parent_directory(&parent);
+       if (chdir(parent.buf) < 0)
+               die_errno(_("could not switch to '%s'"), parent.buf);
+       strbuf_release(&parent);
+#endif
+
+       if (remove_dir_recursively(enlistment, 0))
+               die(_("failed to delete enlistment directory"));
+
+       return 0;
+}
+
+/*
+ * Dummy implementation; Using `get_version_info()` would cause a link error
+ * without this.
+ */
+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
+{
+       die("not implemented");
+}
+
+static int cmd_clone(int argc, const char **argv)
+{
+       const char *branch = NULL;
+       int full_clone = 0, single_branch = 0;
+       struct option clone_options[] = {
+               OPT_STRING('b', "branch", &branch, N_("<branch>"),
+                          N_("branch to checkout after clone")),
+               OPT_BOOL(0, "full-clone", &full_clone,
+                        N_("when cloning, create full working directory")),
+               OPT_BOOL(0, "single-branch", &single_branch,
+                        N_("only download metadata for the branch that will "
+                           "be checked out")),
+               OPT_END(),
+       };
+       const char * const clone_usage[] = {
+               N_("scalar clone [<options>] [--] <repo> [<dir>]"),
+               NULL
+       };
+       const char *url;
+       char *enlistment = NULL, *dir = NULL;
+       struct strbuf buf = STRBUF_INIT;
+       int res;
+
+       argc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);
+
+       if (argc == 2) {
+               url = argv[0];
+               enlistment = xstrdup(argv[1]);
+       } else if (argc == 1) {
+               url = argv[0];
+
+               strbuf_addstr(&buf, url);
+               /* Strip trailing slashes, if any */
+               while (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))
+                       strbuf_setlen(&buf, buf.len - 1);
+               /* Strip suffix `.git`, if any */
+               strbuf_strip_suffix(&buf, ".git");
+
+               enlistment = find_last_dir_sep(buf.buf);
+               if (!enlistment) {
+                       die(_("cannot deduce worktree name from '%s'"), url);
+               }
+               enlistment = xstrdup(enlistment + 1);
+       } else {
+               usage_msg_opt(_("You must specify a repository to clone."),
+                             clone_usage, clone_options);
+       }
+
+       if (is_directory(enlistment))
+               die(_("directory '%s' exists already"), enlistment);
+
+       dir = xstrfmt("%s/src", enlistment);
+
+       strbuf_reset(&buf);
+       if (branch)
+               strbuf_addf(&buf, "init.defaultBranch=%s", branch);
+       else {
+               char *b = repo_default_branch_name(the_repository, 1);
+               strbuf_addf(&buf, "init.defaultBranch=%s", b);
+               free(b);
+       }
+
+       if ((res = run_git("-c", buf.buf, "init", "--", dir, NULL)))
+               goto cleanup;
+
+       if (chdir(dir) < 0) {
+               res = error_errno(_("could not switch to '%s'"), dir);
+               goto cleanup;
+       }
+
+       setup_git_directory();
+
+       /* common-main already logs `argv` */
+       trace2_def_repo(the_repository);
+
+       if (!branch && !(branch = remote_default_branch(url))) {
+               res = error(_("failed to get default branch for '%s'"), url);
+               goto cleanup;
+       }
+
+       if (set_config("remote.origin.url=%s", url) ||
+           set_config("remote.origin.fetch="
+                      "+refs/heads/%s:refs/remotes/origin/%s",
+                      single_branch ? branch : "*",
+                      single_branch ? branch : "*") ||
+           set_config("remote.origin.promisor=true") ||
+           set_config("remote.origin.partialCloneFilter=blob:none")) {
+               res = error(_("could not configure remote in '%s'"), dir);
+               goto cleanup;
+       }
+
+       if (!full_clone &&
+           (res = run_git("sparse-checkout", "init", "--cone", NULL)))
+               goto cleanup;
+
+       if (set_recommended_config(0))
+               return error(_("could not configure '%s'"), dir);
+
+       if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
+               warning(_("partial clone failed; attempting full clone"));
+
+               if (set_config("remote.origin.promisor") ||
+                   set_config("remote.origin.partialCloneFilter")) {
+                       res = error(_("could not configure for full clone"));
+                       goto cleanup;
+               }
+
+               if ((res = run_git("fetch", "--quiet", "origin", NULL)))
+                       goto cleanup;
+       }
+
+       if ((res = set_config("branch.%s.remote=origin", branch)))
+               goto cleanup;
+       if ((res = set_config("branch.%s.merge=refs/heads/%s",
+                             branch, branch)))
+               goto cleanup;
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "origin/%s", branch);
+       res = run_git("checkout", "-f", "-t", buf.buf, NULL);
+       if (res)
+               goto cleanup;
+
+       res = register_dir();
+
+cleanup:
+       free(enlistment);
+       free(dir);
+       strbuf_release(&buf);
+       return res;
+}
+
+static int cmd_list(int argc, const char **argv)
+{
+       if (argc != 1)
+               die(_("`scalar list` does not take arguments"));
+
+       if (run_git("config", "--global", "--get-all", "scalar.repo", NULL) < 0)
+               return -1;
+       return 0;
+}
+
+static int cmd_register(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END(),
+       };
+       const char * const usage[] = {
+               N_("scalar register [<enlistment>]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usage, 0);
+
+       setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+       return register_dir();
+}
+
+static int get_scalar_repos(const char *key, const char *value, void *data)
+{
+       struct string_list *list = data;
+
+       if (!strcmp(key, "scalar.repo"))
+               string_list_append(list, value);
+
+       return 0;
+}
+
+static int cmd_reconfigure(int argc, const char **argv)
+{
+       int all = 0;
+       struct option options[] = {
+               OPT_BOOL('a', "all", &all,
+                        N_("reconfigure all registered enlistments")),
+               OPT_END(),
+       };
+       const char * const usage[] = {
+               N_("scalar reconfigure [--all | <enlistment>]"),
+               NULL
+       };
+       struct string_list scalar_repos = STRING_LIST_INIT_DUP;
+       int i, res = 0;
+       struct repository r = { NULL };
+       struct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usage, 0);
+
+       if (!all) {
+               setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+               return set_recommended_config(1);
+       }
+
+       if (argc > 0)
+               usage_msg_opt(_("--all or <enlistment>, but not both"),
+                             usage, options);
+
+       git_config(get_scalar_repos, &scalar_repos);
+
+       for (i = 0; i < scalar_repos.nr; i++) {
+               const char *dir = scalar_repos.items[i].string;
+
+               strbuf_reset(&commondir);
+               strbuf_reset(&gitdir);
+
+               if (chdir(dir) < 0) {
+                       warning_errno(_("could not switch to '%s'"), dir);
+                       res = -1;
+               } else if (discover_git_directory(&commondir, &gitdir) < 0) {
+                       warning_errno(_("git repository gone in '%s'"), dir);
+                       res = -1;
+               } else {
+                       git_config_clear();
+
+                       the_repository = &r;
+                       r.commondir = commondir.buf;
+                       r.gitdir = gitdir.buf;
+
+                       if (set_recommended_config(1) < 0)
+                               res = -1;
+               }
+       }
+
+       string_list_clear(&scalar_repos, 1);
+       strbuf_release(&commondir);
+       strbuf_release(&gitdir);
+
+       return res;
+}
+
+static int cmd_run(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END(),
+       };
+       struct {
+               const char *arg, *task;
+       } tasks[] = {
+               { "config", NULL },
+               { "commit-graph", "commit-graph" },
+               { "fetch", "prefetch" },
+               { "loose-objects", "loose-objects" },
+               { "pack-files", "incremental-repack" },
+               { NULL, NULL }
+       };
+       struct strbuf buf = STRBUF_INIT;
+       const char *usagestr[] = { NULL, NULL };
+       int i;
+
+       strbuf_addstr(&buf, N_("scalar run <task> [<enlistment>]\nTasks:\n"));
+       for (i = 0; tasks[i].arg; i++)
+               strbuf_addf(&buf, "\t%s\n", tasks[i].arg);
+       usagestr[0] = buf.buf;
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usagestr, 0);
+
+       if (!argc)
+               usage_with_options(usagestr, options);
+
+       if (!strcmp("all", argv[0])) {
+               i = -1;
+       } else {
+               for (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)
+                       ; /* keep looking for the task */
+
+               if (i > 0 && !tasks[i].arg) {
+                       error(_("no such task: '%s'"), argv[0]);
+                       usage_with_options(usagestr, options);
+               }
+       }
+
+       argc--;
+       argv++;
+       setup_enlistment_directory(argc, argv, usagestr, options, NULL);
+       strbuf_release(&buf);
+
+       if (i == 0)
+               return register_dir();
+
+       if (i > 0)
+               return run_git("maintenance", "run",
+                              "--task", tasks[i].task, NULL);
+
+       if (register_dir())
+               return -1;
+       for (i = 1; tasks[i].arg; i++)
+               if (run_git("maintenance", "run",
+                           "--task", tasks[i].task, NULL))
+                       return -1;
+       return 0;
+}
+
+static int remove_deleted_enlistment(struct strbuf *path)
+{
+       int res = 0;
+       strbuf_realpath_forgiving(path, path->buf, 1);
+
+       if (run_git("config", "--global",
+                   "--unset", "--fixed-value",
+                   "scalar.repo", path->buf, NULL) < 0)
+               res = -1;
+
+       if (run_git("config", "--global",
+                   "--unset", "--fixed-value",
+                   "maintenance.repo", path->buf, NULL) < 0)
+               res = -1;
+
+       return res;
+}
+
+static int cmd_unregister(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END(),
+       };
+       const char * const usage[] = {
+               N_("scalar unregister [<enlistment>]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usage, 0);
+
+       /*
+        * Be forgiving when the enlistment or worktree does not even exist any
+        * longer; This can be the case if a user deleted the worktree by
+        * mistake and _still_ wants to unregister the thing.
+        */
+       if (argc == 1) {
+               struct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;
+
+               strbuf_addf(&src_path, "%s/src/.git", argv[0]);
+               strbuf_addf(&workdir_path, "%s/.git", argv[0]);
+               if (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {
+                       /* remove possible matching registrations */
+                       int res = -1;
+
+                       strbuf_strip_suffix(&src_path, "/.git");
+                       res = remove_deleted_enlistment(&src_path) && res;
+
+                       strbuf_strip_suffix(&workdir_path, "/.git");
+                       res = remove_deleted_enlistment(&workdir_path) && res;
+
+                       strbuf_release(&src_path);
+                       strbuf_release(&workdir_path);
+                       return res;
+               }
+               strbuf_release(&src_path);
+               strbuf_release(&workdir_path);
+       }
+
+       setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+       return unregister_dir();
+}
+
+static int cmd_delete(int argc, const char **argv)
+{
+       char *cwd = xgetcwd();
+       struct option options[] = {
+               OPT_END(),
+       };
+       const char * const usage[] = {
+               N_("scalar delete <enlistment>"),
+               NULL
+       };
+       struct strbuf enlistment = STRBUF_INIT;
+       int res = 0;
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usage, 0);
+
+       if (argc != 1)
+               usage_with_options(usage, options);
+
+       setup_enlistment_directory(argc, argv, usage, options, &enlistment);
+
+       if (dir_inside_of(cwd, enlistment.buf) >= 0)
+               res = error(_("refusing to delete current working directory"));
+       else {
+               close_object_store(the_repository->objects);
+               res = delete_enlistment(&enlistment);
+       }
+       strbuf_release(&enlistment);
+       free(cwd);
+
+       return res;
+}
+
+static int cmd_version(int argc, const char **argv)
+{
+       int verbose = 0, build_options = 0;
+       struct option options[] = {
+               OPT__VERBOSE(&verbose, N_("include Git version")),
+               OPT_BOOL(0, "build-options", &build_options,
+                        N_("include Git's build options")),
+               OPT_END(),
+       };
+       const char * const usage[] = {
+               N_("scalar verbose [-v | --verbose] [--build-options]"),
+               NULL
+       };
+       struct strbuf buf = STRBUF_INIT;
+
+       argc = parse_options(argc, argv, NULL, options,
+                            usage, 0);
+
+       if (argc != 0)
+               usage_with_options(usage, options);
+
+       get_version_info(&buf, build_options);
+       fprintf(stderr, "%s\n", buf.buf);
+       strbuf_release(&buf);
+
+       return 0;
+}
+
+static struct {
+       const char *name;
+       int (*fn)(int, const char **);
+} builtins[] = {
+       { "clone", cmd_clone },
+       { "list", cmd_list },
+       { "register", cmd_register },
+       { "unregister", cmd_unregister },
+       { "run", cmd_run },
+       { "reconfigure", cmd_reconfigure },
+       { "delete", cmd_delete },
+       { "version", cmd_version },
+       { NULL, NULL},
+};
+
+int cmd_main(int argc, const char **argv)
+{
+       struct strbuf scalar_usage = STRBUF_INIT;
+       int i;
+
+       if (argc > 1) {
+               argv++;
+               argc--;
+
+               for (i = 0; builtins[i].name; i++)
+                       if (!strcmp(builtins[i].name, argv[0]))
+                               return !!builtins[i].fn(argc, argv);
+       }
+
+       strbuf_addstr(&scalar_usage,
+                     N_("scalar <command> [<options>]\n\nCommands:\n"));
+       for (i = 0; builtins[i].name; i++)
+               strbuf_addf(&scalar_usage, "\t%s\n", builtins[i].name);
+
+       usage(scalar_usage.buf);
+}
diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt
new file mode 100644 (file)
index 0000000..f416d63
--- /dev/null
@@ -0,0 +1,145 @@
+scalar(1)
+=========
+
+NAME
+----
+scalar - an opinionated repository management tool
+
+SYNOPSIS
+--------
+[verse]
+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]
+scalar list
+scalar register [<enlistment>]
+scalar unregister [<enlistment>]
+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]
+scalar reconfigure [ --all | <enlistment> ]
+scalar delete <enlistment>
+
+DESCRIPTION
+-----------
+
+Scalar is an opinionated repository management tool. By creating new
+repositories or registering existing repositories with Scalar, your Git
+experience will speed up. Scalar sets advanced Git config settings,
+maintains your repositories in the background, and helps reduce data sent
+across the network.
+
+An important Scalar concept is the enlistment: this is the top-level directory
+of the project. It usually contains the subdirectory `src/` which is a Git
+worktree. This encourages the separation between tracked files (inside `src/`)
+and untracked files, such as build artifacts (outside `src/`). When registering
+an existing Git worktree with Scalar whose name is not `src`, the enlistment
+will be identical to the worktree.
+
+The `scalar` command implements various subcommands, and different options
+depending on the subcommand. With the exception of `clone`, `list` and
+`reconfigure --all`, all subcommands expect to be run in an enlistment.
+
+COMMANDS
+--------
+
+Clone
+~~~~~
+
+clone [<options>] <url> [<enlistment>]::
+       Clones the specified repository, similar to linkgit:git-clone[1]. By
+       default, only commit and tree objects are cloned. Once finished, the
+       worktree is located at `<enlistment>/src`.
++
+The sparse-checkout feature is enabled (except when run with `--full-clone`)
+and the only files present are those in the top-level directory. Use
+`git sparse-checkout set` to expand the set of directories you want to see,
+or `git sparse-checkout disable` to expand to all files (see
+linkgit:git-sparse-checkout[1] for more details). You can explore the
+subdirectories outside your sparse-checkout by using `git ls-tree
+HEAD[:<directory>]`.
+
+-b <name>::
+--branch <name>::
+       Instead of checking out the branch pointed to by the cloned
+       repository's HEAD, check out the `<name>` branch instead.
+
+--[no-]single-branch::
+       Clone only the history leading to the tip of a single branch, either
+       specified by the `--branch` option or the primary branch remote's
+       `HEAD` points at.
++
+Further fetches into the resulting repository will only update the
+remote-tracking branch for the branch this option was used for the initial
+cloning. If the HEAD at the remote did not point at any branch when
+`--single-branch` clone was made, no remote-tracking branch is created.
+
+--[no-]full-clone::
+       A sparse-checkout is initialized by default. This behavior can be
+       turned off via `--full-clone`.
+
+List
+~~~~
+
+list::
+       List enlistments that are currently registered by Scalar. This
+       subcommand does not need to be run inside an enlistment.
+
+Register
+~~~~~~~~
+
+register [<enlistment>]::
+       Adds the enlistment's repository to the list of registered repositories
+       and starts background maintenance. If `<enlistment>` is not provided,
+       then the enlistment associated with the current working directory is
+       registered.
++
+Note: when this subcommand is called in a worktree that is called `src/`, its
+parent directory is considered to be the Scalar enlistment. If the worktree is
+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.
+
+Unregister
+~~~~~~~~~~
+
+unregister [<enlistment>]::
+       Remove the specified repository from the list of repositories
+       registered with Scalar and stop the scheduled background maintenance.
+
+Run
+~~~
+
+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::
+       Run the given maintenance task (or all tasks, if `all` was specified).
+       Except for `all` and `config`, this subcommand simply hands off to
+       linkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and
+       `pack-files` to `incremental-repack`).
++
+These tasks are run automatically as part of the scheduled maintenance,
+as soon as the repository is registered with Scalar. It should therefore
+not be necessary to run this subcommand manually.
++
+The `config` task is specific to Scalar and configures all those
+opinionated default settings that make Git work more efficiently with
+large repositories. As this task is run as part of `scalar clone`
+automatically, explicit invocations of this task are rarely needed.
+
+Reconfigure
+~~~~~~~~~~~
+
+After a Scalar upgrade, or when the configuration of a Scalar enlistment
+was somehow corrupted or changed by mistake, this subcommand allows to
+reconfigure the enlistment.
+
+With the `--all` option, all enlistments currently registered with Scalar
+will be reconfigured. Use this option after each Scalar upgrade.
+
+Delete
+~~~~~~
+
+delete <enlistment>::
+       This subcommand lets you delete an existing Scalar enlistment from your
+       local file system, unregistering the repository.
+
+SEE ALSO
+--------
+linkgit:git-clone[1], linkgit:git-maintenance[1].
+
+Scalar
+---
+Associated with the linkgit:git[1] suite
diff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile
new file mode 100644 (file)
index 0000000..6170672
--- /dev/null
@@ -0,0 +1,78 @@
+# Run scalar tests
+#
+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin
+#
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
+RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
+TEST_LINT ?= test-lint
+
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = ../../../t/test-results
+endif
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
+
+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
+
+all: $(DEFAULT_TEST_TARGET)
+
+test: $(TEST_LINT)
+       $(MAKE) aggregate-results-and-cleanup
+
+prove: $(TEST_LINT)
+       @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+       $(MAKE) clean-except-prove-cache
+
+$(T):
+       @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean-except-prove-cache:
+       $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
+       $(RM) -r valgrind/bin
+
+clean: clean-except-prove-cache
+       $(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
+
+test-lint-duplicates:
+       @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+               test -z "$$dups" || { \
+               echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+       @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+               test -z "$$bad" || { \
+               echo >&2 "non-executable tests:" $$bad; exit 1; }
+
+test-lint-shell-syntax:
+       @'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)
+
+aggregate-results-and-cleanup: $(T)
+       $(MAKE) aggregate-results
+       $(MAKE) clean
+
+aggregate-results:
+       for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
+               echo "$$f"; \
+       done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+
+valgrind:
+       $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+test-results:
+       mkdir -p test-results
+
+.PHONY: $(T) aggregate-results clean valgrind
diff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh
new file mode 100755 (executable)
index 0000000..2e1502a
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='test the `scalar` command'
+
+TEST_DIRECTORY=$PWD/../../../t
+export TEST_DIRECTORY
+
+# Make it work with --no-bin-wrappers
+PATH=$PWD/..:$PATH
+
+. ../../../t/test-lib.sh
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'scalar shows a usage' '
+       test_expect_code 129 scalar -h
+'
+
+test_expect_success 'scalar unregister' '
+       git init vanish/src &&
+       scalar register vanish/src &&
+       git config --get --global --fixed-value \
+               maintenance.repo "$(pwd)/vanish/src" &&
+       scalar list >scalar.repos &&
+       grep -F "$(pwd)/vanish/src" scalar.repos &&
+       rm -rf vanish/src/.git &&
+       scalar unregister vanish &&
+       test_must_fail git config --get --global --fixed-value \
+               maintenance.repo "$(pwd)/vanish/src" &&
+       scalar list >scalar.repos &&
+       ! grep -F "$(pwd)/vanish/src" scalar.repos
+'
+
+test_expect_success 'set up repository to clone' '
+       test_commit first &&
+       test_commit second &&
+       test_commit third &&
+       git switch -c parallel first &&
+       mkdir -p 1/2 &&
+       test_commit 1/2/3 &&
+       git config uploadPack.allowFilter true &&
+       git config uploadPack.allowAnySHA1InWant true
+'
+
+test_expect_success 'scalar clone' '
+       second=$(git rev-parse --verify second:second.t) &&
+       scalar clone "file://$(pwd)" cloned --single-branch &&
+       (
+               cd cloned/src &&
+
+               git config --get --global --fixed-value maintenance.repo \
+                       "$(pwd)" &&
+
+               git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
+               echo "refs/remotes/origin/parallel" >expect &&
+               test_cmp expect actual &&
+
+               test_path_is_missing 1/2 &&
+               test_must_fail git rev-list --missing=print $second &&
+               git rev-list $second &&
+               git cat-file blob $second >actual &&
+               echo "second" >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'scalar reconfigure' '
+       git init one/src &&
+       scalar register one &&
+       git -C one/src config core.preloadIndex false &&
+       scalar reconfigure one &&
+       test true = "$(git -C one/src config core.preloadIndex)" &&
+       git -C one/src config core.preloadIndex false &&
+       scalar reconfigure -a &&
+       test true = "$(git -C one/src config core.preloadIndex)"
+'
+
+test_expect_success 'scalar delete without enlistment shows a usage' '
+       test_expect_code 129 scalar delete
+'
+
+test_expect_success 'scalar delete with enlistment' '
+       scalar delete cloned &&
+       test_path_is_missing cloned
+'
+
+test_done
index 7f767b5c38fe2c1650868057349152b2ddebaa1c..71f1fd94bde8b0cd1653677612b0f21e5b36bcd4 100755 (executable)
@@ -296,10 +296,9 @@ cache_miss () {
        done
 }
 
-# Usage: check_parents PARENTS_EXPR
+# Usage: check_parents [REVS...]
 check_parents () {
-       assert test $# = 1
-       missed=$(cache_miss "$1") || exit $?
+       missed=$(cache_miss "$@") || exit $?
        local indent=$(($indent + 1))
        for miss in $missed
        do
@@ -753,7 +752,7 @@ process_split_commit () {
        fi
        createcount=$(($createcount + 1))
        debug "parents: $parents"
-       check_parents "$parents"
+       check_parents $parents
        newparents=$(cache_get $parents) || exit $?
        debug "newparents: $newparents"
 
index 4153b6532195142c19f83c09ab2991d0abc821ad..1c1f76f04aaed3f15fec9d750762f46ee86c4cab 100755 (executable)
@@ -1445,7 +1445,7 @@ test_expect_success 'subtree descendant check' '
        ) &&
        test_create_commit "$test_count" folder_subtree/0 &&
        test_create_commit "$test_count" folder_subtree/b &&
-       cherry=$(cd "$test_count"; git rev-parse HEAD) &&
+       cherry=$(cd "$test_count" && git rev-parse HEAD) &&
        (
                cd "$test_count" &&
                git checkout branch
index b1fcbe0d6fa847dd936467c0d8d1aeffbf90d1cc..94a5b8a36453f67d6d7de945c0d2427acd2f8188 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -232,13 +232,13 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
 
                rlen = strlcpy(interp_path, expanded_path.buf,
                               sizeof(interp_path));
+               strbuf_release(&expanded_path);
                if (rlen >= sizeof(interp_path)) {
                        logerror("interpolated path too large: %s",
                                 interp_path);
                        return NULL;
                }
 
-               strbuf_release(&expanded_path);
                loginfo("Interpolated dir '%s'", interp_path);
 
                dir = interp_path;
@@ -326,22 +326,18 @@ static int run_access_hook(struct daemon_service *service, const char *dir,
 {
        struct child_process child = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
-       const char *argv[8];
-       const char **arg = argv;
        char *eol;
        int seen_errors = 0;
 
-       *arg++ = access_hook;
-       *arg++ = service->name;
-       *arg++ = path;
-       *arg++ = hi->hostname.buf;
-       *arg++ = get_canon_hostname(hi);
-       *arg++ = get_ip_address(hi);
-       *arg++ = hi->tcp_port.buf;
-       *arg = NULL;
+       strvec_push(&child.args, access_hook);
+       strvec_push(&child.args, service->name);
+       strvec_push(&child.args, path);
+       strvec_push(&child.args, hi->hostname.buf);
+       strvec_push(&child.args, get_canon_hostname(hi));
+       strvec_push(&child.args, get_ip_address(hi));
+       strvec_push(&child.args, hi->tcp_port.buf);
 
        child.use_shell = 1;
-       child.argv = argv;
        child.no_stdin = 1;
        child.no_stderr = 1;
        child.out = -1;
@@ -922,7 +918,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
 #endif
        }
 
-       cld.argv = cld_argv.v;
+       strvec_pushv(&cld.args, cld_argv.v);
        cld.in = incoming;
        cld.out = dup(incoming);
 
diff --git a/diff.c b/diff.c
index 861282db1c3283ad5cd6234237408bb5cf223d2b..c862771a58939f0cd2a20b2056c8d93b17c7be26 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -18,6 +18,7 @@
 #include "submodule-config.h"
 #include "submodule.h"
 #include "hashmap.h"
+#include "mem-pool.h"
 #include "ll-merge.h"
 #include "string-list.h"
 #include "strvec.h"
@@ -773,6 +774,7 @@ struct emitted_diff_symbol {
        int flags;
        int indent_off;   /* Offset to first non-whitespace character */
        int indent_width; /* The visual width of the indentation */
+       unsigned id;
        enum diff_symbol s;
 };
 #define EMITTED_DIFF_SYMBOL_INIT { 0 }
@@ -798,9 +800,9 @@ static void append_emitted_diff_symbol(struct diff_options *o,
 }
 
 struct moved_entry {
-       struct hashmap_entry ent;
        const struct emitted_diff_symbol *es;
        struct moved_entry *next_line;
+       struct moved_entry *next_match;
 };
 
 struct moved_block {
@@ -808,11 +810,6 @@ struct moved_block {
        int wsd; /* The whitespace delta of this block */
 };
 
-static void moved_block_clear(struct moved_block *b)
-{
-       memset(b, 0, sizeof(*b));
-}
-
 #define INDENT_BLANKLINE INT_MIN
 
 static void fill_es_indent_data(struct emitted_diff_symbol *es)
@@ -856,79 +853,41 @@ static void fill_es_indent_data(struct emitted_diff_symbol *es)
 }
 
 static int compute_ws_delta(const struct emitted_diff_symbol *a,
-                           const struct emitted_diff_symbol *b,
-                           int *out)
-{
-       int a_len = a->len,
-           b_len = b->len,
-           a_off = a->indent_off,
-           a_width = a->indent_width,
-           b_off = b->indent_off,
+                           const struct emitted_diff_symbol *b)
+{
+       int a_width = a->indent_width,
            b_width = b->indent_width;
-       int delta;
 
-       if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
-               *out = INDENT_BLANKLINE;
-               return 1;
-       }
-
-       if (a->s == DIFF_SYMBOL_PLUS)
-               delta = a_width - b_width;
-       else
-               delta = b_width - a_width;
-
-       if (a_len - a_off != b_len - b_off ||
-           memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
-               return 0;
+       if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE)
+               return INDENT_BLANKLINE;
 
-       *out = delta;
-
-       return 1;
+       return a_width - b_width;
 }
 
-static int cmp_in_block_with_wsd(const struct diff_options *o,
-                                const struct moved_entry *cur,
-                                const struct moved_entry *match,
-                                struct moved_block *pmb,
-                                int n)
-{
-       struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
-       int al = cur->es->len, bl = match->es->len, cl = l->len;
-       const char *a = cur->es->line,
-                  *b = match->es->line,
-                  *c = l->line;
-       int a_off = cur->es->indent_off,
-           a_width = cur->es->indent_width,
-           c_off = l->indent_off,
-           c_width = l->indent_width;
+static int cmp_in_block_with_wsd(const struct moved_entry *cur,
+                                const struct emitted_diff_symbol *l,
+                                struct moved_block *pmb)
+{
+       int a_width = cur->es->indent_width, b_width = l->indent_width;
        int delta;
 
-       /*
-        * We need to check if 'cur' is equal to 'match'.  As those
-        * are from the same (+/-) side, we do not need to adjust for
-        * indent changes. However these were found using fuzzy
-        * matching so we do have to check if they are equal. Here we
-        * just check the lengths. We delay calling memcmp() to check
-        * the contents until later as if the length comparison for a
-        * and c fails we can avoid the call all together.
-        */
-       if (al != bl)
+       /* The text of each line must match */
+       if (cur->es->id != l->id)
                return 1;
 
-       /* If 'l' and 'cur' are both blank then they match. */
-       if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
+       /*
+        * If 'l' and 'cur' are both blank then we don't need to check the
+        * indent. We only need to check cur as we know the strings match.
+        * */
+       if (a_width == INDENT_BLANKLINE)
                return 0;
 
        /*
         * The indent changes of the block are known and stored in pmb->wsd;
         * however we need to check if the indent changes of the current line
-        * match those of the current block and that the text of 'l' and 'cur'
-        * after the indentation match.
+        * match those of the current block.
         */
-       if (cur->es->s == DIFF_SYMBOL_PLUS)
-               delta = a_width - c_width;
-       else
-               delta = c_width - a_width;
+       delta = b_width - a_width;
 
        /*
         * If the previous lines of this block were all blank then set its
@@ -937,166 +896,165 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
        if (pmb->wsd == INDENT_BLANKLINE)
                pmb->wsd = delta;
 
-       return !(delta == pmb->wsd && al - a_off == cl - c_off &&
-                !memcmp(a, b, al) && !
-                memcmp(a + a_off, c + c_off, al - a_off));
+       return delta != pmb->wsd;
 }
 
-static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
-                          const struct hashmap_entry *eptr,
-                          const struct hashmap_entry *entry_or_key,
-                          const void *keydata)
+struct interned_diff_symbol {
+       struct hashmap_entry ent;
+       struct emitted_diff_symbol *es;
+};
+
+static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data,
+                                   const struct hashmap_entry *eptr,
+                                   const struct hashmap_entry *entry_or_key,
+                                   const void *keydata)
 {
        const struct diff_options *diffopt = hashmap_cmp_fn_data;
-       const struct moved_entry *a, *b;
+       const struct emitted_diff_symbol *a, *b;
        unsigned flags = diffopt->color_moved_ws_handling
                         & XDF_WHITESPACE_FLAGS;
 
-       a = container_of(eptr, const struct moved_entry, ent);
-       b = container_of(entry_or_key, const struct moved_entry, ent);
-
-       if (diffopt->color_moved_ws_handling &
-           COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
-               /*
-                * As there is not specific white space config given,
-                * we'd need to check for a new block, so ignore all
-                * white space. The setup of the white space
-                * configuration for the next block is done else where
-                */
-               flags |= XDF_IGNORE_WHITESPACE;
+       a = container_of(eptr, const struct interned_diff_symbol, ent)->es;
+       b = container_of(entry_or_key, const struct interned_diff_symbol, ent)->es;
 
-       return !xdiff_compare_lines(a->es->line, a->es->len,
-                                   b->es->line, b->es->len,
-                                   flags);
+       return !xdiff_compare_lines(a->line + a->indent_off,
+                                   a->len - a->indent_off,
+                                   b->line + b->indent_off,
+                                   b->len - b->indent_off, flags);
 }
 
-static struct moved_entry *prepare_entry(struct diff_options *o,
-                                        int line_no)
+static void prepare_entry(struct diff_options *o, struct emitted_diff_symbol *l,
+                         struct interned_diff_symbol *s)
 {
-       struct moved_entry *ret = xmalloc(sizeof(*ret));
-       struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
        unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
-       unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
-
-       hashmap_entry_init(&ret->ent, hash);
-       ret->es = l;
-       ret->next_line = NULL;
+       unsigned int hash = xdiff_hash_string(l->line + l->indent_off,
+                                             l->len - l->indent_off, flags);
 
-       return ret;
+       hashmap_entry_init(&s->ent, hash);
+       s->es = l;
 }
 
-static void add_lines_to_move_detection(struct diff_options *o,
-                                       struct hashmap *add_lines,
-                                       struct hashmap *del_lines)
+struct moved_entry_list {
+       struct moved_entry *add, *del;
+};
+
+static struct moved_entry_list *add_lines_to_move_detection(struct diff_options *o,
+                                                           struct mem_pool *entry_mem_pool)
 {
        struct moved_entry *prev_line = NULL;
-
+       struct mem_pool interned_pool;
+       struct hashmap interned_map;
+       struct moved_entry_list *entry_list = NULL;
+       size_t entry_list_alloc = 0;
+       unsigned id = 0;
        int n;
+
+       hashmap_init(&interned_map, interned_diff_symbol_cmp, o, 8096);
+       mem_pool_init(&interned_pool, 1024 * 1024);
+
        for (n = 0; n < o->emitted_symbols->nr; n++) {
-               struct hashmap *hm;
-               struct moved_entry *key;
+               struct interned_diff_symbol key;
+               struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+               struct interned_diff_symbol *s;
+               struct moved_entry *entry;
 
-               switch (o->emitted_symbols->buf[n].s) {
-               case DIFF_SYMBOL_PLUS:
-                       hm = add_lines;
-                       break;
-               case DIFF_SYMBOL_MINUS:
-                       hm = del_lines;
-                       break;
-               default:
+               if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) {
                        prev_line = NULL;
                        continue;
                }
 
                if (o->color_moved_ws_handling &
                    COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
-                       fill_es_indent_data(&o->emitted_symbols->buf[n]);
-               key = prepare_entry(o, n);
-               if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
-                       prev_line->next_line = key;
+                       fill_es_indent_data(l);
 
-               hashmap_add(hm, &key->ent);
-               prev_line = key;
+               prepare_entry(o, l, &key);
+               s = hashmap_get_entry(&interned_map, &key, ent, &key.ent);
+               if (s) {
+                       l->id = s->es->id;
+               } else {
+                       l->id = id;
+                       ALLOC_GROW_BY(entry_list, id, 1, entry_list_alloc);
+                       hashmap_add(&interned_map,
+                                   memcpy(mem_pool_alloc(&interned_pool,
+                                                         sizeof(key)),
+                                          &key, sizeof(key)));
+               }
+               entry = mem_pool_alloc(entry_mem_pool, sizeof(*entry));
+               entry->es = l;
+               entry->next_line = NULL;
+               if (prev_line && prev_line->es->s == l->s)
+                       prev_line->next_line = entry;
+               prev_line = entry;
+               if (l->s == DIFF_SYMBOL_PLUS) {
+                       entry->next_match = entry_list[l->id].add;
+                       entry_list[l->id].add = entry;
+               } else {
+                       entry->next_match = entry_list[l->id].del;
+                       entry_list[l->id].del = entry;
+               }
        }
+
+       hashmap_clear(&interned_map);
+       mem_pool_discard(&interned_pool, 0);
+
+       return entry_list;
 }
 
 static void pmb_advance_or_null(struct diff_options *o,
-                               struct moved_entry *match,
-                               struct hashmap *hm,
+                               struct emitted_diff_symbol *l,
                                struct moved_block *pmb,
-                               int pmb_nr)
+                               int *pmb_nr)
 {
-       int i;
-       for (i = 0; i < pmb_nr; i++) {
+       int i, j;
+
+       for (i = 0, j = 0; i < *pmb_nr; i++) {
+               int match;
                struct moved_entry *prev = pmb[i].match;
                struct moved_entry *cur = (prev && prev->next_line) ?
                                prev->next_line : NULL;
-               if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
-                       pmb[i].match = cur;
-               } else {
-                       pmb[i].match = NULL;
-               }
-       }
-}
 
-static void pmb_advance_or_null_multi_match(struct diff_options *o,
-                                           struct moved_entry *match,
-                                           struct hashmap *hm,
-                                           struct moved_block *pmb,
-                                           int pmb_nr, int n)
-{
-       int i;
-       char *got_match = xcalloc(1, pmb_nr);
-
-       hashmap_for_each_entry_from(hm, match, ent) {
-               for (i = 0; i < pmb_nr; i++) {
-                       struct moved_entry *prev = pmb[i].match;
-                       struct moved_entry *cur = (prev && prev->next_line) ?
-                                       prev->next_line : NULL;
-                       if (!cur)
-                               continue;
-                       if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
-                               got_match[i] |= 1;
-               }
-       }
+               if (o->color_moved_ws_handling &
+                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                       match = cur &&
+                               !cmp_in_block_with_wsd(cur, l, &pmb[i]);
+               else
+                       match = cur && cur->es->id == l->id;
 
-       for (i = 0; i < pmb_nr; i++) {
-               if (got_match[i]) {
-                       /* Advance to the next line */
-                       pmb[i].match = pmb[i].match->next_line;
-               } else {
-                       moved_block_clear(&pmb[i]);
+               if (match) {
+                       pmb[j] = pmb[i];
+                       pmb[j++].match = cur;
                }
        }
-
-       free(got_match);
+       *pmb_nr = j;
 }
 
-static int shrink_potential_moved_blocks(struct moved_block *pmb,
-                                        int pmb_nr)
-{
-       int lp, rp;
-
-       /* Shrink the set of potential block to the remaining running */
-       for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
-               while (lp < pmb_nr && pmb[lp].match)
-                       lp++;
-               /* lp points at the first NULL now */
+static void fill_potential_moved_blocks(struct diff_options *o,
+                                       struct moved_entry *match,
+                                       struct emitted_diff_symbol *l,
+                                       struct moved_block **pmb_p,
+                                       int *pmb_alloc_p, int *pmb_nr_p)
 
-               while (rp > -1 && !pmb[rp].match)
-                       rp--;
-               /* rp points at the last non-NULL */
+{
+       struct moved_block *pmb = *pmb_p;
+       int pmb_alloc = *pmb_alloc_p, pmb_nr = *pmb_nr_p;
 
-               if (lp < pmb_nr && rp > -1 && lp < rp) {
-                       pmb[lp] = pmb[rp];
-                       memset(&pmb[rp], 0, sizeof(pmb[rp]));
-                       rp--;
-                       lp++;
-               }
+       /*
+        * The current line is the start of a new block.
+        * Setup the set of potential blocks.
+        */
+       for (; match; match = match->next_match) {
+               ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
+               if (o->color_moved_ws_handling &
+                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                       pmb[pmb_nr].wsd = compute_ws_delta(l, match->es);
+               else
+                       pmb[pmb_nr].wsd = 0;
+               pmb[pmb_nr++].match = match;
        }
 
-       /* Remember the number of running sets */
-       return rp + 1;
+       *pmb_p = pmb;
+       *pmb_alloc_p = pmb_alloc;
+       *pmb_nr_p = pmb_nr;
 }
 
 /*
@@ -1115,6 +1073,8 @@ static int shrink_potential_moved_blocks(struct moved_block *pmb,
  * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
  * Think of a way to unify them.
  */
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+  (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
 static int adjust_last_block(struct diff_options *o, int n, int block_length)
 {
        int i, alnum_count = 0;
@@ -1131,95 +1091,85 @@ static int adjust_last_block(struct diff_options *o, int n, int block_length)
                }
        }
        for (i = 1; i < block_length + 1; i++)
-               o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+               o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK;
        return 0;
 }
 
 /* Find blocks of moved code, delegate actual coloring decision to helper */
 static void mark_color_as_moved(struct diff_options *o,
-                               struct hashmap *add_lines,
-                               struct hashmap *del_lines)
+                               struct moved_entry_list *entry_list)
 {
        struct moved_block *pmb = NULL; /* potentially moved blocks */
        int pmb_nr = 0, pmb_alloc = 0;
        int n, flipped_block = 0, block_length = 0;
+       enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
 
 
        for (n = 0; n < o->emitted_symbols->nr; n++) {
-               struct hashmap *hm = NULL;
-               struct moved_entry *key;
                struct moved_entry *match = NULL;
                struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
-               enum diff_symbol last_symbol = 0;
 
                switch (l->s) {
                case DIFF_SYMBOL_PLUS:
-                       hm = del_lines;
-                       key = prepare_entry(o, n);
-                       match = hashmap_get_entry(hm, key, ent, NULL);
-                       free(key);
+                       match = entry_list[l->id].del;
                        break;
                case DIFF_SYMBOL_MINUS:
-                       hm = add_lines;
-                       key = prepare_entry(o, n);
-                       match = hashmap_get_entry(hm, key, ent, NULL);
-                       free(key);
+                       match = entry_list[l->id].add;
                        break;
                default:
                        flipped_block = 0;
                }
 
-               if (!match) {
-                       int i;
-
-                       adjust_last_block(o, n, block_length);
-                       for(i = 0; i < pmb_nr; i++)
-                               moved_block_clear(&pmb[i]);
+               if (pmb_nr && (!match || l->s != moved_symbol)) {
+                       if (!adjust_last_block(o, n, block_length) &&
+                           block_length > 1) {
+                               /*
+                                * Rewind in case there is another match
+                                * starting at the second line of the block
+                                */
+                               match = NULL;
+                               n -= block_length;
+                       }
                        pmb_nr = 0;
                        block_length = 0;
                        flipped_block = 0;
-                       last_symbol = l->s;
+               }
+               if (!match) {
+                       moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
                        continue;
                }
 
                if (o->color_moved == COLOR_MOVED_PLAIN) {
-                       last_symbol = l->s;
                        l->flags |= DIFF_SYMBOL_MOVED_LINE;
                        continue;
                }
 
-               if (o->color_moved_ws_handling &
-                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
-                       pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
-               else
-                       pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
-
-               pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
+               pmb_advance_or_null(o, l, pmb, &pmb_nr);
 
                if (pmb_nr == 0) {
-                       /*
-                        * The current line is the start of a new block.
-                        * Setup the set of potential blocks.
-                        */
-                       hashmap_for_each_entry_from(hm, match, ent) {
-                               ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
-                               if (o->color_moved_ws_handling &
-                                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
-                                       if (compute_ws_delta(l, match->es,
-                                                            &pmb[pmb_nr].wsd))
-                                               pmb[pmb_nr++].match = match;
-                               } else {
-                                       pmb[pmb_nr].wsd = 0;
-                                       pmb[pmb_nr++].match = match;
-                               }
-                       }
+                       int contiguous = adjust_last_block(o, n, block_length);
+
+                       if (!contiguous && block_length > 1)
+                               /*
+                                * Rewind in case there is another match
+                                * starting at the second line of the block
+                                */
+                               n -= block_length;
+                       else
+                               fill_potential_moved_blocks(o, match, l,
+                                                           &pmb, &pmb_alloc,
+                                                           &pmb_nr);
 
-                       if (adjust_last_block(o, n, block_length) &&
-                           pmb_nr && last_symbol != l->s)
+                       if (contiguous && pmb_nr && moved_symbol == l->s)
                                flipped_block = (flipped_block + 1) % 2;
                        else
                                flipped_block = 0;
 
+                       if (pmb_nr)
+                               moved_symbol = l->s;
+                       else
+                               moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
+
                        block_length = 0;
                }
 
@@ -1229,17 +1179,12 @@ static void mark_color_as_moved(struct diff_options *o,
                        if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
                                l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
                }
-               last_symbol = l->s;
        }
        adjust_last_block(o, n, block_length);
 
-       for(n = 0; n < pmb_nr; n++)
-               moved_block_clear(&pmb[n]);
        free(pmb);
 }
 
-#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
-  (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
 static void dim_moved_lines(struct diff_options *o)
 {
        int n;
@@ -1573,7 +1518,9 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
 static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
                             const char *line, int len, unsigned flags)
 {
-       struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
+       struct emitted_diff_symbol e = {
+               .line = line, .len = len, .flags = flags, .s = s
+       };
 
        if (o->emitted_symbols)
                append_emitted_diff_symbol(o, &e);
@@ -4639,16 +4586,20 @@ void diff_setup_done(struct diff_options *options)
                options->set_default(options);
 
        if (HAS_MULTI_BITS(options->output_format & check_mask))
-               die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
+               die(_("options '%s', '%s', '%s', and '%s' cannot be used together"),
+                       "--name-only", "--name-status", "--check", "-s");
 
        if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
-               die(_("-G, -S and --find-object are mutually exclusive"));
+               die(_("options '%s', '%s', and '%s' cannot be used together"),
+                       "-G", "-S", "--find-object");
 
        if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_G_REGEX_MASK))
-               die(_("-G and --pickaxe-regex are mutually exclusive, use --pickaxe-regex with -S"));
+               die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s'"),
+                       "-G", "--pickaxe-regex", "--pickaxe-regex", "-S");
 
        if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_ALL_OBJFIND_MASK))
-               die(_("--pickaxe-all and --find-object are mutually exclusive, use --pickaxe-all with -G and -S"));
+               die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"),
+                       "--pickaxe-all", "--find-object", "--pickaxe-all", "-G", "-S");
 
        /*
         * Most of the time we can say "there are changes"
@@ -6345,24 +6296,18 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
 
        if (o->emitted_symbols) {
                if (o->color_moved) {
-                       struct hashmap add_lines, del_lines;
-
-                       if (o->color_moved_ws_handling &
-                           COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
-                               o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
-
-                       hashmap_init(&del_lines, moved_entry_cmp, o, 0);
-                       hashmap_init(&add_lines, moved_entry_cmp, o, 0);
+                       struct mem_pool entry_pool;
+                       struct moved_entry_list *entry_list;
 
-                       add_lines_to_move_detection(o, &add_lines, &del_lines);
-                       mark_color_as_moved(o, &add_lines, &del_lines);
+                       mem_pool_init(&entry_pool, 1024 * 1024);
+                       entry_list = add_lines_to_move_detection(o,
+                                                                &entry_pool);
+                       mark_color_as_moved(o, entry_list);
                        if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
                                dim_moved_lines(o);
 
-                       hashmap_clear_and_free(&add_lines, struct moved_entry,
-                                               ent);
-                       hashmap_clear_and_free(&del_lines, struct moved_entry,
-                                               ent);
+                       mem_pool_discard(&entry_pool, 0);
+                       free(entry_list);
                }
 
                for (i = 0; i < esm.nr; i++)
@@ -6921,19 +6866,15 @@ static char *run_textconv(struct repository *r,
                          size_t *outsize)
 {
        struct diff_tempfile *temp;
-       const char *argv[3];
-       const char **arg = argv;
        struct child_process child = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
        int err = 0;
 
        temp = prepare_temp_file(r, spec->path, spec);
-       *arg++ = pgm;
-       *arg++ = temp->name;
-       *arg = NULL;
+       strvec_push(&child.args, pgm);
+       strvec_push(&child.args, temp->name);
 
        child.use_shell = 1;
-       child.argv = argv;
        child.out = -1;
        if (start_command(&child)) {
                remove_tempfile();
diff --git a/dir.c b/dir.c
index 5aa6fbad0b76ad3ad026a8775761f9c7ea1776e8..d91295f2bcdcf86f0b1c41a32ac6a17d5d3cf65e 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -727,7 +727,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
        }
 
        if (given->patternlen < 2 ||
-           *given->pattern == '*' ||
+           *given->pattern != '/' ||
            strstr(given->pattern, "**")) {
                /* Not a cone pattern. */
                warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -819,9 +819,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                /* we already included this at the parent level */
                warning(_("your sparse-checkout file may have issues: pattern '%s' is repeated"),
                        given->pattern);
-               hashmap_remove(&pl->parent_hashmap, &translated->ent, &data);
-               free(data);
-               free(translated);
+               goto clear_hashmaps;
        }
 
        return;
@@ -3160,6 +3158,7 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
        int ret = 0, original_len = path->len, len, kept_down = 0;
        int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
        int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
+       int purge_original_cwd = (flag & REMOVE_DIR_PURGE_ORIGINAL_CWD);
        struct object_id submodule_head;
 
        if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
@@ -3215,9 +3214,14 @@ static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
        closedir(dir);
 
        strbuf_setlen(path, original_len);
-       if (!ret && !keep_toplevel && !kept_down)
-               ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
-       else if (kept_up)
+       if (!ret && !keep_toplevel && !kept_down) {
+               if (!purge_original_cwd &&
+                   startup_info->original_cwd &&
+                   !strcmp(startup_info->original_cwd, path->buf))
+                       ret = -1; /* Do not remove current working directory */
+               else
+                       ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
+       } else if (kept_up)
                /*
                 * report the uplevel that it is not an error that we
                 * did not rmdir() our directory.
@@ -3283,6 +3287,9 @@ int remove_path(const char *name)
                slash = dirs + (slash - name);
                do {
                        *slash = '\0';
+                       if (startup_info->original_cwd &&
+                           !strcmp(startup_info->original_cwd, dirs))
+                               break;
                } while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
                free(dirs);
        }
diff --git a/dir.h b/dir.h
index 83f46c0fb4c4415c79d3a9fcdddbcbf372b35415..8e02dfb505d163eca3c33d17e495be8f166b83e5 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -495,6 +495,9 @@ int get_sparse_checkout_patterns(struct pattern_list *pl);
 /* Remove the contents of path, but leave path itself. */
 #define REMOVE_DIR_KEEP_TOPLEVEL 04
 
+/* Remove the_original_cwd too */
+#define REMOVE_DIR_PURGE_ORIGINAL_CWD 0x08
+
 /*
  * Remove path and its contents, recursively. flags is a combination
  * of the above REMOVE_DIR_* constants. Return 0 on success.
@@ -504,7 +507,11 @@ int get_sparse_checkout_patterns(struct pattern_list *pl);
  */
 int remove_dir_recursively(struct strbuf *path, int flag);
 
-/* tries to remove the path with empty directories along it, ignores ENOENT */
+/*
+ * Tries to remove the path, along with leading empty directories so long as
+ * those empty directories are not startup_info->original_cwd.  Ignores
+ * ENOENT.
+ */
 int remove_path(const char *path);
 
 int fspathcmp(const char *a, const char *b);
index fdd3eeafa94791791aefc976988f4cd0b5a5f58f..8b9648281d7b53ad34a8dffca32ae2940b3a81c5 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "strbuf.h"
+#include "strvec.h"
 #include "run-command.h"
 #include "sigchain.h"
 
@@ -55,7 +56,6 @@ static int launch_specified_editor(const char *editor, const char *path,
 
        if (strcmp(editor, ":")) {
                struct strbuf realpath = STRBUF_INIT;
-               const char *args[] = { editor, NULL, NULL };
                struct child_process p = CHILD_PROCESS_INIT;
                int ret, sig;
                int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2);
@@ -77,10 +77,10 @@ static int launch_specified_editor(const char *editor, const char *path,
                }
 
                strbuf_realpath(&realpath, path, 1);
-               args[1] = realpath.buf;
 
-               p.argv = args;
-               p.env = env;
+               strvec_pushl(&p.args, editor, realpath.buf, NULL);
+               if (env)
+                       strvec_pushv(&p.env_array, (const char **)env);
                p.use_shell = 1;
                p.trace2_child_class = "editor";
                if (start_command(&p) < 0) {
index 9da7f3c1a19ee5d3b6c727e4f9c6ec8c599fd7e8..fd0501e77a5b8d96dad8b5fe3714b74f13786be7 100644 (file)
@@ -17,6 +17,7 @@
 #include "commit.h"
 #include "strvec.h"
 #include "object-store.h"
+#include "tmp-objdir.h"
 #include "chdir-notify.h"
 #include "shallow.h"
 
@@ -42,6 +43,7 @@ const char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files;
+int use_fsync = -1;
 size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 96 * 1024 * 1024;
@@ -168,6 +170,10 @@ void setup_git_env(const char *git_dir)
        args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
        args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
        args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
+       if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+               args.disable_ref_updates = 1;
+       }
+
        repo_set_gitdir(the_repository, git_dir, &args);
        strvec_clear(&to_free);
 
@@ -331,10 +337,14 @@ static void update_relative_gitdir(const char *name,
                                   void *data)
 {
        char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
+       struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
+
        trace_printf_key(&trace_setup_key,
                         "setup: move $GIT_DIR to '%s'",
                         path);
        set_git_dir_1(path);
+       if (tmp_objdir)
+               tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
        free(path);
 }
 
index 34987a2c30d5a617bc44a9a632151abc354b034c..dd6ec449f2dbc87dfb0e9922d1e1c772ccae419e 100644 (file)
@@ -297,7 +297,7 @@ static int find_common(struct fetch_negotiator *negotiator,
        struct packet_reader reader;
 
        if (args->stateless_rpc && multi_ack == 1)
-               die(_("--stateless-rpc requires multi_ack_detailed"));
+               die(_("the option '%s' requires '%s'"), "--stateless-rpc", "multi_ack_detailed");
 
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
index 5216191488e20115949418aab527988ba7b81e7c..baca57d5b64c9f30bad08a505ccd7f1a9ba38546 100644 (file)
@@ -533,14 +533,14 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                else {
                        buf = payload.buf;
                        len = payload.len;
-                       if (check_signature(payload.buf, payload.len, sig.buf,
-                                           sig.len, &sigc) &&
+                       sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+                       sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+                       if (check_signature(&sigc, sig.buf, sig.len) &&
                            !sigc.output)
                                strbuf_addstr(&sig, "gpg verification failed.\n");
                        else
                                strbuf_addstr(&sig, sigc.output);
                }
-               signature_check_clear(&sigc);
 
                if (!tag_number++) {
                        fmt_tag_signature(&tagbuf, &sig, buf, len);
@@ -564,6 +564,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                }
                strbuf_release(&payload);
                strbuf_release(&sig);
+               signature_check_clear(&sigc);
        next:
                free(origbuf);
        }
@@ -649,12 +650,15 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
        memset(&merge_parents, 0, sizeof(merge_parents));
 
-       /* get current branch */
+       /* learn the commit that we merge into and the current branch name */
        current_branch = current_branch_to_free =
                resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
        if (!current_branch)
                die("No current branch");
-       if (starts_with(current_branch, "refs/heads/"))
+
+       if (opts->into_name)
+               current_branch = opts->into_name;
+       else if (starts_with(current_branch, "refs/heads/"))
                current_branch += 11;
 
        find_merge_parents(&merge_parents, in, &head_oid);
index f2ab0e0085ada6509c02427503b1ea04b18951e0..99054042dc5e574b2ef1c00394331955239e0324 100644 (file)
@@ -9,6 +9,7 @@ struct fmt_merge_msg_opts {
        unsigned add_title:1,
                credit_people:1;
        int shortlog_len;
+       const char *into_name;
 };
 
 extern int merge_log_config;
index 5fa54a7afe4bf9ef05d4fa400e677f4f5c94ea4a..1229c8296b92547b89bc1cc29e6f2415fa1370af 100644 (file)
 /*
  * See if our compiler is known to support flexible array members.
  */
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
-# define FLEX_ARRAY /* empty */
+
+/*
+ * Check vendor specific quirks first, before checking the
+ * __STDC_VERSION__, as vendor compilers can lie and we need to be
+ * able to work them around.  Note that by not defining FLEX_ARRAY
+ * here, we can fall back to use the "safer but a bit wasteful" one
+ * later.
+ */
+#if defined(__SUNPRO_C) && (__SUNPRO_C <= 0x580)
 #elif defined(__GNUC__)
 # if (__GNUC__ >= 3)
 #  define FLEX_ARRAY /* empty */
 # else
 #  define FLEX_ARRAY 0 /* older GNU extension */
 # endif
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
 #endif
 
 /*
@@ -489,11 +498,12 @@ static inline int git_has_dir_sep(const char *path)
 struct strbuf;
 
 /* General helper functions */
-void vreportf(const char *prefix, const char *err, va_list params);
 NORETURN void usage(const char *err);
 NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
 NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
 NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -528,6 +538,7 @@ static inline int const_error(void)
 typedef void (*report_fn)(const char *, va_list params);
 
 void set_die_routine(NORETURN_PTR report_fn routine);
+report_fn get_die_message_routine(void);
 void set_error_routine(report_fn routine);
 report_fn get_error_routine(void);
 void set_warn_routine(report_fn routine);
index 64319bed43f2b4916910480223f1089ce90990ef..4c8118010a81f4da9cd2e52029cb9d6c7992975d 100755 (executable)
@@ -3607,6 +3607,22 @@ package GITCVS::updater;
 use strict;
 use warnings;
 use DBI;
+our $_use_fsync;
+
+# n.b. consider using Git.pm
+sub use_fsync {
+    if (!defined($_use_fsync)) {
+        my $x = $ENV{GIT_TEST_FSYNC};
+        if (defined $x) {
+            local $ENV{GIT_CONFIG};
+            delete $ENV{GIT_CONFIG};
+            my $v = ::safe_pipe_capture('git', '-c', "test.fsync=$x",
+                                        qw(config --type=bool test.fsync));
+            $_use_fsync = defined($v) ? ($v eq "true\n") : 1;
+        }
+    }
+    $_use_fsync;
+}
 
 =head1 METHODS
 
@@ -3676,6 +3692,9 @@ sub new
                                 $self->{dbuser},
                                 $self->{dbpass});
     die "Error connecting to database\n" unless defined $self->{dbh};
+    if ($self->{dbdriver} eq 'SQLite' && !use_fsync()) {
+        $self->{dbh}->do('PRAGMA synchronous = OFF');
+    }
 
     $self->{tables} = {};
     foreach my $table ( keys %{$self->{dbh}->table_info(undef,undef,undef,'TABLE')->fetchall_hashref('TABLE_NAME')} )
index 2b4500226aa7a48d2b2644d3b1f351342cda20f4..cb37545455e9d8b782c078582a7e5a8b902af845 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -56,6 +56,21 @@ defaultBlockSize = 1<<20
 
 p4_access_checked = False
 
+re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
+re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
+
+def format_size_human_readable(num):
+    """ Returns a number of units (typically bytes) formatted as a human-readable
+        string.
+    """
+    if num < 1024:
+        return '{:d} B'.format(num)
+    for unit in ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
+        num /= 1024.0
+        if num < 1024.0:
+            return "{:3.1f} {}B".format(num, unit)
+    return "{:.1f} YiB".format(num)
+
 def p4_build_cmd(cmd):
     """Build a suitable p4 command line.
 
@@ -337,17 +352,19 @@ def p4_read_pipe(c, ignore_error=False, raw=False):
     real_cmd = p4_build_cmd(c)
     return read_pipe(real_cmd, ignore_error, raw=raw)
 
-def read_pipe_lines(c):
+def read_pipe_lines(c, raw=False):
     if verbose:
         sys.stderr.write('Reading pipe: %s\n' % str(c))
 
     expand = not isinstance(c, list)
     p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
     pipe = p.stdout
-    val = [decode_text_stream(line) for line in pipe.readlines()]
+    lines = pipe.readlines()
+    if not raw:
+        lines = [decode_text_stream(line) for line in lines]
     if pipe.close() or p.wait():
         die('Command failed: %s' % str(c))
-    return val
+    return lines
 
 def p4_read_pipe_lines(c):
     """Specifically invoke p4 on the command supplied. """
@@ -577,20 +594,12 @@ def p4_type(f):
 #
 def p4_keywords_regexp_for_type(base, type_mods):
     if base in ("text", "unicode", "binary"):
-        kwords = None
         if "ko" in type_mods:
-            kwords = 'Id|Header'
+            return re_ko_keywords
         elif "k" in type_mods:
-            kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
+            return re_k_keywords
         else:
             return None
-        pattern = r"""
-            \$              # Starts with a dollar, followed by...
-            (%s)            # one of the keywords, followed by...
-            (:[^$\n]+)?     # possibly an old expansion, followed by...
-            \$              # another dollar
-            """ % kwords
-        return pattern
     else:
         return None
 
@@ -1532,80 +1541,6 @@ class P4UserMap:
         except IOError:
             self.getUserMapFromPerforceServer()
 
-class P4Debug(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = []
-        self.description = "A tool to debug the output of p4 -G."
-        self.needsGit = False
-
-    def run(self, args):
-        j = 0
-        for output in p4CmdList(args):
-            print('Element: %d' % j)
-            j += 1
-            print(output)
-        return True
-
-class P4RollBack(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-            optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
-        ]
-        self.description = "A tool to debug the multi-branch import. Don't use :)"
-        self.rollbackLocalBranches = False
-
-    def run(self, args):
-        if len(args) != 1:
-            return False
-        maxChange = int(args[0])
-
-        if "p4ExitCode" in p4Cmd("changes -m 1"):
-            die("Problems executing p4");
-
-        if self.rollbackLocalBranches:
-            refPrefix = "refs/heads/"
-            lines = read_pipe_lines("git rev-parse --symbolic --branches")
-        else:
-            refPrefix = "refs/remotes/"
-            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
-
-        for line in lines:
-            if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
-                line = line.strip()
-                ref = refPrefix + line
-                log = extractLogMessageFromGitCommit(ref)
-                settings = extractSettingsGitLog(log)
-
-                depotPaths = settings['depot-paths']
-                change = settings['change']
-
-                changed = False
-
-                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
-                                                           for p in depotPaths]))) == 0:
-                    print("Branch %s did not exist at change %s, deleting." % (ref, maxChange))
-                    system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
-                    continue
-
-                while change and int(change) > maxChange:
-                    changed = True
-                    if self.verbose:
-                        print("%s is at %s ; rewinding towards %s" % (ref, change, maxChange))
-                    system("git update-ref %s \"%s^\"" % (ref, ref))
-                    log = extractLogMessageFromGitCommit(ref)
-                    settings =  extractSettingsGitLog(log)
-
-
-                    depotPaths = settings['depot-paths']
-                    change = settings['change']
-
-                if changed:
-                    print("%s rewound to %s" % (ref, change))
-
-        return True
-
 class P4Submit(Command, P4UserMap):
 
     conflict_behavior_choices = ("ask", "skip", "quit")
@@ -1753,18 +1688,13 @@ class P4Submit(Command, P4UserMap):
 
         return result
 
-    def patchRCSKeywords(self, file, pattern):
-        # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
+    def patchRCSKeywords(self, file, regexp):
+        # Attempt to zap the RCS keywords in a p4 controlled file matching the given regex
         (handle, outFileName) = tempfile.mkstemp(dir='.')
         try:
-            outFile = os.fdopen(handle, "w+")
-            inFile = open(file, "r")
-            regexp = re.compile(pattern, re.VERBOSE)
-            for line in inFile.readlines():
-                line = regexp.sub(r'$\1$', line)
-                outFile.write(line)
-            inFile.close()
-            outFile.close()
+            with os.fdopen(handle, "wb") as outFile, open(file, "rb") as inFile:
+                for line in inFile.readlines():
+                    outFile.write(regexp.sub(br'$\1$', line))
             # Forcibly overwrite the original file
             os.unlink(file)
             shutil.move(outFileName, file)
@@ -2091,25 +2021,24 @@ class P4Submit(Command, P4UserMap):
             # the patch to see if that's possible.
             if gitConfigBool("git-p4.attemptRCSCleanup"):
                 file = None
-                pattern = None
                 kwfiles = {}
                 for file in editedFiles | filesToDelete:
                     # did this file's delta contain RCS keywords?
-                    pattern = p4_keywords_regexp_for_file(file)
-
-                    if pattern:
+                    regexp = p4_keywords_regexp_for_file(file)
+                    if regexp:
                         # this file is a possibility...look for RCS keywords.
-                        regexp = re.compile(pattern, re.VERBOSE)
-                        for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
+                        for line in read_pipe_lines(
+                            ["git", "diff", "%s^..%s" % (id, id), file],
+                            raw=True):
                             if regexp.search(line):
                                 if verbose:
-                                    print("got keyword match on %s in %s in %s" % (pattern, line, file))
-                                kwfiles[file] = pattern
+                                    print("got keyword match on %s in %s in %s" % (regex.pattern, line, file))
+                                kwfiles[file] = regexp
                                 break
 
-                for file in kwfiles:
+                for file, regexp in kwfiles.items():
                     if verbose:
-                        print("zapping %s with %s" % (line,pattern))
+                        print("zapping %s with %s" % (line, regexp.pattern))
                     # File is being deleted, so not open in p4.  Must
                     # disable the read-only bit on windows.
                     if self.isWindows and file not in editedFiles:
@@ -2966,7 +2895,8 @@ class P4Sync(Command, P4UserMap):
                 size = int(self.stream_file['fileSize'])
             else:
                 size = 0 # deleted files don't get a fileSize apparently
-            sys.stdout.write('\r%s --> %s (%i MB)\n' % (file_path, relPath, size/1024/1024))
+            sys.stdout.write('\r%s --> %s (%s)\n' % (
+                file_path, relPath, format_size_human_readable(size)))
             sys.stdout.flush()
 
         (type_base, type_mods) = split_p4_type(file["type"])
@@ -3029,12 +2959,9 @@ class P4Sync(Command, P4UserMap):
 
         # Note that we do not try to de-mangle keywords on utf16 files,
         # even though in theory somebody may want that.
-        pattern = p4_keywords_regexp_for_type(type_base, type_mods)
-        if pattern:
-            regexp = re.compile(pattern, re.VERBOSE)
-            text = ''.join(decode_text_stream(c) for c in contents)
-            text = regexp.sub(r'$\1$', text)
-            contents = [ encode_text_stream(text) ]
+        regexp = p4_keywords_regexp_for_type(type_base, type_mods)
+        if regexp:
+            contents = [regexp.sub(br'$\1$', c) for c in contents]
 
         if self.largeFileSystem:
             (git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
@@ -3064,9 +2991,8 @@ class P4Sync(Command, P4UserMap):
         if not err and 'fileSize' in self.stream_file:
             required_bytes = int((4 * int(self.stream_file["fileSize"])) - calcDiskFree())
             if required_bytes > 0:
-                err = 'Not enough space left on %s! Free at least %i MB.' % (
-                    os.getcwd(), required_bytes/1024/1024
-                )
+                err = 'Not enough space left on %s! Free at least %s.' % (
+                    os.getcwd(), format_size_human_readable(required_bytes))
 
         if err:
             f = None
@@ -3110,7 +3036,9 @@ class P4Sync(Command, P4UserMap):
             size = int(self.stream_file["fileSize"])
             if size > 0:
                 progress = 100*self.stream_file['streamContentSize']/size
-                sys.stdout.write('\r%s %d%% (%i MB)' % (self.stream_file['depotFile'], progress, int(size/1024/1024)))
+                sys.stdout.write('\r%s %d%% (%s)' % (
+                    self.stream_file['depotFile'], progress,
+                    format_size_human_readable(size)))
                 sys.stdout.flush()
 
         self.stream_have_file_info = True
@@ -3623,7 +3551,8 @@ class P4Sync(Command, P4UserMap):
             self.updateOptionDict(description)
 
             if not self.silent:
-                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+                sys.stdout.write("\rImporting revision %s (%d%%)" % (
+                    change, (cnt * 100) // len(changes)))
                 sys.stdout.flush()
             cnt = cnt + 1
 
@@ -4363,13 +4292,11 @@ def printUsage(commands):
     print("")
 
 commands = {
-    "debug" : P4Debug,
     "submit" : P4Submit,
     "commit" : P4Submit,
     "sync" : P4Sync,
     "rebase" : P4Rebase,
     "clone" : P4Clone,
-    "rollback" : P4RollBack,
     "branches" : P4Branches,
     "unshelve" : P4Unshelve,
 }
diff --git a/git.c b/git.c
index 5ff21be21f323c794399a3605f7396e1b3bba686..edda922ce6d423b8b734b47b979f4912b9a4d281 100644 (file)
--- a/git.c
+++ b/git.c
@@ -185,7 +185,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--git-dir")) {
                        if (*argc < 2) {
-                               fprintf(stderr, _("no directory given for --git-dir\n" ));
+                               fprintf(stderr, _("no directory given for '%s' option\n" ), "--git-dir");
                                usage(git_usage_string);
                        }
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -213,7 +213,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
-                               fprintf(stderr, _("no directory given for --work-tree\n" ));
+                               fprintf(stderr, _("no directory given for '%s' option\n" ), "--work-tree");
                                usage(git_usage_string);
                        }
                        setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -297,7 +297,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "-C")) {
                        if (*argc < 2) {
-                               fprintf(stderr, _("no directory given for -C\n" ));
+                               fprintf(stderr, _("no directory given for '%s' option\n" ), "-C");
                                usage(git_usage_string);
                        }
                        if ((*argv)[1][0]) {
@@ -421,27 +421,30 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        int status, help;
        struct stat st;
        const char *prefix;
+       int run_setup = (p->option & (RUN_SETUP | RUN_SETUP_GENTLY));
 
-       prefix = NULL;
        help = argc == 2 && !strcmp(argv[1], "-h");
-       if (!help) {
-               if (p->option & RUN_SETUP)
-                       prefix = setup_git_directory();
-               else if (p->option & RUN_SETUP_GENTLY) {
-                       int nongit_ok;
-                       prefix = setup_git_directory_gently(&nongit_ok);
-               }
-               precompose_argv_prefix(argc, argv, NULL);
-               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
-                   !(p->option & DELAY_PAGER_CONFIG))
-                       use_pager = check_pager_config(p->cmd);
-               if (use_pager == -1 && p->option & USE_PAGER)
-                       use_pager = 1;
-
-               if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
-                   startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
-                       trace_repo_setup(prefix);
+       if (help && (run_setup & RUN_SETUP))
+               /* demote to GENTLY to allow 'git cmd -h' outside repo */
+               run_setup = RUN_SETUP_GENTLY;
+
+       if (run_setup & RUN_SETUP) {
+               prefix = setup_git_directory();
+       } else if (run_setup & RUN_SETUP_GENTLY) {
+               int nongit_ok;
+               prefix = setup_git_directory_gently(&nongit_ok);
+       } else {
+               prefix = NULL;
        }
+       precompose_argv_prefix(argc, argv, NULL);
+       if (use_pager == -1 && run_setup &&
+               !(p->option & DELAY_PAGER_CONFIG))
+               use_pager = check_pager_config(p->cmd);
+       if (use_pager == -1 && p->option & USE_PAGER)
+               use_pager = 1;
+       if (run_setup && startup_info->have_repository)
+               /* get_git_dir() may set up repo, avoid that */
+               trace_repo_setup(prefix);
        commit_pager_choice();
 
        if (!help && get_super_prefix()) {
index 3e7255a2a91103323232f55a6ae7770266595f9a..b52eb0e2e04b37c6868f7573d750b688a4ec0e68 100644 (file)
@@ -19,8 +19,8 @@ struct gpg_format {
        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,
+                                   struct gpg_format *fmt,
+                                   const char *signature,
                                    size_t signature_size);
        int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
                           const char *signing_key);
@@ -53,12 +53,12 @@ static const char *ssh_sigs[] = {
 };
 
 static int verify_gpg_signed_buffer(struct signature_check *sigc,
-                                   struct gpg_format *fmt, const char *payload,
-                                   size_t payload_size, const char *signature,
+                                   struct gpg_format *fmt,
+                                   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,
+                                   struct gpg_format *fmt,
+                                   const char *signature,
                                    size_t signature_size);
 static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
                           const char *signing_key);
@@ -314,8 +314,8 @@ error:
 }
 
 static int verify_gpg_signed_buffer(struct signature_check *sigc,
-                                   struct gpg_format *fmt, const char *payload,
-                                   size_t payload_size, const char *signature,
+                                   struct gpg_format *fmt,
+                                   const char *signature,
                                    size_t signature_size)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
@@ -343,14 +343,13 @@ static int verify_gpg_signed_buffer(struct signature_check *sigc,
                     NULL);
 
        sigchain_push(SIGPIPE, SIG_IGN);
-       ret = pipe_command(&gpg, payload, payload_size, &gpg_stdout, 0,
+       ret = pipe_command(&gpg, sigc->payload, sigc->payload_len, &gpg_stdout, 0,
                           &gpg_stderr, 0);
        sigchain_pop(SIGPIPE);
 
        delete_tempfile(&temp);
 
        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);
 
@@ -426,8 +425,8 @@ cleanup:
 }
 
 static int verify_ssh_signed_buffer(struct signature_check *sigc,
-                                   struct gpg_format *fmt, const char *payload,
-                                   size_t payload_size, const char *signature,
+                                   struct gpg_format *fmt,
+                                   const char *signature,
                                    size_t signature_size)
 {
        struct child_process ssh_keygen = CHILD_PROCESS_INIT;
@@ -440,6 +439,13 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
        struct strbuf ssh_principals_err = STRBUF_INIT;
        struct strbuf ssh_keygen_out = STRBUF_INIT;
        struct strbuf ssh_keygen_err = STRBUF_INIT;
+       struct strbuf verify_time = STRBUF_INIT;
+       const struct date_mode verify_date_mode = {
+               .type = DATE_STRFTIME,
+               .strftime_fmt = "%Y%m%d%H%M%S",
+               /* SSH signing key validity has no timezone information - Use the local timezone */
+               .local = 1,
+       };
 
        if (!ssh_allowed_signers) {
                error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));
@@ -457,11 +463,16 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                return -1;
        }
 
+       if (sigc->payload_timestamp)
+               strbuf_addf(&verify_time, "-Overify-time=%s",
+                       show_date(sigc->payload_timestamp, 0, &verify_date_mode));
+
        /* 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,
+                    verify_time.buf,
                     NULL);
        ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
                           &ssh_principals_err, 0);
@@ -479,8 +490,9 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                             "-Y", "check-novalidate",
                             "-n", "git",
                             "-s", buffer_file->filename.buf,
+                            verify_time.buf,
                             NULL);
-               pipe_command(&ssh_keygen, payload, payload_size,
+               pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,
                                   &ssh_keygen_out, 0, &ssh_keygen_err, 0);
 
                /*
@@ -513,6 +525,7 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                                     "-f", ssh_allowed_signers,
                                     "-I", principal,
                                     "-s", buffer_file->filename.buf,
+                                    verify_time.buf,
                                     NULL);
 
                        if (ssh_revocation_file) {
@@ -526,7 +539,7 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                        }
 
                        sigchain_push(SIGPIPE, SIG_IGN);
-                       ret = pipe_command(&ssh_keygen, payload, payload_size,
+                       ret = pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,
                                           &ssh_keygen_out, 0, &ssh_keygen_err, 0);
                        sigchain_pop(SIGPIPE);
 
@@ -540,7 +553,6 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
                }
        }
 
-       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 */
@@ -558,12 +570,48 @@ out:
        strbuf_release(&ssh_principals_err);
        strbuf_release(&ssh_keygen_out);
        strbuf_release(&ssh_keygen_err);
+       strbuf_release(&verify_time);
 
        return ret;
 }
 
-int check_signature(const char *payload, size_t plen, const char *signature,
-       size_t slen, struct signature_check *sigc)
+static int parse_payload_metadata(struct signature_check *sigc)
+{
+       const char *ident_line = NULL;
+       size_t ident_len;
+       struct ident_split ident;
+       const char *signer_header;
+
+       switch (sigc->payload_type) {
+       case SIGNATURE_PAYLOAD_COMMIT:
+               signer_header = "committer";
+               break;
+       case SIGNATURE_PAYLOAD_TAG:
+               signer_header = "tagger";
+               break;
+       case SIGNATURE_PAYLOAD_UNDEFINED:
+       case SIGNATURE_PAYLOAD_PUSH_CERT:
+               /* Ignore payloads we don't want to parse */
+               return 0;
+       default:
+               BUG("invalid value for sigc->payload_type");
+       }
+
+       ident_line = find_commit_header(sigc->payload, signer_header, &ident_len);
+       if (!ident_line || !ident_len)
+               return 1;
+
+       if (split_ident_line(&ident, ident_line, ident_len))
+               return 1;
+
+       if (!sigc->payload_timestamp && ident.date_begin && ident.date_end)
+               sigc->payload_timestamp = parse_timestamp(ident.date_begin, NULL, 10);
+
+       return 0;
+}
+
+int check_signature(struct signature_check *sigc,
+                   const char *signature, size_t slen)
 {
        struct gpg_format *fmt;
        int status;
@@ -575,8 +623,10 @@ int check_signature(const char *payload, size_t plen, const char *signature,
        if (!fmt)
                die(_("bad/incompatible signature '%s'"), signature);
 
-       status = fmt->verify_signed_buffer(sigc, fmt, payload, plen, signature,
-                                          slen);
+       if (parse_payload_metadata(sigc))
+               return 1;
+
+       status = fmt->verify_signed_buffer(sigc, fmt, signature, slen);
 
        if (status && !sigc->output)
                return !!status;
@@ -593,7 +643,7 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
                                                            sigc->output;
 
        if (flags & GPG_VERIFY_VERBOSE && sigc->payload)
-               fputs(sigc->payload, stdout);
+               fwrite(sigc->payload, 1, sigc->payload_len, stdout);
 
        if (output)
                fputs(output, stderr);
@@ -707,6 +757,21 @@ int git_gpg_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
+/*
+ * Returns 1 if `string` contains a literal ssh key, 0 otherwise
+ * `key` will be set to the start of the actual key if a prefix is present.
+ */
+static int is_literal_ssh_key(const char *string, const char **key)
+{
+       if (skip_prefix(string, "key::", key))
+               return 1;
+       if (starts_with(string, "ssh-")) {
+               *key = string;
+               return 1;
+       }
+       return 0;
+}
+
 static char *get_ssh_key_fingerprint(const char *signing_key)
 {
        struct child_process ssh_keygen = CHILD_PROCESS_INIT;
@@ -714,15 +779,16 @@ static char *get_ssh_key_fingerprint(const char *signing_key)
        struct strbuf fingerprint_stdout = STRBUF_INIT;
        struct strbuf **fingerprint;
        char *fingerprint_ret;
+       const char *literal_key = NULL;
 
        /*
         * 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-")) {
+       if (is_literal_ssh_key(signing_key, &literal_key)) {
                strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL);
-               ret = pipe_command(&ssh_keygen, signing_key,
-                                  strlen(signing_key), &fingerprint_stdout, 0,
+               ret = pipe_command(&ssh_keygen, literal_key,
+                                  strlen(literal_key), &fingerprint_stdout, 0,
                                   NULL, 0);
        } else {
                strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf",
@@ -757,6 +823,7 @@ static const char *get_default_ssh_signing_key(void)
        const char **argv;
        int n;
        char *default_key = NULL;
+       const char *literal_key = NULL;
 
        if (!ssh_default_key_command)
                die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
@@ -774,7 +841,11 @@ static const char *get_default_ssh_signing_key(void)
 
        if (!ret) {
                keys = strbuf_split_max(&key_stdout, '\n', 2);
-               if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
+               if (keys[0] && is_literal_ssh_key(keys[0]->buf, &literal_key)) {
+                       /*
+                        * We only use `is_literal_ssh_key` here to check validity
+                        * The prefix will be stripped when the key is used.
+                        */
                        default_key = strbuf_detach(keys[0], NULL);
                } else {
                        warning(_("gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"),
@@ -889,19 +960,20 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
        struct tempfile *key_file = NULL, *buffer_file = NULL;
        char *ssh_signing_key_file = NULL;
        struct strbuf ssh_signature_filename = STRBUF_INIT;
+       const char *literal_key = NULL;
 
        if (!signing_key || signing_key[0] == '\0')
                return error(
                        _("user.signingkey needs to be set for ssh signing"));
 
-       if (starts_with(signing_key, "ssh-")) {
+       if (is_literal_ssh_key(signing_key, &literal_key)) {
                /* 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 ||
+               keylen = strlen(literal_key);
+               if (write_in_full(key_file->fd, literal_key, keylen) < 0 ||
                    close_tempfile_gently(key_file) < 0) {
                        error_errno(_("failed writing ssh signing key to '%s'"),
                                    key_file->filename.buf);
index beefacbb1e9025b8d65a83aea74c6ce3913535cd..b30cbdcd3da546888fb1f8f206205c2373ad633b 100644 (file)
@@ -15,8 +15,18 @@ enum signature_trust_level {
        TRUST_ULTIMATE,
 };
 
+enum payload_type {
+       SIGNATURE_PAYLOAD_UNDEFINED,
+       SIGNATURE_PAYLOAD_COMMIT,
+       SIGNATURE_PAYLOAD_TAG,
+       SIGNATURE_PAYLOAD_PUSH_CERT,
+};
+
 struct signature_check {
        char *payload;
+       size_t payload_len;
+       enum payload_type payload_type;
+       timestamp_t payload_timestamp;
        char *output;
        char *gpg_status;
 
@@ -70,9 +80,8 @@ const char *get_signing_key(void);
  * 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);
+int check_signature(struct signature_check *sigc,
+                   const char *signature, size_t slen);
 void print_signature_buffer(const struct signature_check *sigc,
                            unsigned flags);
 
diff --git a/grep.c b/grep.c
index fe847a0111a209279656c5a14318d2fa196df2ed..7bb0360869a64bca7af1e2d7faecb937a63b7910 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -26,10 +26,10 @@ static struct grep_opt grep_defaults = {
        .pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED,
        .colors = {
                [GREP_COLOR_CONTEXT] = "",
-               [GREP_COLOR_FILENAME] = "",
+               [GREP_COLOR_FILENAME] = GIT_COLOR_MAGENTA,
                [GREP_COLOR_FUNCTION] = "",
-               [GREP_COLOR_LINENO] = "",
-               [GREP_COLOR_COLUMNNO] = "",
+               [GREP_COLOR_LINENO] = GIT_COLOR_GREEN,
+               [GREP_COLOR_COLUMNNO] = GIT_COLOR_GREEN,
                [GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED,
                [GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED,
                [GREP_COLOR_SELECTED] = "",
@@ -362,6 +362,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        int jitret;
        int patinforet;
        size_t jitsizearg;
+       int literal = !opt->ignore_case && (p->fixed || p->is_fixed);
 
        /*
         * Call pcre2_general_context_create() before calling any
@@ -382,8 +383,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                }
                options |= PCRE2_CASELESS;
        }
-       if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
-           !(!opt->ignore_case && (p->fixed || p->is_fixed)))
+       if (!opt->ignore_locale && is_utf8_locale() && !literal)
                options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
 
 #ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER
@@ -699,6 +699,14 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
        return compile_pattern_or(list);
 }
 
+static struct grep_expr *grep_not_expr(struct grep_expr *expr)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_NOT;
+       z->u.unary = expr;
+       return z;
+}
+
 static struct grep_expr *grep_true_expr(void)
 {
        struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -797,7 +805,7 @@ void compile_grep_patterns(struct grep_opt *opt)
                }
        }
 
-       if (opt->all_match || header_expr)
+       if (opt->all_match || opt->no_body_match || header_expr)
                opt->extended = 1;
        else if (!opt->extended)
                return;
@@ -808,6 +816,9 @@ void compile_grep_patterns(struct grep_opt *opt)
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
 
+       if (opt->no_body_match && opt->pattern_expression)
+               opt->pattern_expression = grep_not_expr(opt->pattern_expression);
+
        if (!header_expr)
                return;
 
@@ -1057,6 +1068,8 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
                        if (h && (*col < 0 || tmp.rm_so < *col))
                                *col = tmp.rm_so;
                }
+               if (x->u.atom->token == GREP_PATTERN_BODY)
+                       opt->body_hit |= h;
                break;
        case GREP_NODE_NOT:
                /*
@@ -1825,16 +1838,19 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs)
         * we do not have to do the two-pass grep when we do not check
         * buffer-wide "all-match".
         */
-       if (!opt->all_match)
+       if (!opt->all_match && !opt->no_body_match)
                return grep_source_1(opt, gs, 0);
 
        /* Otherwise the toplevel "or" terms hit a bit differently.
         * We first clear hit markers from them.
         */
        clr_hit_marker(opt->pattern_expression);
+       opt->body_hit = 0;
        grep_source_1(opt, gs, 1);
 
-       if (!chk_hit_marker(opt->pattern_expression))
+       if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
+               return 0;
+       if (opt->no_body_match && opt->body_hit)
                return 0;
 
        return grep_source_1(opt, gs, 0);
diff --git a/grep.h b/grep.h
index 3e8815c347b561b72b092aa3ccf781fbcc1b82a1..6a1f0ab01729b8a11eb873a93829f7ee57d07dd4 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -148,6 +148,8 @@ struct grep_opt {
        int word_regexp;
        int fixed;
        int all_match;
+       int no_body_match;
+       int body_hit;
 #define GREP_BINARY_DEFAULT    0
 #define GREP_BINARY_NOMATCH    1
 #define GREP_BINARY_TEXT       2
diff --git a/help.c b/help.c
index 973e47cdc30ce05603fb935b6574482b3556b31a..71444906ddfb24c6604a2da547cb94675553bcb0 100644 (file)
--- a/help.c
+++ b/help.c
@@ -643,7 +643,7 @@ const char *help_unknown_cmd(const char *cmd)
                else if (autocorrect == AUTOCORRECT_PROMPT) {
                        char *answer;
                        struct strbuf msg = STRBUF_INIT;
-                       strbuf_addf(&msg, _("Run '%s' instead? (y/N)"), assumed);
+                       strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed);
                        answer = git_prompt(msg.buf, PROMPT_ECHO);
                        strbuf_release(&msg);
                        if (!(starts_with(answer, "y") ||
index 3d6e2ff17f83c7c5d1fd42578f9dc073686becab..807fb8839e7859ca4e9af298f28a74e5bb7455ec 100644 (file)
@@ -480,7 +480,7 @@ static void run_service(const char **argv, int buffer_input)
                strvec_pushf(&cld.env_array,
                             "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
 
-       cld.argv = argv;
+       strvec_pushv(&cld.args, argv);
        if (buffer_input || gzipped_request || req_len >= 0)
                cld.in = -1;
        cld.git_cmd = 1;
@@ -659,8 +659,9 @@ static NORETURN void die_webcgi(const char *err, va_list params)
 {
        if (dead <= 1) {
                struct strbuf hdr = STRBUF_INIT;
+               report_fn die_message_fn = get_die_message_routine();
 
-               vreportf("fatal: ", err, params);
+               die_message_fn(err, params);
 
                http_status(&hdr, 500, "Internal Server Error");
                hdr_nocache(&hdr);
index c7c7d391ac5b8172496fec464539c9d9ad237b91..58b394cd47f3f068177c622e927532497126e14c 100644 (file)
@@ -141,7 +141,7 @@ int cmd_main(int argc, const char **argv)
 
        if (packfile) {
                if (!index_pack_args.nr)
-                       die(_("--packfile requires --index-pack-args"));
+                       die(_("the option '%s' requires '%s'"), "--packfile", "--index-pack-args");
 
                fetch_single_packfile(&packfile_hash, argv[arg],
                                      index_pack_args.v);
@@ -150,7 +150,7 @@ int cmd_main(int argc, const char **argv)
        }
 
        if (index_pack_args.nr)
-               die(_("--index-pack-args can only be used with --packfile"));
+               die(_("the option '%s' requires '%s'"), "--index-pack-args", "--packfile");
 
        if (commits_on_stdin) {
                commits = walker_targets_stdin(&commit_id, &write_ref);
diff --git a/http.c b/http.c
index f92859f43fa53e0352b239ca43a769c5f9ff4aae..229da4d14882d9c9855ab418ad64f30fe62e485a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -2126,8 +2126,9 @@ int finish_http_pack_request(struct http_pack_request *preq)
 
        ip.git_cmd = 1;
        ip.in = tmpfile_fd;
-       ip.argv = preq->index_pack_args ? preq->index_pack_args
-                                       : default_index_pack_args;
+       strvec_pushv(&ip.args, preq->index_pack_args ?
+                    preq->index_pack_args :
+                    default_index_pack_args);
 
        if (preq->preserve_index_pack_stdout)
                ip.out = 0;
index 644893fd8cfff6a9ee9cda0b512c2adb9c8a6953..d3e7a40b648c7dc6eb30880ac3e04908342a8b09 100644 (file)
@@ -513,8 +513,9 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
        if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
 
-       status = check_signature(payload.buf, payload.len, signature.buf,
-                                signature.len, &sigc);
+       sigc.payload_type = SIGNATURE_PAYLOAD_COMMIT;
+       sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+       status = check_signature(&sigc, signature.buf, signature.len);
        if (status && !sigc.output)
                show_sig_lines(opt, status, "No signature\n");
        else
@@ -583,8 +584,9 @@ static int show_one_mergetag(struct commit *commit,
        status = -1;
        if (parse_signature(extra->value, extra->len, &payload, &signature)) {
                /* could have a good signature */
-               status = check_signature(payload.buf, payload.len,
-                                        signature.buf, signature.len, &sigc);
+               sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+               sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+               status = check_signature(&sigc, signature.buf, signature.len);
                if (sigc.output)
                        strbuf_addstr(&verify_message, sigc.output);
                else
index 0342f104836b69a7889b3fa686c7c359c27e5dd6..c319797021938a9df635c8db146ac6555331c7db 100644 (file)
@@ -3841,9 +3841,22 @@ static void process_entry(struct merge_options *opt,
                if (opt->renormalize &&
                    blob_unchanged(opt, &ci->stages[0], &ci->stages[side],
                                   path)) {
-                       ci->merged.is_null = 1;
-                       ci->merged.clean = 1;
-                       assert(!ci->df_conflict && !ci->path_conflict);
+                       if (!ci->path_conflict) {
+                               /*
+                                * Blob unchanged after renormalization, so
+                                * there's no modify/delete conflict after all;
+                                * we can just remove the file.
+                                */
+                               ci->merged.is_null = 1;
+                               ci->merged.clean = 1;
+                                /*
+                                 * file goes away => even if there was a
+                                 * directory/file conflict there isn't one now.
+                                 */
+                               ci->df_conflict = 0;
+                       } else {
+                               /* rename/delete, so conflict remains */
+                       }
                } else if (ci->path_conflict &&
                           oideq(&ci->stages[0].oid, &ci->stages[side].oid)) {
                        /*
index 7ab1446df4e9a8624392ab4399c2d272324d54ef..8be57f48de738a7f61de8b18e10a41e2007ac045 100644 (file)
@@ -680,6 +680,49 @@ void add_to_alternates_memory(const char *reference)
                             '\n', NULL, 0);
 }
 
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
+{
+       struct object_directory *new_odb;
+
+       /*
+        * Make sure alternates are initialized, or else our entry may be
+        * overwritten when they are.
+        */
+       prepare_alt_odb(the_repository);
+
+       /*
+        * Make a new primary odb and link the old primary ODB in as an
+        * alternate
+        */
+       new_odb = xcalloc(1, sizeof(*new_odb));
+       new_odb->path = xstrdup(dir);
+
+       /*
+        * Disable ref updates while a temporary odb is active, since
+        * the objects in the database may roll back.
+        */
+       new_odb->disable_ref_updates = 1;
+       new_odb->will_destroy = will_destroy;
+       new_odb->next = the_repository->objects->odb;
+       the_repository->objects->odb = new_odb;
+       return new_odb->next;
+}
+
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
+{
+       struct object_directory *cur_odb = the_repository->objects->odb;
+
+       if (strcmp(old_path, cur_odb->path))
+               BUG("expected %s as primary object store; found %s",
+                   old_path, cur_odb->path);
+
+       if (cur_odb->next != restore_odb)
+               BUG("we expect the old primary object store to be the first alternate");
+
+       the_repository->objects->odb = restore_odb;
+       free_object_directory(cur_odb);
+}
+
 /*
  * Compute the exact path an alternate is at and returns it. In case of
  * error NULL is returned and the human readable error is added to `err`
@@ -794,7 +837,7 @@ static void fill_alternate_refs_command(struct child_process *cmd,
                }
        }
 
-       cmd->env = local_repo_env;
+       strvec_pushv(&cmd->env_array, (const char **)local_repo_env);
        cmd->out = -1;
 }
 
@@ -1806,8 +1849,11 @@ int hash_object_file(const struct git_hash_algo *algo, const void *buf,
 /* Finalize a file on disk, and close it. */
 static void close_loose_object(int fd)
 {
-       if (fsync_object_files)
-               fsync_or_die(fd, "loose object file");
+       if (!the_repository->objects->odb->will_destroy) {
+               if (fsync_object_files)
+                       fsync_or_die(fd, "loose object file");
+       }
+
        if (close(fd) != 0)
                die_errno(_("error when closing loose object file"));
 }
index 952efb6a4be25bbf6947b789246eedc0a09bdcd5..6f89482df030cb28c91a7d2620903473e661370f 100644 (file)
@@ -27,6 +27,18 @@ struct object_directory {
        uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
        struct oidtree *loose_objects_cache;
 
+       /*
+        * This is a temporary object store created by the tmp_objdir
+        * facility. Disable ref updates since the objects in the store
+        * might be discarded on rollback.
+        */
+       int disable_ref_updates;
+
+       /*
+        * This object store is ephemeral, so there is no need to fsync.
+        */
+       int will_destroy;
+
        /*
         * Path to the alternative object store. If this is a relative path,
         * it is relative to the current working directory.
@@ -58,6 +70,17 @@ void add_to_alternates_file(const char *dir);
  */
 void add_to_alternates_memory(const char *dir);
 
+/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
 /*
  * Populate and return the loose object cache array corresponding to the
  * given object ID.
@@ -68,6 +91,9 @@ struct oidtree *odb_loose_cache(struct object_directory *odb,
 /* Empty the loose object cache for the specified object directory. */
 void odb_clear_loose_cache(struct object_directory *odb);
 
+/* Clear and free the specified object directory */
+void free_object_directory(struct object_directory *odb);
+
 struct packed_git {
        struct hashmap_entry packmap_ent;
        struct packed_git *next;
index 23a24e678a8e33f82b417fa6f005aae7de7bc29f..c37501fc1202d17bf1002d39b06afed864c11be3 100644 (file)
--- a/object.c
+++ b/object.c
@@ -199,7 +199,7 @@ struct object *lookup_object_by_type(struct repository *r,
        case OBJ_BLOB:
                return (struct object *)lookup_blob(r, oid);
        default:
-               die("BUG: unknown object type %d", type);
+               BUG("unknown object type %d", type);
        }
 }
 
@@ -513,7 +513,7 @@ struct raw_object_store *raw_object_store_new(void)
        return o;
 }
 
-static void free_object_directory(struct object_directory *odb)
+void free_object_directory(struct object_directory *odb)
 {
        free(odb->path);
        odb_clear_loose_cache(odb);
index e30b051c8a3fe3d50f4bea750efb87326e38362e..835b2d271645ce08b7f98214748d3ffe4296edb8 100644 (file)
@@ -324,7 +324,8 @@ void close_pack_index(struct packed_git *p)
        }
 }
 
-void close_pack_revindex(struct packed_git *p) {
+static void close_pack_revindex(struct packed_git *p)
+{
        if (!p->revindex_map)
                return;
 
@@ -1068,7 +1069,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               if (len <= used || (bitsizeof(long) - 7) <= shift) {
+               if (len <= used || (bitsizeof(long) - 7) < shift) {
                        error("bad object header");
                        size = used = 0;
                        break;
index 186146779d4aade93bf42cd63dd6e5bcd69e69c8..a3f6723857bf120f611e4f506e85428132f7ccc3 100644 (file)
@@ -90,7 +90,6 @@ uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
-void close_pack_revindex(struct packed_git *);
 void close_pack(struct packed_git *);
 void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
index 3c811e1e4a7e135cb302b807d15725816730f3b3..d346dbe2100ddceb53f8ce149ad4ed794b772f53 100644 (file)
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
+#include "branch.h"
 #include "cache.h"
 #include "commit.h"
 #include "color.h"
@@ -293,3 +294,18 @@ int parse_opt_passthru_argv(const struct option *opt, const char *arg, int unset
 
        return 0;
 }
+
+int parse_opt_tracking_mode(const struct option *opt, const char *arg, int unset)
+{
+       if (unset)
+               *(enum branch_track *)opt->value = BRANCH_TRACK_NEVER;
+       else if (!arg || !strcmp(arg, "direct"))
+               *(enum branch_track *)opt->value = BRANCH_TRACK_EXPLICIT;
+       else if (!strcmp(arg, "inherit"))
+               *(enum branch_track *)opt->value = BRANCH_TRACK_INHERIT;
+       else
+               return error(_("option `%s' expects \"%s\" or \"%s\""),
+                            "--track", "direct", "inherit");
+
+       return 0;
+}
index fc5b43ff0b2566abd1d4b5aa198cb21aa01f2045..a8283037be966596184862aa358bff6fd37194f3 100644 (file)
@@ -404,8 +404,9 @@ is_abbreviated:
        return PARSE_OPT_UNKNOWN;
 }
 
-static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
-                           const struct option *options)
+static enum parse_opt_result parse_nodash_opt(struct parse_opt_ctx_t *p,
+                                             const char *arg,
+                                             const struct option *options)
 {
        const struct option *all_opts = options;
 
@@ -415,7 +416,7 @@ static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
                if (options->short_name == arg[0] && arg[1] == '\0')
                        return get_value(p, options, all_opts, OPT_SHORT);
        }
-       return -2;
+       return PARSE_OPT_ERROR;
 }
 
 static void check_typos(const char *arg, const struct option *options)
@@ -1075,6 +1076,6 @@ void NORETURN usage_msg_opt(const char *msg,
                   const char * const *usagestr,
                   const struct option *options)
 {
-       fprintf(stderr, "fatal: %s\n\n", msg);
+       die_message("%s\n", msg); /* The extra \n is intentional */
        usage_with_options(usagestr, options);
 }
index 275fb440818d539e91e44a9f5824dd5edcf65420..e22846d3b7be06fd2af2f81532633d6d773f0bab 100644 (file)
@@ -301,6 +301,8 @@ enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
                                           const char *, int);
 int parse_opt_passthru(const struct option *, const char *, int);
 int parse_opt_passthru_argv(const struct option *, const char *, int);
+/* value is enum branch_track* */
+int parse_opt_tracking_mode(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', "verbose", (var), (h))
 #define OPT__QUIET(var, h)    OPT_COUNTUP('q', "quiet",   (var), (h))
index 2341dc990102b0ffcf63636855cb6df3a864a603..402ebb808081e386da46786fe1d5a1521e3cc4ac 100644 (file)
@@ -58,8 +58,7 @@ struct pathspec {
 #define GUARD_PATHSPEC(ps, mask) \
        do { \
                if ((ps)->magic & ~(mask))             \
-                       die("BUG:%s:%d: unsupported magic %x",  \
-                           __FILE__, __LINE__, (ps)->magic & ~(mask)); \
+                       BUG("unsupported magic %x", (ps)->magic & ~(mask)); \
        } while (0)
 
 /* parse_pathspec flags */
index 35ff5a68963db935c48b1113e0da14361f8ad246..6ce2e283c8d18b3de72cbec12957ebd532d25333 100644 (file)
@@ -6,7 +6,7 @@ use constant rev_map_fmt => 'NH*';
 use vars qw/$_no_metadata
             $_repack $_repack_flags $_use_svm_props $_head
             $_use_svnsync_props $no_reuse_existing
-           $_use_log_author $_add_author_from $_localtime/;
+           $_use_log_author $_add_author_from $_localtime $_use_fsync/;
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use IPC::Open3;
@@ -2269,6 +2269,19 @@ sub mkfile {
        }
 }
 
+# TODO: move this to Git.pm?
+sub use_fsync {
+       if (!defined($_use_fsync)) {
+               my $x = $ENV{GIT_TEST_FSYNC};
+               if (defined $x) {
+                       my $v = command_oneline('-c', "test.fsync=$x",
+                                       qw(config --type=bool test.fsync));
+                       $_use_fsync = defined($v) ? ($v eq "true\n") : 1;
+               }
+       }
+       $_use_fsync;
+}
+
 sub rev_map_set {
        my ($self, $rev, $commit, $update_ref, $uuid) = @_;
        defined $commit or die "missing arg3\n";
@@ -2290,7 +2303,7 @@ sub rev_map_set {
        my $sync;
        # both of these options make our .rev_db file very, very important
        # and we can't afford to lose it because rebuild() won't work
-       if ($self->use_svm_props || $self->no_metadata) {
+       if (($self->use_svm_props || $self->no_metadata) && use_fsync()) {
                require File::Copy;
                $sync = 1;
                File::Copy::copy($db, $db_lock) or die "rev_map_set(@_): ",
index dcd8436c25706cdb998117d117cf2864361cc85b..19fabb4acf47dbb4fe0b23d25ce6a11bf3149ab7 100644 (file)
@@ -221,6 +221,10 @@ General advice:
 - Adjust the strings so that they're easy to translate. Most of the
   advice in `info '(gettext)Preparing Strings'` applies here.
 
+- Strings referencing numbers of items may need to be split into singular and
+  plural forms; see the Q\_() wrapper in the C sub-section below for an
+  example.
+
 - If something is unclear or ambiguous you can use a "TRANSLATORS"
   comment to tell the translators what to make of it. These will be
   extracted by xgettext(1) and put in the "po/\*.po" files, e.g. from
index 1af5b093ae8a91ec8906b0ebac9f505d7df4b075..ee6114e3f0aa1dcf8737794bbe8dc8f6e23f5f7b 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1275,28 +1275,66 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
-       const char *options[] = { "match", "exclude" };
+       struct {
+               char *name;
+               enum {
+                       DESCRIBE_ARG_BOOL,
+                       DESCRIBE_ARG_INTEGER,
+                       DESCRIBE_ARG_STRING,
+               } type;
+       }  option[] = {
+               { "tags", DESCRIBE_ARG_BOOL},
+               { "abbrev", DESCRIBE_ARG_INTEGER },
+               { "exclude", DESCRIBE_ARG_STRING },
+               { "match", DESCRIBE_ARG_STRING },
+       };
        const char *arg = start;
 
        for (;;) {
-               const char *matched = NULL;
+               int found = 0;
                const char *argval;
                size_t arglen = 0;
+               int optval = 0;
                int i;
 
-               for (i = 0; i < ARRAY_SIZE(options); i++) {
-                       if (match_placeholder_arg_value(arg, options[i], &arg,
-                                                       &argval, &arglen)) {
-                               matched = options[i];
+               for (i = 0; !found && i < ARRAY_SIZE(option); i++) {
+                       switch (option[i].type) {
+                       case DESCRIBE_ARG_BOOL:
+                               if (match_placeholder_bool_arg(arg, option[i].name, &arg, &optval)) {
+                                       if (optval)
+                                               strvec_pushf(args, "--%s", option[i].name);
+                                       else
+                                               strvec_pushf(args, "--no-%s", option[i].name);
+                                       found = 1;
+                               }
+                               break;
+                       case DESCRIBE_ARG_INTEGER:
+                               if (match_placeholder_arg_value(arg, option[i].name, &arg,
+                                                               &argval, &arglen)) {
+                                       char *endptr;
+                                       if (!arglen)
+                                               return 0;
+                                       strtol(argval, &endptr, 10);
+                                       if (endptr - argval != arglen)
+                                               return 0;
+                                       strvec_pushf(args, "--%s=%.*s", option[i].name, (int)arglen, argval);
+                                       found = 1;
+                               }
+                               break;
+                       case DESCRIBE_ARG_STRING:
+                               if (match_placeholder_arg_value(arg, option[i].name, &arg,
+                                                               &argval, &arglen)) {
+                                       if (!arglen)
+                                               return 0;
+                                       strvec_pushf(args, "--%s=%.*s", option[i].name, (int)arglen, argval);
+                                       found = 1;
+                               }
                                break;
                        }
                }
-               if (!matched)
+               if (!found)
                        break;
 
-               if (!arglen)
-                       return 0;
-               strvec_pushf(args, "--%s=%.*s", matched, (int)arglen, argval);
        }
        return arg - start;
 }
index 5ded21a017f1089c5a5f63c998c3b11631c0a5d6..50df17279d1d5b2615fb8dd5a13853a2ea87b8eb 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -8,15 +8,12 @@
 static char *do_askpass(const char *cmd, const char *prompt)
 {
        struct child_process pass = CHILD_PROCESS_INIT;
-       const char *args[3];
        static struct strbuf buffer = STRBUF_INIT;
        int err = 0;
 
-       args[0] = cmd;
-       args[1] = prompt;
-       args[2] = NULL;
+       strvec_push(&pass.args, cmd);
+       strvec_push(&pass.args, prompt);
 
-       pass.argv = args;
        pass.out = -1;
 
        if (start_command(&pass))
index cac89a2f4f2c8d4c6471f0a55bc725cf17a2d9b9..30a4de5c2d8a1447dc23d6b5d61278df0f796bbb 100644 (file)
@@ -556,7 +556,7 @@ int show_range_diff(const char *range1, const char *range2,
        struct string_list branch2 = STRING_LIST_INIT_DUP;
 
        if (range_diff_opts->left_only && range_diff_opts->right_only)
-               res = error(_("--left-only and --right-only are mutually exclusive"));
+               res = error(_("options '%s' and '%s' cannot be used together"), "--left-only", "--right-only");
 
        if (!res && read_patches(range1, &branch1, range_diff_opts->other_arg))
                res = error(_("could not parse log for '%s'"), range1);
index 7260fce31d0cf5b407c32f09a4636fb8a3363a27..f7a2f17bfd94d7073cead2cfbebf877f6ab9fb98 100644 (file)
@@ -341,7 +341,7 @@ static int objectsize_atom_parser(struct ref_format *format, struct used_atom *a
                else
                        oi.info.disk_sizep = &oi.disk_size;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(objectsize) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "objectsize", arg);
        return 0;
 }
 
@@ -374,7 +374,7 @@ static int subject_atom_parser(struct ref_format *format, struct used_atom *atom
        else if (!strcmp(arg, "sanitize"))
                atom->u.contents.option = C_SUB_SANITIZE;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(subject) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "subject", arg);
        return 0;
 }
 
@@ -428,7 +428,7 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato
                if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
                        return strbuf_addf_ret(err, -1, _("positive value expected contents:lines=%s"), arg);
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(contents) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "contents", arg);
        return 0;
 }
 
@@ -440,7 +440,7 @@ static int raw_atom_parser(struct ref_format *format, struct used_atom *atom,
        else if (!strcmp(arg, "size"))
                atom->u.raw_data.option = RAW_LENGTH;
        else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(raw) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "raw", arg);
        return 0;
 }
 
@@ -459,7 +459,7 @@ static int oid_atom_parser(struct ref_format *format, struct used_atom *atom,
                if (atom->u.oid.length < MINIMUM_ABBREV)
                        atom->u.oid.length = MINIMUM_ABBREV;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized argument '%s' in %%(%s)"), arg, atom->name);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), atom->name, arg);
        return 0;
 }
 
@@ -531,7 +531,7 @@ static int align_atom_parser(struct ref_format *format, struct used_atom *atom,
                else if ((position = parse_align_position(s)) >= 0)
                        align->position = position;
                else {
-                       strbuf_addf(err, _("unrecognized %%(align) argument: %s"), s);
+                       strbuf_addf(err, _("unrecognized %%(%s) argument: %s"), "align", s);
                        string_list_clear(&params, 0);
                        return -1;
                }
@@ -557,7 +557,7 @@ static int if_atom_parser(struct ref_format *format, struct used_atom *atom,
        } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
                atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
        } else
-               return strbuf_addf_ret(err, -1, _("unrecognized %%(if) argument: %s"), arg);
+               return strbuf_addf_ret(err, -1, _("unrecognized %%(%s) argument: %s"), "if", arg);
        return 0;
 }
 
@@ -841,7 +841,7 @@ static void if_then_else_handler(struct ref_formatting_stack **stack)
        struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;
 
        if (!if_then_else->then_atom_seen)
-               die(_("format: %%(if) atom used without a %%(then) atom"));
+               die(_("format: %%(%s) atom used without a %%(%s) atom"), "if", "then");
 
        if (if_then_else->else_atom_seen) {
                /*
@@ -907,7 +907,7 @@ static int then_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        if (cur->at_end == if_then_else_handler)
                if_then_else = (struct if_then_else *)cur->at_end_data;
        if (!if_then_else)
-               return strbuf_addf_ret(err, -1, _("format: %%(then) atom used without an %%(if) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(%s) atom used without a %%(%s) atom"), "then", "if");
        if (if_then_else->then_atom_seen)
                return strbuf_addf_ret(err, -1, _("format: %%(then) atom used more than once"));
        if (if_then_else->else_atom_seen)
@@ -943,9 +943,9 @@ static int else_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
        if (prev->at_end == if_then_else_handler)
                if_then_else = (struct if_then_else *)prev->at_end_data;
        if (!if_then_else)
-               return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without an %%(if) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(%s) atom used without a %%(%s) atom"), "else", "if");
        if (!if_then_else->then_atom_seen)
-               return strbuf_addf_ret(err, -1, _("format: %%(else) atom used without a %%(then) atom"));
+               return strbuf_addf_ret(err, -1, _("format: %%(%s) atom used without a %%(%s) atom"), "else", "then");
        if (if_then_else->else_atom_seen)
                return strbuf_addf_ret(err, -1, _("format: %%(else) atom used more than once"));
        if_then_else->else_atom_seen = 1;
diff --git a/refs.c b/refs.c
index 4338875d86bb952af38067d91df86514dbaebd90..addb26293b4ffc1b0c997379acce5d13d70991c8 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1083,9 +1083,10 @@ int ref_transaction_update(struct ref_transaction *transaction,
 {
        assert(err);
 
-       if ((new_oid && !is_null_oid(new_oid)) ?
-           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
-           !refname_is_safe(refname)) {
+       if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
+           ((new_oid && !is_null_oid(new_oid)) ?
+                    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
+                          !refname_is_safe(refname))) {
                strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
                            refname);
                return -1;
@@ -1721,8 +1722,6 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                if (refs_read_raw_ref(refs, refname, oid, &sb_refname,
                                      &read_flags, failure_errno)) {
                        *flags |= read_flags;
-                       if (errno)
-                               *failure_errno = errno;
 
                        /* In reading mode, refs must eventually resolve */
                        if (resolve_flags & RESOLVE_REF_READING)
@@ -2006,10 +2005,12 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
        return refs;
 }
 
-void base_ref_store_init(struct ref_store *refs,
-                        const struct ref_storage_be *be)
+void base_ref_store_init(struct ref_store *refs, struct repository *repo,
+                        const char *path, const struct ref_storage_be *be)
 {
        refs->be = be;
+       refs->repo = repo;
+       refs->gitdir = xstrdup(path);
 }
 
 /* backend functions */
@@ -2144,7 +2145,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
                break;
        }
 
-       if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+       if (refs->repo->objects->odb->disable_ref_updates) {
                strbuf_addstr(err,
                              _("ref updates forbidden inside quarantine environment"));
                return -1;
diff --git a/refs.h b/refs.h
index bb50d1eb195cfa6ae9aa968a2186454eb7eeaf52..8f91a7f9ff27ec0909c32d14122bf0f1a5c49573 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -463,7 +463,29 @@ int delete_reflog(const char *refname);
 
 /*
  * Callback to process a reflog entry found by the iteration functions (see
- * below)
+ * below).
+ *
+ * The committer parameter is a single string, in the form
+ * "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" (without double quotes).
+ *
+ * The timestamp parameter gives the time when entry was created as the number
+ * of seconds since the UNIX epoch.
+ *
+ * The tz parameter gives the timezone offset for the user who created
+ * the reflog entry, and its value gives a positive or negative offset
+ * from UTC.  Its absolute value is formed by multiplying the hour
+ * part by 100 and adding the minute part.  For example, 1 hour ahead
+ * of UTC, CET == "+0100", is represented as positive one hundred (not
+ * postiive sixty).
+ *
+ * The msg parameter is a single complete line; a reflog message given
+ * to refs_delete_ref, refs_update_ref, etc. is returned to the
+ * callback normalized---each run of whitespaces are squashed into a
+ * single whitespace, trailing whitespace, if exists, is trimmed, and
+ * then a single LF is added at the end.
+ *
+ * The cb_data is a caller-supplied pointer given to the iterator
+ * functions.
  */
 typedef int each_reflog_ent_fn(
                struct object_id *old_oid, struct object_id *new_oid,
@@ -615,12 +637,24 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
  */
 #define REF_FORCE_CREATE_REFLOG (1 << 1)
 
+/*
+ * Blindly write an object_id. This is useful for testing data corruption
+ * scenarios.
+ */
+#define REF_SKIP_OID_VERIFICATION (1 << 10)
+
+/*
+ * Skip verifying refname. This is useful for testing data corruption scenarios.
+ */
+#define REF_SKIP_REFNAME_VERIFICATION (1 << 11)
+
 /*
  * Bitmask of all of the flags that are allowed to be passed in to
  * ref_transaction_update() and friends:
  */
-#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
-       (REF_NO_DEREF | REF_FORCE_CREATE_REFLOG)
+#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS                                  \
+       (REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
+        REF_SKIP_REFNAME_VERIFICATION)
 
 /*
  * Add a reference update to transaction. `new_oid` is the value that
@@ -786,8 +820,7 @@ enum ref_type ref_type(const char *refname);
 enum expire_reflog_flags {
        EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
        EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
-       EXPIRE_REFLOGS_VERBOSE = 1 << 2,
-       EXPIRE_REFLOGS_REWRITE = 1 << 3
+       EXPIRE_REFLOGS_REWRITE = 1 << 2,
 };
 
 /*
index 756d07c72475fcc4c23d3067611ba94d53adeb64..2b0771ca53b7854c46ab231b22ecf8be919ca19a 100644 (file)
@@ -26,7 +26,8 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
        be_copy->name = store->be->name;
        trace_printf_key(&trace_refs, "ref_store for %s\n", gitdir);
        res->refs = store;
-       base_ref_store_init((struct ref_store *)res, be_copy);
+       base_ref_store_init((struct ref_store *)res, store->repo, gitdir,
+                           be_copy);
        return (struct ref_store *)res;
 }
 
@@ -47,7 +48,8 @@ static int debug_transaction_prepare(struct ref_store *refs,
        transaction->ref_store = drefs->refs;
        res = drefs->refs->be->transaction_prepare(drefs->refs, transaction,
                                                   err);
-       trace_printf_key(&trace_refs, "transaction_prepare: %d\n", res);
+       trace_printf_key(&trace_refs, "transaction_prepare: %d \"%s\"\n", res,
+                        err->buf);
        return res;
 }
 
@@ -284,6 +286,7 @@ static int debug_print_reflog_ent(struct object_id *old_oid,
        int ret;
        char o[GIT_MAX_HEXSZ + 1] = "null";
        char n[GIT_MAX_HEXSZ + 1] = "null";
+       char *msgend = strchrnul(msg, '\n');
        if (old_oid)
                oid_to_hex_r(o, old_oid);
        if (new_oid)
@@ -291,8 +294,10 @@ static int debug_print_reflog_ent(struct object_id *old_oid,
 
        ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg,
                      dbg->cb_data);
-       trace_printf_key(&trace_refs, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n",
-               dbg->refname, ret, o, n, committer, (long int)timestamp, msg);
+       trace_printf_key(&trace_refs,
+                        "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n",
+                        dbg->refname, ret, o, n, committer,
+                        (long int)timestamp, (int)(msgend - msg), msg);
        return ret;
 }
 
index 237a2afb5d7efb510c66bff2d23a21fd5effe487..43a3b882d7c50c231ae714f79b4a4fedfa7e1c9d 100644 (file)
@@ -16,8 +16,7 @@
  * This backend uses the following flags in `ref_update::flags` for
  * internal bookkeeping purposes. Their numerical values must not
  * conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW,
- * REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in
- * `ref_update::flags`.
+ * or REF_HAVE_OLD, which are also stored in `ref_update::flags`.
  */
 
 /*
@@ -87,16 +86,12 @@ static struct ref_store *files_ref_store_create(struct repository *repo,
        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);
+       base_ref_store_init(ref_store, repo, gitdir, &refs_be_files);
        refs->store_flags = flags;
-
        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(repo, sb.buf, flags);
-       strbuf_release(&sb);
+       refs->packed_ref_store =
+               packed_ref_store_create(repo, refs->gitcommondir, flags);
 
        chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
        chdir_notify_reparent("files-backend $GIT_COMMONDIR",
@@ -387,7 +382,6 @@ stat_ref:
        if (lstat(path, &st) < 0) {
                int ignore_errno;
                myerr = errno;
-               errno = 0;
                if (myerr != ENOENT)
                        goto out;
                if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
@@ -404,7 +398,6 @@ stat_ref:
                strbuf_reset(&sb_contents);
                if (strbuf_readlink(&sb_contents, path, st.st_size) < 0) {
                        myerr = errno;
-                       errno = 0;
                        if (myerr == ENOENT || myerr == EINVAL)
                                /* inconsistent with lstat; retry */
                                goto stat_ref;
@@ -474,6 +467,7 @@ out:
 
        strbuf_release(&sb_path);
        strbuf_release(&sb_contents);
+       errno = 0;
        return ret;
 }
 
@@ -1354,7 +1348,8 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
 }
 
 static int write_ref_to_lockfile(struct ref_lock *lock,
-                                const struct object_id *oid, struct strbuf *err);
+                                const struct object_id *oid,
+                                int skip_oid_verification, struct strbuf *err);
 static int commit_ref_update(struct files_ref_store *refs,
                             struct ref_lock *lock,
                             const struct object_id *oid, const char *logmsg,
@@ -1501,7 +1496,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
        }
        oidcpy(&lock->old_oid, &orig_oid);
 
-       if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+       if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
            commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
                error("unable to write current sha1 into %s: %s", newrefname, err.buf);
                strbuf_release(&err);
@@ -1521,7 +1516,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 
        flag = log_all_ref_updates;
        log_all_ref_updates = LOG_REFS_NONE;
-       if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+       if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) ||
            commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
                error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
                strbuf_release(&err);
@@ -1756,26 +1751,31 @@ static int files_log_ref_write(struct files_ref_store *refs,
  * errors, rollback the lockfile, fill in *err and return -1.
  */
 static int write_ref_to_lockfile(struct ref_lock *lock,
-                                const struct object_id *oid, struct strbuf *err)
+                                const struct object_id *oid,
+                                int skip_oid_verification, struct strbuf *err)
 {
        static char term = '\n';
        struct object *o;
        int fd;
 
-       o = parse_object(the_repository, oid);
-       if (!o) {
-               strbuf_addf(err,
-                           "trying to write ref '%s' with nonexistent object %s",
-                           lock->ref_name, oid_to_hex(oid));
-               unlock_ref(lock);
-               return -1;
-       }
-       if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
-               strbuf_addf(err,
-                           "trying to write non-commit object %s to branch '%s'",
-                           oid_to_hex(oid), lock->ref_name);
-               unlock_ref(lock);
-               return -1;
+       if (!skip_oid_verification) {
+               o = parse_object(the_repository, oid);
+               if (!o) {
+                       strbuf_addf(
+                               err,
+                               "trying to write ref '%s' with nonexistent object %s",
+                               lock->ref_name, oid_to_hex(oid));
+                       unlock_ref(lock);
+                       return -1;
+               }
+               if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
+                       strbuf_addf(
+                               err,
+                               "trying to write non-commit object %s to branch '%s'",
+                               oid_to_hex(oid), lock->ref_name);
+                       unlock_ref(lock);
+                       return -1;
+               }
        }
        fd = get_lock_file_fd(&lock->lk);
        if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
@@ -2189,7 +2189,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
 }
 
 static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator,
-                                  struct object_id *peeled)
+                                     struct object_id *peeled)
 {
        BUG("ref_iterator_peel() called for reflog_iterator");
 }
@@ -2575,8 +2575,10 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                         * The reference already has the desired
                         * value, so we don't need to write it.
                         */
-               } else if (write_ref_to_lockfile(lock, &update->new_oid,
-                                                err)) {
+               } else if (write_ref_to_lockfile(
+                                  lock, &update->new_oid,
+                                  update->flags & REF_SKIP_OID_VERIFICATION,
+                                  err)) {
                        char *write_err = strbuf_detach(err, NULL);
 
                        /*
@@ -3079,11 +3081,12 @@ cleanup:
 }
 
 struct expire_reflog_cb {
-       unsigned int flags;
        reflog_expiry_should_prune_fn *should_prune_fn;
        void *policy_cb;
        FILE *newlog;
        struct object_id last_kept_oid;
+       unsigned int rewrite:1,
+                    dry_run:1;
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
@@ -3091,33 +3094,27 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
                             const char *message, void *cb_data)
 {
        struct expire_reflog_cb *cb = cb_data;
-       struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
+       reflog_expiry_should_prune_fn *fn = cb->should_prune_fn;
 
-       if (cb->flags & EXPIRE_REFLOGS_REWRITE)
+       if (cb->rewrite)
                ooid = &cb->last_kept_oid;
 
-       if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz,
-                                  message, policy_cb)) {
-               if (!cb->newlog)
-                       printf("would prune %s", message);
-               else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
-                       printf("prune %s", message);
-       } else {
-               if (cb->newlog) {
-                       fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
-                               oid_to_hex(ooid), oid_to_hex(noid),
-                               email, timestamp, tz, message);
-                       oidcpy(&cb->last_kept_oid, noid);
-               }
-               if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
-                       printf("keep %s", message);
-       }
+       if (fn(ooid, noid, email, timestamp, tz, message, cb->policy_cb))
+               return 0;
+
+       if (cb->dry_run)
+               return 0; /* --dry-run */
+
+       fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", oid_to_hex(ooid),
+               oid_to_hex(noid), email, timestamp, tz, message);
+       oidcpy(&cb->last_kept_oid, noid);
+
        return 0;
 }
 
 static int files_reflog_expire(struct ref_store *ref_store,
                               const char *refname,
-                              unsigned int flags,
+                              unsigned int expire_flags,
                               reflog_expiry_prepare_fn prepare_fn,
                               reflog_expiry_should_prune_fn should_prune_fn,
                               reflog_expiry_cleanup_fn cleanup_fn,
@@ -3135,7 +3132,8 @@ static int files_reflog_expire(struct ref_store *ref_store,
        const struct object_id *oid;
 
        memset(&cb, 0, sizeof(cb));
-       cb.flags = flags;
+       cb.rewrite = !!(expire_flags & EXPIRE_REFLOGS_REWRITE);
+       cb.dry_run = !!(expire_flags & EXPIRE_REFLOGS_DRY_RUN);
        cb.policy_cb = policy_cb_data;
        cb.should_prune_fn = should_prune_fn;
 
@@ -3171,7 +3169,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
 
        files_reflog_path(refs, &log_file_sb, refname);
        log_file = strbuf_detach(&log_file_sb, NULL);
-       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+       if (!cb.dry_run) {
                /*
                 * Even though holding $GIT_DIR/logs/$reflog.lock has
                 * no locking implications, we use the lock_file
@@ -3198,7 +3196,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
        refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
        (*cleanup_fn)(cb.policy_cb);
 
-       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+       if (!cb.dry_run) {
                /*
                 * It doesn't make sense to adjust a reference pointed
                 * to by a symbolic ref based on expiring entries in
@@ -3208,7 +3206,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
                 */
                int update = 0;
 
-               if ((flags & EXPIRE_REFLOGS_UPDATE_REF) &&
+               if ((expire_flags & EXPIRE_REFLOGS_UPDATE_REF) &&
                    !is_null_oid(&cb.last_kept_oid)) {
                        int ignore_errno;
                        int type;
index 67152c664e2e4bb0ee92d3f2ebc19cdd9840c895..d91a2018f6011d6c8e79a97e5742baa3c32a9f4a 100644 (file)
@@ -194,20 +194,19 @@ static int release_snapshot(struct snapshot *snapshot)
 }
 
 struct ref_store *packed_ref_store_create(struct repository *repo,
-                                         const char *path,
+                                         const char *gitdir,
                                          unsigned int store_flags)
 {
        struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
+       struct strbuf sb = STRBUF_INIT;
 
-       base_ref_store_init(ref_store, &refs_be_packed);
-       ref_store->repo = repo;
-       ref_store->gitdir = xstrdup(path);
+       base_ref_store_init(ref_store, repo, gitdir, &refs_be_packed);
        refs->store_flags = store_flags;
 
-       refs->path = xstrdup(path);
+       strbuf_addf(&sb, "%s/packed-refs", gitdir);
+       refs->path = strbuf_detach(&sb, NULL);
        chdir_notify_reparent("packed-refs", &refs->path);
-
        return ref_store;
 }
 
index f61a73ec25b4cb55d96bac23ada11a53e2781f95..9dd8a344c34dd7ae078f8226abf044219d5f3a3c 100644 (file)
@@ -14,7 +14,7 @@ struct ref_transaction;
  */
 
 struct ref_store *packed_ref_store_create(struct repository *repo,
-                                         const char *path,
+                                         const char *gitdir,
                                          unsigned int store_flags);
 
 /*
index 46a839539e33451f7debc980a1a23744a7094d05..7ff6fba4f0d3ef138525edf6fd5b9511e2cfe07d 100644 (file)
@@ -710,8 +710,8 @@ int parse_loose_ref_contents(const char *buf, struct object_id *oid,
  * Fill in the generic part of refs and add it to our collection of
  * reference stores.
  */
-void base_ref_store_init(struct ref_store *refs,
-                        const struct ref_storage_be *be);
+void base_ref_store_init(struct ref_store *refs, struct repository *repo,
+                        const char *path, const struct ref_storage_be *be);
 
 /*
  * Support GIT_TRACE_REFS by optionally wrapping the given ref_store instance.
index e207706a64481eff6e0ea50f7c3a27093268fb26..87c77539b5bd6ab0b9909c51801719258507b424 100644 (file)
@@ -21,7 +21,7 @@ struct block_writer {
        uint8_t *buf;
        uint32_t block_size;
 
-       /* Offset ofof the global header. Nonzero in the first block only. */
+       /* Offset of the global header. Nonzero in the first block only. */
        uint32_t header_off;
 
        /* How often to restart keys. */
index f6f16def92145494fca3023692338f8f2c8b36c2..93941f214570ace6e82e3c0a8b3c83d8b945405f 100644 (file)
@@ -32,6 +32,8 @@ const char *reftable_error_str(int err)
                return "wrote empty table";
        case REFTABLE_REFNAME_ERROR:
                return "invalid refname";
+       case REFTABLE_ENTRY_TOO_BIG_ERROR:
+               return "entry too large";
        case -1:
                return "general error";
        default:
index 24461e8a80256817cc3cade717a70b7ba2d66369..d08c16abefbc3d8562c29cf1703d1a6908f71dcd 100644 (file)
@@ -24,8 +24,8 @@ https://developers.google.com/open-source/licenses/bsd
 static void write_test_table(struct strbuf *buf,
                             struct reftable_ref_record refs[], int n)
 {
-       int min = 0xffffffff;
-       int max = 0;
+       uint64_t min = 0xffffffff;
+       uint64_t max = 0;
        int i = 0;
        int err;
 
@@ -207,11 +207,11 @@ static void test_merged(void)
                },
        };
 
-       struct reftable_ref_record want[] = {
-               r2[0],
-               r1[1],
-               r3[0],
-               r3[1],
+       struct reftable_ref_record *want[] = {
+               &r2[0],
+               &r1[1],
+               &r3[0],
+               &r3[1],
        };
 
        struct reftable_ref_record *refs[] = { r1, r2, r3 };
@@ -250,7 +250,7 @@ static void test_merged(void)
 
        EXPECT(ARRAY_SIZE(want) == len);
        for (i = 0; i < len; i++) {
-               EXPECT(reftable_ref_record_equal(&want[i], &out[i],
+               EXPECT(reftable_ref_record_equal(want[i], &out[i],
                                                 GIT_SHA1_RAWSZ));
        }
        for (i = 0; i < len; i++) {
@@ -345,10 +345,10 @@ static void test_merged_logs(void)
                        .value_type = REFTABLE_LOG_DELETION,
                },
        };
-       struct reftable_log_record want[] = {
-               r2[0],
-               r3[0],
-               r1[1],
+       struct reftable_log_record *want[] = {
+               &r2[0],
+               &r3[0],
+               &r1[1],
        };
 
        struct reftable_log_record *logs[] = { r1, r2, r3 };
@@ -387,7 +387,7 @@ static void test_merged_logs(void)
 
        EXPECT(ARRAY_SIZE(want) == len);
        for (i = 0; i < len; i++) {
-               EXPECT(reftable_log_record_equal(&want[i], &out[i],
+               EXPECT(reftable_log_record_equal(want[i], &out[i],
                                                 GIT_SHA1_RAWSZ));
        }
 
index 5f6bcc2f775fdbc2c7b6ea81baf6f5011fd35282..70c7aedba2ccb0ad73731abc2742ec2947200499 100644 (file)
@@ -155,6 +155,40 @@ static void test_log_buffer_size(void)
        strbuf_release(&buf);
 }
 
+static void test_log_overflow(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char msg[256] = { 0 };
+       struct reftable_write_options opts = {
+               .block_size = ARRAY_SIZE(msg),
+       };
+       int err;
+       struct reftable_log_record
+               log = { .refname = "refs/heads/master",
+                       .update_index = 0xa,
+                       .value_type = REFTABLE_LOG_UPDATE,
+                       .value = { .update = {
+                                          .name = "Han-Wen Nienhuys",
+                                          .email = "hanwen@google.com",
+                                          .tz_offset = 100,
+                                          .time = 0x5e430672,
+                                          .message = msg,
+                                  } } };
+       struct reftable_writer *w =
+               reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
+       uint8_t hash1[GIT_SHA1_RAWSZ]  = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
+
+       memset(msg, 'x', sizeof(msg) - 1);
+       log.value.update.old_hash = hash1;
+       log.value.update.new_hash = hash2;
+       reftable_writer_set_limits(w, update_index, update_index);
+       err = reftable_writer_add_log(w, &log);
+       EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
+       reftable_writer_free(w);
+       strbuf_release(&buf);
+}
+
 static void test_log_write_read(void)
 {
        int N = 2;
@@ -648,5 +682,6 @@ int readwrite_test_main(int argc, const char *argv[])
        RUN_TEST(test_table_refs_for_no_index);
        RUN_TEST(test_table_refs_for_obj_index);
        RUN_TEST(test_write_empty_table);
+       RUN_TEST(test_log_overflow);
        return 0;
 }
index 6f89bedf1a586a0beb2444024acbacc8cdc76edd..4c457aaaf8906384e65edfeb06057a86dcb594e4 100644 (file)
@@ -53,6 +53,10 @@ enum reftable_error {
 
        /* Invalid ref name. */
        REFTABLE_REFNAME_ERROR = -10,
+
+       /* Entry does not fit. This can happen when writing outsize reflog
+          messages. */
+       REFTABLE_ENTRY_TOO_BIG_ERROR = -11,
 };
 
 /* convert the numeric error code to a string. The string should not be
index af36462ced5cea90daac8e600dee2ba3f9a00605..a560dc1725596d9ae7e8ec775079ceeff6012e27 100644 (file)
@@ -35,6 +35,9 @@ struct reftable_write_options {
         */
        uint32_t hash_id;
 
+       /* Default mode for creating files. If unset, use 0666 (+umask) */
+       unsigned int default_permissions;
+
        /* boolean: do not check ref names for validity or dir/file conflicts.
         */
        unsigned skip_name_check : 1;
index df5021ebf08fea19111aa21dbef4c8e67f1c3562..56bf5f2d84ae5f9bad040e68768f99274c11e48c 100644 (file)
@@ -469,7 +469,7 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
        strbuf_addstr(&add->lock_file_name, ".lock");
 
        add->lock_file_fd = open(add->lock_file_name.buf,
-                                O_EXCL | O_CREAT | O_WRONLY, 0644);
+                                O_EXCL | O_CREAT | O_WRONLY, 0666);
        if (add->lock_file_fd < 0) {
                if (errno == EEXIST) {
                        err = REFTABLE_LOCK_ERROR;
@@ -478,6 +478,13 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
                }
                goto done;
        }
+       if (st->config.default_permissions) {
+               if (chmod(add->lock_file_name.buf, st->config.default_permissions) < 0) {
+                       err = REFTABLE_IO_ERROR;
+                       goto done;
+               }
+       }
+
        err = stack_uptodate(st);
        if (err < 0)
                goto done;
@@ -644,7 +651,12 @@ int reftable_addition_add(struct reftable_addition *add,
                err = REFTABLE_IO_ERROR;
                goto done;
        }
-
+       if (add->stack->config.default_permissions) {
+               if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
+                       err = REFTABLE_IO_ERROR;
+                       goto done;
+               }
+       }
        wr = reftable_new_writer(reftable_fd_write, &tab_fd,
                                 &add->stack->config);
        err = write_table(wr, arg);
@@ -900,7 +912,7 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
        strbuf_addstr(&lock_file_name, ".lock");
 
        lock_file_fd =
-               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644);
+               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
        if (lock_file_fd < 0) {
                if (errno == EEXIST) {
                        err = 1;
@@ -931,8 +943,8 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                strbuf_addstr(&subtab_lock, ".lock");
 
                sublock_file_fd = open(subtab_lock.buf,
-                                      O_EXCL | O_CREAT | O_WRONLY, 0644);
-               if (sublock_file_fd > 0) {
+                                      O_EXCL | O_CREAT | O_WRONLY, 0666);
+               if (sublock_file_fd >= 0) {
                        close(sublock_file_fd);
                } else if (sublock_file_fd < 0) {
                        if (errno == EEXIST) {
@@ -967,7 +979,7 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                goto done;
 
        lock_file_fd =
-               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644);
+               open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
        if (lock_file_fd < 0) {
                if (errno == EEXIST) {
                        err = 1;
@@ -977,6 +989,12 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                goto done;
        }
        have_lock = 1;
+       if (st->config.default_permissions) {
+               if (chmod(lock_file_name.buf, st->config.default_permissions) < 0) {
+                       err = REFTABLE_IO_ERROR;
+                       goto done;
+               }
+       }
 
        format_name(&new_table_name, st->readers[first]->min_update_index,
                    st->readers[last]->max_update_index);
index eb0b7228b0c65bf5461282be7dfd3e6cb7786c9b..f4c743db80c391c1b6c91d3d5f178c76b4393964 100644 (file)
@@ -17,6 +17,7 @@ https://developers.google.com/open-source/licenses/bsd
 #include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
+#include "reader.h"
 
 #include <sys/types.h>
 #include <dirent.h>
@@ -138,8 +139,11 @@ static int write_test_log(struct reftable_writer *wr, void *arg)
 static void test_reftable_stack_add_one(void)
 {
        char *dir = get_tmp_dir(__LINE__);
-
-       struct reftable_write_options cfg = { 0 };
+       struct strbuf scratch = STRBUF_INIT;
+       int mask = umask(002);
+       struct reftable_write_options cfg = {
+               .default_permissions = 0660,
+       };
        struct reftable_stack *st = NULL;
        int err;
        struct reftable_ref_record ref = {
@@ -149,8 +153,7 @@ static void test_reftable_stack_add_one(void)
                .value.symref = "master",
        };
        struct reftable_ref_record dest = { NULL };
-
-
+       struct stat stat_result = { 0 };
        err = reftable_new_stack(&st, dir, cfg);
        EXPECT_ERR(err);
 
@@ -160,6 +163,7 @@ static void test_reftable_stack_add_one(void)
        err = reftable_stack_read_ref(st, ref.refname, &dest);
        EXPECT_ERR(err);
        EXPECT(0 == strcmp("master", dest.value.symref));
+       EXPECT(st->readers_len > 0);
 
        printf("testing print functionality:\n");
        err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
@@ -168,9 +172,30 @@ static void test_reftable_stack_add_one(void)
        err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
        EXPECT(err == REFTABLE_FORMAT_ERROR);
 
+#ifndef GIT_WINDOWS_NATIVE
+       strbuf_addstr(&scratch, dir);
+       strbuf_addstr(&scratch, "/tables.list");
+       err = stat(scratch.buf, &stat_result);
+       EXPECT(!err);
+       EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+
+       strbuf_reset(&scratch);
+       strbuf_addstr(&scratch, dir);
+       strbuf_addstr(&scratch, "/");
+       /* do not try at home; not an external API for reftable. */
+       strbuf_addstr(&scratch, st->readers[0]->name);
+       err = stat(scratch.buf, &stat_result);
+       EXPECT(!err);
+       EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
+#else
+       (void) stat_result;
+#endif
+
        reftable_ref_record_release(&dest);
        reftable_stack_destroy(st);
+       strbuf_release(&scratch);
        clear_dir(dir);
+       umask(mask);
 }
 
 static void test_reftable_stack_uptodate(void)
index 3ca721e9f64c6a1d29e1efe333dff59894a928cd..35c8649c9b73cdd06d279a6364440d017c66a9fc 100644 (file)
@@ -239,6 +239,9 @@ static int writer_add_record(struct reftable_writer *w,
        writer_reinit_block_writer(w, reftable_record_type(rec));
        err = block_writer_add(w->block_writer, rec);
        if (err < 0) {
+               /* we are writing into memory, so an error can only mean it
+                * doesn't fit. */
+               err = REFTABLE_ENTRY_TOO_BIG_ERROR;
                goto done;
        }
 
index d69156312bda65c7f081a0bfd8c91b043f676487..0dabef2dd7c565f3f2c2ecd74a0ca295bd874afb 100644 (file)
@@ -1061,7 +1061,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
        client.in = -1;
        client.out = -1;
        client.git_cmd = 1;
-       client.argv = client_argv;
+       strvec_pushv(&client.args, client_argv);
        if (start_command(&client))
                exit(1);
        write_or_die(client.in, preamble->buf, preamble->len);
index b93e91a212eb98aae494acc544edcd71b77a4761..00ca5571a1ab77e8a1b2d505d59e6ff59d629dd9 100644 (file)
@@ -17,6 +17,9 @@ void prepare_repo_settings(struct repository *r)
        char *strval;
        int manyfiles;
 
+       if (!r->gitdir)
+               BUG("Cannot add settings for uninitialized repository");
+
        if (r->settings.initialized++)
                return;
 
index c7ea706c205fee1f84301668c620906ec5c06650..34610c5a33e013169de14189274fd4ed42512a96 100644 (file)
@@ -82,6 +82,8 @@ void repo_set_gitdir(struct repository *repo,
        expand_base_dir(&repo->objects->odb->path, o->object_dir,
                        repo->commondir, "objects");
 
+       repo->objects->odb->disable_ref_updates = o->disable_ref_updates;
+
        free(repo->objects->alternate_db);
        repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
        expand_base_dir(&repo->graft_file, o->graft_file,
index 98f95834706eb9b3ab93bf6d9eb34edacaed4ce9..2b5cf97f31e88934d9b23d96a97cffc60a9606fd 100644 (file)
@@ -162,6 +162,7 @@ struct set_gitdir_args {
        const char *graft_file;
        const char *index_file;
        const char *alternate_db;
+       int disable_ref_updates;
 };
 
 void repo_set_gitdir(struct repository *repo, const char *root,
index 1981a0859f0e24cadec5e496e9616dc39f23330c..ad4286fbdde521c3eebc048577bc61a341c9e6ee 100644 (file)
@@ -44,10 +44,15 @@ static inline int want_ancestry(const struct rev_info *revs);
 
 void show_object_with_name(FILE *out, struct object *obj, const char *name)
 {
-       const char *p;
-
        fprintf(out, "%s ", oid_to_hex(&obj->oid));
-       for (p = name; *p && *p != '\n'; p++)
+       /*
+        * This "for (const char *p = ..." is made as a first step towards
+        * making use of such declarations elsewhere in our codebase.  If
+        * it causes compilation problems on your platform, please report
+        * it to the Git mailing list at git@vger.kernel.org. In the meantime,
+        * adding -std=gnu99 to CFLAGS may help if you are with older GCC.
+        */
+       for (const char *p = name; *p && *p != '\n'; p++)
                fputc(*p, out);
        fputc('\n', out);
 }
@@ -2295,11 +2300,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->left_only = 1;
        } else if (!strcmp(arg, "--right-only")) {
                if (revs->left_only)
-                       die("--right-only is incompatible with --left-only");
+                       die(_("options '%s' and '%s' cannot be used together"), "--right-only", "--left-only");
                revs->right_only = 1;
        } else if (!strcmp(arg, "--cherry")) {
                if (revs->left_only)
-                       die("--cherry is incompatible with --left-only");
+                       die(_("options '%s' and '%s' cannot be used together"), "--cherry", "--left-only");
                revs->cherry_mark = 1;
                revs->right_only = 1;
                revs->max_parents = 1;
@@ -2308,12 +2313,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->count = 1;
        } else if (!strcmp(arg, "--cherry-mark")) {
                if (revs->cherry_pick)
-                       die("--cherry-mark is incompatible with --cherry-pick");
+                       die(_("options '%s' and '%s' cannot be used together"), "--cherry-mark", "--cherry-pick");
                revs->cherry_mark = 1;
                revs->limited = 1; /* needs limit_list() */
        } else if (!strcmp(arg, "--cherry-pick")) {
                if (revs->cherry_mark)
-                       die("--cherry-pick is incompatible with --cherry-mark");
+                       die(_("options '%s' and '%s' cannot be used together"), "--cherry-pick", "--cherry-mark");
                revs->cherry_pick = 1;
                revs->limited = 1;
        } else if (!strcmp(arg, "--objects")) {
@@ -2493,7 +2498,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
        } else if (!strcmp(arg, "--invert-grep")) {
-               revs->invert_grep = 1;
+               revs->grep_filter.no_body_match = 1;
        } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
                if (strcmp(optarg, "none"))
                        git_log_output_encoding = xstrdup(optarg);
@@ -2519,7 +2524,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return opts;
        }
        if (revs->graph && revs->track_linear)
-               die("--show-linear-break and --graph are incompatible");
+               die(_("options '%s' and '%s' cannot be used together"), "--show-linear-break", "--graph");
 
        return 1;
 }
@@ -2862,24 +2867,24 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        compile_grep_patterns(&revs->grep_filter);
 
        if (revs->reverse && revs->reflog_info)
-               die("cannot combine --reverse with --walk-reflogs");
+               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--walk-reflogs");
        if (revs->reflog_info && revs->limited)
                die("cannot combine --walk-reflogs with history-limiting options");
        if (revs->rewrite_parents && revs->children.name)
-               die("cannot combine --parents and --children");
+               die(_("options '%s' and '%s' cannot be used together"), "--parents", "--children");
 
        /*
         * Limitations on the graph functionality
         */
        if (revs->reverse && revs->graph)
-               die("cannot combine --reverse with --graph");
+               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--graph");
 
        if (revs->reflog_info && revs->graph)
-               die("cannot combine --walk-reflogs with --graph");
+               die(_("options '%s' and '%s' cannot be used together"), "--walk-reflogs", "--graph");
        if (revs->no_walk && revs->graph)
-               die("cannot combine --no-walk with --graph");
+               die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
        if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
-               die("cannot use --grep-reflog without --walk-reflogs");
+               die(_("the option '%s' requires '%s'"), "--grep-reflog", "--walk-reflogs");
 
        if (revs->line_level_traverse &&
            (revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
@@ -3778,7 +3783,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                                     (char *)message, strlen(message));
        strbuf_release(&buf);
        unuse_commit_buffer(commit, message);
-       return opt->invert_grep ? !retval : retval;
+       return retval;
 }
 
 static inline int want_ancestry(const struct rev_info *revs)
index 5578bb4720ae37c2824a220a487dce0451bc7a85..3f66147bfd390abdd98de4f366014bdce88179c2 100644 (file)
@@ -246,8 +246,6 @@ struct rev_info {
 
        /* Filter by commit log message */
        struct grep_opt grep_filter;
-       /* Negate the match of grep_filter */
-       int invert_grep;
 
        /* Display history graph */
        struct git_graph *graph;
index 1f58c17b6ce5691157ec38dfdf6733fdf990be67..69dde42f1e7f5e5886f2ea2d9caba02dd9ab8eff 100644 (file)
@@ -340,15 +340,6 @@ static void child_close_pair(int fd[2])
        child_close(fd[1]);
 }
 
-/*
- * parent will make it look like the child spewed a fatal error and died
- * this is needed to prevent changes to t0061.
- */
-static void fake_fatal(const char *err, va_list params)
-{
-       vreportf("fatal: ", err, params);
-}
-
 static void child_error_fn(const char *err, va_list params)
 {
        const char msg[] = "error() should not be called in child\n";
@@ -372,15 +363,16 @@ static void NORETURN child_die_fn(const char *err, va_list params)
 static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
 {
        static void (*old_errfn)(const char *err, va_list params);
+       report_fn die_message_routine = get_die_message_routine();
 
        old_errfn = get_error_routine();
-       set_error_routine(fake_fatal);
+       set_error_routine(die_message_routine);
        errno = cerr->syserr;
 
        switch (cerr->err) {
        case CHILD_ERR_CHDIR:
                error_errno("exec '%s': cd to '%s' failed",
-                           cmd->argv[0], cmd->dir);
+                           cmd->args.v[0], cmd->dir);
                break;
        case CHILD_ERR_DUP2:
                error_errno("dup2() in child failed");
@@ -392,12 +384,12 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
                error_errno("sigprocmask failed restoring signals");
                break;
        case CHILD_ERR_ENOENT:
-               error_errno("cannot run %s", cmd->argv[0]);
+               error_errno("cannot run %s", cmd->args.v[0]);
                break;
        case CHILD_ERR_SILENT:
                break;
        case CHILD_ERR_ERRNO:
-               error_errno("cannot exec '%s'", cmd->argv[0]);
+               error_errno("cannot exec '%s'", cmd->args.v[0]);
                break;
        }
        set_error_routine(old_errfn);
@@ -405,7 +397,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
 
 static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
 {
-       if (!cmd->argv[0])
+       if (!cmd->args.v[0])
                BUG("command is empty");
 
        /*
@@ -415,11 +407,11 @@ static int prepare_cmd(struct strvec *out, const struct child_process *cmd)
        strvec_push(out, SHELL_PATH);
 
        if (cmd->git_cmd) {
-               prepare_git_cmd(out, cmd->argv);
+               prepare_git_cmd(out, cmd->args.v);
        } else if (cmd->use_shell) {
-               prepare_shell_cmd(out, cmd->argv);
+               prepare_shell_cmd(out, cmd->args.v);
        } else {
-               strvec_pushv(out, cmd->argv);
+               strvec_pushv(out, cmd->args.v);
        }
 
        /*
@@ -654,15 +646,10 @@ static void trace_run_command(const struct child_process *cp)
                sq_quote_buf_pretty(&buf, cp->dir);
                strbuf_addch(&buf, ';');
        }
-       /*
-        * The caller is responsible for initializing cp->env from
-        * cp->env_array if needed. We only check one place.
-        */
-       if (cp->env)
-               trace_add_env(&buf, cp->env);
+       trace_add_env(&buf, cp->env_array.v);
        if (cp->git_cmd)
                strbuf_addstr(&buf, " git");
-       sq_quote_argv_pretty(&buf, cp->argv);
+       sq_quote_argv_pretty(&buf, cp->args.v);
 
        trace_printf("%s", buf.buf);
        strbuf_release(&buf);
@@ -675,11 +662,6 @@ int start_command(struct child_process *cmd)
        int failed_errno;
        char *str;
 
-       if (!cmd->argv)
-               cmd->argv = cmd->args.v;
-       if (!cmd->env)
-               cmd->env = cmd->env_array.v;
-
        /*
         * In case of errors we must keep the promise to close FDs
         * that have been passed in via ->in and ->out.
@@ -728,7 +710,7 @@ int start_command(struct child_process *cmd)
                        str = "standard error";
 fail_pipe:
                        error("cannot create %s pipe for %s: %s",
-                               str, cmd->argv[0], strerror(failed_errno));
+                               str, cmd->args.v[0], strerror(failed_errno));
                        child_process_clear(cmd);
                        errno = failed_errno;
                        return -1;
@@ -757,7 +739,7 @@ fail_pipe:
                failed_errno = errno;
                cmd->pid = -1;
                if (!cmd->silent_exec_failure)
-                       error_errno("cannot run %s", cmd->argv[0]);
+                       error_errno("cannot run %s", cmd->args.v[0]);
                goto end_of_spawn;
        }
 
@@ -769,7 +751,7 @@ fail_pipe:
                set_cloexec(null_fd);
        }
 
-       childenv = prep_childenv(cmd->env);
+       childenv = prep_childenv(cmd->env_array.v);
        atfork_prepare(&as);
 
        /*
@@ -867,7 +849,7 @@ fail_pipe:
        }
        atfork_parent(&as);
        if (cmd->pid < 0)
-               error_errno("cannot fork() for %s", cmd->argv[0]);
+               error_errno("cannot fork() for %s", cmd->args.v[0]);
        else if (cmd->clean_on_exit)
                mark_child_for_cleanup(cmd->pid, cmd);
 
@@ -884,7 +866,7 @@ fail_pipe:
                 * At this point we know that fork() succeeded, but exec()
                 * failed. Errors have been reported to our stderr.
                 */
-               wait_or_whine(cmd->pid, cmd->argv[0], 0);
+               wait_or_whine(cmd->pid, cmd->args.v[0], 0);
                child_err_spew(cmd, &cerr);
                failed_errno = errno;
                cmd->pid = -1;
@@ -901,7 +883,7 @@ end_of_spawn:
 #else
 {
        int fhin = 0, fhout = 1, fherr = 2;
-       const char **sargv = cmd->argv;
+       const char **sargv = cmd->args.v;
        struct strvec nargv = STRVEC_INIT;
 
        if (cmd->no_stdin)
@@ -928,20 +910,20 @@ end_of_spawn:
                fhout = dup(cmd->out);
 
        if (cmd->git_cmd)
-               cmd->argv = prepare_git_cmd(&nargv, cmd->argv);
+               cmd->args.v = prepare_git_cmd(&nargv, sargv);
        else if (cmd->use_shell)
-               cmd->argv = prepare_shell_cmd(&nargv, cmd->argv);
+               cmd->args.v = prepare_shell_cmd(&nargv, sargv);
 
-       cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, (char**) cmd->env,
+       cmd->pid = mingw_spawnvpe(cmd->args.v[0], cmd->args.v, (char**) cmd->env_array.v,
                        cmd->dir, fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
-               error_errno("cannot spawn %s", cmd->argv[0]);
+               error_errno("cannot spawn %s", cmd->args.v[0]);
        if (cmd->clean_on_exit && cmd->pid >= 0)
                mark_child_for_cleanup(cmd->pid, cmd);
 
        strvec_clear(&nargv);
-       cmd->argv = sargv;
+       cmd->args.v = sargv;
        if (fhin != 0)
                close(fhin);
        if (fhout != 1)
@@ -991,7 +973,7 @@ end_of_spawn:
 
 int finish_command(struct child_process *cmd)
 {
-       int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0);
+       int ret = wait_or_whine(cmd->pid, cmd->args.v[0], 0);
        trace2_child_exit(cmd, ret);
        child_process_clear(cmd);
        invalidate_lstat_cache();
@@ -1000,7 +982,7 @@ int finish_command(struct child_process *cmd)
 
 int finish_command_in_signal(struct child_process *cmd)
 {
-       int ret = wait_or_whine(cmd->pid, cmd->argv[0], 1);
+       int ret = wait_or_whine(cmd->pid, cmd->args.v[0], 1);
        trace2_child_exit(cmd, ret);
        return ret;
 }
@@ -1038,7 +1020,7 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
                                 const char *const *env, const char *tr2_class)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
-       cmd.argv = argv;
+       strvec_pushv(&cmd.args, argv);
        cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
        cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
        cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
@@ -1048,7 +1030,8 @@ int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
        cmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0;
        cmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0;
        cmd.dir = dir;
-       cmd.env = env;
+       if (env)
+               strvec_pushv(&cmd.env_array, (const char **)env);
        cmd.trace2_child_class = tr2_class;
        return run_command(&cmd);
 }
@@ -1081,7 +1064,9 @@ static void *run_thread(void *data)
 
 static NORETURN void die_async(const char *err, va_list params)
 {
-       vreportf("fatal: ", err, params);
+       report_fn die_message_fn = get_die_message_routine();
+
+       die_message_fn(err, params);
 
        if (in_async()) {
                struct async *async = pthread_getspecific(async_key);
@@ -1334,7 +1319,8 @@ int run_hook_ve(const char *const *env, const char *name, va_list args)
        strvec_push(&hook.args, p);
        while ((p = va_arg(args, const char *)))
                strvec_push(&hook.args, p);
-       hook.env = env;
+       if (env)
+               strvec_pushv(&hook.env_array, (const char **)env);
        hook.no_stdin = 1;
        hook.stdout_to_stderr = 1;
        hook.trace2_hook_name = name;
index 4987826258490752d06f9e407b478fff23940ae3..2be5f5d6422e54b84df300abd41974df3cd169da 100644 (file)
 struct child_process {
 
        /**
-        * The .argv member is set up as an array of string pointers (NULL
-        * terminated), of which .argv[0] is the program name to run (usually
-        * without a path). If the command to run is a git command, set argv[0] to
-        * the command name without the 'git-' prefix and set .git_cmd = 1.
+        * The .args is a `struct strvec', use that API to manipulate
+        * it, e.g. strvec_pushv() to add an existing "const char **"
+        * vector.
         *
-        * Note that the ownership of the memory pointed to by .argv stays with the
-        * caller, but it should survive until `finish_command` completes. If the
-        * .argv member is NULL, `start_command` will point it at the .args
-        * `strvec` (so you may use one or the other, but you must use exactly
-        * one). The memory in .args will be cleaned up automatically during
-        * `finish_command` (or during `start_command` when it is unsuccessful).
+        * If the command to run is a git command, set the first
+        * element in the strvec to the command name without the
+        * 'git-' prefix and set .git_cmd = 1.
         *
+        * The memory in .args will be cleaned up automatically during
+        * `finish_command` (or during `start_command` when it is unsuccessful).
         */
-       const char **argv;
-
        struct strvec args;
+
+       /**
+        * Like .args the .env_array is a `struct strvec'.
+        *
+        * To modify the environment of the sub-process, specify an array of
+        * environment settings. Each string in the array manipulates the
+        * environment.
+        *
+        * - If the string is of the form "VAR=value", i.e. it contains '='
+        *   the variable is added to the child process's environment.
+        *
+        * - If the string does not contain '=', it names an environment
+        *   variable that will be removed from the child process's environment.
+        *
+        * The memory in .env_array will be cleaned up automatically during
+        * `finish_command` (or during `start_command` when it is unsuccessful).
+        */
        struct strvec env_array;
        pid_t pid;
 
@@ -96,23 +109,6 @@ struct child_process {
         */
        const char *dir;
 
-       /**
-        * To modify the environment of the sub-process, specify an array of
-        * string pointers (NULL terminated) in .env:
-        *
-        * - If the string is of the form "VAR=value", i.e. it contains '='
-        *   the variable is added to the child process's environment.
-        *
-        * - If the string does not contain '=', it names an environment
-        *   variable that will be removed from the child process's environment.
-        *
-        * If the .env member is NULL, `start_command` will point it at the
-        * .env_array `strvec` (so you may use one or the other, but not both).
-        * The memory in .env_array will be cleaned up automatically during
-        * `finish_command` (or during `start_command` when it is unsuccessful).
-        */
-       const char *const *env;
-
        unsigned no_stdin:1;
        unsigned no_stdout:1;
        unsigned no_stderr:1;
index 795b370dd34fbfce9198afad8f7dc9974358f4b6..6abd72160ccd46791d5a262021804e07ab9ea37f 100644 (file)
@@ -1164,18 +1164,14 @@ static int run_rewrite_hook(const struct object_id *oldoid,
                            const struct object_id *newoid)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
-       const char *argv[3];
        int code;
        struct strbuf sb = STRBUF_INIT;
+       const char *hook_path = find_hook("post-rewrite");
 
-       argv[0] = find_hook("post-rewrite");
-       if (!argv[0])
+       if (!hook_path)
                return 0;
 
-       argv[1] = "amend";
-       argv[2] = NULL;
-
-       proc.argv = argv;
+       strvec_pushl(&proc.args, hook_path, "amend", NULL);
        proc.in = -1;
        proc.stdout_to_stderr = 1;
        proc.trace2_hook_name = "post-rewrite";
@@ -3501,17 +3497,12 @@ static int error_failed_squash(struct repository *r,
 
 static int do_exec(struct repository *r, const char *command_line)
 {
-       struct strvec child_env = STRVEC_INIT;
        const char *child_argv[] = { NULL, NULL };
        int dirty, status;
 
        fprintf(stderr, _("Executing: %s\n"), command_line);
        child_argv[0] = command_line;
-       strvec_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
-       strvec_pushf(&child_env, "GIT_WORK_TREE=%s",
-                    absolute_path(get_git_work_tree()));
-       status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
-                                         child_env.v);
+       status = run_command_v_opt(child_argv, RUN_USING_SHELL);
 
        /* force re-reading of the cache */
        if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
@@ -3541,8 +3532,6 @@ static int do_exec(struct repository *r, const char *command_line)
                status = 1;
        }
 
-       strvec_clear(&child_env);
-
        return status;
 }
 
@@ -4234,6 +4223,8 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts,
 
        cmd.git_cmd = 1;
 
+       if (startup_info->original_cwd)
+               cmd.dir = startup_info->original_cwd;
        strvec_push(&cmd.args, "checkout");
        strvec_push(&cmd.args, commit);
        strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
diff --git a/setup.c b/setup.c
index 347d7181ae907c027f01cbc8ec42a2b64de06ebc..af3b8c09abe398edd0f45828b81ecec72b3900b6 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -12,6 +12,7 @@ static int work_tree_config_is_bogus;
 
 static struct startup_info the_startup_info;
 struct startup_info *startup_info = &the_startup_info;
+const char *tmp_original_cwd;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -432,6 +433,69 @@ void setup_work_tree(void)
        initialized = 1;
 }
 
+static void setup_original_cwd(void)
+{
+       struct strbuf tmp = STRBUF_INIT;
+       const char *worktree = NULL;
+       int offset = -1;
+
+       if (!tmp_original_cwd)
+               return;
+
+       /*
+        * startup_info->original_cwd points to the current working
+        * directory we inherited from our parent process, which is a
+        * directory we want to avoid removing.
+        *
+        * For convience, we would like to have the path relative to the
+        * worktree instead of an absolute path.
+        *
+        * Yes, startup_info->original_cwd is usually the same as 'prefix',
+        * but differs in two ways:
+        *   - prefix has a trailing '/'
+        *   - if the user passes '-C' to git, that modifies the prefix but
+        *     not startup_info->original_cwd.
+        */
+
+       /* Normalize the directory */
+       strbuf_realpath(&tmp, tmp_original_cwd, 1);
+       free((char*)tmp_original_cwd);
+       tmp_original_cwd = NULL;
+       startup_info->original_cwd = strbuf_detach(&tmp, NULL);
+
+       /*
+        * Get our worktree; we only protect the current working directory
+        * if it's in the worktree.
+        */
+       worktree = get_git_work_tree();
+       if (!worktree)
+               goto no_prevention_needed;
+
+       offset = dir_inside_of(startup_info->original_cwd, worktree);
+       if (offset >= 0) {
+               /*
+                * If startup_info->original_cwd == worktree, that is already
+                * protected and we don't need original_cwd as a secondary
+                * protection measure.
+                */
+               if (!*(startup_info->original_cwd + offset))
+                       goto no_prevention_needed;
+
+               /*
+                * original_cwd was inside worktree; precompose it just as
+                * we do prefix so that built up paths will match
+                */
+               startup_info->original_cwd = \
+                       precompose_string_if_needed(startup_info->original_cwd
+                                                   + offset);
+               return;
+       }
+
+no_prevention_needed:
+       free((char*)startup_info->original_cwd);
+       startup_info->original_cwd = NULL;
+}
+
 static int read_worktree_config(const char *var, const char *value, void *vdata)
 {
        struct repository_format *data = vdata;
@@ -1330,6 +1394,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                setenv(GIT_PREFIX_ENVIRONMENT, "", 1);
        }
 
+       setup_original_cwd();
 
        strbuf_release(&dir);
        strbuf_release(&gitdir);
index 96512f85b316e7f3b56df94cd1e7912dbb6020f0..76965a17d444829b4a5b5edc099d12bf1fcc7ea3 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -160,7 +160,7 @@ void strbuf_grow(struct strbuf *sb, size_t amount);
 static inline void strbuf_setlen(struct strbuf *sb, size_t len)
 {
        if (len > (sb->alloc ? sb->alloc - 1 : 0))
-               die("BUG: strbuf_setlen() beyond buffer");
+               BUG("strbuf_setlen() beyond buffer");
        sb->len = len;
        if (sb->buf != strbuf_slopbuf)
                sb->buf[len] = '\0';
index dfa790d3ff91c637efc14843a9a26e507f116b83..cae56ae6b8077547a35eda62bb3d250ca6d6510b 100644 (file)
@@ -187,7 +187,7 @@ static int handshake_capabilities(struct child_process *process,
                                *supported_capabilities |= capabilities[i].flag;
                } else {
                        die("subprocess '%s' requested unsupported capability '%s'",
-                           process->argv[0], p);
+                           process->args.v[0], p);
                }
        }
 
index 5232d02020c725abdf0b486843c703403dcfc012..c667baa949b685c6c78c41eb8d272410c7574fe4 100644 (file)
@@ -279,7 +279,9 @@ static void do_remove_scheduled_dirs(int new_len)
 {
        while (removal.len > new_len) {
                removal.buf[removal.len] = '\0';
-               if (rmdir(removal.buf))
+               if ((startup_info->original_cwd &&
+                    !strcmp(removal.buf, startup_info->original_cwd)) ||
+                   rmdir(removal.buf))
                        break;
                do {
                        removal.len--;
@@ -293,6 +295,10 @@ void schedule_dir_for_removal(const char *name, int len)
 {
        int match_len, last_slash, i, previous_slash;
 
+       if (startup_info->original_cwd &&
+           !strcmp(name, startup_info->original_cwd))
+               return; /* Do not remove the current working directory */
+
        match_len = last_slash = i =
                longest_path_match(name, len, removal.buf, removal.len,
                                   &previous_slash);
index 882d26eee30b115f4c656e0ab5048f4e2e96319b..46cd5fc5273dc0bcb907ab49ce8c97c4783053be 100644 (file)
@@ -71,12 +71,10 @@ clean-chainlint:
 
 check-chainlint:
        @mkdir -p '$(CHAINLINTTMP_SQ)' && \
-       err=0 && \
-       for i in $(CHAINLINTTESTS); do \
-               $(CHAINLINT) <chainlint/$$i.test | \
-               sed -e '/^# LINT: /d' >'$(CHAINLINTTMP_SQ)'/$$i.actual && \
-               diff -u chainlint/$$i.expect '$(CHAINLINTTMP_SQ)'/$$i.actual || err=1; \
-       done && exit $$err
+       sed -e '/^# LINT: /d' $(patsubst %,chainlint/%.test,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/tests && \
+       sed -e '/^[     ]*$$/d' $(patsubst %,chainlint/%.expect,$(CHAINLINTTESTS)) >'$(CHAINLINTTMP_SQ)'/expect && \
+       $(CHAINLINT) '$(CHAINLINTTMP_SQ)'/tests | grep -v '^[   ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
+       diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
 
 test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
        test-lint-filenames
index 29f72354bf17e959abcc2e14243816b1a0262e52..f48e0542cdc42ab12d9b46ffa6cee05d84431e46 100644 (file)
--- a/t/README
+++ b/t/README
@@ -466,6 +466,12 @@ explicitly providing repositories when accessing submodule objects is
 complete or needs to be abandoned for whatever reason (in which case the
 migrated codepaths still retain their performance benefits).
 
+GIT_TEST_REQUIRE_PREREQ=<list> allows specifying a space separated list of
+prereqs that are required to succeed. If a prereq in this list is triggered by
+a test and then fails then the whole test run will abort. This can help to make
+sure the expected tests are executed and not silently skipped when their
+dependency breaks or is simply not present in a new environment.
+
 Naming Tests
 ------------
 
index 7913e206ed6b73d16779e91d6a9197e602626c57..7f2b83bdc8181f6d653f40bf99a40bf8e7cc03f5 100755 (executable)
@@ -6,6 +6,7 @@ success=0
 failed=0
 broken=0
 total=0
+missing_prereq=
 
 while read file
 do
@@ -30,10 +31,26 @@ do
                        broken=$(($broken + $value)) ;;
                total)
                        total=$(($total + $value)) ;;
+               missing_prereq)
+                       missing_prereq="$missing_prereq,$value" ;;
                esac
        done <"$file"
 done
 
+if test -n "$missing_prereq"
+then
+       unique_missing_prereq=$(
+               echo $missing_prereq |
+               tr -s "," "\n" |
+               grep -v '^$' |
+               sort -u |
+               paste -s -d ' ')
+       if test -n "$unique_missing_prereq"
+       then
+               printf "\nmissing prereq: $unique_missing_prereq\n\n"
+       fi
+fi
+
 if test -n "$failed_tests"
 then
        printf "\nfailed test(s):$failed_tests\n\n"
index d3b299e75cb1831d2c1044d8df979bc81f22e177..09e86f9ba0804f70d6561b5bda03d69535f11dcf 100644 (file)
@@ -161,7 +161,7 @@ test_expect_success 'blame huge graft' '
                        GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \
                        git commit -a -m "$i$j" &&
                        commit=$(git rev-parse --verify HEAD) &&
-                       graft="$graft$commit "
+                       graft="$graft$commit " || return 1
                done
        done &&
        printf "%s " $graft >.git/info/grafts &&
index 8a25c5b855e7ea9d28d208a3302ecb68965f34b6..dc4ce37cb5188a63cf3a831a16d21363cfffd3f2 100644 (file)
@@ -24,9 +24,9 @@
 # in order to avoid misinterpreting the ")" in constructs such as "x=$(...)"
 # and "case $x in *)" as ending the subshell.
 #
-# Lines missing a final "&&" are flagged with "?!AMP?!", and lines which chain
-# commands with ";" internally rather than "&&" are flagged "?!SEMI?!". A line
-# may be flagged for both violations.
+# Lines missing a final "&&" are flagged with "?!AMP?!", as are lines which
+# chain commands with ";" internally rather than "&&". A line may be flagged
+# for both violations.
 #
 # Detection of a missing &&-link in a multi-line subshell is complicated by the
 # fact that the last statement before the closing ")" must not end with "&&".
@@ -47,8 +47,8 @@
 # "?!AMP?!" violation is removed from the "bar" line (retrieved from the "hold"
 # area) since the final statement of a subshell must not end with "&&". The
 # final line of a subshell may still break the &&-chain by using ";" internally
-# to chain commands together rather than "&&", so "?!SEMI?!" is never removed
-# from a line (even though "?!AMP?!" might be).
+# to chain commands together rather than "&&", but an internal "?!AMP?!" is
+# never removed from a line even though a line-ending "?!AMP?!" might be.
 #
 # Care is taken to recognize the last _statement_ of a multi-line subshell, not
 # necessarily the last textual _line_ within the subshell, since &&-chaining
 # receives similar treatment.
 #
 # Swallowing here-docs with arbitrary tags requires a bit of finesse. When a
-# line such as "cat <<EOF >out" is seen, the here-doc tag is moved to the front
-# of the line enclosed in angle brackets as a sentinel, giving "<EOF>cat >out".
+# line such as "cat <<EOF" is seen, the here-doc tag is copied to the front of
+# the line enclosed in angle brackets as a sentinel, giving "<EOF>cat <<EOF".
 # As each subsequent line is read, it is appended to the target line and a
 # (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see if
 # the content inside "<...>" matches the entirety of the newly-read line. For
 # instance, if the next line read is "some data", when concatenated with the
-# target line, it becomes "<EOF>cat >out\nsome data", and a match is attempted
+# target line, it becomes "<EOF>cat <<EOF\nsome data", and a match is attempted
 # to see if "EOF" matches "some data". Since it doesn't, the next line is
 # attempted. When a line consisting of only "EOF" (and possible whitespace) is
-# encountered, it is appended to the target line giving "<EOF>cat >out\nEOF",
+# encountered, it is appended to the target line giving "<EOF>cat <<EOF\nEOF",
 # in which case the "EOF" inside "<...>" does match the text following the
 # newline, thus the closing here-doc tag has been found. The closing tag line
 # and the "<...>" prefix on the target line are then discarded, leaving just
-# the target line "cat >out".
-#
-# To facilitate regression testing (and manual debugging), a ">" annotation is
-# applied to the line containing ")" which closes a subshell, ">>" to a line
-# closing a nested subshell, and ">>>" to a line closing both at once. This
-# makes it easy to detect whether the heuristics correctly identify
-# end-of-subshell.
+# the target line "cat <<EOF".
 #------------------------------------------------------------------------------
 
 # incomplete line -- slurp up next line
@@ -94,9 +88,9 @@
 
 # here-doc -- swallow it to avoid false hits within its body (but keep the
 # command to which it was attached)
-/<<[   ]*[-\\'"]*[A-Za-z0-9_]/ {
-       s/^\(.*\)<<[    ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
-       s/[     ]*<<//
+/<<-*[         ]*[\\'"]*[A-Za-z0-9_]/ {
+       /"[^"]*<<[^"]*"/bnotdoc
+       s/^\(.*<<-*[    ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1\2/
        :hered
        N
        /^<\([^>]*\)>.*\n[      ]*\1[   ]*$/!{
        s/^<[^>]*>//
        s/\n.*$//
 }
+:notdoc
 
 # one-liner "(...) &&"
 /^[    ]*!*[   ]*(..*)[        ]*&&[   ]*$/boneline
@@ -126,7 +121,7 @@ b
 # "&&" (but not ";" in a string)
 :oneline
 /;/{
-       /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+       /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
 }
 b
 
@@ -136,11 +131,15 @@ b
        h
        bnextln
 }
-# "(..." line -- split off and stash "(", then process "..." as its own line
+# "(..." line -- "(" opening subshell cuddled with command; temporarily replace
+# "(" with sentinel "^" and process the line as if "(" had been seen solo on
+# the preceding line; this temporary replacement prevents several rules from
+# accidentally thinking "(" introduces a nested subshell; "^" is changed back
+# to "(" at output time
 x
-s/.*/(/
+s/.*//
 x
-s/(//
+s/(/^/
 bslurp
 
 :nextln
@@ -157,8 +156,10 @@ s/.*\n//
        /"[^'"]*'[^'"]*"/!bsqstr
 }
 :folded
-# here-doc -- swallow it
-/<<[   ]*[-\\'"]*[A-Za-z0-9_]/bheredoc
+# here-doc -- swallow it (but not "<<" in a string)
+/<<-*[         ]*[\\'"]*[A-Za-z0-9_]/{
+       /"[^"]*<<[^"]*"/!bheredoc
+}
 # comment or empty line -- discard since final non-comment, non-empty line
 # before closing ")", "done", "elsif", "else", or "fi" will need to be
 # re-visited to drop "suspect" marking since final line of those constructs
@@ -171,12 +172,12 @@ s/.*\n//
        /"[^"]*#[^"]*"/!s/[     ]#.*$//
 }
 # one-liner "case ... esac"
-/^[    ]*case[         ]*..*esac/bchkchn
+/^[    ^]*case[        ]*..*esac/bchkchn
 # multi-line "case ... esac"
-/^[    ]*case[         ]..*[   ]in/bcase
+/^[    ^]*case[        ]..*[   ]in/bcase
 # multi-line "for ... done" or "while ... done"
-/^[    ]*for[  ]..*[   ]in/bcont
-/^[    ]*while[        ]/bcont
+/^[    ^]*for[         ]..*[   ]in/bcont
+/^[    ^]*while[       ]/bcont
 /^[    ]*do[   ]/bcont
 /^[    ]*do[   ]*$/bcont
 /;[    ]*do/bcont
@@ -187,7 +188,7 @@ s/.*\n//
 /||[   ]*exit[         ]/bcont
 /||[   ]*exit[         ]*$/bcont
 # multi-line "if...elsif...else...fi"
-/^[    ]*if[   ]/bcont
+/^[    ^]*if[  ]/bcont
 /^[    ]*then[         ]/bcont
 /^[    ]*then[         ]*$/bcont
 /;[    ]*then/bcont
@@ -200,15 +201,15 @@ s/.*\n//
 /^[    ]*fi[   ]*[<>|]/bdone
 /^[    ]*fi[   ]*)/bdone
 # nested one-liner "(...) &&"
-/^[    ]*(.*)[         ]*&&[   ]*$/bchkchn
+/^[    ^]*(.*)[        ]*&&[   ]*$/bchkchn
 # nested one-liner "(...)"
-/^[    ]*(.*)[         ]*$/bchkchn
+/^[    ^]*(.*)[        ]*$/bchkchn
 # nested one-liner "(...) >x" (or "2>x" or "<x" or "|x")
-/^[    ]*(.*)[         ]*[0-9]*[<>|]/bchkchn
+/^[    ^]*(.*)[        ]*[0-9]*[<>|]/bchkchn
 # nested multi-line "(...\n...)"
-/^[    ]*(/bnest
+/^[    ^]*(/bnest
 # multi-line "{...\n...}"
-/^[    ]*{/bblock
+/^[    ^]*{/bblock
 # closing ")" on own line -- exit subshell
 /^[    ]*)/bclssolo
 # "$((...))" -- arithmetic expansion; not closing ")"
@@ -230,16 +231,18 @@ s/.*\n//
 # string and not ";;" in one-liner "case...esac")
 /;/{
        /;;/!{
-               /"[^"]*;[^"]*"/!s/^/?!SEMI?!/
+               /"[^"]*;[^"]*"/!s/;/; ?!AMP?!/
        }
 }
 # line ends with pipe "...|" -- valid; not missing "&&"
 /|[    ]*$/bcont
 # missing end-of-line "&&" -- mark suspect
-/&&[   ]*$/!s/^/?!AMP?!/
+/&&[   ]*$/!s/$/ ?!AMP?!/
 :cont
 # retrieve and print previous line
 x
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 n
 bslurp
 
@@ -280,8 +283,7 @@ bfolded
 # found here-doc -- swallow it to avoid false hits within its body (but keep
 # the command to which it was attached)
 :heredoc
-s/^\(.*\)<<[   ]*[-\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\2>\1<</
-s/[    ]*<<//
+s/^\(.*\)<<\(-*[       ]*\)[\\'"]*\([A-Za-z0-9_][A-Za-z0-9_]*\)['"]*/<\3>\1?!HERE?!\2\3/
 :hdocsub
 N
 /^<\([^>]*\)>.*\n[     ]*\1[   ]*$/!{
@@ -295,7 +297,15 @@ bfolded
 # found "case ... in" -- pass through untouched
 :case
 x
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 n
+:cascom
+/^[    ]*#/{
+       N
+       s/.*\n//
+       bcascom
+}
 /^[    ]*esac/bslurp
 bcase
 
@@ -303,7 +313,7 @@ bcase
 # that line legitimately lacks "&&"
 :else
 x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
 x
 bcont
 
@@ -311,7 +321,7 @@ bcont
 # "suspect" from final contained line since that line legitimately lacks "&&"
 :done
 x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
 x
 # is 'done' or 'fi' cuddled with ")" to close subshell?
 /done.*)/bclose
@@ -322,11 +332,18 @@ bchkchn
 :nest
 x
 :nstslrp
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 n
+:nstcom
+# comment -- not closing ")" if in comment
+/^[    ]*#/{
+       N
+       s/.*\n//
+       bnstcom
+}
 # closing ")" on own line -- stop nested slurp
 /^[    ]*)/bnstcl
-# comment -- not closing ")" if in comment
-/^[    ]*#/bnstcnt
 # "$((...))" -- arithmetic expansion; not closing ")"
 /\$(([^)][^)]*))[^)]*$/bnstcnt
 # "$(...)" -- command substitution; not closing ")"
@@ -337,7 +354,6 @@ n
 x
 bnstslrp
 :nstcl
-s/^/>>/
 # is it "))" which closes nested and parent subshells?
 /)[    ]*)/bslurp
 bchkchn
@@ -345,7 +361,15 @@ bchkchn
 # found multi-line "{...\n...}" block -- pass through untouched
 :block
 x
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 n
+:blkcom
+/^[    ]*#/{
+       N
+       s/.*\n//
+       bblkcom
+}
 # closing "}" -- stop block slurp
 /}/bchkchn
 bblock
@@ -354,16 +378,22 @@ bblock
 # since that line legitimately lacks "&&" and exit subshell loop
 :clssolo
 x
-s/?!AMP?!//
+s/\( ?!AMP?!\)* ?!AMP?!$//
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 p
 x
-s/^/>/
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 b
 
 # found closing "...)" -- exit subshell loop
 :close
 x
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 p
 x
-s/^/>/
+s/^\([         ]*\)^/\1(/
+s/?!HERE?!/<</g
 b
index 09457d31966193da7a324f0cbc417ce05354f15a..46ee1046af73582ea5adf5e91b0be62025684bb6 100644 (file)
@@ -2,8 +2,8 @@
        foo &&
        bar=$((42 + 1)) &&
        baz
->) &&
+) &&
 (
-?!AMP?!        bar=$((42 + 1))
+       bar=$((42 + 1)) ?!AMP?!
        baz
->)
+)
index c4a830d1c1d6df9ae72c1d5c7777ceb056383388..4c34eaee45e2f706e80560edfa271f4a5e245a9e 100644 (file)
@@ -2,9 +2,9 @@
        foo &&
        bar=(gumbo stumbo wumbo) &&
        baz
->) &&
+) &&
 (
        foo &&
        bar=${#bar[@]} &&
        baz
->)
+)
index 3be939ed388c3668f030f78034a8c0204ee55dd2..f76fde1ffba91d7becf17c0990c39ac25a7083f0 100644 (file)
@@ -1,4 +1,4 @@
 (
        nothing &&
        something
->)
+)
index f6dd14302b2bed89e65634d9d57b0a97dbf7ead5..0fdf15b3e14c6aaac3bbbc8f2c4f04035845cd4c 100644 (file)
@@ -3,7 +3,7 @@
        nothing &&
 
        something
-# LINT: swallow blank lines since final _statement_ before subshell end is
+# LINT: ignore blank lines since final _statement_ before subshell end is
 # LINT: significant to "&&"-check, not final _line_ (which might be blank)
 
 
diff --git a/t/chainlint/block-comment.expect b/t/chainlint/block-comment.expect
new file mode 100644 (file)
index 0000000..d10b2ee
--- /dev/null
@@ -0,0 +1,6 @@
+(
+       {
+               echo a &&
+               echo b
+       }
+)
diff --git a/t/chainlint/block-comment.test b/t/chainlint/block-comment.test
new file mode 100644 (file)
index 0000000..df2beea
--- /dev/null
@@ -0,0 +1,8 @@
+(
+       {
+               # show a
+               echo a &&
+               # show b
+               echo b
+       }
+)
index fed7e89ae8ffaec249c2a3a86a915979a34681f0..da60257ebc4e1d2e019e2b8fee8da3742849ddd7 100644 (file)
@@ -7,6 +7,6 @@
        bar &&
        {
                echo c
-?!AMP?!        }
+       } ?!AMP?!
        baz
->)
+)
index d859151af1d9ac89709a3c972b75dbdc8cd53321..0a82fd579f6751a8ed7a88505d2c7988eaf3ae5c 100644 (file)
@@ -1,6 +1,5 @@
 (
-# LINT: missing "&&" in block not currently detected (for consistency with
-# LINT: --chain-lint at top level and to provide escape hatch if needed)
+# LINT: missing "&&" after first "echo"
        foo &&
        {
                echo a
index 55b0f42a534d65caefa2089c16eff01fd00462c8..cfb58fb6b937be645f79167eae7fa27ec27d9618 100644 (file)
@@ -1,6 +1,6 @@
 (
        foo &&
-?!AMP?!        bar
+       bar ?!AMP?!
        baz &&
        wop
->)
+)
index 3cc67b65d0c9ddf6739c9060c7cf3ff45fa277b8..2a44aa73b71a9794c4b51d7c13dc71ab25239596 100644 (file)
@@ -1,6 +1,6 @@
 (
        foo &&
-# LINT: missing "&&" from 'bar'
+# LINT: missing "&&" from "bar"
        bar
        baz &&
 # LINT: final statement before closing ")" legitimately lacks "&&"
diff --git a/t/chainlint/case-comment.expect b/t/chainlint/case-comment.expect
new file mode 100644 (file)
index 0000000..1e4b054
--- /dev/null
@@ -0,0 +1,8 @@
+(
+       case "$x" in
+       x) foo ;;
+       *)
+               bar
+               ;;
+       esac
+)
diff --git a/t/chainlint/case-comment.test b/t/chainlint/case-comment.test
new file mode 100644 (file)
index 0000000..641c157
--- /dev/null
@@ -0,0 +1,11 @@
+(
+       case "$x" in
+       # found foo
+       x) foo ;;
+       # found other
+       *)
+               # treat it as bar
+               bar
+               ;;
+       esac
+)
index 41f121fbbf9c9664640fc81c3e9e4315dadd42c3..31f280d8ceb3152a057c26cabe926f9defb8bdac 100644 (file)
@@ -4,16 +4,16 @@
        *) bar ;;
        esac &&
        foobar
->) &&
+) &&
 (
        case "$x" in
        x) foo ;;
        *) bar ;;
-?!AMP?!        esac
+       esac ?!AMP?!
        foobar
->) &&
+) &&
 (
        case "$x" in 1) true;; esac &&
-?!AMP?!        case "$y" in 2) false;; esac
+       case "$y" in 2) false;; esac ?!AMP?!
        foobar
->)
+)
index 5ef6ff7db5eafbc0abb835901e321ed13265a025..4cb086bf87b04c709601ca610aad6db39bbd8bb4 100644 (file)
@@ -1,5 +1,5 @@
 (
-# LINT: "...)" arms in 'case' not misinterpreted as subshell-closing ")"
+# LINT: "...)" arms in "case" not misinterpreted as subshell-closing ")"
        case "$x" in
        x) foo ;;
        *) bar ;;
@@ -7,7 +7,7 @@
        foobar
 ) &&
 (
-# LINT: missing "&&" on 'esac'
+# LINT: missing "&&" on "esac"
        case "$x" in
        x) foo ;;
        *) bar ;;
@@ -15,7 +15,7 @@
        foobar
 ) &&
 (
-# LINT: "...)" arm in one-liner 'case' not misinterpreted as closing ")"
+# LINT: "...)" arm in one-liner "case" not misinterpreted as closing ")"
        case "$x" in 1) true;; esac &&
 # LINT: same but missing "&&"
        case "$y" in 2) false;; esac
index 2a910f9d66604cd2f4a95377a9071a247e1e6d6e..72d482f76dd20f0d2ca7bbffd177417a257992e2 100644 (file)
@@ -1,4 +1,3 @@
-(
-cd foo &&
+(cd foo &&
        (bar &&
->>>            baz))
+               baz))
index 184688718a9c030097cab0bea570c829189ef34b..0f87db9ae6891f8536c6eec73b71e5f049ca9667 100644 (file)
@@ -1,25 +1,25 @@
 (
        foo
->) &&
+) &&
 (
        bar
->) >out &&
+) >out &&
 (
        baz
->) 2>err &&
+) 2>err &&
 (
        boo
->) <input &&
+) <input &&
 (
        bip
->) | wuzzle &&
+) | wuzzle &&
 (
        bop
->) | fazz      fozz &&
+) | fazz       fozz &&
 (
        bup
->) |
+) |
 fuzzle &&
 (
        yop
->)
+)
index ad4118e537e42e1156146c11ce80f288381b1af3..c72e4df9e709d1e26f393d6e2d906626d94aa3a3 100644 (file)
@@ -2,8 +2,8 @@
        foo &&
        bar=$(gobble) &&
        baz
->) &&
+) &&
 (
-?!AMP?!        bar=$(gobble blocks)
+       bar=$(gobble blocks) ?!AMP?!
        baz
->)
+)
index 3be939ed388c3668f030f78034a8c0204ee55dd2..f76fde1ffba91d7becf17c0990c39ac25a7083f0 100644 (file)
@@ -1,4 +1,4 @@
 (
        nothing &&
        something
->)
+)
index 9674b88cf252f7409241c847d6219547ff2b07cc..2fca1834095817a0aba1bce38a5a837dea0debae 100644 (file)
@@ -1,10 +1,9 @@
-(
-for i in a b c; do
+(for i in a b c; do
    if test "$(echo $(waffle bat))" = "eleventeen" &&
      test "$x" = "$y"; then
      :
    else
      echo >file
    fi
-> done) &&
+ done) &&
 test ! -f file
index 571bbd85cdfc41031de1026bb3c24ae3a9a604f7..5efeda58b2a7196d74999a514268ef0099f661d6 100644 (file)
@@ -1,4 +1,4 @@
-# LINT: 'for' loop cuddled with "(" and ")" and nested 'if' with complex
+# LINT: "for" loop cuddled with "(" and ")" and nested "if" with complex
 # LINT: multi-line condition; indented with spaces, not tabs
 (for i in a b c; do
    if test "$(echo $(waffle bat))" = "eleventeen" &&
index ab2a026fbc2f68048d67174fad32134ded3d2ce7..1d8ed58c4948ea2b975de2742ccad603f48042dc 100644 (file)
@@ -1,7 +1,6 @@
-(
-if test -z ""; then
+(if test -z ""; then
     echo empty
  else
     echo bizzy
-> fi) &&
+ fi) &&
 echo foobar
index eed774a9d64390449699238b4ad0e16c58c15390..7c53f4efe3eba3c3da52e620a89e87c371e15db3 100644 (file)
@@ -1,4 +1,4 @@
-# LINT: 'if' cuddled with "(" and ")"; indented with spaces, not tabs
+# LINT: "if" cuddled with "(" and ")"; indented with spaces, not tabs
 (if test -z ""; then
     echo empty
  else
index 8c0260d7f1d9ce7e85a8b0d47ffe97f57f39b92e..9cf260708e6a5c07282f37a03560781db8504ec0 100644 (file)
@@ -1,5 +1,4 @@
-(
- while read x
+( while read x
   do foobar bop || exit 1
->  done <file ) &&
+  done <file ) &&
 outside subshell
index a841d781f04f01ff1c906027d0b139fb854d4fb9..3c2a62f7518937e86ff2723f62e17be9d28e8eda 100644 (file)
@@ -1,4 +1,4 @@
-# LINT: 'while' loop cuddled with "(" and ")", with embedded (allowed)
+# LINT: "while" loop cuddled with "(" and ")", with embedded (allowed)
 # LINT: "|| exit {n}" to exit loop early, and using redirection "<" to feed
 # LINT: loop; indented with spaces, not tabs
 ( while read x
index b506d4622197a837c66ca56dd22cf7d191664e68..c3e0be404742cb62808ffa43026573dbe5cb9cd1 100644 (file)
@@ -1,21 +1,17 @@
-(
-cd foo &&
+(cd foo &&
        bar
->) &&
+) &&
 
-(
-?!AMP?!cd foo
+(cd foo ?!AMP?!
        bar
->) &&
+) &&
 
 (
        cd foo &&
->      bar) &&
+       bar) &&
 
-(
-cd foo &&
->      bar) &&
+(cd foo &&
+       bar) &&
 
-(
-?!AMP?!cd foo
->      bar)
+(cd foo ?!AMP?!
+       bar)
index 0499fa4180596cc4f8c054a7e2a8a640bdb679ce..257b5b5eed3a23a6549f3a6f2b5ea77a776ad7e9 100644 (file)
@@ -1,5 +1,4 @@
-# LINT: first subshell statement cuddled with opening "("; for implementation
-# LINT: simplicity, "(..." is split into two lines, "(" and "..."
+# LINT: first subshell statement cuddled with opening "("
 (cd foo &&
        bar
 ) &&
index 84d8bdebc02660d3a4ad0aeb341a9f991547b46c..f76aa60466adaf394b2e368e5feb3b54e23b30f8 100644 (file)
@@ -5,7 +5,7 @@
                bar &&
                baz
        done
->) &&
+) &&
 (
        while true
        do
@@ -13,7 +13,7 @@
                bar &&
                baz
        done
->) &&
+) &&
 (
        i=0 &&
        while test $i -lt 10
@@ -21,4 +21,4 @@
                echo $i || exit
                i=$(($i + 1))
        done
->)
+)
index bf78454f74c83d27b059b504859213abdb9acc6c..da80339f781230c19bc7203603d6310454dba4a9 100644 (file)
@@ -2,4 +2,4 @@
        foo || exit 1
        bar &&
        baz
->)
+)
index c33cf56ee73ab7b75c4194d842443d019fa08440..6671b8cd842de110882342fc4ee359eb7e4d1375 100644 (file)
@@ -1,11 +1,11 @@
 (
        for i in a b c
        do
-?!AMP?!                echo $i
-               cat
-?!AMP?!        done
+               echo $i ?!AMP?!
+               cat <<-EOF
+       done ?!AMP?!
        for i in a b c; do
                echo $i &&
                cat $i
        done
->)
+)
index 7db76262bc2e0089ea97581417b7ca95b2c9c2b4..6cb34281582055b118b75f37a006e6c3ae5c962c 100644 (file)
@@ -1,17 +1,17 @@
 (
-# LINT: 'for', 'do', 'done' do not need "&&"
+# LINT: "for", "do", "done" do not need "&&"
        for i in a b c
        do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
                echo $i
 # LINT: last statement of while does not need "&&"
                cat <<-\EOF
                bar
                EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
        done
 
-# LINT: 'do' on same line as 'for'
+# LINT: "do" on same line as "for"
        for i in a b c; do
                echo $i &&
                cat $i
index f011e335e5f12c36460836635dd220153de8f9e1..2af9ced71cc331414ce22e5d4ef3fc1320b3c15d 100644 (file)
@@ -1,2 +1,2 @@
 (
->      cat)
+       cat <<-INPUT)
index e5fb752d2fc22ac421704925a51fc952ecf530e1..f8b3aa73c4f180be48afff988c0f7cece67e45d4 100644 (file)
@@ -1,5 +1,5 @@
 (
-       x=$(bobble &&
-?!AMP?!>>              wiffle)
+       x=$(bobble <<-END &&
+               wiffle) ?!AMP?!
        echo $x
->)
+)
index 32038a070c2ad881f7136028ceb1be41f57c9a43..2578191ca8a809c8bbc271c9f1b8b744d9d8444e 100644 (file)
@@ -1,4 +1,4 @@
 (
-?!AMP?!        cat && echo "multi-line string"
+       cat <<-TXT && echo "multi-line  string" ?!AMP?!
        bap
->)
+)
index 534b065e38baa213da2a224e147cd41c649e55e8..110059ba58420e5924de64edb0ec44346b43cb34 100644 (file)
@@ -1,9 +1,7 @@
-boodle wobba        gorgo snoot        wafta snurb &&
+boodle wobba        gorgo snoot        wafta snurb <<EOF &&
 
-cat >foo &&
+cat <<-Arbitrary_Tag_42 >foo &&
 
-cat >bar &&
+cat <<zump >boo &&
 
-cat >boo &&
-
-horticulture
+horticulture <<EOF
index ad4ce8afd9b55650a2ce3751fbef3d67d07d534d..3f5f92cad34761a1daffa00e819a08c1fc5a07a1 100644 (file)
@@ -14,13 +14,6 @@ boz
 woz
 Arbitrary_Tag_42
 
-# LINT: swallow 'quoted' here-doc
-cat <<'FUMP' >bar &&
-snoz
-boz
-woz
-FUMP
-
 # LINT: swallow "quoted" here-doc
 cat <<"zump" >boo &&
 snoz
index 03d3ceb22d8971ddad5c81c1c5a7714d7f988f8e..03b82a3e58c21e1300befb317f19c7f063fba3ec 100644 (file)
@@ -3,10 +3,10 @@
        do
                if false
                then
-?!AMP?!                        echo "err"
+                       echo "err" ?!AMP?!
                        exit 1
-?!AMP?!                fi
+               fi ?!AMP?!
                foo
-?!AMP?!        done
+       done ?!AMP?!
        bar
->)
+)
index daf22da16476aac9e7467bf73cae8470fe29e98f..f0cf19cfadac8c1ae9d09a668bafc99517c50cdf 100644 (file)
@@ -3,13 +3,13 @@
        do
                if false
                then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
                        echo "err"
                        exit 1
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
                fi
                foo
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
        done
        bar
 )
index 5953c7bfbc2e792af695f1f711ea06662a61dcb5..44d86c35976ce1957aa0b4fb90f6b7e31f230d3c 100644 (file)
@@ -1,19 +1,20 @@
 (
        if test -n ""
        then
-?!AMP?!                echo very
+               echo very ?!AMP?!
                echo empty
        elif test -z ""
+       then
                echo foo
        else
                echo foo &&
-               cat
-?!AMP?!        fi
+               cat <<-EOF
+       fi ?!AMP?!
        echo poodle
->) &&
+) &&
 (
        if test -n ""; then
                echo very &&
-?!AMP?!                echo empty
-       if
->)
+               echo empty
+       fi
+)
index 9bd8e9a4c68c2b3b5fd367c6b28763d9765d7d5d..2055336c2b9ed2e975b21d20310f2821bbef7b6f 100644 (file)
@@ -1,28 +1,29 @@
 (
-# LINT: 'if', 'then', 'elif', 'else', 'fi' do not need "&&"
+# LINT: "if", "then", "elif", "else", "fi" do not need "&&"
        if test -n ""
        then
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
                echo very
-# LINT: last statement before 'elif' does not need "&&"
+# LINT: last statement before "elif" does not need "&&"
                echo empty
        elif test -z ""
-# LINT: last statement before 'else' does not need "&&"
+       then
+# LINT: last statement before "else" does not need "&&"
                echo foo
        else
                echo foo &&
-# LINT: last statement before 'fi' does not need "&&"
+# LINT: last statement before "fi" does not need "&&"
                cat <<-\EOF
                bar
                EOF
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
        fi
        echo poodle
 ) &&
 (
-# LINT: 'then' on same line as 'if'
+# LINT: "then" on same line as "if"
        if test -n ""; then
                echo very &&
                echo empty
-       if
+       fi
 )
index 2f3ebabdc286f440dec9bc0df2596e2fa7ff17ee..ffac8f901857eef401cdcfa6d60734c92a96b416 100644 (file)
@@ -1,4 +1,4 @@
 line 1 line 2 line 3 line 4 &&
 (
        line 5  line 6  line 7  line 8
->)
+)
index fc9f250ac48e61c1555eea13bdea0c84f691dbb8..dd0dace077f0e093ccda9dc33b3296830e13c8d2 100644 (file)
@@ -1,9 +1,8 @@
 (
        foobar &&
-?!AMP?!        barfoo
+       barfoo ?!AMP?!
        flibble "not a # comment"
->) &&
+) &&
 
-(
-cd foo &&
->      flibble "not a # comment")
+(cd foo &&
+       flibble "not a # comment")
index 088e622c3141e1a298ba79c85aa274dfab90ee53..e1be42376c5ef480c791222dc0cc75d4d01fe1ce 100644 (file)
@@ -3,10 +3,10 @@
        then
                while true
                do
-?!AMP?!                        echo "pop"
+                       echo "pop" ?!AMP?!
                        echo "glup"
-?!AMP?!                done
+               done ?!AMP?!
                foo
-?!AMP?!        fi
+       fi ?!AMP?!
        bar
->)
+)
index 93e8ba8e4d9acdefcd9f6e716c6e109cf790ee45..dfcc3f98fb11ce442100f39c844382e2719997ae 100644 (file)
@@ -3,13 +3,13 @@
        then
                while true
                do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
                        echo "pop"
                        echo "glup"
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
                done
                foo
-# LINT: missing "&&" on 'fi'
+# LINT: missing "&&" on "fi"
        fi
        bar
 )
index 59b6c8b850a16d087e5d322c8ee25dcbeb553fa5..300058341b6f303dce9d8e8105b6f16fa25f2ebf 100644 (file)
@@ -3,16 +3,16 @@
        x=$(
                echo bar |
                cat
->>     ) &&
+       ) &&
        echo ok
->) |
+) |
 sort &&
 (
        bar &&
        x=$(echo bar |
                cat
->>     ) &&
+       ) &&
        y=$(echo baz |
->>             fip) &&
+               fip) &&
        echo fail
->)
+)
index 170cb5999322ea7798bceafb2222274c4112adde..ab0dadf748e859968e654cf8d6a28a3a5882460b 100644 (file)
@@ -1,15 +1,9 @@
 (
        x="line 1               line 2          line 3" &&
-?!AMP?!        y='line 1               line2'
+       y="line 1               line2" ?!AMP?!
        foobar
->) &&
-(
-       echo "there's nothing to see here" &&
-       exit
->) &&
+) &&
 (
        echo "xyz" "abc         def             ghi" &&
-       echo 'xyz' 'abc         def             ghi' &&
-       echo 'xyz' "abc         def             ghi" &&
        barfoo
->)
+)
index 287ab897054874972076ec6913bf8c8144196296..4a0af2107da6dfb0035ead1ada8add0353cc2199 100644 (file)
@@ -3,25 +3,13 @@
                line 2
                line 3" &&
 # LINT: missing "&&" on assignment
-       y='line 1
-               line2'
+       y="line 1
+               line2"
        foobar
 ) &&
-(
-# LINT: apostrophe (in a contraction) within string not misinterpreted as
-# LINT: starting multi-line single-quoted string
-       echo "there's nothing to see here" &&
-       exit
-) &&
 (
        echo "xyz" "abc
                def
                ghi" &&
-       echo 'xyz' 'abc
-               def
-               ghi' &&
-       echo 'xyz' "abc
-               def
-               ghi" &&
        barfoo
 )
index cf18429d03977403e66f2fd37a8f94a6ddf9be3e..ad4c2d949ebf5de221876bd83795c6caee1a4aa2 100644 (file)
@@ -1,5 +1,5 @@
 ! (foo && bar) &&
 ! (foo && bar) >baz &&
 
-?!SEMI?!! (foo; bar) &&
-?!SEMI?!! (foo; bar) >baz
+! (foo; ?!AMP?! bar) &&
+! (foo; ?!AMP?! bar) >baz
index c2a59ffc335cb1c4ed7a30c7a59597de4f24ef91..2a86885ee6a330450a76591248b60b89e601816f 100644 (file)
@@ -1,19 +1,19 @@
 (
        (cd foo &&
                bar
->>     ) &&
+       ) &&
        (cd foo &&
                bar
-?!AMP?!>>      )
+       ) ?!AMP?!
        (
                cd foo &&
->>             bar) &&
+               bar) &&
        (
                cd foo &&
-?!AMP?!>>              bar)
+               bar) ?!AMP?!
        (cd foo &&
->>             bar) &&
+               bar) &&
        (cd foo &&
-?!AMP?!>>              bar)
+               bar) ?!AMP?!
        foobar
->)
+)
index 0c9ef1cfc6959e1b4093200769b2401467e927ad..e3bef63f7548cb0c187ae938280029dd470922bb 100644 (file)
@@ -1,7 +1,7 @@
-cat >foop &&
+cat <<ARBITRARY >foop &&
 
 (
-       cat &&
-?!AMP?!        cat
+       cat <<-INPUT_END &&
+       cat <<-EOT ?!AMP?!
        foobar
->)
+)
index 15b68d437379d79ed563205e6246779bc84b967a..be4b27a305bec54678ae4669a1666405ec06f966 100644 (file)
@@ -2,10 +2,8 @@
        foo &&
        (
                bar &&
-               # bottles wobble while fiddles gobble
-               # minor numbers of cows (or do they?)
                baz &&
                snaff
-?!AMP?!>>      )
+       ) ?!AMP?!
        fuzzy
->)
+)
index 0ff136ab3cf1706b52f839686b259df0211d3914..0215cdb1921b5bf27c302ffdf4c48e5bf5a4848a 100644 (file)
@@ -7,7 +7,7 @@
                # minor numbers of cows (or do they?)
                baz &&
                snaff
-# LINT: missing "&&" on ')'
+# LINT: missing "&&" on ")"
        )
        fuzzy
 )
index c8165ad19ec5da791ac15fe5800d374885057633..41a48adaa2b8ff62784f9fe022281e094063e96a 100644 (file)
@@ -3,10 +3,10 @@
        (
                echo a &&
                echo b
->>     ) >file &&
+       ) >file &&
        cd foo &&
        (
                echo a
                echo b
->>     ) >file
->)
+       ) >file
+)
index 998b05a47d300570faf8ff16b584517677a21567..440ee9992da3491a85fae254bdb7eb14176d4a29 100644 (file)
@@ -7,7 +7,6 @@
 
        cd foo &&
        (
-# LINT: nested multi-line subshell not presently checked for missing "&&"
                echo a
                echo b
        ) >file
diff --git a/t/chainlint/not-heredoc.expect b/t/chainlint/not-heredoc.expect
new file mode 100644 (file)
index 0000000..2e9bb13
--- /dev/null
@@ -0,0 +1,14 @@
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+       echo "<<<<<<< ours" &&
+       echo ourside &&
+       echo "=======" &&
+       echo theirside &&
+       echo ">>>>>>> theirs" ?!AMP?!
+       poodle
+) >merged
diff --git a/t/chainlint/not-heredoc.test b/t/chainlint/not-heredoc.test
new file mode 100644 (file)
index 0000000..9aa5734
--- /dev/null
@@ -0,0 +1,16 @@
+# LINT: "<< ours" inside string is not here-doc
+echo "<<<<<<< ours" &&
+echo ourside &&
+echo "=======" &&
+echo theirside &&
+echo ">>>>>>> theirs" &&
+
+(
+# LINT: "<< ours" inside string is not here-doc
+       echo "<<<<<<< ours" &&
+       echo ourside &&
+       echo "=======" &&
+       echo theirside &&
+       echo ">>>>>>> theirs"
+       poodle
+) >merged
index 237f22734963d62c7dafb25883530c73f730b32b..57a7a444c15033cd817502ea4ce9c4b833567acb 100644 (file)
@@ -2,8 +2,8 @@
 (foo && bar) |
 (foo && bar) >baz &&
 
-?!SEMI?!(foo; bar) &&
-?!SEMI?!(foo; bar) |
-?!SEMI?!(foo; bar) >baz
+(foo; ?!AMP?! bar) &&
+(foo; ?!AMP?! bar) |
+(foo; ?!AMP?! bar) >baz &&
 
 (foo "bar; baz")
index ec9acb98253de1e3c3f94c9843142b45745088b1..be9858fa29efbe862bf07269a35abe7324568e22 100644 (file)
@@ -3,10 +3,10 @@
 (foo && bar) |
 (foo && bar) >baz &&
 
-# LINT: top-level one-liner subshell missing internal "&&"
+# LINT: top-level one-liner subshell missing internal "&&" and broken &&-chain
 (foo; bar) &&
 (foo; bar) |
-(foo; bar) >baz
+(foo; bar) >baz &&
 
 # LINT: ";" in string not misinterpreted as broken &&-chain
 (foo "bar; baz")
index 98b3d881fda9a8d488fe6bbd5ffe7cdf263fb60a..1290fd1ff27153f8b2daabb1e5fdec711b530ab2 100644 (file)
@@ -1,4 +1,4 @@
 (
        p4 print -1 //depot/fiddle#42 >file &&
        foobar
->)
+)
index 211b901dbc423086a6b6ebfa80a695f5f21b8929..2cfc0282970db02dd37eaf1c0c079e22a233cef1 100644 (file)
@@ -3,6 +3,6 @@
        bar |
        baz &&
        fish |
-?!AMP?!        cow
+       cow ?!AMP?!
        sunder
->)
+)
index e6af4de91672f9bb9736dc598ef49e3557672b3d..dd82534c6678624eb259c70d51f7a61fab603994 100644 (file)
@@ -4,7 +4,7 @@
        bar |
        baz &&
 
-# LINT: final line of pipe sequence ('cow') lacking "&&"
+# LINT: final line of pipe sequence ("cow") lacking "&&"
        fish |
        cow
 
index 1d79384606d2a2f3d3ceb72a9cfaa3ee7adbc95e..ed0b3707ae90139de0aec8e4ccd8d08301cff1dc 100644 (file)
@@ -1,20 +1,19 @@
 (
-?!AMP?!?!SEMI?!        cat foo ; echo bar
-?!SEMI?!       cat foo ; echo bar
->) &&
+       cat foo ; ?!AMP?! echo bar ?!AMP?!
+       cat foo ; ?!AMP?! echo bar
+) &&
 (
-?!SEMI?!       cat foo ; echo bar &&
-?!SEMI?!       cat foo ; echo bar
->) &&
+       cat foo ; ?!AMP?! echo bar &&
+       cat foo ; ?!AMP?! echo bar
+) &&
 (
        echo "foo; bar" &&
-?!SEMI?!       cat foo; echo bar
->) &&
+       cat foo; ?!AMP?! echo bar
+) &&
 (
-?!SEMI?!       foo;
->) &&
-(
-cd foo &&
+       foo;
+) &&
+(cd foo &&
        for i in a b c; do
-?!SEMI?!               echo;
->      done)
+               echo;
+       done)
index d82c8ebbc00680cf3fc3753d07791865630962ba..67e1192c50ab0c47a74152a4fd59a6d70797b717 100644 (file)
        cat foo; echo bar
 ) &&
 (
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
        foo;
 ) &&
 (cd foo &&
        for i in a b c; do
-# LINT: unnecessary terminating semicolon
+# LINT: semicolon unnecessary but legitimate
                echo;
        done)
index 74723e734043bd6300318b91a4874509c850460c..029d129299a0a5c68d45661071ba3ae144cd5377 100644 (file)
@@ -1,11 +1,10 @@
 (
-       echo wobba             gorgo snoot             wafta snurb &&
-?!AMP?!        cat >bip
-       echo >bop
->) &&
+       echo wobba             gorgo snoot             wafta snurb <<-EOF &&
+       cat <<EOF >bip ?!AMP?!
+       echo <<-EOF >bop
+) &&
 (
-       cat >bup &&
-       cat >bup2 &&
-       cat >bup3 &&
+       cat <<-ARBITRARY >bup &&
+       cat <<-ARBITRARY3 >bup3 &&
        meep
->)
+)
index f6b3ba4214a41de2ca9f9b91d2407e2f9dc4683c..d40eb65583f92f375dddbd3db76a893562724703 100644 (file)
@@ -8,10 +8,10 @@
        nevermore...
        EOF
 
-# LINT: missing "&&" on 'cat'
+# LINT: missing "&&" on "cat"
        cat <<EOF >bip
        fish fly high
-       EOF
+EOF
 
 # LINT: swallow here-doc (EOF is last line of subshell)
        echo <<-\EOF >bop
        glink
        FIZZ
        ARBITRARY
-       cat <<-'ARBITRARY2' >bup2 &&
-       glink
-       FIZZ
-       ARBITRARY2
        cat <<-"ARBITRARY3" >bup3 &&
        glink
        FIZZ
index 51162821d7e146caf59f8c769da22ec5ca07047a..b7015361bfe6a3555d02e97d2bdc0413b8f8c432 100644 (file)
@@ -2,13 +2,13 @@
        (foo && bar) &&
        (foo && bar) |
        (foo && bar) >baz &&
-?!SEMI?!       (foo; bar) &&
-?!SEMI?!       (foo; bar) |
-?!SEMI?!       (foo; bar) >baz &&
+       (foo; ?!AMP?! bar) &&
+       (foo; ?!AMP?! bar) |
+       (foo; ?!AMP?! bar) >baz &&
        (foo || exit 1) &&
        (foo || exit 1) |
        (foo || exit 1) >baz &&
-?!AMP?!        (foo && bar)
-?!AMP?!?!SEMI?!        (foo && bar; baz)
+       (foo && bar) ?!AMP?!
+       (foo && bar; ?!AMP?! baz) ?!AMP?!
        foobar
->)
+)
index c9913429e64b4d4c50318625832366dcf0007b64..1cccc7bf7e1a47d9a922dda821b9eac513c83104 100644 (file)
@@ -1,10 +1,10 @@
 (
        chks="sub1sub2sub3sub4" &&
-       chks_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
+       chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+) &&
        chkms="main-sub1main-sub2main-sub3main-sub4" &&
-       chkms_sub=$(cat | sed 's,^,sub dir/,'
->>) &&
+       chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
+) &&
        subfiles=$(git ls-files) &&
        check_equal "$subfiles" "$chkms$chks"
->)
+)
index 277d8358dfd5f8d104665c420be234d40da9fb4e..02f3129232a0d114bf90211b9a6508385d0115bd 100644 (file)
@@ -3,7 +3,7 @@
 sub2
 sub3
 sub4" &&
-       chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+       chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
 $chks
 TXT
 ) &&
@@ -11,7 +11,7 @@ TXT
 main-sub2
 main-sub3
 main-sub4" &&
-       chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+       chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
 $chkms
 TXT
 ) &&
index 13cff2c0a511c6f81171f667a63632b78d387bad..0d3a9b3d128940a9515d9924964e9efeeaf48152 100644 (file)
@@ -1,11 +1,11 @@
 (
        while true
        do
-?!AMP?!                echo foo
-               cat
-?!AMP?!        done
+               echo foo ?!AMP?!
+               cat <<-EOF
+       done ?!AMP?!
        while true; do
                echo foo &&
                cat bar
        done
->)
+)
index f1df085bf03bf4b417b8231c35302328e27b4dbf..d09fb016e4405c5d9f7378b2ee5b9a634c4a9dc2 100644 (file)
@@ -1,17 +1,17 @@
 (
-# LINT: 'while, 'do', 'done' do not need "&&"
+# LINT: "while", "do", "done" do not need "&&"
        while true
        do
-# LINT: missing "&&" on 'echo'
+# LINT: missing "&&" on "echo"
                echo foo
 # LINT: last statement of while does not need "&&"
                cat <<-\EOF
                bar
                EOF
-# LINT: missing "&&" on 'done'
+# LINT: missing "&&" on "done"
        done
 
-# LINT: 'do' on same line as 'while'
+# LINT: "do" on same line as "while"
        while true; do
                echo foo &&
                cat bar
index 7b4278462bb7166522369aa9d74147fa36c03835..e37396dd9c2c2fe01b9bf2acc8d6a2c6456869fc 100644 (file)
@@ -3,6 +3,7 @@
 
 #if defined(GIT_WINDOWS_NATIVE)
 #include "lazyload.h"
+#include <winnt.h>
 
 static int cmd_sync(void)
 {
@@ -86,7 +87,8 @@ static int cmd_dropcaches(void)
 {
        HANDLE hProcess = GetCurrentProcess();
        HANDLE hToken;
-       DECLARE_PROC_ADDR(ntdll.dll, DWORD, NtSetSystemInformation, INT, PVOID, ULONG);
+       DECLARE_PROC_ADDR(ntdll.dll, DWORD, NTAPI, NtSetSystemInformation, INT, PVOID,
+               ULONG);
        SYSTEM_MEMORY_LIST_COMMAND command;
        int status;
 
index 0d9f08931a15f10a546ea2a9840f733cfb9955e0..b736ef16421ba14e81f8e8c1654d936051efecbb 100644 (file)
@@ -1,83 +1,39 @@
 #include "test-tool.h"
 #include "cache.h"
 #include "config.h"
-#include "blob.h"
-#include "commit.h"
-#include "tree.h"
-#include "sparse-index.h"
-
-static void print_cache_entry(struct cache_entry *ce)
-{
-       const char *type;
-       printf("%06o ", ce->ce_mode & 0177777);
-
-       if (S_ISSPARSEDIR(ce->ce_mode))
-               type = tree_type;
-       else if (S_ISGITLINK(ce->ce_mode))
-               type = commit_type;
-       else
-               type = blob_type;
-
-       printf("%s %s\t%s\n",
-              type,
-              oid_to_hex(&ce->oid),
-              ce->name);
-}
-
-static void print_cache(struct index_state *istate)
-{
-       int i;
-       for (i = 0; i < istate->cache_nr; i++)
-               print_cache_entry(istate->cache[i]);
-}
 
 int cmd__read_cache(int argc, const char **argv)
 {
-       struct repository *r = the_repository;
        int i, cnt = 1;
        const char *name = NULL;
-       int table = 0, expand = 0;
 
        initialize_the_repository();
 
-       for (++argv, --argc; *argv && starts_with(*argv, "--"); ++argv, --argc) {
-               if (skip_prefix(*argv, "--print-and-refresh=", &name))
-                       continue;
-               if (!strcmp(*argv, "--table"))
-                       table = 1;
-               else if (!strcmp(*argv, "--expand"))
-                       expand = 1;
+       if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) {
+               argc--;
+               argv++;
        }
 
-       if (argc == 1)
-               cnt = strtol(argv[0], NULL, 0);
+       if (argc == 2)
+               cnt = strtol(argv[1], NULL, 0);
        setup_git_directory();
        git_config(git_default_config, NULL);
 
-       prepare_repo_settings(r);
-       r->settings.command_requires_full_index = 0;
-
        for (i = 0; i < cnt; i++) {
-               repo_read_index(r);
-
-               if (expand)
-                       ensure_full_index(r->index);
-
+               read_cache();
                if (name) {
                        int pos;
 
-                       refresh_index(r->index, REFRESH_QUIET,
+                       refresh_index(&the_index, REFRESH_QUIET,
                                      NULL, NULL, NULL);
-                       pos = index_name_pos(r->index, name, strlen(name));
+                       pos = index_name_pos(&the_index, name, strlen(name));
                        if (pos < 0)
                                die("%s not in index", name);
                        printf("%s is%s up to date\n", name,
-                              ce_uptodate(r->index->cache[pos]) ? "" : " not");
+                              ce_uptodate(the_index.cache[pos]) ? "" : " not");
                        write_file(name, "%d\n", i);
                }
-               if (table)
-                       print_cache(r->index);
-               discard_index(r->index);
+               discard_cache();
        }
        return 0;
 }
index 73461c29d3ce2797592248fc27328802cac94d62..3e4ddaee70557690e14c09ad5da34ddbab4a781a 100644 (file)
@@ -5,6 +5,48 @@
 #include "object-store.h"
 #include "repository.h"
 
+struct flag_definition {
+       const char *name;
+       uint64_t mask;
+};
+
+#define FLAG_DEF(x)     \
+       {               \
+#x, (x) \
+       }
+
+static unsigned int parse_flags(const char *str, struct flag_definition *defs)
+{
+       struct string_list masks = STRING_LIST_INIT_DUP;
+       int i = 0;
+       unsigned int result = 0;
+
+       if (!strcmp(str, "0"))
+               return 0;
+
+       string_list_split(&masks, str, ',', 64);
+       for (; i < masks.nr; i++) {
+               const char *name = masks.items[i].string;
+               struct flag_definition *def = defs;
+               int found = 0;
+               while (def->name) {
+                       if (!strcmp(def->name, name)) {
+                               result |= def->mask;
+                               found = 1;
+                               break;
+                       }
+                       def++;
+               }
+               if (!found)
+                       die("unknown flag \"%s\"", name);
+       }
+
+       string_list_clear(&masks, 0);
+       return result;
+}
+
+static struct flag_definition empty_flags[] = { { NULL, 0 } };
+
 static const char *notnull(const char *arg, const char *name)
 {
        if (!arg)
@@ -12,9 +54,10 @@ static const char *notnull(const char *arg, const char *name)
        return arg;
 }
 
-static unsigned int arg_flags(const char *arg, const char *name)
+static unsigned int arg_flags(const char *arg, const char *name,
+                             struct flag_definition *defs)
 {
-       return atoi(notnull(arg, name));
+       return parse_flags(notnull(arg, name), defs);
 }
 
 static const char **get_store(const char **argv, struct ref_store **refs)
@@ -64,10 +107,13 @@ static const char **get_store(const char **argv, struct ref_store **refs)
        return argv + 1;
 }
 
+static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
+                                              FLAG_DEF(PACK_REFS_ALL),
+                                              { NULL, 0 } };
 
 static int cmd_pack_refs(struct ref_store *refs, const char **argv)
 {
-       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
 
        return refs_pack_refs(refs, flags);
 }
@@ -81,16 +127,27 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
        return refs_create_symref(refs, refname, target, logmsg);
 }
 
+static struct flag_definition transaction_flags[] = {
+       FLAG_DEF(REF_NO_DEREF),
+       FLAG_DEF(REF_FORCE_CREATE_REFLOG),
+       FLAG_DEF(REF_SKIP_OID_VERIFICATION),
+       FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION),
+       { NULL, 0 }
+};
+
 static int cmd_delete_refs(struct ref_store *refs, const char **argv)
 {
-       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
        const char *msg = *argv++;
        struct string_list refnames = STRING_LIST_INIT_NODUP;
+       int result;
 
        while (*argv)
                string_list_append(&refnames, *argv++);
 
-       return refs_delete_refs(refs, msg, &refnames, flags);
+       result = refs_delete_refs(refs, msg, &refnames, flags);
+       string_list_clear(&refnames, 0);
+       return result;
 }
 
 static int cmd_rename_ref(struct ref_store *refs, const char **argv)
@@ -120,7 +177,7 @@ static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
 {
        struct object_id oid = *null_oid();
        const char *refname = notnull(*argv++, "refname");
-       int resolve_flags = arg_flags(*argv++, "resolve-flags");
+       int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags);
        int flags;
        const char *ref;
        int ignore_errno;
@@ -152,9 +209,9 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
                       const char *committer, timestamp_t timestamp,
                       int tz, const char *msg, void *cb_data)
 {
-       printf("%s %s %s %"PRItime" %d %s\n",
-              oid_to_hex(old_oid), oid_to_hex(new_oid),
-              committer, timestamp, tz, msg);
+       printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid),
+              oid_to_hex(new_oid), committer, timestamp, tz,
+              *msg == '\n' ? "" : "\t", msg);
        return 0;
 }
 
@@ -208,11 +265,11 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv)
        const char *msg = notnull(*argv++, "msg");
        const char *refname = notnull(*argv++, "refname");
        const char *sha1_buf = notnull(*argv++, "old-sha1");
-       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
        struct object_id old_oid;
 
        if (get_oid_hex(sha1_buf, &old_oid))
-               die("not sha-1");
+               die("cannot parse %s as %s", sha1_buf, the_hash_algo->name);
 
        return refs_delete_ref(refs, msg, refname, &old_oid, flags);
 }
@@ -223,13 +280,14 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
        const char *refname = notnull(*argv++, "refname");
        const char *new_sha1_buf = notnull(*argv++, "new-sha1");
        const char *old_sha1_buf = notnull(*argv++, "old-sha1");
-       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
        struct object_id old_oid;
        struct object_id new_oid;
 
-       if (get_oid_hex(old_sha1_buf, &old_oid) ||
-           get_oid_hex(new_sha1_buf, &new_oid))
-               die("not sha-1");
+       if (get_oid_hex(old_sha1_buf, &old_oid))
+               die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+       if (get_oid_hex(new_sha1_buf, &new_oid))
+               die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
 
        return refs_update_ref(refs, msg, refname,
                               &new_oid, &old_oid,
index 3c4fb862234da8bd50bbe820d8e6510e0b4630e9..913775a14b758e5b4dd89cbce9ead4ff104abbc1 100644 (file)
@@ -31,7 +31,7 @@ static int parallel_next(struct child_process *cp,
        if (number_callbacks >= 4)
                return 0;
 
-       strvec_pushv(&cp->args, d->argv);
+       strvec_pushv(&cp->args, d->args.v);
        strbuf_addstr(err, "preloaded output of a child\n");
        number_callbacks++;
        return 1;
@@ -274,7 +274,7 @@ static int quote_stress_test(int argc, const char **argv)
                if (i < skip)
                        continue;
 
-               cp.argv = args.v;
+               strvec_pushv(&cp.args, args.v);
                strbuf_reset(&out);
                if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
                        return error("Failed to spawn child process");
@@ -396,7 +396,7 @@ int cmd__run_command(int argc, const char **argv)
        }
        if (argc < 3)
                return 1;
-       proc.argv = (const char **)argv + 2;
+       strvec_pushv(&proc.args, (const char **)argv + 2);
 
        if (!strcmp(argv[1], "start-command-ENOENT")) {
                if (start_command(&proc) < 0 && errno == ENOENT)
@@ -408,7 +408,8 @@ int cmd__run_command(int argc, const char **argv)
                exit(run_command(&proc));
 
        jobs = atoi(argv[2]);
-       proc.argv = (const char **)argv + 3;
+       strvec_clear(&proc.args);
+       strvec_pushv(&proc.args, (const char **)argv + 3);
 
        if (!strcmp(argv[1], "run-command-parallel"))
                exit(run_processes_parallel(jobs, parallel_next,
index 92b69de635296d32d38d1f7d7589d5f8b6fbc296..ff22f2fa2c57efbda1246f1aab0da494eb026fd1 100644 (file)
@@ -15,6 +15,6 @@ int cmd__subprocess(int argc, const char **argv)
                argv++;
        }
        cp.git_cmd = 1;
-       cp.argv = (const char **)argv + 1;
+       strvec_pushv(&cp.args, (const char **)argv + 1);
        return run_command(&cp);
 }
index f93633f895a1926aa6ea4ca712305cccb5826a2d..59b124bb5f147f3a8cf1876230306c4fc5bdd60f 100644 (file)
@@ -262,8 +262,9 @@ static int print_usage(void)
  *    [] the "cmd_name" event has been generated.
  *    [] this writes various "def_param" events for interesting config values.
  *
- * We further assume that if we return (rather than exit()), trace2_cmd_exit()
- * will be called by test-tool.c:cmd_main().
+ * We return from here and let test-tool.c::cmd_main() pass the exit
+ * code to common-main.c::main(), which will use it to call
+ * trace2_cmd_exit().
  */
 int cmd__trace2(int argc, const char **argv)
 {
index a3f285f5156670bb956494f2778891145339eaba..3e7ee1386aa9fcb572652dc38768d099cdfc8368 100644 (file)
@@ -90,7 +90,12 @@ test_lazy_prereq RFC1991 '
 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_EXPIRED="${GNUPGHOME}/expired_ssh_signing_key"
+GPGSSH_KEY_NOTYETVALID="${GNUPGHOME}/notyetvalid_ssh_signing_key"
+GPGSSH_KEY_TIMEBOXEDVALID="${GNUPGHOME}/timeboxed_valid_ssh_signing_key"
+GPGSSH_KEY_TIMEBOXEDINVALID="${GNUPGHOME}/timeboxed_invalid_ssh_signing_key"
 GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key"
+GPGSSH_KEY_ECDSA="${GNUPGHOME}/ecdsa_ssh_signing_key"
 GPGSSH_KEY_PASSPHRASE="super_secret"
 GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile"
 
@@ -105,21 +110,63 @@ test_lazy_prereq GPGSSH '
        echo $ssh_version | grep -q "find-principals:missing signature file"
        test $? = 0 || exit 1;
 
-       # some broken versions of ssh-keygen segfault on find-principals;
-       # avoid testing with them.
-       ssh-keygen -Y find-principals -f /dev/null -s /dev/null
-       test $? = 139 && exit 1
-
+       # Setup some keys and an allowed signers file
        mkdir -p "${GNUPGHOME}" &&
        chmod 0700 "${GNUPGHOME}" &&
        (setfacl -k "${GNUPGHOME}" 2>/dev/null || true) &&
        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
+       ssh-keygen -t ecdsa -N "" -f "${GPGSSH_KEY_ECDSA}" >/dev/null &&
+       ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null &&
+
+       cat >"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF &&
+       "principal with number 1" $(cat "${GPGSSH_KEY_PRIMARY}.pub")"
+       "principal with number 2" $(cat "${GPGSSH_KEY_SECONDARY}.pub")"
+       "principal with number 3" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")"
+       "principal with number 4" $(cat "${GPGSSH_KEY_ECDSA}.pub")"
+       EOF
+
+       # Verify if at least one key and ssh-keygen works as expected
+       echo "testpayload" |
+       ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_PRIMARY}" >gpgssh_prereq.sig &&
+       ssh-keygen -Y find-principals -f "${GPGSSH_ALLOWED_SIGNERS}" -s gpgssh_prereq.sig &&
+       echo "testpayload" |
+       ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with number 1" -s gpgssh_prereq.sig
+'
+
+test_lazy_prereq GPGSSH_VERIFYTIME '
+       # Check if ssh-keygen has a verify-time option by passing an invalid date to it
+       ssh-keygen -Overify-time=INVALID -Y check-novalidate -s doesnotmatter 2>&1 | grep -q -F "Invalid \"verify-time\"" &&
+
+       # Set up keys with key lifetimes
+       ssh-keygen -t ed25519 -N "" -C "timeboxed valid key" -f "${GPGSSH_KEY_TIMEBOXEDVALID}" >/dev/null &&
+       key_valid=$(cat "${GPGSSH_KEY_TIMEBOXEDVALID}.pub") &&
+       ssh-keygen -t ed25519 -N "" -C "timeboxed invalid key" -f "${GPGSSH_KEY_TIMEBOXEDINVALID}" >/dev/null &&
+       key_invalid=$(cat "${GPGSSH_KEY_TIMEBOXEDINVALID}.pub") &&
+       ssh-keygen -t ed25519 -N "" -C "expired key" -f "${GPGSSH_KEY_EXPIRED}" >/dev/null &&
+       key_expired=$(cat "${GPGSSH_KEY_EXPIRED}.pub") &&
+       ssh-keygen -t ed25519 -N "" -C "not yet valid key" -f "${GPGSSH_KEY_NOTYETVALID}" >/dev/null &&
+       key_notyetvalid=$(cat "${GPGSSH_KEY_NOTYETVALID}.pub") &&
+
+       # Timestamps outside of test_tick span
+       ts2005a=20050401000000 ts2005b=200504020000 &&
+       # Timestamps within test_tick span
+       ts2005c=20050407000000 ts2005d=200504100000 &&
+       # Definitely not yet valid / expired timestamps
+       ts2000=20000101000000 ts2999=29990101000000 &&
+
+       cat >>"${GPGSSH_ALLOWED_SIGNERS}" <<-EOF &&
+       "timeboxed valid key" valid-after="$ts2005c",valid-before="$ts2005d" $key_valid"
+       "timeboxed invalid key" valid-after="$ts2005a",valid-before="$ts2005b" $key_invalid"
+       "principal with expired key" valid-before="$ts2000" $key_expired"
+       "principal with not yet valid key" valid-after="$ts2999" $key_notyetvalid"
+       EOF
+
+       # and verify ssh-keygen verifies the key lifetime
+       echo "testpayload" |
+       ssh-keygen -Y sign -n "git" -f "${GPGSSH_KEY_EXPIRED}" >gpgssh_verifytime_prereq.sig &&
+       ! (ssh-keygen -Y verify -n "git" -f "${GPGSSH_ALLOWED_SIGNERS}" -I "principal with expired key" -s gpgssh_verifytime_prereq.sig)
 '
 
 sanitize_pgp() {
index 3aa7a3ffd8b0103752987872c84e75ea2b299b9e..e5eb28df4ef6cc80fe24c728595d683ab3d00b85 100644 (file)
@@ -3,7 +3,7 @@
 test_expect_success 'determine default pager' '
        test_might_fail git config --unset core.pager &&
        less=$(
-               unset PAGER GIT_PAGER;
+               sane_unset PAGER GIT_PAGER &&
                git var GIT_PAGER
        ) &&
        test -n "$less"
index 0b0aa9858f5dbf16f5c98639e25a8891846fe300..ca58d6c9b59493a37d4bf301e2c2d099fd841e91 100755 (executable)
@@ -24,17 +24,17 @@ test_perf_default_repo
 test_expect_success "setup repo" '
        if git rev-parse --verify refs/heads/p0006-ballast^{commit}
        then
-               echo Assuming synthetic repo from many-files.sh
-               git branch br_base            master
-               git branch br_ballast         p0006-ballast
-               git config --local core.sparsecheckout 1
+               echo Assuming synthetic repo from many-files.sh &&
+               git branch br_base            master &&
+               git branch br_ballast         p0006-ballast &&
+               git config --local core.sparsecheckout 1 &&
                cat >.git/info/sparse-checkout <<-EOF
                /*
                !ballast/*
                EOF
        else
-               echo Assuming non-synthetic repo...
-               git branch br_base            $(git rev-list HEAD | tail -n 1)
+               echo Assuming non-synthetic repo... &&
+               git branch br_base            $(git rev-list HEAD | tail -n 1) &&
                git branch br_ballast         HEAD
        fi &&
        git checkout -q br_ballast &&
index 78cc23fe2f32eff777aab058b12d109ac81bc83c..900b385c4bbc2d0bf9a803f06b48fe1c25909e86 100755 (executable)
@@ -24,21 +24,21 @@ test_perf_default_repo
 test_expect_success "setup repo" '
        if git rev-parse --verify refs/heads/p0006-ballast^{commit}
        then
-               echo Assuming synthetic repo from many-files.sh
-               git branch br_base            master
-               git branch br_ballast         p0006-ballast^
-               git branch br_ballast_alias   p0006-ballast^
-               git branch br_ballast_plus_1  p0006-ballast
-               git config --local core.sparsecheckout 1
+               echo Assuming synthetic repo from many-files.sh &&
+               git branch br_base            master &&
+               git branch br_ballast         p0006-ballast^ &&
+               git branch br_ballast_alias   p0006-ballast^ &&
+               git branch br_ballast_plus_1  p0006-ballast &&
+               git config --local core.sparsecheckout 1 &&
                cat >.git/info/sparse-checkout <<-EOF
                /*
                !ballast/*
                EOF
        else
-               echo Assuming non-synthetic repo...
-               git branch br_base            $(git rev-list HEAD | tail -n 1)
-               git branch br_ballast         HEAD^ || error "no ancestor commit from current head"
-               git branch br_ballast_alias   HEAD^
+               echo Assuming non-synthetic repo... &&
+               git branch br_base            $(git rev-list HEAD | tail -n 1) &&
+               git branch br_ballast         HEAD^ || error "no ancestor commit from current head" &&
+               git branch br_ballast_alias   HEAD^ &&
                git branch br_ballast_plus_1  HEAD
        fi &&
        git checkout -q br_ballast &&
index 09595264f09fa4b1dc28b3e5d2a3d6482d998bcc..25d8ff7443e77cafbcda0a5b08ed2335d2e798f9 100755 (executable)
@@ -9,8 +9,8 @@ test_perf_default_repo
 test_expect_success "setup repo" '
        if git rev-parse --verify refs/heads/p0006-ballast^{commit}
        then
-               echo Assuming synthetic repo from many-files.sh
-               git config --local core.sparsecheckout 1
+               echo Assuming synthetic repo from many-files.sh &&
+               git config --local core.sparsecheckout 1 &&
                cat >.git/info/sparse-checkout <<-EOF
                /*
                !ballast/*
index dd18a9ce2b115fbf92a4787f5336cb4c9f549d18..439e9c8e3c6484fe3a6f34a4f5903b07397d0c1f 100755 (executable)
@@ -19,9 +19,9 @@ test_expect_success 'setup' '
                printf "a" >>refname &&
                for j in $(test_seq 1 $i)
                do
-                       printf "a*" >>refglob.$i
+                       printf "a*" >>refglob.$i || return 1
                done &&
-               echo b >>refglob.$i
+               echo b >>refglob.$i || return 1
        done &&
        test_commit test $(cat refname).t "" $(cat refname).t
 '
index dda8a7486634488d65ca30ca6da1e795c4eb3a3a..a75969cbb1571ba5da46438c94397ee2130b9be9 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success "setup" '
        do
                printf "start\ncreate refs/heads/%d PRE\ncommit\n" $i &&
                printf "start\nupdate refs/heads/%d POST PRE\ncommit\n" $i &&
-               printf "start\ndelete refs/heads/%d POST\ncommit\n" $i
+               printf "start\ndelete refs/heads/%d POST\ncommit\n" $i || return 1
        done >instructions
 '
 
@@ -22,7 +22,7 @@ test_perf "update-ref" '
        do
                git update-ref refs/heads/branch PRE &&
                git update-ref refs/heads/branch POST PRE &&
-               git update-ref -d refs/heads/branch
+               git update-ref -d refs/heads/branch || return 1
        done
 '
 
index c2b97d2487bbcabf3ee1becc7bb8b7c0fb0ac784..f767d834f2ea08fe1415a24b4c20de63decf43e2 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success "setup $n bad commits" '
                echo "committer C <c@example.com> 1234567890 +0000" &&
                echo "data <<EOF" &&
                echo "$i.Q." &&
-               echo "EOF"
+               echo "EOF" || return 1
        done | q_to_nul | git fast-import
 '
 
index bfd332120c8df8dc27c77a2b74c1e2bf02f9e1d0..cb777c74a24f55624604f782675d3356365a37fb 100755 (executable)
@@ -113,5 +113,9 @@ test_perf_on_all git checkout -f -
 test_perf_on_all git reset
 test_perf_on_all git reset --hard
 test_perf_on_all git reset -- does-not-exist
+test_perf_on_all git diff
+test_perf_on_all git diff --cached
+test_perf_on_all git blame $SPARSE_CONE/a
+test_perf_on_all git blame $SPARSE_CONE/f3/a
 
 test_done
index 43d5a34e8cadc8c9284e6586f426de5fa9857a9f..e6b0277729b19631ad49740070b092ff08121972 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'setup rebasing on top of a lot of changes' '
                git add unrelated-file$i &&
                test_tick &&
                git commit -m commit$i-reverse unrelated-file$i ||
-               break
+               return 1
        done &&
        git checkout to-rebase &&
        test_commit our-patch interesting-file
diff --git a/t/perf/p4002-diff-color-moved.sh b/t/perf/p4002-diff-color-moved.sh
new file mode 100755 (executable)
index 0000000..ab2af93
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='Tests diff --color-moved performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# The endpoints of the diff can be customized by setting TEST_REV_A
+# and TEST_REV_B in the environment when running this test.
+
+rev="${TEST_REV_A:-v2.28.0}"
+if ! rev_a="$(git rev-parse --quiet --verify "$rev")"
+then
+       skip_all="skipping because '$rev' was not found. \
+                 Use TEST_REV_A and TEST_REV_B to set the revs to use"
+       test_done
+fi
+rev="${TEST_REV_B:-v2.29.0}"
+if ! rev_b="$(git rev-parse --quiet --verify "$rev")"
+then
+       skip_all="skipping because '$rev' was not found. \
+                 Use TEST_REV_A and TEST_REV_B to set the revs to use"
+       test_done
+fi
+
+GIT_PAGER_IN_USE=1
+test_export GIT_PAGER_IN_USE rev_a rev_b
+
+test_perf 'diff --no-color-moved --no-color-moved-ws large change' '
+       git diff --no-color-moved --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved --no-color-moved-ws large change' '
+       git diff --color-moved=zebra --no-color-moved-ws $rev_a $rev_b
+'
+
+test_perf 'diff --color-moved-ws=allow-indentation-change large change' '
+       git diff --color-moved=zebra --color-moved-ws=allow-indentation-change \
+               $rev_a $rev_b
+'
+
+test_perf 'log --no-color-moved --no-color-moved-ws' '
+       git log --no-color-moved --no-color-moved-ws --no-merges --patch \
+               -n1000 $rev_b
+'
+
+test_perf 'log --color-moved --no-color-moved-ws' '
+       git log --color-moved=zebra --no-color-moved-ws --no-merges --patch \
+               -n1000 $rev_b
+'
+
+test_perf 'log --color-moved-ws=allow-indentation-change' '
+       git log --color-moved=zebra --color-moved-ws=allow-indentation-change \
+               --no-merges --patch -n1000 $rev_b
+'
+
+test_done
index 228593d9ad6b3a5c9a0b45eb5814a3a8746d42bc..c16f6a3ff698c088db427ae9d7b0a8f16f59a8c6 100755 (executable)
@@ -21,8 +21,8 @@ test_expect_success 'set up thread-counting tests' '
        threads= &&
        while test $t -gt 0
        do
-               threads="$t $threads"
-               t=$((t / 2))
+               threads="$t $threads" &&
+               t=$((t / 2)) || return 1
        done
 '
 
index 35c0cbdf49fbc14d659f9abff7e8624a6ae14cec..af173a7b73e398c33b5629717cc890ea14a5fa33 100755 (executable)
@@ -126,11 +126,11 @@ done
 # Measure pack loading with 10,000 packs.
 test_expect_success 'generate lots of packs' '
        for i in $(test_seq 10000); do
-               echo "blob"
-               echo "data <<EOF"
-               echo "blob $i"
-               echo "EOF"
-               echo "checkpoint"
+               echo "blob" &&
+               echo "data <<EOF" &&
+               echo "blob $i" &&
+               echo "EOF" &&
+               echo "checkpoint" || return 1
        done |
        git -c fastimport.unpackLimit=0 fast-import
 '
index 5eb5044a103cabd1d3dba4980a00f0d66d4818a5..c8be58f3c763beb7b890925251300494365b3900 100755 (executable)
@@ -119,10 +119,10 @@ test_expect_success "one time repo setup" '
        fi &&
 
        mkdir 1_file 10_files 100_files 1000_files 10000_files &&
-       for i in $(test_seq 1 10); do touch 10_files/$i; done &&
-       for i in $(test_seq 1 100); do touch 100_files/$i; done &&
-       for i in $(test_seq 1 1000); do touch 1000_files/$i; done &&
-       for i in $(test_seq 1 10000); do touch 10000_files/$i; done &&
+       for i in $(test_seq 1 10); do touch 10_files/$i || return 1; done &&
+       for i in $(test_seq 1 100); do touch 100_files/$i || return 1; done &&
+       for i in $(test_seq 1 1000); do touch 1000_files/$i || return 1; done &&
+       for i in $(test_seq 1 10000); do touch 10000_files/$i || return 1; done &&
        git add 1_file 10_files 100_files 1000_files 10000_files &&
        git commit -qm "Add files" &&
 
index 780a7402d5191f72f7b50a48da4adf665090349f..407252bac70fbfc7b3a30fe29833f5944b3de8a5 100644 (file)
@@ -161,7 +161,7 @@ test_run_perf_ () {
        test_cleanup=:
        test_export_="test_cleanup"
        export test_cleanup test_export_
-       "$GTIME" -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+       "$GTIME" -f "%E %U %S" -o test_time.$i "$TEST_SHELL_PATH" -c '
 . '"$TEST_DIRECTORY"/test-lib-functions.sh'
 test_export () {
        test_export_="$test_export_ $*"
index 7603ad2f82b28264ce19ae77d6bbc250fb9a94fa..3235ab4d53c9dfc282155f1da2caa44d7f40962c 100755 (executable)
@@ -331,7 +331,7 @@ test_expect_success 'init with separate gitdir' '
 
 test_expect_success 'explicit bare & --separate-git-dir incompatible' '
        test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err &&
-       test_i18ngrep "mutually exclusive" err
+       test_i18ngrep "cannot be used together" err
 '
 
 test_expect_success 'implicit bare & --separate-git-dir incompatible' '
index a5ec6a0315ca09893e5c2b7403e91f2a0be0109c..eba75a2490ce7e0918e6a981d42ec8f5d9fd9b2d 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
 '
 
 test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
-       OUT=$( ((trap "" PIPE; large_git; echo $? 1>&3) | :) 3>&1 ) &&
+       OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
        test_match_signal 13 "$OUT"
 '
 
index 42d2314804966fae851eb3973310d615d5e1e053..5575dade8eee8184b24d6685213b9a30dcee4364 100755 (executable)
@@ -200,7 +200,7 @@ test_expect_success 'setup' '
        do
                : >$dir/not-ignored &&
                : >$dir/ignored-and-untracked &&
-               : >$dir/ignored-but-in-index
+               : >$dir/ignored-but-in-index || return 1
        done &&
        git add -f ignored-but-in-index a/ignored-but-in-index &&
        cat <<-\EOF >a/.gitignore &&
index e094975b13bf270992d9cac8999bb0972a633198..1cb6aa6824321656264e427f299899acf0754357 100755 (executable)
@@ -220,7 +220,7 @@ test_expect_success 'grow / shrink' '
        for n in $(test_seq 51)
        do
                echo put key$n value$n >> in &&
-               echo NULL >> expect
+               echo NULL >> expect || return 1
        done &&
        echo size >> in &&
        echo 64 51 >> expect &&
@@ -231,7 +231,7 @@ test_expect_success 'grow / shrink' '
        for n in $(test_seq 12)
        do
                echo remove key$n >> in &&
-               echo value$n >> expect
+               echo value$n >> expect || return 1
        done &&
        echo size >> in &&
        echo 256 40 >> expect &&
index 4125ab8b88403d2924e46e6c1fe0658e1ec8dba9..35cc8c3b39896cb5caf6740d6847f5dec66e1dea 100755 (executable)
@@ -23,10 +23,10 @@ test_expect_success setup '
 
        git config core.autocrlf false &&
 
-       for w in Hello world how are you; do echo $w; done >one &&
+       test_write_lines Hello world how are you >one &&
        mkdir dir &&
-       for w in I am very very fine thank you; do echo $w; done >dir/two &&
-       for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&
+       test_write_lines I am very very fine thank you >dir/two &&
+       test_write_lines Oh here is NULQin text here | q_to_nul >three &&
        git add . &&
 
        git commit -m initial &&
@@ -36,7 +36,7 @@ test_expect_success setup '
        two=$(git rev-parse HEAD:dir/two) &&
        three=$(git rev-parse HEAD:three) &&
 
-       for w in Some extra lines here; do echo $w; done >>one &&
+       test_write_lines Some extra lines here >>one &&
        git diff >patch.file &&
        patched=$(git hash-object --stdin <one) &&
        git read-tree --reset -u HEAD
@@ -47,7 +47,7 @@ test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
        git config core.autocrlf input &&
        git config core.safecrlf true &&
 
-       for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+       test_write_lines I am all CRLF | append_cr >allcrlf &&
        test_must_fail git add allcrlf
 '
 
@@ -56,7 +56,7 @@ test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' '
        git config core.autocrlf input &&
        git config core.safecrlf true &&
 
-       for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+       test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
        test_must_fail git add mixed
 '
 
@@ -65,7 +65,7 @@ test_expect_success 'safecrlf: autocrlf=true, all LF' '
        git config core.autocrlf true &&
        git config core.safecrlf true &&
 
-       for w in I am all LF; do echo $w; done >alllf &&
+       test_write_lines I am all LF >alllf &&
        test_must_fail git add alllf
 '
 
@@ -74,7 +74,7 @@ test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' '
        git config core.autocrlf true &&
        git config core.safecrlf true &&
 
-       for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+       test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
        test_must_fail git add mixed
 '
 
@@ -83,10 +83,10 @@ test_expect_success 'safecrlf: print warning only once' '
        git config core.autocrlf input &&
        git config core.safecrlf warn &&
 
-       for w in I am all LF; do echo $w; done >doublewarn &&
+       test_write_lines I am all LF >doublewarn &&
        git add doublewarn &&
        git commit -m "nowarn" &&
-       for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
+       test_write_lines Oh here is CRLFQ in text | q_to_cr >doublewarn &&
        git add doublewarn 2>err &&
        grep "CRLF will be replaced by LF" err >err.warnings &&
        test_line_count = 1 err.warnings
@@ -104,7 +104,7 @@ test_expect_success 'safecrlf: no warning with safecrlf=false' '
        git config core.autocrlf input &&
        git config core.safecrlf false &&
 
-       for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+       test_write_lines I am all CRLF | append_cr >allcrlf &&
        git add allcrlf 2>err &&
        test_must_be_empty err
 '
@@ -352,9 +352,9 @@ test_expect_success 'setting up for new autocrlf tests' '
        git config core.autocrlf false &&
        git config core.safecrlf false &&
        rm -rf .????* * &&
-       for w in I am all LF; do echo $w; done >alllf &&
-       for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
-       for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+       test_write_lines I am all LF >alllf &&
+       test_write_lines Oh here is CRLFQ in text | q_to_cr >mixed &&
+       test_write_lines I am all CRLF | append_cr >allcrlf &&
        git add -A . &&
        git commit -m "alllf, allcrlf and mixed only" &&
        git tag -a -m "message" autocrlf-checkpoint
index 33dfc9cd562327b725a3338ac0e9b6787f58c9fe..bad37abad2c376182ca0241afcfcb54a3f4f700e 100755 (executable)
@@ -76,13 +76,13 @@ test_expect_success setup '
        git config filter.rot13.clean ./rot13.sh &&
 
        {
-           echo "*.t filter=rot13"
+           echo "*.t filter=rot13" &&
            echo "*.i ident"
        } >.gitattributes &&
 
        {
-           echo a b c d e f g h i j k l m
-           echo n o p q r s t u v w x y z
+           echo a b c d e f g h i j k l m &&
+           echo n o p q r s t u v w x y z &&
            echo '\''$Id$'\''
        } >test &&
        cat test >test.t &&
@@ -118,17 +118,17 @@ test_expect_success check '
 # If an expanded ident ever gets into the repository, we want to make sure that
 # it is collapsed before being expanded again on checkout
 test_expect_success expanded_in_repo '
-       {
-               echo "File with expanded keywords"
-               echo "\$Id\$"
-               echo "\$Id:\$"
-               echo "\$Id: 0000000000000000000000000000000000000000 \$"
-               echo "\$Id: NoSpaceAtEnd\$"
-               echo "\$Id:NoSpaceAtFront \$"
-               echo "\$Id:NoSpaceAtEitherEnd\$"
-               echo "\$Id: NoTerminatingSymbol"
-               echo "\$Id: Foreign Commit With Spaces \$"
-       } >expanded-keywords.0 &&
+       cat >expanded-keywords.0 <<-\EOF &&
+       File with expanded keywords
+       $Id$
+       $Id:$
+       $Id: 0000000000000000000000000000000000000000 $
+       $Id: NoSpaceAtEnd$
+       $Id:NoSpaceAtFront $
+       $Id:NoSpaceAtEitherEnd$
+       $Id: NoTerminatingSymbol
+       $Id: Foreign Commit With Spaces $
+       EOF
 
        {
                cat expanded-keywords.0 &&
@@ -139,17 +139,17 @@ test_expect_success expanded_in_repo '
        git commit -m "File with keywords expanded" &&
        id=$(git rev-parse --verify :expanded-keywords) &&
 
-       {
-               echo "File with expanded keywords"
-               echo "\$Id: $id \$"
-               echo "\$Id: $id \$"
-               echo "\$Id: $id \$"
-               echo "\$Id: $id \$"
-               echo "\$Id: $id \$"
-               echo "\$Id: $id \$"
-               echo "\$Id: NoTerminatingSymbol"
-               echo "\$Id: Foreign Commit With Spaces \$"
-       } >expected-output.0 &&
+       cat >expected-output.0 <<-EOF &&
+       File with expanded keywords
+       \$Id: $id \$
+       \$Id: $id \$
+       \$Id: $id \$
+       \$Id: $id \$
+       \$Id: $id \$
+       \$Id: $id \$
+       \$Id: NoTerminatingSymbol
+       \$Id: Foreign Commit With Spaces \$
+       EOF
        {
                cat expected-output.0 &&
                printf "\$Id: NoTerminatingSymbolAtEOF"
@@ -159,7 +159,7 @@ test_expect_success expanded_in_repo '
                printf "\$Id: NoTerminatingSymbolAtEOF"
        } >expected-output-crlf &&
        {
-               echo "expanded-keywords ident"
+               echo "expanded-keywords ident" &&
                echo "expanded-keywords-crlf ident text eol=crlf"
        } >>.gitattributes &&
 
@@ -285,7 +285,7 @@ test_expect_success 'required filter with absent smudge field' '
 test_expect_success 'filtering large input to small output should use little memory' '
        test_config filter.devnull.clean "cat >/dev/null" &&
        test_config filter.devnull.required true &&
-       for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+       for i in $(test_seq 1 30); do printf "%1048576d" 1 || return 1; done >30MB &&
        echo "30MB filter=devnull" >.gitattributes &&
        GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
 '
@@ -303,7 +303,7 @@ test_expect_success 'filter that does not read is fine' '
 test_expect_success EXPENSIVE 'filter large file' '
        test_config filter.largefile.smudge cat &&
        test_config filter.largefile.clean cat &&
-       for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+       for i in $(test_seq 1 2048); do printf "%1048576d" 1 || return 1; done >2GB &&
        echo "2GB filter=largefile" >.gitattributes &&
        git add 2GB 2>err &&
        test_must_be_empty err &&
@@ -643,7 +643,7 @@ test_expect_success PERL 'required process filter should process multiple packet
                for FILE in "$TEST_ROOT"/*.file
                do
                        cp "$FILE" . &&
-                       rot13.sh <"$FILE" >"$FILE.rot13"
+                       rot13.sh <"$FILE" >"$FILE.rot13" || return 1
                done &&
 
                echo "*.file filter=protocol" >.gitattributes &&
@@ -682,7 +682,7 @@ test_expect_success PERL 'required process filter should process multiple packet
 
                for FILE in *.file
                do
-                       test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
+                       test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE || return 1
                done
        )
 '
index cdcafcdff724e2c13ee056fb59e915a5371ec13c..f426a185bb9b32e732c0404d65237f8278435f0e 100755 (executable)
@@ -15,8 +15,8 @@ test_expect_success setup '
 
        echo "one text" > .gitattributes &&
 
-       for w in Hello world how are you; do echo $w; done >one &&
-       for w in I am very very fine thank you; do echo $w; done >two &&
+       test_write_lines Hello world how are you >one &&
+       test_write_lines I am very very fine thank you >two &&
        git add . &&
 
        git commit -m initial &&
index 34d1061f321fb406a5e3d6058860da957307dfab..71a5d370cc7bc7d369b7e3099c064bc8cb114d7d 100755 (executable)
@@ -216,7 +216,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        mkdir second &&
        ln -s ../first second/other &&
        mkdir third &&
-       dir="$(cd .git; pwd -P)" &&
+       dir="$(cd .git && pwd -P)" &&
        dir2=third/../second/other/.git &&
        test "$dir" = "$(test-tool path-utils real_path $dir2)" &&
        file="$dir"/index &&
@@ -224,7 +224,7 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        basename=blub &&
        test "$dir/$basename" = "$(cd .git && test-tool path-utils real_path "$basename")" &&
        ln -s ../first/file .git/syml &&
-       sym="$(cd first; pwd -P)"/file &&
+       sym="$(cd first && pwd -P)"/file &&
        test "$sym" = "$(test-tool path-utils real_path "$dir2/syml")"
 '
 
index 74cc59bf8a7de8dfe1f4605c5cd1059d889bc4c6..889db508183f7c858c438aa5144629279660e864 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'oidtree insert and contains' '
        EOF
        {
                echoid insert 444 1 2 3 4 5 a b c d e &&
-               echoid contains 44 441 440 444 4440 4444
+               echoid contains 44 441 440 444 4440 4444 &&
                echo clear
        } | test-tool oidtree >actual &&
        test_cmp expect actual
@@ -37,11 +37,11 @@ test_expect_success 'oidtree insert and contains' '
 test_expect_success 'oidtree each' '
        echoid "" 123 321 321 >expect &&
        {
-               echoid insert f 9 8 123 321 a b c d e
-               echo each 12300
-               echo each 3211
-               echo each 3210
-               echo each 32100
+               echoid insert f 9 8 123 321 a b c d e &&
+               echo each 12300 &&
+               echo each 3211 &&
+               echo each 3210 &&
+               echo each 32100 &&
                echo clear
        } | test-tool oidtree >actual &&
        test_cmp expect actual
index a8ab1748796be96865b1d167620c2059171e8ace..6f9a501c72b3a2520fd4d4734b45fe56a2a6d35e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='verify sort functions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'llist_mergesort()' '
index 7e4ab1795fb99890d5adb8b67ebf2d82042de3e5..5945973552a6af6ac1971920b573086b081f6160 100755 (executable)
@@ -84,7 +84,7 @@ test_expect_success 'get bloom filter for commit with 10 changes' '
        mkdir smallDir &&
        for i in $(test_seq 0 9)
        do
-               echo $i >smallDir/$i
+               echo $i >smallDir/$i || return 1
        done &&
        git add smallDir &&
        git commit -m "commit with 10 changes" &&
@@ -102,7 +102,7 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
        mkdir bigDir &&
        for i in $(test_seq 0 511)
        do
-               echo $i >bigDir/$i
+               echo $i >bigDir/$i || return 1
        done &&
        git add bigDir &&
        git commit -m "commit with 513 changes" &&
index 8853d8afb923e62e5a60d6a52ddcc9a4c6e45aac..522fb2ae696da93e0b8bb8d430f0a4dad6cbd7b3 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Gettext support for Git'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
index 6c74df0dc67e575d171c8ad4a4ab57465b1e622c..8724ce1052ddbf23ce421e15562c2416b2ea0ceb 100755 (executable)
@@ -8,6 +8,7 @@ test_description='Gettext Shell fallbacks'
 GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease
 export GIT_INTERNAL_GETTEXT_TEST_FALLBACKS
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 test_expect_success "sanity: \$GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to $GIT_INTERNAL_GETTEXT_SH_SCHEME)" '
index a29d166e007b7ef1d669231f2da318cecdb6f859..df2ea34932bcfe99cd5719cc2e2f587e6b09c49e 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Perl gettext interface (Git::I18N)'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 if ! test_have_prereq PERL; then
index 8437e51eb545f5c31ad506adfee863f76db27fa5..4f2e0dcb02bda84409789b54f33237798c8f7e19 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description="Gettext reencoding of our *.po/*.mo files works"
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 # The constants used in a tricky observation for undefined behaviour
index c76485b1b60fe3292fdc55d99fe5c576660dffb9..f17abd298c83467b3ead07ff87c08623ff640502 100755 (executable)
@@ -469,7 +469,7 @@ test_expect_success 'rev-list dies for missing objects on cmd line' '
                git -C repo rev-list --ignore-missing --objects \
                        --exclude-promisor-objects "$OBJ" &&
                git -C repo rev-list --ignore-missing --objects-edge-aggressive \
-                       --exclude-promisor-objects "$OBJ"
+                       --exclude-promisor-objects "$OBJ" || return 1
        done
 '
 
index 9c05f5e1f510664ca6ecce60eab5ffe1a30a58ef..ca5c5510c737cce1f7c224a7cd8ad0ab95d3010d 100755 (executable)
@@ -8,6 +8,8 @@ test_description='Two way merge with read-tree -m -u $H $M
 This is identical to t1001, but uses -u to update the work tree as well.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
index 658628375c85b17e44aa501c7624daddff4053f6..39382fa1958152ae0cb88edda55cd63c33a15b6e 100755 (executable)
@@ -211,14 +211,14 @@ done
 test_expect_success "--batch-check for a non-existent named object" '
     test "foobar42 missing
 foobar84 missing" = \
-    "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)"
+    "$( ( echo foobar42 && echo_without_newline foobar84 ) | git cat-file --batch-check)"
 '
 
 test_expect_success "--batch-check for a non-existent hash" '
     test "0000000000000000000000000000000000000042 missing
 0000000000000000000000000000000000000084 missing" = \
-    "$( ( echo 0000000000000000000000000000000000000042;
-        echo_without_newline 0000000000000000000000000000000000000084; ) |
+    "$( ( echo 0000000000000000000000000000000000000042 &&
+        echo_without_newline 0000000000000000000000000000000000000084 ) |
        git cat-file --batch-check)"
 '
 
@@ -226,8 +226,8 @@ test_expect_success "--batch for an existent and a non-existent hash" '
     test "$tag_sha1 tag $tag_size
 $tag_content
 0000000000000000000000000000000000000000 missing" = \
-    "$( ( echo $tag_sha1;
-        echo_without_newline 0000000000000000000000000000000000000000; ) |
+    "$( ( echo $tag_sha1 &&
+        echo_without_newline 0000000000000000000000000000000000000000 ) |
        git cat-file --batch)"
 '
 
@@ -283,7 +283,7 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
 
 test_expect_success 'setup blobs which are likely to delta' '
        test-tool genrandom foo 10240 >foo &&
-       { cat foo; echo plus; } >foo-plus &&
+       { cat foo && echo plus; } >foo-plus &&
        git add foo foo-plus &&
        git commit -m foo &&
        cat >blobs <<-\EOF
@@ -452,9 +452,8 @@ test_expect_success 'the --allow-unknown-type option does not consider replaceme
        # 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 &&
+       test_when_finished "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_sha1" &&
+       test-tool ref-store main update-ref msg "refs/replace/$bogus_short_sha1" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
        cat >expect <<-EOF &&
        commit
index 48bfad07abca6f3ee8430078372108451517aaed..3c0819452653878ae51779798ce90712dc30a901 100755 (executable)
@@ -6,10 +6,10 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-       for d in a a. a0
+       for d in a a- a0
        do
                mkdir "$d" && echo "$d/one" >"$d/one" &&
-               git add "$d"
+               git add "$d" || return 1
        done &&
        echo zero >one &&
        git update-index --add --info-only one &&
index c2df75e4953d897acee5cf590b9c358e5a0b77c2..9fdbb2af80e0a82429289d06b24d4dcfac3f263d 100755 (executable)
@@ -11,9 +11,9 @@ test_description='Try various core-level commands in subdirectory.
 
 test_expect_success setup '
        long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
-       for c in $long; do echo $c; done >one &&
+       test_write_lines $long >one &&
        mkdir dir &&
-       for c in x y z $long a b c; do echo $c; done >dir/two &&
+       test_write_lines x y z $long a b c >dir/two &&
        cp one original.one &&
        cp dir/two original.two
 '
@@ -22,7 +22,7 @@ test_expect_success 'update-index and ls-files' '
        git update-index --add one &&
        case "$(git ls-files)" in
        one) echo pass one ;;
-       *) echo bad one; exit 1 ;;
+       *) echo bad one; return 1 ;;
        esac &&
        (
                cd dir &&
@@ -34,7 +34,7 @@ test_expect_success 'update-index and ls-files' '
        ) &&
        case "$(git ls-files)" in
        dir/two"$LF"one) echo pass both ;;
-       *) echo bad; exit 1 ;;
+       *) echo bad; return 1 ;;
        esac
 '
 
@@ -57,7 +57,7 @@ test_expect_success 'diff-files' '
        echo d >>dir/two &&
        case "$(git diff-files --name-only)" in
        dir/two"$LF"one) echo pass top ;;
-       *) echo bad top; exit 1 ;;
+       *) echo bad top; return 1 ;;
        esac &&
        # diff should not omit leading paths
        (
index a763e27c7da860219493380472a7c863815cb629..a9953b6a71c360a0c39d058ba61f9ff026ec04bf 100755 (executable)
@@ -4,9 +4,6 @@ test_description='git read-tree in partial clones'
 
 TEST_NO_CREATE_REPO=1
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 test_expect_success 'read-tree in partial clone prefetches in one batch' '
index 6bc1d76fb108f38f1fdb9a68904c5251866149fb..4f3aa17c994240173fdb4de70da62611700e11fd 100755 (executable)
@@ -51,42 +51,32 @@ EOF
 test_expect_success 'add a large file or two' '
        git add large1 huge large2 &&
        # make sure we got a single packfile and no loose objects
-       bad= count=0 idx= &&
+       count=0 idx= &&
        for p in .git/objects/pack/pack-*.pack
        do
-               count=$(( $count + 1 ))
-               if test_path_is_file "$p" &&
-                  idx=${p%.pack}.idx && test_path_is_file "$idx"
-               then
-                       continue
-               fi
-               bad=t
+               count=$(( $count + 1 )) &&
+               test_path_is_file "$p" &&
+               idx=${p%.pack}.idx &&
+               test_path_is_file "$idx" || return 1
        done &&
-       test -z "$bad" &&
        test $count = 1 &&
        cnt=$(git show-index <"$idx" | wc -l) &&
        test $cnt = 2 &&
        for l in .git/objects/$OIDPATH_REGEX
        do
-               test_path_is_file "$l" || continue
-               bad=t
+               test_path_is_missing "$l" || return 1
        done &&
-       test -z "$bad" &&
 
        # attempt to add another copy of the same
        git add large3 &&
        bad= count=0 &&
        for p in .git/objects/pack/pack-*.pack
        do
-               count=$(( $count + 1 ))
-               if test_path_is_file "$p" &&
-                  idx=${p%.pack}.idx && test_path_is_file "$idx"
-               then
-                       continue
-               fi
-               bad=t
+               count=$(( $count + 1 )) &&
+               test_path_is_file "$p" &&
+               idx=${p%.pack}.idx &&
+               test_path_is_file "$idx" || return 1
        done &&
-       test -z "$bad" &&
        test $count = 1
 '
 
@@ -115,7 +105,7 @@ test_expect_success 'packsize limit' '
                count=0 &&
                for pi in .git/objects/pack/pack-*.idx
                do
-                       test_path_is_file "$pi" && count=$(( $count + 1 ))
+                       test_path_is_file "$pi" && count=$(( $count + 1 )) || return 1
                done &&
                test $count = 2 &&
 
@@ -128,7 +118,7 @@ test_expect_success 'packsize limit' '
 
                for pi in .git/objects/pack/pack-*.idx
                do
-                       git show-index <"$pi"
+                       git show-index <"$pi" || return 1
                done |
                sed -e "s/^[0-9]* \([0-9a-f]*\) .*/\1/" |
                sort >actual &&
index 272ba1b566b3eaf43798f9ecfb1a77c26db3f59b..42776984fe77912c8bc6a8450935c08134858946 100755 (executable)
@@ -41,7 +41,15 @@ test_expect_success 'setup' '
        )
 '
 
-test_expect_success 'git sparse-checkout list (empty)' '
+test_expect_success 'git sparse-checkout list (not sparse)' '
+       test_must_fail git -C repo sparse-checkout list >list 2>err &&
+       test_must_be_empty list &&
+       test_i18ngrep "this worktree is not sparse" err
+'
+
+test_expect_success 'git sparse-checkout list (not sparse)' '
+       git -C repo sparse-checkout set &&
+       rm repo/.git/info/sparse-checkout &&
        git -C repo sparse-checkout list >list 2>err &&
        test_must_be_empty list &&
        test_i18ngrep "this worktree is not sparse (sparse-checkout file may not exist)" err
@@ -103,6 +111,18 @@ test_expect_success 'clone --sparse' '
        check_files clone a
 '
 
+test_expect_success 'switching to cone mode with non-cone mode patterns' '
+       git init bad-patterns &&
+       (
+               cd bad-patterns &&
+               git sparse-checkout init &&
+               git sparse-checkout add dir &&
+               git config core.sparseCheckoutCone true &&
+               test_must_fail git sparse-checkout add dir 2>err &&
+               grep "existing sparse-checkout patterns do not use cone mode" err
+       )
+'
+
 test_expect_success 'interaction with clone --no-checkout (unborn index)' '
        git clone --no-checkout "file://$(pwd)/repo" clone_no_checkout &&
        git -C clone_no_checkout sparse-checkout init --cone &&
@@ -165,12 +185,14 @@ test_expect_success 'set sparse-checkout using --stdin' '
 '
 
 test_expect_success 'add to sparse-checkout' '
-       cat repo/.git/info/sparse-checkout >expect &&
+       cat repo/.git/info/sparse-checkout >old &&
+       test_when_finished cp old repo/.git/info/sparse-checkout &&
        cat >add <<-\EOF &&
        pattern1
        /folder1/
        pattern2
        EOF
+       cat old >expect &&
        cat add >>expect &&
        git -C repo sparse-checkout add --stdin <add &&
        git -C repo sparse-checkout list >actual &&
@@ -212,12 +234,27 @@ 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 ls-files --sparse >sparse &&
                git -C repo sparse-checkout disable &&
-               test-tool -C repo read-cache --table >cache &&
-               ! grep " tree " cache &&
+               git -C repo ls-files --sparse >full &&
+
+               cat >expect <<-\EOF &&
+               @@ -1,4 +1,7 @@
+                a
+               -deep/
+               -folder1/
+               -folder2/
+               +deep/a
+               +deep/deeper1/a
+               +deep/deeper1/deepest/a
+               +deep/deeper2/a
+               +folder1/a
+               +folder2/a
+               EOF
+
+               diff -u sparse full | tail -n +3 >actual &&
+               test_cmp expect actual &&
+
                git -C repo config --list >config &&
                ! grep index.sparse config
        )
@@ -586,7 +623,7 @@ test_expect_success 'pattern-checks: contained glob characters' '
                !/*/
                something$c-else/
                EOF
-               check_read_tree_errors repo "a" "disabling cone pattern matching"
+               check_read_tree_errors repo "a" "disabling cone pattern matching" || return 1
        done
 '
 
@@ -708,4 +745,25 @@ test_expect_success 'cone mode clears ignored subdirectories' '
        test_cmp expect out
 '
 
+test_expect_success 'malformed cone-mode patterns' '
+       git -C repo sparse-checkout init --cone &&
+       mkdir -p repo/foo/bar &&
+       touch repo/foo/bar/x repo/foo/y &&
+       cat >repo/.git/info/sparse-checkout <<-\EOF &&
+       /*
+       !/*/
+       /foo/
+       !/foo/*/
+       /foo/\*/
+       EOF
+
+       # Listing the patterns will notice the duplicate pattern and
+       # emit a warning. It will list the patterns directly instead
+       # of using the cone-mode translation to a set of directories.
+       git -C repo sparse-checkout list >actual 2>err &&
+       test_cmp repo/.git/info/sparse-checkout actual &&
+       grep "warning: your sparse-checkout file may have issues: pattern .* is repeated" err &&
+       grep "warning: disabling cone pattern matching" err
+'
+
 test_done
index 0fe5b5f482187fa6a1d39f4343e529db10ebd49e..4ba16177528c920e816bdd1cf8db117ca5f6519e 100755 (executable)
@@ -19,6 +19,8 @@ test_expect_success 'setup' '
                mkdir folder1 folder2 deep x &&
                mkdir deep/deeper1 deep/deeper2 deep/before deep/later &&
                mkdir deep/deeper1/deepest &&
+               mkdir deep/deeper1/deepest2 &&
+               mkdir deep/deeper1/deepest3 &&
                echo "after deeper1" >deep/e &&
                echo "after deepest" >deep/deeper1/e &&
                cp a folder1 &&
@@ -30,7 +32,9 @@ test_expect_success 'setup' '
                cp a deep/deeper2 &&
                cp a deep/later &&
                cp a deep/deeper1/deepest &&
-               cp -r deep/deeper1/deepest deep/deeper2 &&
+               cp a deep/deeper1/deepest2 &&
+               cp a deep/deeper1/deepest3 &&
+               cp -r deep/deeper1/ deep/deeper2 &&
                mkdir deep/deeper1/0 &&
                mkdir deep/deeper1/0/0 &&
                touch deep/deeper1/0/1 &&
@@ -126,6 +130,8 @@ test_expect_success 'setup' '
 
                git checkout -b deepest base &&
                echo "updated deepest" >deep/deeper1/deepest/a &&
+               echo "updated deepest2" >deep/deeper1/deepest2/a &&
+               echo "updated deepest3" >deep/deeper1/deepest3/a &&
                git commit -a -m "update deepest" &&
 
                git checkout -f base &&
@@ -200,45 +206,42 @@ test_sparse_unstaged () {
 test_expect_success 'sparse-index contents' '
        init_repos &&
 
-       test-tool -C sparse-index read-cache --table >cache &&
+       git -C sparse-index ls-files --sparse --stage >cache &&
        for dir in folder1 folder2 x
        do
                TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
-               grep "040000 tree $TREE $dir/" cache \
+               grep "040000 $TREE 0    $dir/" cache \
                        || return 1
        done &&
 
        git -C sparse-index sparse-checkout set folder1 &&
 
-       test-tool -C sparse-index read-cache --table >cache &&
+       git -C sparse-index ls-files --sparse --stage >cache &&
        for dir in deep folder2 x
        do
                TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
-               grep "040000 tree $TREE $dir/" cache \
+               grep "040000 $TREE 0    $dir/" cache \
                        || return 1
        done &&
 
        git -C sparse-index sparse-checkout set deep/deeper1 &&
 
-       test-tool -C sparse-index read-cache --table >cache &&
+       git -C sparse-index ls-files --sparse --stage >cache &&
        for dir in deep/deeper2 folder1 folder2 x
        do
                TREE=$(git -C sparse-index rev-parse HEAD:$dir) &&
-               grep "040000 tree $TREE $dir/" cache \
+               grep "040000 $TREE 0    $dir/" cache \
                        || return 1
        done &&
 
-       # Disabling the sparse-index removes tree entries with full ones
+       # Disabling the sparse-index replaces tree entries with full ones
        git -C sparse-index sparse-checkout init --no-sparse-index &&
-
-       test-tool -C sparse-index read-cache --table >cache &&
-       ! grep "040000 tree" cache &&
-       test_sparse_match test-tool read-cache --table
+       test_sparse_match git ls-files --stage --sparse
 '
 
 test_expect_success 'expanded in-memory index matches full index' '
        init_repos &&
-       test_sparse_match test-tool read-cache --expand --table
+       test_sparse_match git ls-files --stage
 '
 
 test_expect_success 'status with options' '
@@ -301,6 +304,14 @@ test_expect_success 'add, commit, checkout' '
        test_all_match git checkout -
 '
 
+test_expect_success 'deep changes during checkout' '
+       init_repos &&
+
+       test_sparse_match git sparse-checkout set deep/deeper1/deepest &&
+       test_all_match git checkout deepest &&
+       test_all_match git checkout base
+'
+
 test_expect_success 'add outside sparse cone' '
        init_repos &&
 
@@ -401,7 +412,7 @@ test_expect_success 'checkout and reset --hard' '
        test_all_match git reset --hard update-folder2
 '
 
-test_expect_success 'diff --staged' '
+test_expect_success 'diff --cached' '
        init_repos &&
 
        write_script edit-contents <<-\EOF &&
@@ -410,10 +421,10 @@ test_expect_success 'diff --staged' '
        run_on_all ../edit-contents &&
 
        test_all_match git diff &&
-       test_all_match git diff --staged &&
+       test_all_match git diff --cached &&
        test_all_match git add README.md &&
        test_all_match git diff &&
-       test_all_match git diff --staged
+       test_all_match git diff --cached
 '
 
 # NEEDSWORK: sparse-checkout behaves differently from full-checkout when
@@ -430,8 +441,8 @@ test_expect_success 'diff with renames and conflicts' '
                test_all_match git checkout rename-base &&
                test_all_match git checkout $branch -- . &&
                test_all_match git status --porcelain=v2 &&
-               test_all_match git diff --staged --no-renames &&
-               test_all_match git diff --staged --find-renames || return 1
+               test_all_match git diff --cached --no-renames &&
+               test_all_match git diff --cached --find-renames || return 1
        done
 '
 
@@ -450,8 +461,8 @@ test_expect_success 'diff with directory/file conflicts' '
                test_all_match git checkout $branch &&
                test_all_match git checkout rename-base -- . &&
                test_all_match git status --porcelain=v2 &&
-               test_all_match git diff --staged --no-renames &&
-               test_all_match git diff --staged --find-renames || return 1
+               test_all_match git diff --cached --no-renames &&
+               test_all_match git diff --cached --find-renames || return 1
        done
 '
 
@@ -472,21 +483,36 @@ test_expect_success 'log with pathspec outside sparse definition' '
 test_expect_success 'blame with pathspec inside sparse definition' '
        init_repos &&
 
-       test_all_match git blame a &&
-       test_all_match git blame deep/a &&
-       test_all_match git blame deep/deeper1/a &&
-       test_all_match git blame deep/deeper1/deepest/a
+       for file in a \
+                       deep/a \
+                       deep/deeper1/a \
+                       deep/deeper1/deepest/a
+       do
+               test_all_match git blame $file
+       done
 '
 
-# TODO: blame currently does not support blaming files outside of the
-# sparse definition. It complains that the file doesn't exist locally.
-test_expect_failure 'blame with pathspec outside sparse definition' '
+# Without a revision specified, blame will error if passed any file that
+# is not present in the working directory (even if the file is tracked).
+# Here we just verify that this is also true with sparse checkouts.
+test_expect_success 'blame with pathspec outside sparse definition' '
        init_repos &&
+       test_sparse_match git sparse-checkout set &&
 
-       test_all_match git blame folder1/a &&
-       test_all_match git blame folder2/a &&
-       test_all_match git blame deep/deeper2/a &&
-       test_all_match git blame deep/deeper2/deepest/a
+       for file in a \
+                       deep/a \
+                       deep/deeper1/a \
+                       deep/deeper1/deepest/a
+       do
+               test_sparse_match test_must_fail git blame $file &&
+               cat >expect <<-EOF &&
+               fatal: Cannot lstat '"'"'$file'"'"': No such file or directory
+               EOF
+               # We compare sparse-checkout-err and sparse-index-err in
+               # `test_sparse_match`. Given we know they are the same, we
+               # only check the content of sparse-index-err here.
+               test_cmp expect sparse-index-err
+       done
 '
 
 test_expect_success 'checkout and reset (mixed)' '
@@ -772,9 +798,9 @@ test_expect_success 'submodule handling' '
 
        # 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
+       git -C sparse-index ls-files --sparse --stage >cache &&
+       grep "100644 .* modules/a" cache &&
+       grep "160000 $(git -C initial-repo rev-parse HEAD) 0    modules/sub" cache
 '
 
 # When working with a sparse index, some commands will need to expand the
@@ -784,9 +810,15 @@ test_expect_success 'submodule handling' '
 test_expect_success 'sparse-index is expanded and converted back' '
        init_repos &&
 
-       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
                git -C sparse-index reset -- folder1/a &&
        test_region index convert_to_sparse trace2.txt &&
+       test_region index ensure_full_index trace2.txt &&
+
+       # ls-files expands on read, but does not write.
+       rm trace2.txt &&
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+               git -C sparse-index ls-files &&
        test_region index ensure_full_index trace2.txt
 '
 
@@ -829,10 +861,10 @@ ensure_not_expanded () {
        then
                shift &&
                test_must_fail env \
-                       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+                       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
                        git -C sparse-index "$@" || return 1
        else
-               GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+               GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
                        git -C sparse-index "$@" || return 1
        fi &&
        test_region ! index ensure_full_index trace2.txt
@@ -842,6 +874,7 @@ test_expect_success 'sparse-index is not expanded' '
        init_repos &&
 
        ensure_not_expanded status &&
+       ensure_not_expanded ls-files --sparse &&
        ensure_not_expanded commit --allow-empty -m empty &&
        echo >>sparse-index/a &&
        ensure_not_expanded commit -a -m a &&
@@ -922,6 +955,158 @@ test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
        )
 '
 
+test_expect_success 'sparse index is not expanded: diff' '
+       init_repos &&
+
+       write_script edit-contents <<-\EOF &&
+       echo text >>$1
+       EOF
+
+       # Add file within cone
+       test_sparse_match git sparse-checkout set deep &&
+       run_on_all ../edit-contents deep/testfile &&
+       test_all_match git add deep/testfile &&
+       run_on_all ../edit-contents deep/testfile &&
+
+       test_all_match git diff &&
+       test_all_match git diff --cached &&
+       ensure_not_expanded diff &&
+       ensure_not_expanded diff --cached &&
+
+       # Add file outside cone
+       test_all_match git reset --hard &&
+       run_on_all mkdir newdirectory &&
+       run_on_all ../edit-contents newdirectory/testfile &&
+       test_sparse_match git sparse-checkout set newdirectory &&
+       test_all_match git add newdirectory/testfile &&
+       run_on_all ../edit-contents newdirectory/testfile &&
+       test_sparse_match git sparse-checkout set &&
+
+       test_all_match git diff &&
+       test_all_match git diff --cached &&
+       ensure_not_expanded diff &&
+       ensure_not_expanded diff --cached &&
+
+       # Merge conflict outside cone
+       # The sparse checkout will report a warning that is not in the
+       # full checkout, so we use `run_on_all` instead of
+       # `test_all_match`
+       run_on_all git reset --hard &&
+       test_all_match git checkout merge-left &&
+       test_all_match test_must_fail git merge merge-right &&
+
+       test_all_match git diff &&
+       test_all_match git diff --cached &&
+       ensure_not_expanded diff &&
+       ensure_not_expanded diff --cached
+'
+
+test_expect_success 'sparse index is not expanded: blame' '
+       init_repos &&
+
+       for file in a \
+                       deep/a \
+                       deep/deeper1/a \
+                       deep/deeper1/deepest/a
+       do
+               ensure_not_expanded blame $file
+       done
+'
+
+test_expect_success 'sparse index is not expanded: fetch/pull' '
+       init_repos &&
+
+       git -C sparse-index remote add full "file://$(pwd)/full-checkout" &&
+       ensure_not_expanded fetch full &&
+       git -C full-checkout commit --allow-empty -m "for pull merge" &&
+       git -C sparse-index commit --allow-empty -m "for pull merge" &&
+       ensure_not_expanded pull full base
+'
+
+test_expect_success 'ls-files' '
+       init_repos &&
+
+       # Use a smaller sparse-checkout for reduced output
+       test_sparse_match git sparse-checkout set &&
+
+       # Behavior agrees by default. Sparse index is expanded.
+       test_all_match git ls-files &&
+
+       # With --sparse, the sparse index data changes behavior.
+       git -C sparse-index ls-files --sparse >actual &&
+
+       cat >expect <<-\EOF &&
+       a
+       deep/
+       e
+       folder1-
+       folder1.x
+       folder1/
+       folder10
+       folder2/
+       g
+       x/
+       z
+       EOF
+
+       test_cmp expect actual &&
+
+       # With --sparse and no sparse index, nothing changes.
+       git -C sparse-checkout ls-files >dense &&
+       git -C sparse-checkout ls-files --sparse >sparse &&
+       test_cmp dense sparse &&
+
+       # Set up a strange condition of having a file edit
+       # outside of the sparse-checkout cone. This is just
+       # to verify that sparse-checkout and sparse-index
+       # behave the same in this case.
+       write_script edit-content <<-\EOF &&
+       mkdir folder1 &&
+       echo content >>folder1/a
+       EOF
+       run_on_sparse ../edit-content &&
+
+       # ls-files does not currently notice modified files whose
+       # cache entries are marked SKIP_WORKTREE. This may change
+       # in the future, but here we test that sparse index does
+       # not accidentally create a change of behavior.
+       test_sparse_match git ls-files --modified &&
+       test_must_be_empty sparse-checkout-out &&
+       test_must_be_empty sparse-index-out &&
+
+       git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
+       test_must_be_empty sparse-index-out &&
+
+       # Add folder1 to the sparse-checkout cone and
+       # check that ls-files shows the expanded files.
+       test_sparse_match git sparse-checkout add folder1 &&
+       test_sparse_match git ls-files --modified &&
+
+       test_all_match git ls-files &&
+       git -C sparse-index ls-files --sparse >actual &&
+
+       cat >expect <<-\EOF &&
+       a
+       deep/
+       e
+       folder1-
+       folder1.x
+       folder1/0/0/0
+       folder1/0/1
+       folder1/a
+       folder10
+       folder2/
+       g
+       x/
+       z
+       EOF
+
+       test_cmp expect actual &&
+
+       # Double-check index expansion is avoided
+       ensure_not_expanded ls-files --sparse
+'
+
 # NEEDSWORK: a sparse-checkout behaves differently from a full checkout
 # in this scenario, but it shouldn't.
 test_expect_success 'reset mixed and checkout orphan' '
@@ -937,13 +1122,13 @@ test_expect_success 'reset mixed and checkout orphan' '
        # the sparse checkouts skip "adding" the other side of
        # the conflict.
        test_sparse_match git reset --mixed HEAD~1 &&
-       test_sparse_match test-tool read-cache --table --expand &&
+       test_sparse_match git ls-files --stage &&
        test_sparse_match git status --porcelain=v2 &&
 
        # At this point, sparse-checkouts behave differently
        # from the full-checkout.
        test_sparse_match git checkout --orphan new-branch &&
-       test_sparse_match test-tool read-cache --table --expand &&
+       test_sparse_match git ls-files --stage &&
        test_sparse_match git status --porcelain=v2
 '
 
index 9ff46f3b0471fb146bec00329459040619edd88e..78359f1f4a2d736f44b84075013e647a7fab3660 100755 (executable)
@@ -8,6 +8,7 @@ test_description='Test git config in different settings'
 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 'clear default config' '
@@ -717,8 +718,8 @@ test_expect_success bool '
        rm -f result &&
        for i in 1 2 3 4
        do
-           git config --bool --get bool.true$i >>result
-           git config --bool --get bool.false$i >>result
+           git config --bool --get bool.true$i >>result &&
+           git config --bool --get bool.false$i >>result || return 1
        done &&
        test_cmp expect result'
 
@@ -901,7 +902,7 @@ test_expect_success 'get --expiry-date' '
        EOF
        : "work around heredoc parsing bug fixed in dash 0.5.7 (in ec2c84d)" &&
        {
-               echo "$rel_out $(git config --expiry-date date.valid1)"
+               echo "$rel_out $(git config --expiry-date date.valid1)" &&
                git config --expiry-date date.valid2 &&
                git config --expiry-date date.valid3 &&
                git config --expiry-date date.valid4 &&
index 0000e664e7b6d8c62cc12032f71224b832829e9d..0506f3d6bba6cb64255a1b7685481b8ccdb7784d 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='Test wacky input to git config'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Leaving off the newline is intentional!
index 930dce06f0fdff755d4ea0b4f81c8fed776440c6..0a7099d6f52b68cf9abc263933267925a994378a 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='support for reading config from a blob'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create config blob' '
index 88b119a0a35dc6608a789b0415a97650c88b2e19..b38e158d3b212a0157f5d0f862103713c6a39e57 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test git config-set API in different settings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # 'check_config get_* section.key value' verifies that the entry for
index b4a9158307fd210079211165fb507a037ceb75f0..537435b90ae9314a8062c81186fd00853d38b230 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test read_early_config()'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'read early config' '
index 6049d9170814320e0d9035c357d76dcfe9887708..09b10c144ba9d162635a12d66b8796b9cd94fb6c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test git config in different settings (with --default)'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'uses --default when entry missing' '
index 0d4f73acaa87e5f29a6346abb8d1dc6736d41794..cf58cf025cd2af621f6d58cdaf02d3be0480190c 100755 (executable)
@@ -4,9 +4,6 @@
 #
 
 test_description='Test git update-ref and basic ref logging'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 Z=$ZERO_OID
@@ -321,8 +318,9 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000   Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success "verifying $m's log (logged by touch)" '
-       test_when_finished "rm -rf .git/$m .git/logs expect" &&
-       test_cmp expect .git/logs/$m
+       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test-tool ref-store main for-each-reflog-ent $m >actual &&
+       test_cmp actual expect
 '
 
 test_expect_success "create $m (logged by config)" '
@@ -350,8 +348,9 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000   Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
 test_expect_success "verifying $m's log (logged by config)" '
-       test_when_finished "rm -f .git/$m .git/logs/$m expect" &&
-       test_cmp expect .git/logs/$m
+       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test-tool ref-store main for-each-reflog-ent $m >actual &&
+       test_cmp actual expect
 '
 
 test_expect_success 'set up for querying the reflog' '
@@ -467,7 +466,8 @@ $h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000       co
 $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 commit (merge): Merged initial commit and a later commit.
 EOF
 test_expect_success 'git commit logged updates' '
-       test_cmp expect .git/logs/$m
+       test-tool ref-store main for-each-reflog-ent $m >actual &&
+       test_cmp expect actual
 '
 unset h_TEST h_OTHER h_FIXED h_MERGED
 
@@ -1368,7 +1368,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches
 (
        for i in $(test_seq 33)
        do
-               echo "create refs/heads/$i HEAD"
+               echo "create refs/heads/$i HEAD" || exit 1
        done >large_input &&
        run_with_limited_open_files git update-ref --stdin <large_input &&
        git rev-parse --verify -q refs/heads/33
@@ -1379,7 +1379,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches
 (
        for i in $(test_seq 33)
        do
-               echo "delete refs/heads/$i HEAD"
+               echo "delete refs/heads/$i HEAD" || exit 1
        done >large_input &&
        run_with_limited_open_files git update-ref --stdin <large_input &&
        test_must_fail git rev-parse --verify -q refs/heads/33
index 4d261e80c6ff3117b06f3b383c45074aed3effdf..9252a581abf8a702fffb01bc7c388666207d5251 100755 (executable)
@@ -79,7 +79,7 @@ test_expect_success 'show-ref --verify -q' '
 test_expect_success 'show-ref -d' '
        {
                echo $(git rev-parse refs/tags/A) refs/tags/A &&
-               echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+               echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}" &&
                echo $(git rev-parse refs/tags/C) refs/tags/C
        } >expect &&
        git show-ref -d A C >actual &&
@@ -124,14 +124,14 @@ test_expect_success 'show-ref -d' '
 test_expect_success 'show-ref --heads, --tags, --head, pattern' '
        for branch in B main side
        do
-               echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+               echo $(git rev-parse refs/heads/$branch) refs/heads/$branch || return 1
        done >expect.heads &&
        git show-ref --heads >actual &&
        test_cmp expect.heads actual &&
 
        for tag in A B C
        do
-               echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+               echo $(git rev-parse refs/tags/$tag) refs/tags/$tag || return 1
        done >expect.tags &&
        git show-ref --tags >actual &&
        test_cmp expect.tags actual &&
@@ -149,7 +149,7 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
 
        {
                echo $(git rev-parse HEAD) HEAD &&
-               echo $(git rev-parse refs/heads/B) refs/heads/B
+               echo $(git rev-parse refs/heads/B) refs/heads/B &&
                echo $(git rev-parse refs/tags/B) refs/tags/B
        } >expect &&
        git show-ref --head B >actual &&
@@ -157,8 +157,8 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' '
 
        {
                echo $(git rev-parse HEAD) HEAD &&
-               echo $(git rev-parse refs/heads/B) refs/heads/B
-               echo $(git rev-parse refs/tags/B) refs/tags/B
+               echo $(git rev-parse refs/heads/B) refs/heads/B &&
+               echo $(git rev-parse refs/tags/B) refs/tags/B &&
                echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
        } >expect &&
        git show-ref --head -d B >actual &&
index b729c1f4803040733c88791123f3da6ca267f9e6..13c2b43bbae8343c2402952bba8ac9635001b453 100755 (executable)
@@ -261,69 +261,69 @@ test_expect_success REFFILES 'empty directory should not fool 1-arg delete' '
        git update-ref --stdin
 '
 
-test_expect_success 'D/F conflict prevents add long + delete short' '
+test_expect_success REFFILES 'D/F conflict prevents add long + delete short' '
        df_test refs/df-al-ds --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents add short + delete long' '
+test_expect_success REFFILES 'D/F conflict prevents add short + delete long' '
        df_test refs/df-as-dl --add-del foo foo/bar
 '
 
-test_expect_success 'D/F conflict prevents delete long + add short' '
+test_expect_success REFFILES 'D/F conflict prevents delete long + add short' '
        df_test refs/df-dl-as --del-add foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents delete short + add long' '
+test_expect_success REFFILES 'D/F conflict prevents delete short + add long' '
        df_test refs/df-ds-al --del-add foo foo/bar
 '
 
-test_expect_success 'D/F conflict prevents add long + delete short packed' '
+test_expect_success REFFILES 'D/F conflict prevents add long + delete short packed' '
        df_test refs/df-al-dsp --pack --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents add short + delete long packed' '
+test_expect_success REFFILES 'D/F conflict prevents add short + delete long packed' '
        df_test refs/df-as-dlp --pack --add-del foo foo/bar
 '
 
-test_expect_success 'D/F conflict prevents delete long packed + add short' '
+test_expect_success REFFILES 'D/F conflict prevents delete long packed + add short' '
        df_test refs/df-dlp-as --pack --del-add foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents delete short packed + add long' '
+test_expect_success REFFILES 'D/F conflict prevents delete short packed + add long' '
        df_test refs/df-dsp-al --pack --del-add foo foo/bar
 '
 
 # Try some combinations involving symbolic refs...
 
-test_expect_success 'D/F conflict prevents indirect add long + delete short' '
+test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short' '
        df_test refs/df-ial-ds --sym-add --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents indirect add long + indirect delete short' '
+test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short' '
        df_test refs/df-ial-ids --sym-add --sym-del --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents indirect add short + indirect delete long' '
+test_expect_success REFFILES 'D/F conflict prevents indirect add short + indirect delete long' '
        df_test refs/df-ias-idl --sym-add --sym-del --add-del foo foo/bar
 '
 
-test_expect_success 'D/F conflict prevents indirect delete long + indirect add short' '
+test_expect_success REFFILES 'D/F conflict prevents indirect delete long + indirect add short' '
        df_test refs/df-idl-ias --sym-add --sym-del --del-add foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents indirect add long + delete short packed' '
+test_expect_success REFFILES 'D/F conflict prevents indirect add long + delete short packed' '
        df_test refs/df-ial-dsp --sym-add --pack --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents indirect add long + indirect delete short packed' '
+test_expect_success REFFILES 'D/F conflict prevents indirect add long + indirect delete short packed' '
        df_test refs/df-ial-idsp --sym-add --sym-del --pack --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents add long + indirect delete short packed' '
+test_expect_success REFFILES 'D/F conflict prevents add long + indirect delete short packed' '
        df_test refs/df-al-idsp --sym-del --pack --add-del foo/bar foo
 '
 
-test_expect_success 'D/F conflict prevents indirect delete long packed + indirect add short' '
+test_expect_success REFFILES 'D/F conflict prevents indirect delete long packed + indirect add short' '
        df_test refs/df-idlp-ias --sym-add --sym-del --pack --del-add foo/bar foo
 '
 
index c89cef2d26be9252c075b58767919f4186259305..1a3ee8845d67e92190261e33d93c98f540972f7f 100755 (executable)
@@ -17,8 +17,7 @@ test_expect_success 'setup' '
 test_expect_success REFFILES 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
        N=`find .git/refs -type f | wc -l` &&
        test "$N" != 0 &&
-       ALL_OR_PRUNE_FLAG=3 &&
-       $RUN pack-refs ${ALL_OR_PRUNE_FLAG} &&
+       $RUN pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
        N=`find .git/refs -type f` &&
        test -z "$N"
 '
@@ -35,8 +34,7 @@ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
        git rev-parse FOO -- &&
        git rev-parse refs/tags/new-tag -- &&
        m=$(git rev-parse main) &&
-       REF_NO_DEREF=1 &&
-       $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
+       $RUN delete-refs REF_NO_DEREF nothing FOO refs/tags/new-tag &&
        test_must_fail git rev-parse --symbolic-full-name FOO &&
        test_must_fail git rev-parse FOO -- &&
        test_must_fail git rev-parse refs/tags/new-tag --
@@ -89,13 +87,13 @@ test_expect_success 'for_each_reflog()' '
 test_expect_success 'for_each_reflog_ent()' '
        $RUN for-each-reflog-ent HEAD >actual &&
        head -n1 actual | grep one &&
-       tail -n2 actual | head -n1 | grep recreate-main
+       tail -n1 actual | grep recreate-main
 '
 
 test_expect_success 'for_each_reflog_ent_reverse()' '
        $RUN for-each-reflog-ent-reverse HEAD >actual &&
        head -n1 actual | grep recreate-main &&
-       tail -n2 actual | head -n1 | grep one
+       tail -n1 actual | grep one
 '
 
 test_expect_success 'reflog_exists(HEAD)' '
index c95c5d47aad47f0dc10ce1db6dcca3405430ae21..e6a7f7334b6a96e4aa310532c8dc7d6c727fdd16 100755 (executable)
@@ -75,13 +75,13 @@ test_expect_success 'for_each_reflog()' '
 test_expect_success 'for_each_reflog_ent()' '
        $RUN for-each-reflog-ent HEAD >actual &&
        head -n1 actual | grep first &&
-       tail -n2 actual | head -n1 | grep main.to.new
+       tail -n1 actual | grep main.to.new
 '
 
 test_expect_success 'for_each_reflog_ent_reverse()' '
        $RUN for-each-reflog-ent-reverse HEAD >actual &&
        head -n1 actual | grep main.to.new &&
-       tail -n2 actual | head -n1 | grep first
+       tail -n1 actual | grep first
 '
 
 test_expect_success 'reflog_exists(HEAD)' '
index d42f067ff8ca0feefcb7cc28a6fc549bd31827bd..d7ddf7612d48363ccbac0a4a80d8399bae7b453f 100755 (executable)
@@ -349,12 +349,12 @@ test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
                printf "$zf%02d $zf%02d %s\t" $i $(($i+1)) "$ident" &&
                if test $i = 75; then
                        for j in $(test_seq 1 89); do
-                               printf X
+                               printf X || return 1
                        done
                else
                        printf X
                fi &&
-               printf "\n"
+               printf "\n" || return 1
        done >.git/logs/refs/heads/reflogskip &&
        git rev-parse reflogskip@{73} >actual &&
        echo ${zf}03 >expect &&
index dc9e402c55574d981e161d4e38e74617c411f46d..dbe15a0be1051608c4dc6dd727e1589e9422b7b5 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='Test fsck --lost-found'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 4c77cf89a6ce63496393b6715a4e69d6081f62e5..ff1c967d550318e0b17ad3c9d1e08654c51e7506 100755 (executable)
@@ -9,7 +9,8 @@ TEST_PASSES_SANITIZE_LEAK=true
 
 test_expect_success setup '
        test_commit one &&
-       test_commit two
+       test_commit two &&
+       main_sha1=$(git rev-parse refs/heads/main)
 '
 
 test_expect_success 'fast-import: fail on invalid branch name ".badbranchname"' '
@@ -43,16 +44,16 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"'
 '
 
 test_expect_success 'git branch shows badly named ref as warning' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch >output 2>error &&
        test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
        ! grep -e "broken\.\.\.ref" output
 '
 
 test_expect_success 'branch -d can delete badly named ref' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch -d broken...ref &&
        git branch >output 2>error &&
        ! grep -e "broken\.\.\.ref" error &&
@@ -60,8 +61,8 @@ test_expect_success 'branch -d can delete badly named ref' '
 '
 
 test_expect_success 'branch -D can delete badly named ref' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch -D broken...ref &&
        git branch >output 2>error &&
        ! grep -e "broken\.\.\.ref" error &&
@@ -90,7 +91,7 @@ test_expect_success 'branch -D cannot delete absolute path' '
 '
 
 test_expect_success 'git branch cannot create a badly named ref' '
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test_must_fail git branch broken...ref &&
        git branch >output 2>error &&
        ! grep -e "broken\.\.\.ref" error &&
@@ -98,7 +99,7 @@ test_expect_success 'git branch cannot create a badly named ref' '
 '
 
 test_expect_success 'branch -m cannot rename to a bad ref name' '
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test_might_fail git branch -D goodref &&
        git branch goodref &&
        test_must_fail git branch -m goodref broken...ref &&
@@ -109,8 +110,9 @@ test_expect_success 'branch -m cannot rename to a bad ref name' '
 '
 
 test_expect_failure 'branch -m can rename from a bad ref name' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch -m broken...ref renamed &&
        test_cmp_rev main renamed &&
        git branch >output 2>error &&
@@ -119,7 +121,7 @@ test_expect_failure 'branch -m can rename from a bad ref name' '
 '
 
 test_expect_success 'push cannot create a badly named ref' '
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref &&
        git branch >output 2>error &&
        ! grep -e "broken\.\.\.ref" error &&
@@ -139,7 +141,7 @@ test_expect_failure 'push --mirror can delete badly named ref' '
                cd dest &&
                test_commit two &&
                git checkout --detach &&
-               cp .git/refs/heads/main .git/refs/heads/broken...ref
+               test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION
        ) &&
        git -C src push --mirror "file://$top/dest" &&
        git -C dest branch >output 2>error &&
@@ -148,11 +150,11 @@ test_expect_failure 'push --mirror can delete badly named ref' '
 '
 
 test_expect_success 'rev-parse skips symref pointing to broken name' '
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch shadow one &&
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/tags/shadow &&
-       test_when_finished "rm -f .git/refs/tags/shadow" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test-tool ref-store main create-symref refs/tags/shadow refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/tags/shadow" &&
        git rev-parse --verify one >expect &&
        git rev-parse --verify shadow >actual 2>err &&
        test_cmp expect actual &&
@@ -160,12 +162,12 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '
 '
 
 test_expect_success 'for-each-ref emits warnings for broken names' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
-       test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git for-each-ref >output 2>error &&
        ! grep -e "broken\.\.\.ref" output &&
        ! grep -e "badname" output &&
@@ -176,8 +178,8 @@ test_expect_success 'for-each-ref emits warnings for broken names' '
 '
 
 test_expect_success 'update-ref -d can delete broken name' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git update-ref -d refs/heads/broken...ref >output 2>error &&
        test_must_be_empty output &&
        test_must_be_empty error &&
@@ -187,8 +189,8 @@ test_expect_success 'update-ref -d can delete broken name' '
 '
 
 test_expect_success 'branch -d can delete broken name' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
        git branch -d broken...ref >output 2>error &&
        test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
        test_must_be_empty error &&
@@ -198,10 +200,11 @@ test_expect_success 'branch -d can delete broken name' '
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref to broken name' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        git update-ref --no-deref -d refs/heads/badname >output 2>error &&
        test_path_is_missing .git/refs/heads/badname &&
        test_must_be_empty output &&
@@ -209,10 +212,10 @@ test_expect_success 'update-ref --no-deref -d can delete symref to broken name'
 '
 
 test_expect_success 'branch -d can delete symref to broken name' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        git branch -d badname >output 2>error &&
        test_path_is_missing .git/refs/heads/badname &&
        test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
@@ -220,8 +223,8 @@ test_expect_success 'branch -d can delete symref to broken name' '
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref to broken name' '
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        git update-ref --no-deref -d refs/heads/badname >output 2>error &&
        test_path_is_missing .git/refs/heads/badname &&
        test_must_be_empty output &&
@@ -229,8 +232,8 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref to brok
 '
 
 test_expect_success 'branch -d can delete dangling symref to broken name' '
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        git branch -d badname >output 2>error &&
        test_path_is_missing .git/refs/heads/badname &&
        test_i18ngrep "Deleted branch badname (was refs/heads/broken\.\.\.ref)" output &&
@@ -238,10 +241,10 @@ test_expect_success 'branch -d can delete dangling symref to broken name' '
 '
 
 test_expect_success 'update-ref -d can delete broken name through symref' '
-       cp .git/refs/heads/main .git/refs/heads/broken...ref &&
-       test_when_finished "rm -f .git/refs/heads/broken...ref" &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
-       test_when_finished "rm -f .git/refs/heads/badname" &&
+       test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref msg &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
        git update-ref -d refs/heads/badname >output 2>error &&
        test_path_is_missing .git/refs/heads/broken...ref &&
        test_must_be_empty output &&
@@ -250,7 +253,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
 
 test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
        printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
-       test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
        test_path_is_missing .git/refs/heads/broken...symref &&
        test_must_be_empty output &&
@@ -259,7 +262,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name
 
 test_expect_success 'branch -d can delete symref with broken name' '
        printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
-       test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git branch -d broken...symref >output 2>error &&
        test_path_is_missing .git/refs/heads/broken...symref &&
        test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
@@ -268,7 +271,7 @@ test_expect_success 'branch -d can delete symref with broken name' '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
        printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
-       test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
        test_path_is_missing .git/refs/heads/broken...symref &&
        test_must_be_empty output &&
@@ -277,7 +280,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br
 
 test_expect_success 'branch -d can delete dangling symref with broken name' '
        printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
-       test_when_finished "rm -f .git/refs/heads/broken...symref" &&
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git branch -d broken...symref >output 2>error &&
        test_path_is_missing .git/refs/heads/broken...symref &&
        test_i18ngrep "Deleted branch broken...symref (was refs/heads/idonotexist)" output &&
index 6337236fd8226b1902f11ebf54ff9e6d02f537e2..de50c0ea018cfa981312c9f146f8265544e64711 100755 (executable)
@@ -94,13 +94,13 @@ test_expect_success 'object with hash and type mismatch' '
        )
 '
 
-test_expect_success POSIXPERM 'zlib corrupt loose object output ' '
+test_expect_success '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 &&
+               chmod +w $oidf &&
                echo extra garbage >>$oidf &&
 
                cat >expect.error <<-EOF &&
index 40958615ebb9c16af55ab81c08584ef784e0574c..94fe413ee3771d1d30b0d70a4354811a6e93d163 100755 (executable)
@@ -9,6 +9,7 @@ exec </dev/null
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 add_line_into_file()
index 65a154a8a20f8df27a3f0eaee9ff9b65d5132639..18688cae17d57e8174f4d1d61eeb2e2ed341eac6 100755 (executable)
@@ -7,6 +7,7 @@ exec </dev/null
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_did_you_mean ()
index 7891a6becf3a0b4092b2a3fd91ed6679e24cdd08..b0119bf8bc8417439bee91bb7c1add21bc460c2e 100755 (executable)
@@ -34,10 +34,7 @@ fi
 test_expect_success 'blob and tree' '
        test_tick &&
        (
-               for i in 0 1 2 3 4 5 6 7 8 9
-               do
-                       echo $i
-               done &&
+               test_write_lines 0 1 2 3 4 5 6 7 8 9 &&
                echo &&
                echo b1rwzyc3
        ) >a0blgqsjc &&
@@ -204,10 +201,7 @@ test_expect_success 'more history' '
        git checkout v1.0.0^0 &&
        git mv a0blgqsjc f5518nwu &&
 
-       for i in h62xsjeu j08bekfvt kg7xflhm
-       do
-               echo $i
-       done >>f5518nwu &&
+       test_write_lines h62xsjeu j08bekfvt kg7xflhm >>f5518nwu &&
        git add f5518nwu &&
 
        test_tick &&
@@ -387,7 +381,7 @@ test_expect_success 'ambiguous commits are printed by type first, then hash orde
        do
                grep $type objects >$type.objects &&
                sort $type.objects >$type.objects.sorted &&
-               test_cmp $type.objects.sorted $type.objects
+               test_cmp $type.objects.sorted $type.objects || return 1
        done
 '
 
index 5f437be8c9e8c932f5d355e3f8636e5cbcd69791..ba43387bf167b598de91bea497251d270214f545 100755 (executable)
@@ -5,6 +5,7 @@ test_description='Tests for rev-parse --prefix'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 3ec2971ee5befd2ad8ffff5e099f05307746e6d1..cdb26a30d726bb0088230c251a58ece0d160c72d 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check that certain rev-parse options work outside repo'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up non-repo directory' '
index 46329c488b19cd41759d78c6d89a4411d193402e..010989f90e63f9ddf5a88d25198998c55782bbd2 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='index file specific tests'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 sane_unset GIT_TEST_SPLIT_INDEX
index decd2527ed6427fe760d082cedc8a37d0df4f312..b4ab166369ec06c69add33eb240d69531459276c 100755 (executable)
@@ -48,10 +48,10 @@ test_expect_success 'enable split index' '
        # NEEDSWORK: Stop hard-coding checksums.
        if test "$indexversion" = "4"
        then
-               own=$(test_oid own_v4)
+               own=$(test_oid own_v4) &&
                base=$(test_oid base_v4)
        else
-               own=$(test_oid own_v3)
+               own=$(test_oid own_v3) &&
                base=$(test_oid base_v3)
        fi &&
 
index f18616ad2be3eaaf6dc72ab9131c88a0c9bdf2fc..79fc97f1d7eb42675c85ef29b8cb36a75d7e7669 100755 (executable)
@@ -21,6 +21,7 @@ test_description='git conflicts when checking files out test.'
 # path1 is occupied by a non-directory.  With "-f" flag, it should remove
 # the conflicting paths and succeed.
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 show_files() {
index 9bb503a97578c1d43a01409a93e2dd4f7c207a43..b16d69ca4ae0e8b61ce00a085273d8ad6f40f104 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'checkout all stage 0 to temporary files' '
                test $(grep $f actual | cut "-d " -f2) = $f &&
                p=$(grep $f actual | cut "-d    " -f1) &&
                test -f $p &&
-               test $(cat $p) = tree1$f
+               test $(cat $p) = tree1$f || return 1
        done
 '
 
@@ -85,7 +85,7 @@ test_expect_success 'checkout all stage 2 to temporary files' '
                test $(grep $f actual | cut "-d " -f2) = $f &&
                p=$(grep $f actual | cut "-d    " -f1) &&
                test -f $p &&
-               test $(cat $p) = tree2$f
+               test $(cat $p) = tree2$f || return 1
        done
 '
 
index 0e7d47ab318338a211bf444f890176086b9fed5d..42601d5a310de33185c40f928b566a45c9985f1c 100755 (executable)
@@ -49,14 +49,14 @@ test_expect_success '"checkout -" detaches again' '
 test_expect_success 'more switches' '
        for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
        do
-               git checkout -b branch$i
+               git checkout -b branch$i || return 1
        done
 '
 
 more_switches () {
        for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
        do
-               git checkout branch$i
+               git checkout branch$i || return 1
        done
 }
 
index f3371e264605626b163b39a240b91717613a0826..947d1587ac8be9a2ab333049a2196c4e211185e6 100755 (executable)
@@ -63,8 +63,17 @@ test_expect_success '--orphan ignores branch.autosetupmerge' '
        git checkout main &&
        git config branch.autosetupmerge always &&
        git checkout --orphan gamma &&
-       test -z "$(git config branch.gamma.merge)" &&
+       test_cmp_config "" --default "" branch.gamma.merge &&
        test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
+       test_must_fail git rev-parse --verify HEAD^ &&
+       git checkout main &&
+       git config branch.autosetupmerge inherit &&
+       git checkout --orphan eta &&
+       test_cmp_config "" --default "" branch.eta.merge &&
+       test_cmp_config "" --default "" branch.eta.remote &&
+       echo refs/heads/eta >expected &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expected actual &&
        test_must_fail git rev-parse --verify HEAD^
 '
 
index 93be1c0eae5ead7eeb4f37dab01dbc1c1fbacdba..3e93506c0455c0fc5666a9695282e5ac3a1f32e1 100755 (executable)
@@ -148,7 +148,7 @@ test_expect_success 'checkout -b to an existing branch fails' '
 test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
        git checkout branch1 &&
        git checkout branch2 &&
-       echo  >expect "fatal: A branch named '\''branch1'\'' already exists." &&
+       echo  >expect "fatal: a branch named '\''branch1'\'' already exists" &&
        test_must_fail git checkout -b @{-1} 2>actual &&
        test_cmp expect actual
 '
index fa9e0987063bd409592f45b5f71f67478a2f9f25..8f13341cf8ee0d82e0adeeb28449e31d104a5243 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'checkout --no-overlay removing last file from directory' '
 
 test_expect_success 'checkout -p --overlay is disallowed' '
        test_must_fail git checkout -p --overlay HEAD 2>actual &&
-       test_i18ngrep "fatal: -p and --overlay are mutually exclusive" actual
+       test_i18ngrep "fatal: options .-p. and .--overlay. cannot be used together" actual
 '
 
 test_expect_success '--no-overlay --theirs with D/F conflict deletes file' '
index 9db11f86dd6e901cfb8f3241da3ee5f1710aa1c4..9c651aefbca44283f3ac5f018557d97fb615106c 100755 (executable)
@@ -149,16 +149,16 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --detach" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--detach. cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git checkout --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
index 4453741b966dc5607cf4350b53cd440cbcab942d..dca35aa3e3e264002703932e56add4322b475fcf 100755 (executable)
@@ -24,4 +24,27 @@ test_expect_success 'checkout --track -b rejects an extra path argument' '
        test_i18ngrep "cannot be used with updating paths" err
 '
 
+test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
+       # Set up tracking config on main
+       test_config branch.main.remote origin &&
+       test_config branch.main.merge refs/heads/some-branch &&
+       test_config branch.autoSetupMerge inherit &&
+       # With --track=inherit, we copy the tracking config from main
+       git checkout --track=inherit -b b1 main &&
+       test_cmp_config origin branch.b1.remote &&
+       test_cmp_config refs/heads/some-branch branch.b1.merge &&
+       # With branch.autoSetupMerge=inherit, we do the same
+       git checkout -b b2 main &&
+       test_cmp_config origin branch.b2.remote &&
+       test_cmp_config refs/heads/some-branch branch.b2.merge &&
+       # But --track overrides this
+       git checkout --track -b b3 main &&
+       test_cmp_config . branch.b3.remote &&
+       test_cmp_config refs/heads/main branch.b3.merge &&
+       # And --track=direct does as well
+       git checkout --track=direct -b b4 main &&
+       test_cmp_config . branch.b4.remote &&
+       test_cmp_config refs/heads/main branch.b4.merge
+'
+
 test_done
index 9bc6a3aa5cd9afbae1bb8b136f2e6e834b09b103..ebb961be293ef935d88c3ad3b8056725d248684b 100755 (executable)
@@ -107,4 +107,32 @@ test_expect_success 'not switching when something is in progress' '
        test_must_fail git switch -d @^
 '
 
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+       # default config does not copy tracking info
+       git switch -c foo-no-inherit foo &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+       # with --track=inherit, we copy tracking info from foo
+       git switch --track=inherit -c foo2 foo &&
+       test_cmp_config origin branch.foo2.remote &&
+       test_cmp_config refs/heads/foo branch.foo2.merge &&
+       # with autoSetupMerge=inherit, we do the same
+       test_config branch.autoSetupMerge inherit &&
+       git switch -c foo3 foo &&
+       test_cmp_config origin branch.foo3.remote &&
+       test_cmp_config refs/heads/foo branch.foo3.merge &&
+       # with --track, we override autoSetupMerge
+       git switch --track -c foo4 foo &&
+       test_cmp_config . branch.foo4.remote &&
+       test_cmp_config refs/heads/foo branch.foo4.merge &&
+       # and --track=direct does as well
+       git switch --track=direct -c foo5 foo &&
+       test_cmp_config . branch.foo5.remote &&
+       test_cmp_config refs/heads/foo branch.foo5.merge &&
+       # no tracking info to inherit from main
+       git switch -c main2 main &&
+       test_cmp_config "" --default "" branch.main2.remote &&
+       test_cmp_config "" --default "" branch.main2.merge
+'
+
 test_done
index b48345bf95f4a34df369d385e74e49d94be96e5f..c22669b39f938d901555e2c9a43d8d1f07de6da8 100755 (executable)
@@ -152,13 +152,13 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git restore --pathspec-from-file=list --patch --source=HEAD^1 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git restore --pathspec-from-file=list --source=HEAD^1 -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git restore --pathspec-file-nul --source=HEAD^1 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git restore --pathspec-from-file=empty_list --source=HEAD^1 2>err &&
        test_i18ngrep -e "you must specify path(s) to restore" err
index 2df3fdde8bf665a2b531dd367b70a7a767ee3dbc..7915e7b8211dbb16f49c9f1e5f0433c6f3230e58 100755 (executable)
@@ -22,6 +22,7 @@ and tries to git update-index --add the following:
 All of the attempts should fail.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mkdir path2 path3
index 6c32d42c8c688845682d578141dbef760a663215..e3c7acdbf9125d68d72d983ed692ce6851f785bd 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='git update-index --again test.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'update-index --add' '
index 22f2c730ae8dbf995605e9134eeb2a5bc84065db..c49cdfb6e582f41ea4010df7f4556f7aad5f1cc4 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git update-index on filesystem w/o symlinks test.
 This tests that git update-index keeps the symbolic link property
 even if a plain file is in the working tree if core.symlinks is false.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
@@ -25,7 +26,7 @@ test_expect_success \
 'the index entry must still be a symbolic link' '
 case "$(git ls-files --stage --cached symlink)" in
 120000" "*symlink) echo pass;;
-*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
+*) echo fail; git ls-files --stage --cached symlink; false;;
 esac'
 
 test_done
index 0114f052280d4022a3b47e427b2da3df5547f24c..e9451cd567cd61ffb678ee33d3c01e646b4cbe3a 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='update-index with options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success basics '
@@ -23,7 +24,7 @@ test_expect_success basics '
        test_cmp expect actual &&
 
        git update-index --add one two three &&
-       for i in one three two; do echo $i; done >expect &&
+       test_write_lines one three two >expect &&
        git ls-files >actual &&
        test_cmp expect actual &&
 
index 30666fc70d28264d036105659871a68323ce2460..b8686aabd38b5ac8a60753bff80fddd4e91a5102 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='skip-worktree bit test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 sane_unset GIT_TEST_SPLIT_INDEX
index a7f3d47aec2591f9da19ce24b2796005ddf87096..963ebe77eb6b49b733f249b4ee93020b35ca4097 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='git update-index for gitlink to .git file.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'submodule with absolute .git file' '
index 94c4cb0672126c6ae0440598a68e99182a4cffbf..acd3650d3c08cc2ef8227c95d6c89dfd0f063b2f 100755 (executable)
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -150,13 +151,13 @@ test_expect_success 'add -u resolves unmerged paths' '
        {
                for path in path1 path2
                do
-                       echo "100644 $one 1     $path"
-                       echo "100644 $two 2     $path"
-                       echo "100644 $three 3   $path"
-               done
-               echo "100644 $one 1     path3"
-               echo "100644 $one 1     path4"
-               echo "100644 $one 3     path5"
+                       echo "100644 $one 1     $path" &&
+                       echo "100644 $two 2     $path" &&
+                       echo "100644 $three 3   $path" || return 1
+               done &&
+               echo "100644 $one 1     path3" &&
+               echo "100644 $one 1     path4" &&
+               echo "100644 $one 3     path5" &&
                echo "100644 $one 3     path6"
        } |
        git update-index --index-info &&
@@ -173,8 +174,8 @@ test_expect_success 'add -u resolves unmerged paths' '
        git add -u &&
        git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
        {
-               echo "100644 $three 0   path1"
-               echo "100644 $two 0     path3"
+               echo "100644 $three 0   path1" &&
+               echo "100644 $two 0     path3" &&
                echo "100644 $two 0     path5"
        } >expect &&
        test_cmp expect actual
index a4eec0a3465bc02fb5c88e007ee4e32aa9c2794f..dba62d69c6c5c18172bdbc62c5138643cf284b00 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='more git add -u'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -97,17 +98,17 @@ test_expect_success modify '
                "
        } >expect &&
        {
-               cat expect
-               echo ":100644 160000 $_empty $ZERO_OID T        yonk"
+               cat expect &&
+               echo ":100644 160000 $_empty $ZERO_OID T        yonk" &&
                echo ":100644 000000 $_empty $ZERO_OID D        zifmia"
        } >expect-files &&
        {
-               cat expect
+               cat expect &&
                echo ":000000 160000 $ZERO_OID $ZERO_OID A      yonk"
        } >expect-index &&
        {
-               echo "100644 $_empty 0  nitfol"
-               echo "160000 $yomin 0   yomin"
+               echo "100644 $_empty 0  nitfol" &&
+               echo "160000 $yomin 0   yomin" &&
                echo "160000 $yonk 0    yonk"
        } >expect-final
 '
index 9ee659098c45fbc18dfb5ccc2292f978320c1ebb..24c60bfd7905ba136ad59f090b40fe23a37d0a1e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git add --all'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index cf0175ad6e414e3a126a205d56a9d910ec173641..db7ca55998666138f3580fe430dfe53ece43be13 100755 (executable)
@@ -116,7 +116,7 @@ test_expect_success 'cache-tree does not ignore dir that has i-t-a entries' '
                mkdir 2 &&
                for f in 1 2/1 2/2 3
                do
-                       echo "$f" >"$f"
+                       echo "$f" >"$f" || return 1
                done &&
                git add 1 2/2 3 &&
                git add -N 2/1 &&
index 2e07365bbb055d27f558c78d26f657e314397398..89424abccd1c5663b8c2a8a6c3b023831887f330 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='giving ignored paths to git add'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index a615d3b4838192af5acac8afd9a19e662553e409..3d28c7f06b2c8717e31fae23d37fda0acc008a48 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success 'worktree prune on normal repo' '
 test_expect_success 'prune files inside $GIT_DIR/worktrees' '
        mkdir .git/worktrees &&
        : >.git/worktrees/abc &&
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        cat >expect <<EOF &&
 Removing worktrees/abc: not a valid directory
 EOF
@@ -34,7 +34,7 @@ test_expect_success 'prune directories without gitdir' '
        cat >expect <<EOF &&
 Removing worktrees/def: gitdir file does not exist
 EOF
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        test_cmp expect actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
@@ -45,7 +45,7 @@ test_expect_success SANITY 'prune directories with unreadable gitdir' '
        : >.git/worktrees/def/def &&
        : >.git/worktrees/def/gitdir &&
        chmod u-r .git/worktrees/def/gitdir &&
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
@@ -55,7 +55,7 @@ test_expect_success 'prune directories with invalid gitdir' '
        mkdir -p .git/worktrees/def/abc &&
        : >.git/worktrees/def/def &&
        : >.git/worktrees/def/gitdir &&
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
@@ -65,7 +65,7 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
        mkdir -p .git/worktrees/def/abc &&
        : >.git/worktrees/def/def &&
        echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
@@ -101,7 +101,7 @@ test_expect_success 'prune duplicate (linked/linked)' '
        git worktree add --detach w2 &&
        sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&
        mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
-       git worktree prune --verbose >actual &&
+       git worktree prune --verbose 2>actual &&
        test_i18ngrep "duplicate entry" actual &&
        test -d .git/worktrees/w1 &&
        ! test -d .git/worktrees/w2
@@ -114,7 +114,7 @@ test_expect_success 'prune duplicate (main/linked)' '
        git -C repo worktree add --detach ../wt &&
        rm -fr wt &&
        mv repo wt &&
-       git -C wt worktree prune --verbose >actual &&
+       git -C wt worktree prune --verbose 2>actual &&
        test_i18ngrep "duplicate entry" actual &&
        ! test -d .git/worktrees/wt
 '
index 4012bd67b04445dd7012e75bcb9bee4057c58ec5..c8a5a0aac6dc25256536d5b18d9c7979d58d7526 100755 (executable)
@@ -134,7 +134,7 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
        git worktree list >out &&
        grep "/prunable  *[0-9a-f].* prunable$" out &&
        ! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
-       git worktree prune --verbose >out &&
+       git worktree prune --verbose 2>out &&
        test_i18ngrep "^Removing worktrees/prunable" out &&
        test_i18ngrep ! "^Removing worktrees/unprunable" out
 '
index 9536d1091954b48a87b74eead0c382ce70355c4e..842937bfb9a8a660bf0696df324ec71f6693ae5b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description="config file in multi worktree"
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index f73741886b6d60e9fb583ce9e1b2606dbb00b31a..5c44453e1c13322e1fcf30d2facac3c2942a274e 100755 (executable)
@@ -45,9 +45,8 @@ test_corrupt_gitfile () {
        git worktree add --detach corrupt &&
        git -C corrupt rev-parse --absolute-git-dir >expect &&
        eval "$butcher" &&
-       git -C "$repairdir" worktree repair >out 2>err &&
-       test_i18ngrep "$problem" out &&
-       test_must_be_empty err &&
+       git -C "$repairdir" worktree repair 2>err &&
+       test_i18ngrep "$problem" err &&
        git -C corrupt rev-parse --absolute-git-dir >actual &&
        test_cmp expect actual
 }
@@ -130,10 +129,9 @@ test_expect_success 'repair broken gitdir' '
        sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
        rm .git/worktrees/orig/gitdir &&
        mv orig moved &&
-       git worktree repair moved >out 2>err &&
+       git worktree repair moved 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir unreadable" out &&
-       test_must_be_empty err
+       test_i18ngrep "gitdir unreadable" err
 '
 
 test_expect_success 'repair incorrect gitdir' '
@@ -141,10 +139,9 @@ test_expect_success 'repair incorrect gitdir' '
        git worktree add --detach orig &&
        sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
        mv orig moved &&
-       git worktree repair moved >out 2>err &&
+       git worktree repair moved 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir incorrect" out &&
-       test_must_be_empty err
+       test_i18ngrep "gitdir incorrect" err
 '
 
 test_expect_success 'repair gitdir (implicit) from linked worktree' '
@@ -152,10 +149,9 @@ test_expect_success 'repair gitdir (implicit) from linked worktree' '
        git worktree add --detach orig &&
        sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
        mv orig moved &&
-       git -C moved worktree repair >out 2>err &&
+       git -C moved worktree repair 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_i18ngrep "gitdir incorrect" out &&
-       test_must_be_empty err
+       test_i18ngrep "gitdir incorrect" err
 '
 
 test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
@@ -163,9 +159,8 @@ test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
        git worktree add --detach orig &&
        cat .git/worktrees/orig/gitdir >expect &&
        mv orig moved &&
-       git worktree repair >out 2>err &&
+       git worktree repair 2>err &&
        test_cmp expect .git/worktrees/orig/gitdir &&
-       test_must_be_empty out &&
        test_must_be_empty err
 '
 
@@ -178,12 +173,11 @@ test_expect_success 'repair multiple gitdir files' '
        sed s,orig2/\.git$,moved2/.git, .git/worktrees/orig2/gitdir >expect2 &&
        mv orig1 moved1 &&
        mv orig2 moved2 &&
-       git worktree repair moved1 moved2 >out 2>err &&
+       git worktree repair moved1 moved2 2>err &&
        test_cmp expect1 .git/worktrees/orig1/gitdir &&
        test_cmp expect2 .git/worktrees/orig2/gitdir &&
-       test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" out &&
-       test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" out &&
-       test_must_be_empty err
+       test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" err &&
+       test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" err
 '
 
 test_expect_success 'repair moved main and linked worktrees' '
diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh
new file mode 100755 (executable)
index 0000000..f6d8d7d
--- /dev/null
@@ -0,0 +1,277 @@
+#!/bin/sh
+
+test_description='Test handling of the current working directory becoming empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit init &&
+
+       git branch fd_conflict &&
+
+       mkdir -p foo/bar &&
+       test_commit foo/bar/baz &&
+
+       git revert HEAD &&
+       git tag reverted &&
+
+       git checkout fd_conflict &&
+       mkdir dirORfile &&
+       test_commit dirORfile/foo &&
+
+       git rm -r dirORfile &&
+       echo not-a-directory >dirORfile &&
+       git add dirORfile &&
+       git commit -m dirORfile &&
+
+       git switch -c df_conflict HEAD~1 &&
+       test_commit random_file &&
+
+       git switch -c undo_fd_conflict fd_conflict &&
+       git revert HEAD
+'
+
+test_incidental_dir_removal () {
+       test_when_finished "git reset --hard" &&
+
+       git checkout foo/bar/baz^{commit} &&
+       test_path_is_dir foo/bar &&
+
+       (
+               cd foo &&
+               "$@" &&
+
+               # Make sure foo still exists, and commands needing it work
+               test-tool getcwd &&
+               git status --porcelain
+       ) &&
+       test_path_is_missing foo/bar/baz &&
+       test_path_is_missing foo/bar &&
+
+       test_path_is_dir foo
+}
+
+test_required_dir_removal () {
+       git checkout df_conflict^{commit} &&
+       test_when_finished "git clean -fdx" &&
+
+       (
+               cd dirORfile &&
+
+               # Ensure command refuses to run
+               test_must_fail "$@" 2>../error &&
+               grep "Refusing to remove.*current working directory" ../error &&
+
+               # ...and that the index and working tree are left clean
+               git diff --exit-code HEAD &&
+
+               # Ensure that getcwd and git status do not error out (which
+               # they might if the current working directory had been removed)
+               test-tool getcwd &&
+               git status --porcelain
+       ) &&
+
+       test_path_is_dir dirORfile
+}
+
+test_expect_success 'checkout does not clean cwd incidentally' '
+       test_incidental_dir_removal git checkout init
+'
+
+test_expect_success 'checkout fails if cwd needs to be removed' '
+       test_required_dir_removal git checkout fd_conflict
+'
+
+test_expect_success 'reset --hard does not clean cwd incidentally' '
+       test_incidental_dir_removal git reset --hard init
+'
+
+test_expect_success 'reset --hard fails if cwd needs to be removed' '
+       test_required_dir_removal git reset --hard fd_conflict
+'
+
+test_expect_success 'merge does not clean cwd incidentally' '
+       test_incidental_dir_removal git merge reverted
+'
+
+# This file uses some simple merges where
+#   Base: 'dirORfile/' exists
+#   Side1: random other file changed
+#   Side2: 'dirORfile/' removed, 'dirORfile' added
+# this should resolve cleanly, but merge-recursive throws merge conflicts
+# because it's dumb.  Add a special test for checking merge-recursive (and
+# merge-ort), then after this just hard require ort for all remaining tests.
+#
+test_expect_success 'merge fails if cwd needs to be removed; recursive friendly' '
+       git checkout foo/bar/baz &&
+       test_when_finished "git clean -fdx" &&
+
+       mkdir dirORfile &&
+       (
+               cd dirORfile &&
+
+               test_must_fail git merge fd_conflict 2>../error
+       ) &&
+
+       test_path_is_dir dirORfile &&
+       grep "Refusing to remove the current working directory" error
+'
+
+GIT_TEST_MERGE_ALGORITHM=ort
+
+test_expect_success 'merge fails if cwd needs to be removed' '
+       test_required_dir_removal git merge fd_conflict
+'
+
+test_expect_success 'cherry-pick does not clean cwd incidentally' '
+       test_incidental_dir_removal git cherry-pick reverted
+'
+
+test_expect_success 'cherry-pick fails if cwd needs to be removed' '
+       test_required_dir_removal git cherry-pick fd_conflict
+'
+
+test_expect_success 'rebase does not clean cwd incidentally' '
+       test_incidental_dir_removal git rebase reverted
+'
+
+test_expect_success 'rebase fails if cwd needs to be removed' '
+       test_required_dir_removal git rebase fd_conflict
+'
+
+test_expect_success 'revert does not clean cwd incidentally' '
+       test_incidental_dir_removal git revert HEAD
+'
+
+test_expect_success 'revert fails if cwd needs to be removed' '
+       test_required_dir_removal git revert undo_fd_conflict
+'
+
+test_expect_success 'rm does not clean cwd incidentally' '
+       test_incidental_dir_removal git rm bar/baz.t
+'
+
+test_expect_success 'apply does not remove cwd incidentally' '
+       git diff HEAD HEAD~1 >patch &&
+       test_incidental_dir_removal git apply ../patch
+'
+
+test_incidental_untracked_dir_removal () {
+       test_when_finished "git reset --hard" &&
+
+       git checkout foo/bar/baz^{commit} &&
+       mkdir -p untracked &&
+       mkdir empty
+       >untracked/random &&
+
+       (
+               cd untracked &&
+               "$@" &&
+
+               # Make sure untracked still exists, and commands needing it work
+               test-tool getcwd &&
+               git status --porcelain
+       ) &&
+       test_path_is_missing empty &&
+       test_path_is_missing untracked/random &&
+
+       test_path_is_dir untracked
+}
+
+test_expect_success 'clean does not remove cwd incidentally' '
+       test_incidental_untracked_dir_removal \
+               git -C .. clean -fd -e warnings . >warnings &&
+       grep "Refusing to remove current working directory" warnings
+'
+
+test_expect_success 'stash does not remove cwd incidentally' '
+       test_incidental_untracked_dir_removal \
+               git stash --include-untracked
+'
+
+test_expect_success '`rm -rf dir` only removes a subset of dir' '
+       test_when_finished "rm -rf a/" &&
+
+       mkdir -p a/b/c &&
+       >a/b/c/untracked &&
+       >a/b/c/tracked &&
+       git add a/b/c/tracked &&
+
+       (
+               cd a/b &&
+               git rm -rf ../b
+       ) &&
+
+       test_path_is_dir a/b &&
+       test_path_is_missing a/b/c/tracked &&
+       test_path_is_file a/b/c/untracked
+'
+
+test_expect_success '`rm -rf dir` even with only tracked files will remove something else' '
+       test_when_finished "rm -rf a/" &&
+
+       mkdir -p a/b/c &&
+       >a/b/c/tracked &&
+       git add a/b/c/tracked &&
+
+       (
+               cd a/b &&
+               git rm -rf ../b
+       ) &&
+
+       test_path_is_missing a/b/c/tracked &&
+       test_path_is_missing a/b/c &&
+       test_path_is_dir a/b
+'
+
+test_expect_success 'git version continues working from a deleted dir' '
+       mkdir tmp &&
+       (
+               cd tmp &&
+               rm -rf ../tmp &&
+               git version
+       )
+'
+
+test_submodule_removal () {
+       path_status=$1 &&
+       shift &&
+
+       test_status=
+       test "$path_status" = dir && test_status=test_must_fail
+
+       test_when_finished "git reset --hard HEAD~1" &&
+       test_when_finished "rm -rf .git/modules/my_submodule" &&
+
+       git checkout foo/bar/baz &&
+
+       git init my_submodule &&
+       touch my_submodule/file &&
+       git -C my_submodule add file &&
+       git -C my_submodule commit -m "initial commit" &&
+       git submodule add ./my_submodule &&
+       git commit -m "Add the submodule" &&
+
+       (
+               cd my_submodule &&
+               $test_status "$@"
+       ) &&
+
+       test_path_is_${path_status} my_submodule
+}
+
+test_expect_success 'rm -r with -C leaves submodule if cwd inside' '
+       test_submodule_removal dir git -C .. rm -r my_submodule/
+'
+
+test_expect_success 'rm -r leaves submodule if cwd inside' '
+       test_submodule_removal dir \
+               git --git-dir=../.git --work-tree=.. rm -r ../my_submodule/
+'
+
+test_expect_success 'rm -rf removes submodule even if cwd inside' '
+       test_submodule_removal missing \
+               git --git-dir=../.git --work-tree=.. rm -rf ../my_submodule/
+'
+
+test_done
index 6ba8b589cd00d3ad401f4018dc0eed7be1b54e05..fbfa210a50b2906e57853095a2830f3bc6e18136 100755 (executable)
@@ -39,10 +39,7 @@ test_expect_success 'ls-files with mixed levels' '
 test_expect_success 'ls-files -c' '
        (
                cd top/sub &&
-               for f in ../y*
-               do
-                       echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git"
-               done >expect.err &&
+               printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../y* >expect.err &&
                echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err &&
                ls ../x* >expect.out &&
                test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
@@ -54,10 +51,7 @@ test_expect_success 'ls-files -c' '
 test_expect_success 'ls-files -o' '
        (
                cd top/sub &&
-               for f in ../x*
-               do
-                       echo "error: pathspec $SQ$f$SQ did not match any file(s) known to git"
-               done >expect.err &&
+               printf "error: pathspec $SQ%s$SQ did not match any file(s) known to git\n" ../x* >expect.err &&
                echo "Did you forget to ${SQ}git add${SQ}?" >>expect.err &&
                ls ../y* >expect.out &&
                test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
index 72d5b014d82c6c3fbdbf693a791e8599138594c0..f9539968e4c16a53e2d0add8b448c7338925828f 100755 (executable)
@@ -193,7 +193,7 @@ match() {
                file=$(cat .git/expected_test_file) &&
                if should_create_test_file "$file"
                then
-                       dirs=${file%/*}
+                       dirs=${file%/*} &&
                        if test "$file" != "$dirs"
                        then
                                mkdir -p -- "$dirs" &&
index 8c5c1ccf3300432fb846a2a78488b33c9d752f7b..1bc3795847dbbbe810e230dee5313d0b20b641aa 100755 (executable)
@@ -168,6 +168,13 @@ test_expect_success 'git branch -M foo bar should fail when bar is checked out'
        test_must_fail git branch -M bar foo
 '
 
+test_expect_success 'git branch -M foo bar should fail when bar is checked out in worktree' '
+       git branch -f bar &&
+       test_when_finished "git worktree remove wt && git branch -D wt" &&
+       git worktree add wt &&
+       test_must_fail git branch -M bar wt
+'
+
 test_expect_success 'git branch -M baz bam should succeed when baz is checked out' '
        git checkout -b baz &&
        git branch bam &&
@@ -888,7 +895,7 @@ test_expect_success '--set-upstream-to fails on a missing src branch' '
 '
 
 test_expect_success '--set-upstream-to fails on a non-ref' '
-       echo "fatal: Cannot setup tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch." >expect &&
+       echo "fatal: cannot set up tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch" >expect &&
        test_must_fail git branch --set-upstream-to HEAD^{} 2>err &&
        test_cmp expect err
 '
@@ -972,15 +979,15 @@ test_expect_success 'disabled option --set-upstream fails' '
        test_must_fail git branch --set-upstream origin/main
 '
 
-test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
+test_expect_success '--set-upstream-to notices an error to set branch as own upstream' "
        git branch --set-upstream-to refs/heads/my13 my13 2>actual &&
        cat >expect <<-\EOF &&
-       warning: Not setting branch my13 as its own upstream.
+       warning: not setting branch 'my13' as its own upstream
        EOF
        test_expect_code 1 git config branch.my13.remote &&
        test_expect_code 1 git config branch.my13.merge &&
        test_cmp expect actual
-'
+"
 
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
@@ -1454,4 +1461,37 @@ test_expect_success 'invalid sort parameter in configuration' '
        )
 '
 
+test_expect_success 'tracking info copied with --track=inherit' '
+       git branch --track=inherit foo2 my1 &&
+       test_cmp_config local branch.foo2.remote &&
+       test_cmp_config refs/heads/main branch.foo2.merge
+'
+
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+       test_unconfig branch.autoSetupMerge &&
+       # default config does not copy tracking info
+       git branch foo-no-inherit my1 &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+       # with autoSetupMerge=inherit, we copy tracking info from my1
+       test_config branch.autoSetupMerge inherit &&
+       git branch foo3 my1 &&
+       test_cmp_config local branch.foo3.remote &&
+       test_cmp_config refs/heads/main branch.foo3.merge &&
+       # no tracking info to inherit from main
+       git branch main2 main &&
+       test_cmp_config "" --default "" branch.main2.remote &&
+       test_cmp_config "" --default "" branch.main2.merge
+'
+
+test_expect_success '--track overrides branch.autoSetupMerge' '
+       test_config branch.autoSetupMerge inherit &&
+       git branch --track=direct foo4 my1 &&
+       test_cmp_config . branch.foo4.remote &&
+       test_cmp_config refs/heads/my1 branch.foo4.merge &&
+       git branch --no-track foo5 my1 &&
+       test_cmp_config "" --default "" branch.foo5.remote &&
+       test_cmp_config "" --default "" branch.foo5.merge
+'
+
 test_done
index 349a810cee11df36dda3c8ed4bbca3df58040001..800fc33165a9eff0abe1f6edc27acb2b4978e317 100755 (executable)
@@ -2,9 +2,6 @@
 
 test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 test_expect_success setup '
index ad9902a06b9f20d9e4d86b676e30fdd906835e67..7a1be73ce8771b851dfc2c7a95d80dece4c2b5cb 100755 (executable)
@@ -4,12 +4,15 @@ test_description='test show-branch'
 
 . ./test-lib.sh
 
+# arbitrary reference time: 2009-08-30 19:20:00
+GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
+
 test_expect_success 'setup' '
        test_commit initial &&
        for i in $(test_seq 1 10)
        do
                git checkout -b branch$i initial &&
-               test_commit --no-tag branch$i
+               test_commit --no-tag branch$i || return 1
        done &&
        git for-each-ref \
                --sort=version:refname \
@@ -49,7 +52,7 @@ test_expect_success 'show-branch with more than 8 branches' '
 test_expect_success 'show-branch with showbranch.default' '
        for branch in $(cat branches.sorted)
        do
-               test_config showbranch.default $branch --add
+               test_config showbranch.default $branch --add || return 1
        done &&
        git show-branch >actual &&
        test_cmp expect actual
@@ -124,7 +127,7 @@ test_expect_success 'show branch --merge-base with one argument' '
        do
                git rev-parse $branch >expect &&
                git show-branch --merge-base $branch >actual &&
-               test_cmp expect actual
+               test_cmp expect actual || return 1
        done
 '
 
@@ -133,7 +136,7 @@ test_expect_success 'show branch --merge-base with two arguments' '
        do
                git rev-parse initial >expect &&
                git show-branch --merge-base initial $branch >actual &&
-               test_cmp expect actual
+               test_cmp expect actual || return 1
        done
 '
 
@@ -146,4 +149,16 @@ test_expect_success 'show branch --merge-base with N arguments' '
        test_cmp expect actual
 '
 
+test_expect_success 'show branch --reflog=2' '
+       sed "s/^>       //" >expect <<-\EOF &&
+       >       ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
+       >        ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
+       >       --
+       >       +  [refs/heads/branch10@{0}] branch10
+       >       ++ [refs/heads/branch10@{1}] initial
+       EOF
+       git show-branch --reflog=2 >actual &&
+       test_cmp actual expect
+'
+
 test_done
index 6e94c6db7b5aa987c29364f32fee8c0d52538adf..d34d77f89348d86518375a66e277bb118ff29c22 100755 (executable)
@@ -1,9 +1,6 @@
 #!/bin/sh
 
 test_description='git branch display tests'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
index 6a521c1a3e5225716c14656aa9230afaf5a7829d..0b61da92b37763213e4cee3744f127971cb6fcb1 100755 (executable)
@@ -1,9 +1,6 @@
 #!/bin/sh
 
 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
 
index ef8b63952e3bc71ed5b7a8c416e85146de4f43a9..bc9d8ee1e6a8b223f084cdc405a52048630ba5b1 100755 (executable)
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
index d47ce00f6944951fbc3b9d09ddf701771a13db7d..7e0a8960af886fb83d555766919e34fcd532bf7c 100755 (executable)
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
@@ -30,7 +31,7 @@ verify_notes () {
        while [ $i -gt 0 ]; do
                echo "    commit #$i" &&
                echo "    note for commit #$i" &&
-               i=$(($i-1));
+               i=$(($i-1)) || return 1
        done > expect &&
        test_cmp expect output
 }
@@ -42,7 +43,7 @@ test_expect_success "setup: create $number_of_commits commits" '
                while [ $nr -lt $number_of_commits ]; do
                        nr=$(($nr+1)) &&
                        test_tick &&
-                       cat <<INPUT_END
+                       cat <<INPUT_END || return 1
 commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
@@ -178,7 +179,7 @@ verify_concatenated_notes () {
                echo "    first note for commit #$i" &&
                echo "    " &&
                echo "    second note for commit #$i" &&
-               i=$(($i-1));
+               i=$(($i-1)) || return 1
        done > expect &&
        test_cmp expect output
 }
index 960d0587e189a57049d64688c995bff580ac42ef..1f5964865ae173e05fab2070c179ff305886bf82 100755 (executable)
@@ -58,7 +58,7 @@ test_expect_success 'many notes created correctly with git-notes' '
        do
                echo "    commit #$i" &&
                echo "    note #$i" &&
-               i=$(($i - 1));
+               i=$(($i - 1)) || return 1
        done > expect &&
        test_cmp expect output
 '
@@ -107,7 +107,7 @@ test_expect_success 'most notes deleted correctly with git-notes' '
        do
                echo "    commit #$i" &&
                echo "    note #$i" &&
-               i=$(($i - 1));
+               i=$(($i - 1)) || return 1
        done > expect &&
        test_cmp expect output
 '
index 6b2d507f3e7f0ef9ed9db25a6129259627ecacfc..bff0aea550f285a6c2073efd50182cbc5fab8656 100755 (executable)
@@ -8,6 +8,7 @@ test_description='Test merging of notes trees in multiple worktrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commit' '
index cfde68f1939baeba49dc1b0d34d2c666042f9177..7e46f4ca850616b695cc6ffa061ca39ac404fb7d 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_success 'merge and rebase should match' '
        if test -s difference
        then
                cat difference
-               (exit 1)
+               false
        else
                echo happy
        fi
@@ -102,7 +102,7 @@ test_expect_success 'merge and rebase should match' '
        if test -s difference
        then
                cat difference
-               (exit 1)
+               false
        else
                echo happy
        fi
@@ -117,7 +117,7 @@ test_expect_success 'picking rebase' '
                echo happy
        else
                git show-branch
-               (exit 1)
+               false
        fi &&
        f=$(git diff-tree --name-only HEAD^ HEAD) &&
        g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
@@ -127,7 +127,7 @@ test_expect_success 'picking rebase' '
        *)
                echo "$f"
                echo "$g"
-               (exit 1)
+               false
        esac
 '
 
index 12eb226957676e7a1de02c0c22f629c3dc78ba74..a38f2da7691e870f8dfa90936c0107d7d080e0d1 100755 (executable)
@@ -25,8 +25,6 @@ Initial setup:
  where A, B, D and G all touch file1, and one, two, three, four all
  touch file "conflict".
 '
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
@@ -826,7 +824,7 @@ test_expect_success 'always cherry-pick with --no-ff' '
        do
                test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
                git diff HEAD~$p original-no-ff-branch~$p > out &&
-               test_must_be_empty out
+               test_must_be_empty out || return 1
        done &&
        test_cmp_rev HEAD~3 original-no-ff-branch~3 &&
        git diff HEAD~3 original-no-ff-branch~3 > out &&
@@ -1341,7 +1339,7 @@ test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
                test_seq 5 | sed "s/$double/&&/" >seq &&
                git add seq &&
                test_tick &&
-               git commit -m seq-$double
+               git commit -m seq-$double || return 1
        done &&
        git tag seq-onto &&
        git reset --hard HEAD~2 &&
diff --git a/t/t3409-rebase-environ.sh b/t/t3409-rebase-environ.sh
new file mode 100755 (executable)
index 0000000..83ffb39
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='git rebase interactive environment'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three
+'
+
+test_expect_success 'rebase --exec does not muck with GIT_DIR' '
+       git rebase --exec "printf %s \$GIT_DIR >environ" HEAD~1 &&
+       test_must_be_empty environ
+'
+
+test_expect_success 'rebase --exec does not muck with GIT_WORK_TREE' '
+       git rebase --exec "printf %s \$GIT_WORK_TREE >environ" HEAD~1 &&
+       test_must_be_empty environ
+'
+
+test_done
index 946e92f8dac84edcad260d4f7354b996e54b61ec..96f2cf22fafd4795e54928b14b5c2b4b2333e8f3 100755 (executable)
@@ -115,9 +115,7 @@ test_expect_success 'at beginning of file' '
        git config core.whitespace "blank-at-eol" &&
        cp beginning file &&
        git commit -m beginning file &&
-       for i in 1 2 3 4 5; do
-               echo $i
-       done >> file &&
+       test_write_lines 1 2 3 4 5 >>file &&
        git commit -m more file &&
        git rebase --whitespace=fix HEAD^^ &&
        test_cmp expect-beginning file
index 4c98d99e7e87476811877b4685f2f5a5eefe0f35..1d0b15380edf3195b10e25a7d8d36f93330b95f5 100755 (executable)
@@ -83,7 +83,7 @@ test_expect_success 'git rebase --fork-point with ambigous refname' '
 
 test_expect_success '--fork-point and --root both given' '
        test_must_fail git rebase --fork-point --root 2>err &&
-       test_i18ngrep "cannot combine" err
+       test_i18ngrep "cannot be used together" err
 '
 
 test_expect_success 'rebase.forkPoint set to false' '
index 4b5b607673329d30ef71c26f673c36ae0058cc67..8617efaaf1e66f6f5d8246a817085ebd2d43c477 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success setup '
 
        for l in a b c d e f g h i j k l m n o
        do
-               echo $l$l$l$l$l$l$l$l$l
+               echo $l$l$l$l$l$l$l$l$l || return 1
        done >oops &&
 
        test_tick &&
index e8375d1c970e3313302a2580d742f1e88732070c..2d53ce754c5fb75ceeaa43dc1ba2f748fd58f43e 100755 (executable)
@@ -29,7 +29,7 @@ test_expect_success setup '
                git add file1 &&
                test_tick &&
                git commit -m "$val" &&
-               git tag $val
+               git tag $val || return 1
        done
 '
 
index bb9ef35dac082e396d5cbcbdc83bc5394fb95a7c..e74a318ac33ac00d10e6517b10b838ac8e0c899a 100755 (executable)
@@ -265,7 +265,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S
 
 test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
        choke_git_rm_setup &&
-       OUT=$( ((trap "" PIPE; git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+       OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
        test_match_signal 13 "$OUT" &&
        test_path_is_missing .git/index.lock
 '
@@ -274,10 +274,7 @@ test_expect_success 'Resolving by removal is not a warning-worthy event' '
        git reset -q --hard &&
        test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
        blob=$(echo blob | git hash-object -w --stdin) &&
-       for stage in 1 2 3
-       do
-               echo "100644 $blob $stage       blob"
-       done | git update-index --index-info &&
+       printf "100644 $blob %d\tblob\n" 1 2 3 | git update-index --index-info &&
        git rm blob >msg 2>&1 &&
        test_i18ngrep ! "needs merge" msg &&
        test_must_fail git ls-files -s --error-unmatch blob
index b2a8db69afc69f20c33309e808711c0502467770..a2a0c820fe38a976b963570453df17a089599f9d 100755 (executable)
@@ -67,10 +67,10 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git rm --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        >empty_list &&
        test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
index 283a66955d6dbb8415697a8a911effa6f291f8e9..b1f90ba3250fa39863f6fe58e85b27613e303df4 100755 (executable)
@@ -141,9 +141,9 @@ test_expect_success 'check correct prefix detection' '
 test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '
        for s in 1 2 3
        do
-               echo $s > stage$s
-               echo "100755 $(git hash-object -w stage$s) $s   file"
-               echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s       symlink"
+               echo $s > stage$s &&
+               echo "100755 $(git hash-object -w stage$s) $s   file" &&
+               echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s       symlink" || return 1
        done | git update-index --index-info &&
        git config core.filemode 0 &&
        git config core.symlinks 0 &&
@@ -177,7 +177,7 @@ test_expect_success 'git add --refresh' '
        git read-tree HEAD &&
        case "$(git diff-index HEAD -- foo)" in
        :100644" "*"M   foo") echo pass;;
-       *) echo fail; (exit 1);;
+       *) echo fail; false;;
        esac &&
        git add --refresh -- foo &&
        test -z "$(git diff-index HEAD -- foo)"
index 6c676645d837477077e9e349bf01398f3aa52b5f..a1801a8cbd4185f80253b019eb40a2c6a378d7ab 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='add -e basic tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 
index 3ef525a559d91b115a3dbe4a1373c8c51fb1fc98..d84071038e051552f5676c5c687e4e5e2646a39a 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='magic pathspec tests using git-add'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 9e35c1fbca68b67336ce2725d91b57a981e8b5c7..4e6b5177c9329b11ee9046d75e6cf5b0dbf358df 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='add --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
@@ -137,19 +138,19 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git add --pathspec-from-file=list --interactive 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list --edit 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --edit" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--edit. cannot be used together" err &&
 
        test_must_fail git add --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git add --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        # This case succeeds, but still prints to stderr
        git add --pathspec-from-file=empty_list 2>err &&
index f3143c92908d013e4394a76578b1a3fbd5328ab4..81f3384eeed4bba508f2e87aeb72a1a6f9a37394 100755 (executable)
@@ -181,13 +181,13 @@ test_expect_success 'git add fails outside of sparse-checkout definition' '
        # 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 ls-files --stage >actual &&
+       grep "^100644 .*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 ls-files --stage >actual &&
+       grep "^100755 .*sparse_entry\$" actual &&
 
        git reset &&
 
index 0544d58a6ea5478f046b571f7e249a5942da37e2..e3cf0ffbe59c449b218cafedd74bd3cf07e82ff1 100755 (executable)
@@ -72,7 +72,8 @@ check_verify_failure () {
 
                # Manually create the broken, we cannot do it with
                # update-ref
-               echo "$bad_tag" >"bad-tag/$tag_ref" &&
+               test-tool -C bad-tag ref-store main delete-refs 0 msg "$tag_ref" &&
+               test-tool -C bad-tag ref-store main update-ref msg "$tag_ref" $bad_tag $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
                # Unlike fsck-ing unreachable content above, this
                # will always fail.
@@ -83,7 +84,8 @@ check_verify_failure () {
                # Make sure the earlier test created it for us
                git rev-parse "$bad_tag" &&
 
-               echo "$bad_tag" >"bad-tag/$tag_ref" &&
+               test-tool -C bad-tag ref-store main delete-refs 0 msg "$tag_ref" &&
+               test-tool -C bad-tag ref-store main update-ref msg "$tag_ref" $bad_tag $ZERO_OID REF_SKIP_OID_VERIFICATION &&
 
                printf "%s tag\t%s\n" "$bad_tag" "$tag_ref" >expected &&
                git -C bad-tag for-each-ref "$tag_ref" >actual &&
index 2c66cfbc3b7fbadaa3f8c7a34a38cba2a6eac37f..686747e55a301a661d75c63223a7d682c30dbd4b 100755 (executable)
@@ -10,6 +10,25 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
+test_expect_success 'usage on cmd and subcommand invalid option' '
+       test_expect_code 129 git stash --invalid-option 2>usage &&
+       grep "or: git stash" usage &&
+
+       test_expect_code 129 git stash push --invalid-option 2>usage &&
+       ! grep "or: git stash" usage
+'
+
+test_expect_success 'usage on main command -h emits a summary of subcommands' '
+       test_expect_code 129 git stash -h >usage &&
+       grep -F "usage: git stash list" usage &&
+       grep -F "or: git stash show" usage
+'
+
+test_expect_failure 'usage for subcommands should emit subcommand usage' '
+       test_expect_code 129 git stash push -h >usage &&
+       grep -F "usage: git stash [push" usage
+'
+
 diff_cmp () {
        for i in "$1" "$2"
        do
@@ -1376,4 +1395,28 @@ test_expect_success 'git stash can pop directory -> file saved changes' '
        )
 '
 
+test_expect_success 'restore untracked files even when we hit conflicts' '
+       git init restore_untracked_after_conflict &&
+       (
+               cd restore_untracked_after_conflict &&
+
+               echo hi >a &&
+               echo there >b &&
+               git add . &&
+               git commit -m first &&
+               echo hello >a &&
+               echo something >c &&
+
+               git stash push --include-untracked &&
+
+               echo conflict >a &&
+               git add a &&
+               git commit -m second &&
+
+               test_must_fail git stash pop &&
+
+               test_path_is_file c
+       )
+'
+
 test_done
index 2b2b366ef94b7ca429416d81cc4858201658d3cb..347a89b030b68dc87ecea8f40dfe9d1b0ca8db16 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test git stash in a worktree'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 55e050cfd4db8ffc2ad0826baa5c50fc2734c726..dead9f18d937599427afe2c29fc177ed00f187ec 100755 (executable)
@@ -88,13 +88,13 @@ test_expect_success 'error conditions' '
        echo fileA.t >list &&
 
        test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git stash push --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err
 '
 
 test_done
index a8ad5462d96d3e1acda9172f4f77806d7a3e8093..0276edbe3d389b70cace45906626276cce3db44b 100755 (executable)
@@ -70,7 +70,7 @@ test_crlf_subject_body_and_contents() {
                        for ref in ${LIB_CRLF_BRANCHES}
                        do
                                cat .crlf-${file}-\"\${ref}\".txt >>expect &&
-                               printf \"\n\" >>expect
+                               printf \"\n\" >>expect || return 1
                        done &&
                        git $command_and_args --format=\"%${atom}\" >actual &&
                        test_cmp expect actual
@@ -90,7 +90,7 @@ test_expect_success 'branch: --verbose works with messages using CRLF' '
        do
                printf "  " >>expect &&
                cat .crlf-subject-${branch}.txt >>expect &&
-               printf "\n" >>expect
+               printf "\n" >>expect || return 1
        done &&
        git branch -v >tmp &&
        # Remove first two columns, and the line for the currently checked out branch
index cce334981e19ea9b38a4ae204531c66bb8a57dc3..bfcaae390f3ad95584ce9cf78b62cc81c33adf67 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Test built-in diff output engine.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
index 68f2ebca58a3213fa438a792510efab84d71871a..3dc90470446dbb81e9c9421dbc7c21d3648d91f4 100755 (executable)
@@ -174,7 +174,7 @@ test_expect_success 'setup for many rename source candidates' '
        do
                for j in 0 1 2 3 4 5 6 7 8 9;
                do
-                       echo "$i$j" >"path$i$j"
+                       echo "$i$j" >"path$i$j" || return 1
                done
        done &&
        git add "path??" &&
index f4485a87c6317c98d82a6af4014520d9e55ee73d..181e9683a7955e18fcd149cd179c58c9f28d940a 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='More rename detection
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
index 3d495e37bb1916741be13afb7664c56b465a7310..8def4d4aee9d2f2b39469fd550020fedab1d0ee7 100755 (executable)
@@ -9,6 +9,8 @@ The rename detection logic should be able to detect pure rename or
 copy of symbolic links, but should not produce rename/copy followed
 by an edit for them.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
index 6f1b323f979a42d663959986e3949f216c1c7669..5c756dc24358cfe28219d2097b96f3655a9e13f0 100755 (executable)
@@ -5,6 +5,8 @@
 
 test_description='Same rename detection as t4003 but testing diff-raw.'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
index 6cdee2a2164d0bc7b3ce8105dc733388afb15b35..dbd4c0da213eb441bdd0ffb6d87748ce1e0de17c 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Test mode change diffs.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 sed_script='s/\(:100644 100755\) \('"$OID_REGEX"'\) \2 /\1 X X /'
index c634653b5be6874f1f9f26f6e2d6c3605d2330e1..b86165cbac5970fda7b169f80edea8deea5ccf12 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Rename interaction with pathspec.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
index 59b7f44f0585796ddfd99951bdf23a4eab9fa259..3480781dabf30aca2a01c40a1ab34eb1e075eccc 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Same rename detection as t4003 but testing diff-raw -z.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
index 1bbced79ece861e2329663b09e8aead3daa983e0..9d9650eba7e9774963b941fe8ebf82925af14ec9 100755 (executable)
@@ -9,6 +9,8 @@ Prepare:
         file0
         path1/file1
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
index 5a25c259fe333912ba1614c2e645e03e5bdd6e02..d7a5f7ae780c0319514fd949dacac98cfe6ce23b 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Test diff of symlinks.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
index 33ff588ebca03807da91c4a786df76c03fb9bff7..c509143c8141e0c4c2f2696080922f339afd432c 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='Binary diff and apply
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >expect.binary-numstat <<\EOF
@@ -122,7 +123,7 @@ test_expect_success 'diff --stat with binary files and big change count' '
        i=0 &&
        while test $i -lt 10000; do
                echo $i &&
-               i=$(($i + 1))
+               i=$(($i + 1)) || return 1
        done >textfile &&
        git add textfile &&
        git diff --cached --stat binfile textfile >output &&
index 28683d059d3bdcc79b7d0ac0a1add9c164e957c5..750aee17ea9650530a5e716020b4ae05ad6c0ef9 100755 (executable)
@@ -19,8 +19,8 @@ test_expect_success setup '
 
        mkdir dir &&
        mkdir dir2 &&
-       for i in 1 2 3; do echo $i; done >file0 &&
-       for i in A B; do echo $i; done >dir/sub &&
+       test_write_lines 1 2 3 >file0 &&
+       test_write_lines A B >dir/sub &&
        cat file0 >file2 &&
        git add file0 file2 dir/sub &&
        git commit -m Initial &&
@@ -32,8 +32,8 @@ test_expect_success setup '
        GIT_COMMITTER_DATE="2006-06-26 00:01:00 +0000" &&
        export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
 
-       for i in 4 5 6; do echo $i; done >>file0 &&
-       for i in C D; do echo $i; done >>dir/sub &&
+       test_write_lines 4 5 6 >>file0 &&
+       test_write_lines C D >>dir/sub &&
        rm -f file2 &&
        git update-index --remove file0 file2 dir/sub &&
        git commit -m "Second${LF}${LF}This is the second commit." &&
@@ -42,9 +42,9 @@ test_expect_success setup '
        GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
        export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
 
-       for i in A B C; do echo $i; done >file1 &&
+       test_write_lines A B C >file1 &&
        git add file1 &&
-       for i in E F; do echo $i; done >>dir/sub &&
+       test_write_lines E F >>dir/sub &&
        git update-index dir/sub &&
        git commit -m Third &&
 
@@ -53,8 +53,8 @@ test_expect_success setup '
        export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
 
        git checkout side &&
-       for i in A B C; do echo $i; done >>file0 &&
-       for i in 1 2; do echo $i; done >>dir/sub &&
+       test_write_lines A B C >>file0 &&
+       test_write_lines 1 2 >>dir/sub &&
        cat dir/sub >file3 &&
        git add file3 &&
        git update-index file0 dir/sub &&
@@ -71,8 +71,8 @@ test_expect_success setup '
        GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
        export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
 
-       for i in A B C; do echo $i; done >>file0 &&
-       for i in 1 2; do echo $i; done >>dir/sub &&
+       test_write_lines A B C >>file0 &&
+       test_write_lines 1 2 >>dir/sub &&
        git update-index file0 dir/sub &&
 
        mkdir dir3 &&
@@ -86,7 +86,7 @@ test_expect_success setup '
        GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" &&
        export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
        git checkout -b rearrange initial &&
-       for i in B A; do echo $i; done >dir/sub &&
+       test_write_lines B A >dir/sub &&
        git add dir/sub &&
        git commit -m "Rearranged lines in dir/sub" &&
        git checkout master &&
index 712d4b5ddf0b1ba028c5d16b1fbb7067d83301fe..7dc5a5c736e3f2db0c4bc29c20321b6a386661df 100755 (executable)
@@ -12,25 +12,25 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_expect_success setup '
-       for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
        cat file >elif &&
        git add file elif &&
        test_tick &&
        git commit -m Initial &&
        git checkout -b side &&
 
-       for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
+       test_write_lines 1 2 5 6 A B C 7 8 9 10 >file &&
        test_chmod +x elif &&
        test_tick &&
        git commit -m "Side changes #1" &&
 
-       for i in D E F; do echo "$i"; done >>file &&
+       test_write_lines D E F >>file &&
        git update-index file &&
        test_tick &&
        git commit -m "Side changes #2" &&
        git tag C2 &&
 
-       for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
+       test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file &&
        git update-index file &&
        test_tick &&
        git commit -m "Side changes #3 with \\n backslash-n in it." &&
@@ -43,18 +43,18 @@ test_expect_success setup '
 
        git checkout side &&
        git checkout -b patchid &&
-       for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file2 &&
-       for i in 1 2 3 A 4 B C 7 8 9 10 D E F 5 6; do echo "$i"; done >file3 &&
-       for i in 8 9 10; do echo "$i"; done >file &&
+       test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >file2 &&
+       test_write_lines 1 2 3 A 4 B C 7 8 9 10 D E F 5 6 >file3 &&
+       test_write_lines 8 9 10 >file &&
        git add file file2 file3 &&
        test_tick &&
        git commit -m "patchid 1" &&
-       for i in 4 A B 7 8 9 10; do echo "$i"; done >file2 &&
-       for i in 8 9 10 5 6; do echo "$i"; done >file3 &&
+       test_write_lines 4 A B 7 8 9 10 >file2 &&
+       test_write_lines 8 9 10 5 6 >file3 &&
        git add file2 file3 &&
        test_tick &&
        git commit -m "patchid 2" &&
-       for i in 10 5 6; do echo "$i"; done >file &&
+       test_write_lines 10 5 6 >file &&
        git add file &&
        test_tick &&
        git commit -m "patchid 3" &&
@@ -325,7 +325,7 @@ test_expect_success 'filename length limit' '
                max=$(
                        for patch in 000[1-9]-*.patch
                        do
-                               echo "$patch" | wc -c
+                               echo "$patch" | wc -c || exit 1
                        done |
                        sort -nr |
                        head -n 1
@@ -343,7 +343,7 @@ test_expect_success 'filename length limit from config' '
                max=$(
                        for patch in 000[1-9]-*.patch
                        do
-                               echo "$patch" | wc -c
+                               echo "$patch" | wc -c || exit 1
                        done |
                        sort -nr |
                        head -n 1
@@ -361,7 +361,7 @@ test_expect_success 'filename limit applies only to basename' '
                max=$(
                        for patch in patches/000[1-9]-*.patch
                        do
-                               echo "${patch#patches/}" | wc -c
+                               echo "${patch#patches/}" | wc -c || exit 1
                        done |
                        sort -nr |
                        head -n 1
@@ -653,7 +653,7 @@ test_expect_success 'excessive subject' '
        git checkout side &&
        before=$(git hash-object file) &&
        before=$(git rev-parse --short $before) &&
-       for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+       test_write_lines 5 6 1 2 3 A 4 B C 7 8 9 10 D E F >>file &&
        after=$(git hash-object file) &&
        after=$(git rev-parse --short $after) &&
        git update-index file &&
@@ -1086,7 +1086,7 @@ test_expect_success TTY 'format-patch --stdout paginates' '
 test_expect_success 'format-patch handles multi-line subjects' '
        rm -rf patches/ &&
        echo content >>file &&
-       for i in one two three; do echo $i; done >msg &&
+       test_write_lines one two three >msg &&
        git add file &&
        git commit -F msg &&
        git format-patch -o patches -1 &&
@@ -1098,7 +1098,7 @@ test_expect_success 'format-patch handles multi-line subjects' '
 test_expect_success 'format-patch handles multi-line encoded subjects' '
        rm -rf patches/ &&
        echo content >>file &&
-       for i in en tvÃ¥ tre; do echo $i; done >msg &&
+       test_write_lines en tvÃ¥ tre >msg &&
        git add file &&
        git commit -F msg &&
        git format-patch -o patches -1 &&
index 2c13b62d3c654807b85307748c1ec52b6f4155ff..9babf13bc9b977737167569adb4586da336a5470 100755 (executable)
@@ -843,7 +843,7 @@ test_expect_success 'whitespace changes with modification reported (diffstat)' '
 
 test_expect_success 'whitespace-only changes reported across renames (diffstat)' '
        git reset --hard &&
-       for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+       for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
        git add x &&
        git commit -m "base" &&
        sed -e "5s/^/ /" x >z &&
@@ -859,7 +859,7 @@ test_expect_success 'whitespace-only changes reported across renames (diffstat)'
 
 test_expect_success 'whitespace-only changes reported across renames' '
        git reset --hard HEAD~1 &&
-       for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+       for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i" || return 1; done >x &&
        git add x &&
        hash_x=$(git hash-object x) &&
        before=$(git rev-parse --short "$hash_x") &&
@@ -1442,6 +1442,143 @@ test_expect_success 'detect permutations inside moved code -- dimmed-zebra' '
        test_cmp expected actual
 '
 
+test_expect_success 'zebra alternate color is only used when necessary' '
+       cat >old.txt <<-\EOF &&
+       line 1A should be marked as oldMoved newMovedAlternate
+       line 1B should be marked as oldMoved newMovedAlternate
+       unchanged
+       line 2A should be marked as oldMoved newMovedAlternate
+       line 2B should be marked as oldMoved newMovedAlternate
+       line 3A should be marked as oldMovedAlternate newMoved
+       line 3B should be marked as oldMovedAlternate newMoved
+       unchanged
+       line 4A should be marked as oldMoved newMovedAlternate
+       line 4B should be marked as oldMoved newMovedAlternate
+       line 5A should be marked as oldMovedAlternate newMoved
+       line 5B should be marked as oldMovedAlternate newMoved
+       line 6A should be marked as oldMoved newMoved
+       line 6B should be marked as oldMoved newMoved
+       EOF
+       cat >new.txt <<-\EOF &&
+         line 1A should be marked as oldMoved newMovedAlternate
+         line 1B should be marked as oldMoved newMovedAlternate
+       unchanged
+         line 3A should be marked as oldMovedAlternate newMoved
+         line 3B should be marked as oldMovedAlternate newMoved
+         line 2A should be marked as oldMoved newMovedAlternate
+         line 2B should be marked as oldMoved newMovedAlternate
+       unchanged
+         line 6A should be marked as oldMoved newMoved
+         line 6B should be marked as oldMoved newMoved
+           line 4A should be marked as oldMoved newMovedAlternate
+           line 4B should be marked as oldMoved newMovedAlternate
+         line 5A should be marked as oldMovedAlternate newMoved
+         line 5B should be marked as oldMovedAlternate newMoved
+       EOF
+       test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+                --color-moved-ws=allow-indentation-change \
+                old.txt new.txt >output &&
+       grep -v index output | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/old.txt b/new.txt<RESET>
+       <BOLD>--- a/old.txt<RESET>
+       <BOLD>+++ b/new.txt<RESET>
+       <CYAN>@@ -1,14 +1,14 @@<RESET>
+       <BOLD;MAGENTA>-line 1A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;MAGENTA>-line 1B should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 1A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 1B should be marked as oldMoved newMovedAlternate<RESET>
+        unchanged<RESET>
+       <BOLD;MAGENTA>-line 2A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;MAGENTA>-line 2B should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;BLUE>-line 3A should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;BLUE>-line 3B should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 3A should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 3B should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>  line 2A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>  line 2B should be marked as oldMoved newMovedAlternate<RESET>
+        unchanged<RESET>
+       <BOLD;MAGENTA>-line 4A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;MAGENTA>-line 4B should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;BLUE>-line 5A should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;BLUE>-line 5B should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;MAGENTA>-line 6A should be marked as oldMoved newMoved<RESET>
+       <BOLD;MAGENTA>-line 6B should be marked as oldMoved newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 6A should be marked as oldMoved newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 6B should be marked as oldMoved newMoved<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>    line 4A should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>    line 4B should be marked as oldMoved newMovedAlternate<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 5A should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>  line 5B should be marked as oldMovedAlternate newMoved<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'short lines of opposite sign do not get marked as moved' '
+       cat >old.txt <<-\EOF &&
+       this line should be marked as moved
+       unchanged
+       unchanged
+       unchanged
+       unchanged
+       too short
+       this line should be marked as oldMoved newMoved
+       this line should be marked as oldMovedAlternate newMoved
+       unchanged 1
+       unchanged 2
+       unchanged 3
+       unchanged 4
+       this line should be marked as oldMoved newMoved/newMovedAlternate
+       EOF
+       cat >new.txt <<-\EOF &&
+       too short
+       unchanged
+       unchanged
+       this line should be marked as moved
+       too short
+       unchanged
+       unchanged
+       this line should be marked as oldMoved newMoved/newMovedAlternate
+       unchanged 1
+       unchanged 2
+       this line should be marked as oldMovedAlternate newMoved
+       this line should be marked as oldMoved newMoved/newMovedAlternate
+       unchanged 3
+       this line should be marked as oldMoved newMoved
+       unchanged 4
+       EOF
+       test_expect_code 1 git diff --no-index --color --color-moved=zebra \
+               old.txt new.txt >output && cat output &&
+       grep -v index output | test_decode_color >actual &&
+       cat >expect <<-\EOF &&
+       <BOLD>diff --git a/old.txt b/new.txt<RESET>
+       <BOLD>--- a/old.txt<RESET>
+       <BOLD>+++ b/new.txt<RESET>
+       <CYAN>@@ -1,13 +1,15 @@<RESET>
+       <BOLD;MAGENTA>-this line should be marked as moved<RESET>
+       <GREEN>+<RESET><GREEN>too short<RESET>
+        unchanged<RESET>
+        unchanged<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as moved<RESET>
+       <GREEN>+<RESET><GREEN>too short<RESET>
+        unchanged<RESET>
+        unchanged<RESET>
+       <RED>-too short<RESET>
+       <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved<RESET>
+       <BOLD;BLUE>-this line should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+        unchanged 1<RESET>
+        unchanged 2<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMovedAlternate newMoved<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+        unchanged 3<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>this line should be marked as oldMoved newMoved<RESET>
+        unchanged 4<RESET>
+       <BOLD;MAGENTA>-this line should be marked as oldMoved newMoved/newMovedAlternate<RESET>
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'cmd option assumes configured colored-moved' '
        test_config color.diff.oldMoved "magenta" &&
        test_config color.diff.newMoved "cyan" &&
@@ -1833,6 +1970,52 @@ test_expect_success '--color-moved treats adjacent blocks as separate for MIN_AL
        test_cmp expected actual
 '
 
+test_expect_success '--color-moved rewinds for MIN_ALNUM_COUNT' '
+       git reset --hard &&
+       test_write_lines >file \
+               A B C one two three four five six seven D E F G H I J &&
+       git add file &&
+       test_write_lines >file \
+               one two A B C D E F G H I J two three four five six seven &&
+       git diff --color-moved=zebra -- file &&
+
+       git diff --color-moved=zebra --color -- file >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+       cat >expected <<-\EOF &&
+       <BOLD>diff --git a/file b/file<RESET>
+       <BOLD>--- a/file<RESET>
+       <BOLD>+++ b/file<RESET>
+       <CYAN>@@ -1,13 +1,8 @@<RESET>
+       <GREEN>+<RESET><GREEN>one<RESET>
+       <GREEN>+<RESET><GREEN>two<RESET>
+        A<RESET>
+        B<RESET>
+        C<RESET>
+       <RED>-one<RESET>
+       <BOLD;MAGENTA>-two<RESET>
+       <BOLD;MAGENTA>-three<RESET>
+       <BOLD;MAGENTA>-four<RESET>
+       <BOLD;MAGENTA>-five<RESET>
+       <BOLD;MAGENTA>-six<RESET>
+       <BOLD;MAGENTA>-seven<RESET>
+        D<RESET>
+        E<RESET>
+        F<RESET>
+       <CYAN>@@ -15,3 +10,9 @@<RESET> <RESET>G<RESET>
+        H<RESET>
+        I<RESET>
+        J<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>two<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>three<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>four<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>five<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>six<RESET>
+       <BOLD;CYAN>+<RESET><BOLD;CYAN>seven<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
 test_expect_success 'move detection with submodules' '
        test_create_repo bananas &&
        echo ripe >bananas/recipe &&
@@ -2023,10 +2206,10 @@ EMPTY=''
 test_expect_success 'compare mixed whitespace delta across moved blocks' '
 
        git reset --hard &&
-       tr Q_ "\t " <<-EOF >text.txt &&
-       ${EMPTY}
-       ____too short without
-       ${EMPTY}
+       tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
+       ^__
+       |____too short without
+       ^
        ___being grouped across blank line
        ${EMPTY}
        context
@@ -2045,7 +2228,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
        git add text.txt &&
        git commit -m "add text.txt" &&
 
-       tr Q_ "\t " <<-EOF >text.txt &&
+       tr "^|Q_" "\f\v\t " <<-EOF >text.txt &&
        context
        lines
        to
@@ -2056,7 +2239,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
        ${EMPTY}
        QQtoo short without
        ${EMPTY}
-       Q_______being grouped across blank line
+       ^Q_______being grouped across blank line
        ${EMPTY}
        Q_QThese two lines have had their
        indentation reduced by four spaces
@@ -2068,16 +2251,16 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
                -c core.whitespace=space-before-tab \
                diff --color --color-moved --ws-error-highlight=all \
                --color-moved-ws=allow-indentation-change >actual.raw &&
-       grep -v "index" actual.raw | test_decode_color >actual &&
+       grep -v "index" actual.raw | tr "\f\v" "^|" | test_decode_color >actual &&
 
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/text.txt b/text.txt<RESET>
        <BOLD>--- a/text.txt<RESET>
        <BOLD>+++ b/text.txt<RESET>
        <CYAN>@@ -1,16 +1,16 @@<RESET>
-       <BOLD;MAGENTA>-<RESET>
-       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>    too short without<RESET>
-       <BOLD;MAGENTA>-<RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET><BRED>  <RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>|    too short without<RESET>
+       <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>^<RESET>
        <BOLD;MAGENTA>-<RESET><BOLD;MAGENTA>   being grouped across blank line<RESET>
        <BOLD;MAGENTA>-<RESET>
         <RESET>context<RESET>
@@ -2097,7 +2280,7 @@ test_expect_success 'compare mixed whitespace delta across moved blocks' '
        <BOLD;YELLOW>+<RESET>
        <BOLD;YELLOW>+<RESET>           <BOLD;YELLOW>too short without<RESET>
        <BOLD;YELLOW>+<RESET>
-       <BOLD;YELLOW>+<RESET>   <BOLD;YELLOW>       being grouped across blank line<RESET>
+       <BOLD;YELLOW>+<RESET><BOLD;YELLOW>^            being grouped across blank line<RESET>
        <BOLD;YELLOW>+<RESET>
        <BOLD;CYAN>+<RESET>     <BRED> <RESET>  <BOLD;CYAN>These two lines have had their<RESET>
        <BOLD;CYAN>+<RESET><BOLD;CYAN>indentation reduced by four spaces<RESET>
index 740696c8f7f2c437ce9260ed30e4ba2b563a30ab..42a2b9a13b7a5bd6cf28cfbe9bbfb9a5a3e5f3e6 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success 'last regexp must not be negated' '
 test_expect_success 'setup hunk header tests' '
        for i in $diffpatterns
        do
-               echo "$i-* diff=$i"
+               echo "$i-* diff=$i" || return 1
        done > .gitattributes &&
 
        # add all test files to the index
index c68729ac098401c16c2d7d232dec35d56e545846..d2b3109c2d37445f2f3cf9b892df67f7519191ff 100755 (executable)
@@ -287,9 +287,9 @@ test_expect_success 'do not color trailing cr in context' '
 '
 
 test_expect_success 'color new trailing blank lines' '
-       { echo a; echo b; echo; echo; } >x &&
+       test_write_lines a b "" "" >x &&
        git add x &&
-       { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+       test_write_lines a "" "" "" c "" "" "" "" >x &&
        git diff --color x >output &&
        cnt=$($grep_a "${blue_grep}" output | wc -l) &&
        test $cnt = 2
index e009826fcbe5a893df748a47d162e010b95d758b..54bb8ef27e7f0089a46ca48ecccf3998deb84a69 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='external diff interface test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 47d6f35dcc4d93bbbdd083d6cdd23e7a5c2dff36..7cb99092938d7dd64e8ab6f4e147fc5f70c0a310 100755 (executable)
@@ -55,7 +55,7 @@ test_expect_success 'cross renames to be detected for regular files' '
 
        git diff-tree five six -r --name-status -B -M | sort >actual &&
        {
-               echo "R100      foo     bar"
+               echo "R100      foo     bar" &&
                echo "R100      bar     foo"
        } | sort >expect &&
        test_cmp expect actual
@@ -66,7 +66,7 @@ test_expect_success 'cross renames to be detected for typechange' '
 
        git diff-tree one two -r --name-status -B -M | sort >actual &&
        {
-               echo "R100      foo     bar"
+               echo "R100      foo     bar" &&
                echo "R100      bar     foo"
        } | sort >expect &&
        test_cmp expect actual
@@ -78,7 +78,7 @@ test_expect_success 'moves and renames' '
        git diff-tree three four -r --name-status -B -M | sort >actual &&
        {
                # see -B -M (#6) in t4008
-               echo "C100      foo     bar"
+               echo "C100      foo     bar" &&
                echo "T100      foo"
        } | sort >expect &&
        test_cmp expect actual
index 6b44ce14933f8ceaa7e5e93eb9eaa0ff2527b66c..e2f0eca4af065071a3ea5096e259212557aa4491 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='common tail optimization'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 z=zzzzzzzz ;# 8
@@ -148,7 +149,7 @@ test_expect_success 'diff -U0' '
 
        for n in $sample
        do
-               git diff -U0 file-?$n
+               git diff -U0 file-?$n || return 1
        done | zc >actual &&
        test_cmp expect actual
 
index 6356961de46c78f5168ab437b9aff50fc979afd0..5397cb7d42d748054eb5a244415c2f5062e3eca2 100755 (executable)
@@ -14,15 +14,9 @@ test_expect_success setup '
 
        (
                echo "A $NS" &&
-               for c in B C D E F G H I J K
-               do
-                       echo "  $c"
-               done &&
+               printf "  %s\n" B C D E F G H I J K &&
                echo "L  $NS" &&
-               for c in M N O P Q R S T U V
-               do
-                       echo "  $c"
-               done
+               printf "  %s\n" M N O P Q R S T U V
        ) >file &&
        git add file &&
 
index cc73161b466b56615e969685f874130af951ddfd..cc3f60d468f4c51e7a6149ce8ef014bdf5a5a92b 100755 (executable)
@@ -60,6 +60,10 @@ test_expect_success 'fg bg attr...' '
        color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
 '
 
+test_expect_success 'reset fg bg attr...' '
+       color "reset blue bold dim ul blink reverse" "[;1;2;4;5;7;34m"
+'
+
 # note that nobold and nodim are the same code (22)
 test_expect_success 'attr negation' '
        color "nobold nodim noul noblink noreverse" "[22;24;25;27m"
@@ -96,6 +100,18 @@ test_expect_success '24-bit colors' '
        color "#ff00ff black" "[38;2;255;0;255;40m"
 '
 
+test_expect_success '"default" foreground' '
+       color "default" "[39m"
+'
+
+test_expect_success '"normal default" to clear background' '
+       color "normal default" "[49m"
+'
+
+test_expect_success '"default" can be combined with attributes' '
+       color "default default no-reverse bold" "[1;27;39;49m"
+'
+
 test_expect_success '"normal" yields no color at all"' '
        color "normal black" "[40m"
 '
index 94ef77e1dfedc28656d78c80b689eca6806a7b0c..6cef0da982faa1fbb86696ca0275eb49c8224090 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
index 32b6e9a4e76217d8de771b405dd3ce57c73643ad..5f8ffef74b6474c9fb97e18cd6c48d449fa26734 100755 (executable)
@@ -4,6 +4,7 @@
 #
 test_description='diff honors config option, diff.suppressBlankEmpty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat <<\EOF >expected ||
index bada0cbd32f76418a76702fbbd5143004876d074..7db92d0d9f461aca48fb4ebf014cd03cb1401a1c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff hunk fusing'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 f() {
index 113304dc596034ff9bfaac65b2ff896b6a181dca..f7be7f5ef0139b8384746518c676d80cb88dd5dd 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='patience diff algorithm'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff-alternative.sh
 
index 561c582d161551147d8e4805aa35ba4178285833..d5abcf4b4c6fa9f828b635dccbea37eae3ed4eb7 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='word diff colors'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
index 0352bf81a90a38adf14fb7a980c98600e1f650b2..76f8034c60fabe2cbd1ea45610e1ce3137755b0d 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Return value of diffs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index f5ce3b29a2ac753470b51bb494ca3836ffc21b11..b5f96fe23bd214f15ac66c1cac0291cfb1b73e58 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff -r -t shows directory additions and deletions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index aeac203c424905be395d5ef6d43f2b606dc3bb1d..9a292bac70c248c9273bf29a94d5ce39f17551df 100755 (executable)
@@ -100,7 +100,7 @@ test_expect_success 'setup for --cc --raw' '
        for i in $(test_seq 1 40)
        do
                blob=$(echo file$i | git hash-object --stdin -w) &&
-               trees="$trees$(echo "100644 blob $blob  file" | git mktree)$LF"
+               trees="$trees$(echo "100644 blob $blob  file" | git mktree)$LF" || return 1
        done
 '
 
index 3c728a3ebf9ce52e5c24c81525d5cb749cfb2957..e70e020ae9349c378b4b922933668932e95acc8d 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff --exit-code with whitespace'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ff7cfd884a44ebe0844c7ac6a21f25ed8374e7d4..0ae0cd3a524da312d7f1cc7b1d4b0f6ed7b2b529 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='diff with unmerged index entries'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -18,7 +20,7 @@ test_expect_success setup '
                        for t in o x
                        do
                                path="$b$o$t" &&
-                               case "$path" in ooo) continue ;; esac
+                               case "$path" in ooo) continue ;; esac &&
                                paths="$paths$path " &&
                                p="     $path" &&
                                case "$b" in x) echo "$m1$p" ;; esac &&
@@ -37,7 +39,7 @@ test_expect_success 'diff-files -0' '
        for path in $paths
        do
                >"$path" &&
-               echo ":000000 100644 $ZERO_OID $ZERO_OID U      $path"
+               echo ":000000 100644 $ZERO_OID $ZERO_OID U      $path" || return 1
        done >diff-files-0.expect &&
        git diff-files -0 >diff-files-0.actual &&
        test_cmp diff-files-0.expect diff-files-0.actual
@@ -50,7 +52,7 @@ test_expect_success 'diff-files -1' '
                echo ":000000 100644 $ZERO_OID $ZERO_OID U      $path" &&
                case "$path" in
                x??) echo ":100644 100644 $blob1 $ZERO_OID M    $path"
-               esac
+               esac || return 1
        done >diff-files-1.expect &&
        git diff-files -1 >diff-files-1.actual &&
        test_cmp diff-files-1.expect diff-files-1.actual
@@ -63,7 +65,7 @@ test_expect_success 'diff-files -2' '
                echo ":000000 100644 $ZERO_OID $ZERO_OID U      $path" &&
                case "$path" in
                ?x?) echo ":100644 100644 $blob2 $ZERO_OID M    $path"
-               esac
+               esac || return 1
        done >diff-files-2.expect &&
        git diff-files -2 >diff-files-2.actual &&
        test_cmp diff-files-2.expect diff-files-2.actual &&
@@ -78,7 +80,7 @@ test_expect_success 'diff-files -3' '
                echo ":000000 100644 $ZERO_OID $ZERO_OID U      $path" &&
                case "$path" in
                ??x) echo ":100644 100644 $blob3 $ZERO_OID M    $path"
-               esac
+               esac || return 1
        done >diff-files-3.expect &&
        git diff-files -3 >diff-files-3.actual &&
        test_cmp diff-files-3.expect diff-files-3.actual
index 53061b104ecc1af2eebb9d79fa2af5655236471d..0a4fc735d44ad525f0924b86e355d4a257650a62 100755 (executable)
@@ -2,6 +2,8 @@
 # Copyright (c) 2011, Google Inc.
 
 test_description='diff --stat-count'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -51,7 +53,7 @@ test_expect_success 'exclude unmerged entries from total file count' '
        git rm -f d &&
        for stage in 1 2 3
        do
-               sed -e "s/ 0    a/ $stage       d/" x
+               sed -e "s/ 0    a/ $stage       d/" x || return 1
        done |
        git update-index --index-info &&
        echo d >d &&
index fd3e86a74f3d92d837e2557236776400687f7a5d..c61b30f96daf57a9c7f3185bdd8ea3c4aeb9dbe3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='histogram diff algorithm'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff-alternative.sh
 
index 9eba436211f147864e0ce6afbcfa3166eedc0255..b5c281edaa7037097f3c8fad28c1bb1994607a20 100755 (executable)
@@ -101,7 +101,7 @@ test_expect_success 'preparation for big change tests' '
        i=0 &&
        while test $i -lt 1000
        do
-               echo $i && i=$(($i + 1))
+               echo $i && i=$(($i + 1)) || return 1
        done >abcd &&
        git commit -m message abcd
 '
index 8c95f152b23b242df5f5aaa0b5b25e6e1826f753..294fb5531372d597a5a551b40e66437d7599e766 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test diff with a bogus tree containing the null sha1'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create bogus tree' '
index 7e5b74f72ee975ef87c8d5e539bc287268e18493..04b8a1542a8ec3ad2ffc28964f21940d034c2ed6 100755 (executable)
@@ -18,13 +18,13 @@ test_expect_success 'trivial merge - combine-diff empty' '
        for i in $(test_seq 1 9)
        do
                echo $i >$i.txt &&
-               git add $i.txt
+               git add $i.txt || return 1
        done &&
        git commit -m "init" &&
        git checkout -b side &&
        for i in $(test_seq 2 9)
        do
-               echo $i/2 >>$i.txt
+               echo $i/2 >>$i.txt || return 1
        done &&
        git commit -a -m "side 2-9" &&
        git checkout main &&
@@ -40,14 +40,14 @@ test_expect_success 'only one truly conflicting path' '
        git checkout side &&
        for i in $(test_seq 2 9)
        do
-               echo $i/3 >>$i.txt
+               echo $i/3 >>$i.txt || return 1
        done &&
        echo "4side" >>4.txt &&
        git commit -a -m "side 2-9 +4" &&
        git checkout main &&
        for i in $(test_seq 1 9)
        do
-               echo $i/3 >>$i.txt
+               echo $i/3 >>$i.txt || return 1
        done &&
        echo "4main" >>4.txt &&
        git commit -a -m "main 1-9 +4" &&
@@ -69,13 +69,13 @@ test_expect_success 'merge introduces new file' '
        git checkout side &&
        for i in $(test_seq 5 9)
        do
-               echo $i/4 >>$i.txt
+               echo $i/4 >>$i.txt || return 1
        done &&
        git commit -a -m "side 5-9" &&
        git checkout main &&
        for i in $(test_seq 1 3)
        do
-               echo $i/4 >>$i.txt
+               echo $i/4 >>$i.txt || return 1
        done &&
        git commit -a -m "main 1-3 +4hello" &&
        git merge side &&
@@ -90,13 +90,13 @@ test_expect_success 'merge removed a file' '
        git checkout side &&
        for i in $(test_seq 5 9)
        do
-               echo $i/5 >>$i.txt
+               echo $i/5 >>$i.txt || return 1
        done &&
        git commit -a -m "side 5-9" &&
        git checkout main &&
        for i in $(test_seq 1 3)
        do
-               echo $i/4 >>$i.txt
+               echo $i/4 >>$i.txt || return 1
        done &&
        git commit -a -m "main 1-3" &&
        git merge side &&
index 1130c8019b4c14975744f31f360886ffb4c3f14c..9aaa068ed9bc9a7853ce8331ec5d66107a50b94b 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Pickaxe options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index bc69e26c524b7cc099aebad7729039a45bedc398..7e6c9d638433caca632d0d6d6dbdbc8c1bd98ec8 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test direct comparison of blobs via git-diff'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 run_diff () {
index 9b433de83630774206fb89dfae1a4396264390cc..d503547732c54d8f952027580dc21a6b65729cdb 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='git apply --stat --summary test, with --recount
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 UNC='s/^\(@@ -[1-9][0-9]*\),[0-9]* \(+[1-9][0-9]*\),[0-9]* @@/\1,999 \2,999 @@/'
index e3443d004d026c86fd783cb8e6e3d03f22676778..b1169193ef5d53e06302b01b9a54ad7ed165232c 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='git apply should handle files with incomplete lines.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # setup
index fae305979a88614bdf33963936d3fce32df83b8c..d1e06fc1ac41354d9a50d6db76663f0b9ff3e698 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='git apply handling copy/rename patch.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # setup
index 3266e394003958b62509b7bfe6652abd03fdfcb7..ed814a839e679d4b394cd88729da8921e0a19b14 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='apply with fuzz and offset'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 dotest () {
@@ -15,15 +17,9 @@ dotest () {
 
 test_expect_success setup '
 
-       for i in 1 2 3 4 5 6 7 8 9 10 11 12
-       do
-               echo $i
-       done >file &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 >file &&
        git update-index --add file &&
-       for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
-       do
-               echo $i
-       done >file &&
+       test_write_lines 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12 >file &&
        cat file >expect &&
        git diff >O0.diff &&
 
index 72467a1e8ee28c4ac2a3d532bd65b9b5503cff06..5c150f3b0b23913ac9b515820431a5651a1b8726 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='git apply --numstat - <patch'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -18,7 +20,10 @@ test_expect_success 'git apply --numstat - < patch' '
 '
 
 test_expect_success 'git apply --numstat - < patch patch' '
-       for i in 1 2; do echo "1        1       text"; done >expect &&
+       cat >expect <<-\EOF &&
+       1       1       text
+       1       1       text
+       EOF
        git apply --numstat - < patch patch >actual &&
        test_cmp expect actual
 '
index cc3aa3314a3448cd1d5c845ce9803f0877db83ce..c558282bc0947548760b0e0cfce68f29f8dc2835 100755 (executable)
@@ -275,4 +275,22 @@ test_expect_success 'apply full-index patch with 3way' '
        git apply --3way --index bin.diff
 '
 
+test_expect_success 'apply delete then new patch with 3way' '
+       git reset --hard main &&
+       test_write_lines 2 > delnew &&
+       git add delnew &&
+       git diff --cached >> new.patch &&
+       git reset --hard &&
+       test_write_lines 1 > delnew &&
+       git add delnew &&
+       git commit -m "delnew" &&
+       rm delnew &&
+       git diff >> delete-then-new.patch &&
+       cat new.patch >> delete-then-new.patch &&
+
+       git checkout -- . &&
+       # Apply must succeed.
+       git apply --3way delete-then-new.patch
+'
+
 test_done
index ac58083fe224100987800e9b5ee3e388d9b4d97c..4dc6d8e7d3c8bb834fda0e777e3083283df00033 100755 (executable)
@@ -6,6 +6,8 @@
 
 test_description='git apply test patches with multiple fragments.'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cp "$TEST_DIRECTORY/t4109/patch1.patch" .
index 09f58112e0229a41ea2a5d2ea6e8c23d2523298d..266302a1829da4a0e289987c7d572a0fb9cdbc73 100755 (executable)
@@ -7,6 +7,8 @@
 test_description='git apply test for patches which require scanning forwards and backwards.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'git apply scan' '
index f9ad183758c28ff648890d1bd4bbd599562cd795..d53aa4222ea3c1405247e07d62fc8efed83daa5d 100755 (executable)
@@ -7,6 +7,8 @@ test_description='git apply should not get confused with rename/copy.
 
 '
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # setup
index b99e65c086391276aeb809b8651507026509e19a..a9f4ddda6c3bd650634219968ecec92e5247cff7 100755 (executable)
@@ -7,18 +7,20 @@ test_description='git apply in reverse
 
 '
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
 
-       for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
+       test_write_lines a b c d e f g h i j k l m n >file1 &&
        perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
 
        git add file1 file2 &&
        git commit -m initial &&
        git tag initial &&
 
-       for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
+       test_write_lines a b c g h i J K L m o n p q >file1 &&
        perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
 
        git commit -a -m second &&
index 0ee93fe845afec2aebb50819d3301c3c1972b34c..c86d05a96fe8d5c7a561028c8dad452f89eb6876 100755 (executable)
@@ -10,25 +10,16 @@ test_description='git apply with rejects
 . ./test-lib.sh
 
 test_expect_success setup '
-       for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
-       do
-               echo $i
-       done >file1 &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >file1 &&
        cat file1 >saved.file1 &&
        git update-index --add file1 &&
        git commit -m initial &&
 
-       for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
-       do
-               echo $i
-       done >file1 &&
+       test_write_lines 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21 >file1 &&
        git diff >patch.1 &&
        cat file1 >clean &&
 
-       for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
-       do
-               echo $i
-       done >expected &&
+       test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21 >expected &&
 
        mv file1 file2 &&
        git update-index --add --remove file1 file2 &&
@@ -38,10 +29,7 @@ test_expect_success setup '
        mv saved.file1 file1 &&
        git update-index --add --remove file1 file2 &&
 
-       for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
-       do
-               echo $i
-       done >file1 &&
+       test_write_lines 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21 >file1 &&
 
        cat file1 >saved.file1
 '
index 65f2e4c3efb9ae5b5459e15df337e07201d78c38..69c9c48e72b4930b3ee21887c33122468f43be4a 100755 (executable)
@@ -7,14 +7,12 @@ test_description='git apply with new style GNU diff with empty context
 
 '
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-       {
-               echo; echo;
-               echo A; echo B; echo C;
-               echo;
-       } >file1 &&
+       test_write_lines "" "" A B C "" >file1 &&
        cat file1 >file1.orig &&
        {
                cat file1 &&
index a9a05838119c85bc017f1404b134d393029843b2..208c961d376b28035e3e7c9563199f9c5867cf11 100755 (executable)
@@ -7,6 +7,8 @@ test_description='git apply --whitespace=strip and configuration file.
 
 '
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 984157f03b9744aa491c888fab9e6aef95dfdc6b..dfa053ff28e8cbeb9ae311732871811777a8a18c 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='apply a patch that is larger than the preimage'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >F  <<\EOF
@@ -45,8 +47,8 @@ test_expect_success 'apply should fail gracefully' '
                echo Oops, should not have succeeded
                false
        else
-               status=$?
-               echo "Status was $status"
+               status=$? &&
+               echo "Status was $status" &&
                if test -f .git/index.lock
                then
                        echo Oops, should not have crashed
index 0ca29821ece3fda711c5c8c82791bed7a829b538..485c7d2d124ade54d00f8752399be91f43eefbc8 100755 (executable)
@@ -230,10 +230,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '
        test_might_fail git config --unset core.whitespace &&
        rm -f .gitattributes &&
 
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
-       { echo a; echo b; echo c; } >expect &&
-       { cat expect; echo; } >one &&
+       test_write_lines a b c >expect &&
+       { cat expect && echo; } >one &&
        git diff -- one >patch &&
 
        git checkout one &&
@@ -242,10 +242,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (1)' '
 '
 
 test_expect_success 'blank at EOF with --whitespace=fix (2)' '
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
-       { echo a; echo c; } >expect &&
-       { cat expect; echo; echo; } >one &&
+       test_write_lines a b >expect &&
+       { cat expect && test_write_lines "" ""; } >one &&
        git diff -- one >patch &&
 
        git checkout one &&
@@ -254,10 +254,10 @@ test_expect_success 'blank at EOF with --whitespace=fix (2)' '
 '
 
 test_expect_success 'blank at EOF with --whitespace=fix (3)' '
-       { echo a; echo b; echo; } >one &&
+       test_write_lines a b "" >one &&
        git add one &&
-       { echo a; echo c; echo; } >expect &&
-       { cat expect; echo; echo; } >one &&
+       test_write_lines a c "" >expect &&
+       { cat expect && test_write_lines "" ""; } >one &&
        git diff -- one >patch &&
 
        git checkout one &&
@@ -266,9 +266,9 @@ test_expect_success 'blank at EOF with --whitespace=fix (3)' '
 '
 
 test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
-       { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+       test_write_lines a b "" "" "" "" "" d >one &&
        git add one &&
-       { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+       test_write_lines a b "" "" "" "" "" "" d >expect &&
        cp expect one &&
        git diff -- one >patch &&
 
@@ -278,7 +278,7 @@ test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
 '
 
 test_expect_success 'blank at EOF with --whitespace=warn' '
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
        echo >>one &&
        cat one >expect &&
@@ -291,7 +291,7 @@ test_expect_success 'blank at EOF with --whitespace=warn' '
 '
 
 test_expect_success 'blank at EOF with --whitespace=error' '
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
        cat one >expect &&
        echo >>one &&
@@ -304,7 +304,7 @@ test_expect_success 'blank at EOF with --whitespace=error' '
 '
 
 test_expect_success 'blank but not empty at EOF' '
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
        echo "   " >>one &&
        cat one >expect &&
@@ -317,13 +317,13 @@ test_expect_success 'blank but not empty at EOF' '
 '
 
 test_expect_success 'applying beyond EOF requires one non-blank context line' '
-       { echo; echo; echo; echo; } >one &&
+       test_write_lines "" "" "" "" >one &&
        git add one &&
-       { echo b; } >>one &&
+       echo b >>one &&
        git diff -- one >patch &&
 
        git checkout one &&
-       { echo a; echo; } >one &&
+       test_write_lines a "" >one &&
        cp one expect &&
        test_must_fail git apply --whitespace=fix patch &&
        test_cmp expect one &&
@@ -333,7 +333,7 @@ test_expect_success 'applying beyond EOF requires one non-blank context line' '
 
 test_expect_success 'tons of blanks at EOF should not apply' '
        for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
-               echo; echo; echo; echo;
+               test_write_lines "" "" "" "" || return 1
        done >one &&
        git add one &&
        echo a >>one &&
@@ -362,9 +362,9 @@ test_expect_success 'missing blank line at end with --whitespace=fix' '
 '
 
 test_expect_success 'two missing blank lines at end with --whitespace=fix' '
-       { echo a; echo; echo b; echo c; } >one &&
+       test_write_lines a "" b c >one &&
        cp one no-blank-lines &&
-       { echo; echo; } >>one &&
+       test_write_lines "" "" >>one &&
        git add one &&
        echo d >>one &&
        cp one expect &&
@@ -381,9 +381,9 @@ test_expect_success 'two missing blank lines at end with --whitespace=fix' '
 '
 
 test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
-       { echo a; echo; } >one &&
+       test_write_lines a "" >one &&
        git add one &&
-       { echo b; echo a; echo; } >one &&
+       test_write_lines b a "" >one &&
        cp one expect &&
        git diff -- one >patch &&
        echo a >one &&
@@ -393,10 +393,10 @@ test_expect_success 'missing blank line at end, insert before end, --whitespace=
 '
 
 test_expect_success 'shrink file with tons of missing blanks at end of file' '
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        cp one no-blank-lines &&
        for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
-               echo; echo; echo; echo;
+               test_write_lines "" "" "" "" || return 1
        done >>one &&
        git add one &&
        echo a >one &&
@@ -412,9 +412,9 @@ test_expect_success 'shrink file with tons of missing blanks at end of file' '
 '
 
 test_expect_success 'missing blanks at EOF must only match blank lines' '
-       { echo a; echo b; } >one &&
+       test_write_lines a b >one &&
        git add one &&
-       { echo c; echo d; } >>one &&
+       test_write_lines c d >>one &&
        git diff -- one >patch &&
 
        echo a >one &&
@@ -434,9 +434,9 @@ test_expect_success 'missing blank line should match context line with spaces' '
        git add one &&
        echo d >>one &&
        git diff -- one >patch &&
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        cp one expect &&
-       { echo; echo d; } >>expect &&
+       test_write_lines "" d >>expect &&
        git add one &&
 
        git apply --whitespace=fix patch &&
@@ -455,7 +455,7 @@ test_expect_success 'same, but with the --ignore-space-option' '
        echo d >>one &&
        cp one expect &&
        git diff -- one >patch &&
-       { echo a; echo b; echo c; } >one &&
+       test_write_lines a b c >one &&
        git add one &&
 
        git checkout-index -f one &&
index 9671de799949f8f67f906cd3db907e3a53cf4eef..090987c89b24b4795bcb80c0461e5c90f3fe5b3b 100755 (executable)
@@ -10,10 +10,7 @@ test_expect_success setup '
        git add file &&
 
        # file-0 is full of whitespace breakages
-       for l in a bb c d eeee f ggg h
-       do
-               echo "$l "
-       done >file-0 &&
+       printf "%s \n" a bb c d eeee f ggg h >file-0 &&
 
        # patch-0 creates a whitespace broken file
        cat file-0 >file &&
index ceb6a79fe0c8ca5b26a9e148215556f2aa344eb9..33860d3829085211d5a5aa4da9468fa52cae4d9b 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='apply empty'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -9,10 +11,9 @@ test_expect_success setup '
        git add empty &&
        test_tick &&
        git commit -m initial &&
-       for i in a b c d e
-       do
-               echo $i
-       done >empty &&
+       git commit --allow-empty -m "empty commit" &&
+       git format-patch --always HEAD~ >empty.patch &&
+       test_write_lines a b c d e >empty &&
        cat empty >expect &&
        git diff |
        sed -e "/^diff --git/d" \
@@ -25,30 +26,42 @@ test_expect_success setup '
 '
 
 test_expect_success 'apply empty' '
-       git reset --hard &&
        rm -f missing &&
+       test_when_finished "git reset --hard" &&
        git apply patch0 &&
        test_cmp expect empty
 '
 
+test_expect_success 'apply empty patch fails' '
+       test_when_finished "git reset --hard" &&
+       test_must_fail git apply empty.patch &&
+       test_must_fail git apply - </dev/null
+'
+
+test_expect_success 'apply with --allow-empty succeeds' '
+       test_when_finished "git reset --hard" &&
+       git apply --allow-empty empty.patch &&
+       git apply --allow-empty - </dev/null
+'
+
 test_expect_success 'apply --index empty' '
-       git reset --hard &&
        rm -f missing &&
+       test_when_finished "git reset --hard" &&
        git apply --index patch0 &&
        test_cmp expect empty &&
        git diff --exit-code
 '
 
 test_expect_success 'apply create' '
-       git reset --hard &&
        rm -f missing &&
+       test_when_finished "git reset --hard" &&
        git apply patch1 &&
        test_cmp expect missing
 '
 
 test_expect_success 'apply --index create' '
-       git reset --hard &&
        rm -f missing &&
+       test_when_finished "git reset --hard" &&
        git apply --index patch1 &&
        test_cmp expect missing &&
        git diff --exit-code
index 305b7e649eb7a123556d89deae0c34ec91265905..aa5cfae2b681c0a73d9e09493c8cdcbb6a273f87 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='apply same filename'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 modify () {
@@ -10,10 +12,7 @@ modify () {
 }
 
 test_expect_success setup '
-       for i in a b c d e f g h i j k l m
-       do
-               echo $i
-       done >same_fn &&
+       test_write_lines a b c d e f g h i j k l m >same_fn &&
        cp same_fn other_fn &&
        git add same_fn other_fn &&
        git commit -m initial
index 6cc741a634b0352c54fe8e5f61f1e99543909b8c..cb3181e8b71a8e25586467c20086063b33ec36d0 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='apply same filename'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 576632f8681e76032c0f87ac0b65833314058035..a1c7686519ebb1284d818fd3f5615853082abc01 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='applying patch with mode bits'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index f8a313bcb98c6e2b98982295773bdc8ac7e13256..f3ea63274258c664fd612dc314e205b9804cc13c 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git apply handling criss-cross rename patch.'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_file() {
index fec1d6fa51faec22da97eb62165c588e2ba9f655..c1e3049c041b849f4b8c1d1322a44ea77fd96845 100755 (executable)
@@ -4,6 +4,8 @@
 
 test_description='git-apply notices removal patches generated by GNU diff'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index c5ed3b17c4a1196e154affc346b5fd3d4e0a7abf..35f1060bc8b47f3f4de129621392c0b23b8f4c18 100755 (executable)
@@ -5,6 +5,8 @@
 
 test_description='git apply filename consistency check'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index d1c16ba33c114477616d82d8e5d9fcbf53ed53ef..aceb4c42b0ffc7e20a0a9cc07843a953e703b269 100755 (executable)
@@ -5,6 +5,8 @@
 
 test_description='git apply submodule tests'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 4c3f264a633b770445e116edfac83872b47916bf..dfec1c5f0f63fca3b0f0f47d2296be93c0e4f1ed 100755 (executable)
@@ -2,6 +2,8 @@
 
 test_description='git apply should exit non-zero with unrecognized input.'
 
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index b19faeb67a3cffcc241ef2487ab8f4d0c598af2a..8bbf8260fa6b982e82f3ecb1e41d81f08adb9f4b 100755 (executable)
@@ -29,8 +29,8 @@ test_expect_success setup '
        x=1 &&
        while test $x -lt $n
        do
-               printf "%63s%d\n" "" $x >>after
-               x=$(( $x + 1 ))
+               printf "%63s%d\n" "" $x >>after &&
+               x=$(( $x + 1 )) || return 1
        done &&
        printf "\t%s\n" d e f >>after &&
        test_expect_code 1 git diff --no-index before after >patch2.patch.raw &&
@@ -40,8 +40,8 @@ test_expect_success setup '
        x=1 &&
        while test $x -lt $n
        do
-               printf "%63s%d\n" "" $x >>expect-2
-               x=$(( $x + 1 ))
+               printf "%63s%d\n" "" $x >>expect-2 &&
+               x=$(( $x + 1 )) || return 1
        done &&
        printf "%64s\n" d e f >>expect-2 &&
 
@@ -52,8 +52,8 @@ test_expect_success setup '
        x=0 &&
        while test $x -lt $n
        do
-               printf "%63s%02d\n" "" $x >>after
-               x=$(( $x + 1 ))
+               printf "%63s%02d\n" "" $x >>after &&
+               x=$(( $x + 1 )) || return 1
        done &&
        printf "\t%s\n" d e f >>after &&
        test_expect_code 1 git diff --no-index before after >patch3.patch.raw &&
@@ -63,8 +63,8 @@ test_expect_success setup '
        x=0 &&
        while test $x -lt $n
        do
-               printf "%63s%02d\n" "" $x >>expect-3
-               x=$(( $x + 1 ))
+               printf "%63s%02d\n" "" $x >>expect-3 &&
+               x=$(( $x + 1 )) || return 1
        done &&
        printf "%64s\n" d e f >>expect-3 &&
 
@@ -73,16 +73,16 @@ test_expect_success setup '
        x=0 &&
        while test $x -lt 50
        do
-               printf "\t%02d\n" $x >>before
-               x=$(( $x + 1 ))
+               printf "\t%02d\n" $x >>before &&
+               x=$(( $x + 1 )) || return 1
        done &&
        cat before >after &&
        printf "%64s\n" a b c >>after &&
        while test $x -lt 100
        do
-               printf "\t%02d\n" $x >>before
-               printf "\t%02d\n" $x >>after
-               x=$(( $x + 1 ))
+               printf "\t%02d\n" $x >>before &&
+               printf "\t%02d\n" $x >>after &&
+               x=$(( $x + 1 )) || return 1
        done &&
        test_expect_code 1 git diff --no-index before after >patch4.patch.raw &&
        sed -e "s/before/test-4/" -e "s/after/test-4/" patch4.patch.raw >patch4.patch &&
@@ -90,16 +90,16 @@ test_expect_success setup '
        x=0 &&
        while test $x -lt 50
        do
-               printf "%63s%02d\n" "" $x >>test-4
-               x=$(( $x + 1 ))
+               printf "%63s%02d\n" "" $x >>test-4 &&
+               x=$(( $x + 1 )) || return 1
        done &&
        cat test-4 >expect-4 &&
        printf "%64s\n" a b c >>expect-4 &&
        while test $x -lt 100
        do
-               printf "%63s%02d\n" "" $x >>test-4
-               printf "%63s%02d\n" "" $x >>expect-4
-               x=$(( $x + 1 ))
+               printf "%63s%02d\n" "" $x >>test-4 &&
+               printf "%63s%02d\n" "" $x >>expect-4 &&
+               x=$(( $x + 1 )) || return 1
        done &&
 
        git config core.whitespace tab-in-indent,tabwidth=63 &&
index 45b5660a47d88f736aa777641084d13b45852969..e5c7439df13389a3caa9f3f76f70e31fea96c90b 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='paths written by git-apply cannot escape the working tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # tests will try to write to ../foo, and we do not
index 2aaaa0d7dedf68934e8d5b4c6610f1a859acbb29..6caff0ca397e442d9d053c0ed1a7808e74bcbfe4 100755 (executable)
@@ -116,7 +116,7 @@ test_expect_success setup '
                git format-patch --stdout first | sed -e "1d"
        } | append_cr >patch1-crlf.eml &&
        {
-               printf "%255s\\n" ""
+               printf "%255s\\n" "" &&
                echo "X-Fake-Field: Line One" &&
                echo "X-Fake-Field: Line Two" &&
                echo "X-Fake-Field: Line Three" &&
@@ -196,6 +196,12 @@ test_expect_success setup '
 
        git format-patch -M --stdout lorem^ >rename-add.patch &&
 
+       git checkout -b empty-commit &&
+       git commit -m "empty commit" --allow-empty &&
+
+       : >empty.patch &&
+       git format-patch --always --stdout empty-commit^ >empty-commit.patch &&
+
        # reset time
        sane_unset test_tick &&
        test_tick
@@ -1152,4 +1158,105 @@ test_expect_success 'apply binary blob in partial clone' '
        git -C client am ../patch
 '
 
+test_expect_success 'an empty input file is error regardless of --empty option' '
+       test_when_finished "git am --abort || :" &&
+       test_must_fail git am --empty=drop empty.patch 2>actual &&
+       echo "Patch format detection failed." >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'invalid when passing the --empty option alone' '
+       test_when_finished "git am --abort || :" &&
+       git checkout empty-commit^ &&
+       test_must_fail git am --empty empty-commit.patch 2>err &&
+       echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+       test_cmp expected err
+'
+
+test_expect_success 'a message without a patch is an error (default)' '
+       test_when_finished "git am --abort || :" &&
+       test_must_fail git am empty-commit.patch >err &&
+       grep "Patch is empty" err
+'
+
+test_expect_success 'a message without a patch is an error where an explicit "--empty=stop" is given' '
+       test_when_finished "git am --abort || :" &&
+       test_must_fail git am --empty=stop empty-commit.patch >err &&
+       grep "Patch is empty." err
+'
+
+test_expect_success 'a message without a patch will be skipped when "--empty=drop" is given' '
+       git am --empty=drop empty-commit.patch >output &&
+       git rev-parse empty-commit^ >expected &&
+       git rev-parse HEAD >actual &&
+       test_cmp expected actual &&
+       grep "Skipping: empty commit" output
+'
+
+test_expect_success 'record as an empty commit when meeting e-mail message that lacks a patch' '
+       git am --empty=keep empty-commit.patch >output &&
+       test_path_is_missing .git/rebase-apply &&
+       git show empty-commit --format="%B" >expected &&
+       git show HEAD --format="%B" >actual &&
+       grep -f actual expected &&
+       grep "Creating an empty commit: empty commit" output
+'
+
+test_expect_success 'skip an empty patch in the middle of an am session' '
+       git checkout empty-commit^ &&
+       test_must_fail git am empty-commit.patch >err &&
+       grep "Patch is empty." err &&
+       grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+       git am --skip &&
+       test_path_is_missing .git/rebase-apply &&
+       git rev-parse empty-commit^ >expected &&
+       git rev-parse HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
+       git checkout empty-commit^ &&
+       test_must_fail git am empty-commit.patch >err &&
+       grep "Patch is empty." err &&
+       grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+       git am --allow-empty >output &&
+       grep "No changes - recorded it as an empty commit." output &&
+       test_path_is_missing .git/rebase-apply &&
+       git show empty-commit --format="%B" >expected &&
+       git show HEAD --format="%B" >actual &&
+       grep -f actual expected
+'
+
+test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
+       git checkout empty-commit^ &&
+       test_must_fail git am empty-commit.patch >err &&
+       : >empty-file &&
+       git add empty-file &&
+       git am --allow-empty &&
+       git show empty-commit --format="%B" >expected &&
+       git show HEAD --format="%B" >actual &&
+       grep -f actual expected &&
+       git diff HEAD^..HEAD --name-only
+'
+
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
+       test_when_finished "git am --abort || :" &&
+       git rev-parse HEAD >expected &&
+       test_must_fail git am seq.patch &&
+       test_must_fail git am --allow-empty >err &&
+       ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+       git rev-parse HEAD >actual &&
+       test_cmp actual expected
+'
+
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
+       test_when_finished "git am --abort || :" &&
+       git rev-parse HEAD >expected &&
+       test_must_fail git am -3 seq.patch &&
+       test_must_fail git am --allow-empty >err &&
+       ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
+       git rev-parse HEAD >actual &&
+       test_cmp actual expected
+'
+
 test_done
index 2374151662b88ceec17777e01b823612fd8edfd4..5ed7e228274ed6d283f138c4fe244f9b3d14e2f3 100755 (executable)
@@ -5,10 +5,7 @@ test_description='am --abort'
 . ./test-lib.sh
 
 test_expect_success setup '
-       for i in a b c d e f g
-       do
-               echo $i
-       done >file-1 &&
+       test_write_lines a b c d e f g >file-1 &&
        cp file-1 file-2 &&
        test_tick &&
        git add file-1 file-2 &&
@@ -43,10 +40,7 @@ do
 
                test_must_fail git am$with3 000[1245]-*.patch &&
                git log --pretty=tformat:%s >actual &&
-               for i in 3 2 initial
-               do
-                       echo $i
-               done >expect &&
+               test_write_lines 3 2 initial >expect &&
                test_cmp expect actual
        '
 
index 7884e3d46b36394d0d6243f7abf40d990dcf8f6d..504955986197224ecc41c15e52fea98a021b9f00 100755 (executable)
@@ -120,48 +120,48 @@ test_expect_success 'diff-filter=A' '
 
 test_expect_success 'diff-filter=M' '
 
-       actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
-       expect=$(echo second) &&
-       verbose test "$actual" = "$expect"
+       git log --pretty="format:%s" --diff-filter=M HEAD >actual &&
+       printf "second" >expect &&
+       test_cmp expect actual
 
 '
 
 test_expect_success 'diff-filter=D' '
 
-       actual=$(git log --no-renames --pretty="format:%s" --diff-filter=D HEAD) &&
-       expect=$(echo sixth ; echo third) &&
-       verbose test "$actual" = "$expect"
+       git log --no-renames --pretty="format:%s" --diff-filter=D HEAD >actual &&
+       printf "sixth\nthird" >expect &&
+       test_cmp expect actual
 
 '
 
 test_expect_success 'diff-filter=R' '
 
-       actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
-       expect=$(echo third) &&
-       verbose test "$actual" = "$expect"
+       git log -M --pretty="format:%s" --diff-filter=R HEAD >actual &&
+       printf "third" >expect &&
+       test_cmp expect actual
 
 '
 
 test_expect_success 'diff-filter=C' '
 
-       actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
-       expect=$(echo fourth) &&
-       verbose test "$actual" = "$expect"
+       git log -C -C --pretty="format:%s" --diff-filter=C HEAD >actual &&
+       printf "fourth" >expect &&
+       test_cmp expect actual
 
 '
 
 test_expect_success 'git log --follow' '
 
-       actual=$(git log --follow --pretty="format:%s" ichi) &&
-       expect=$(echo third ; echo second ; echo initial) &&
-       verbose test "$actual" = "$expect"
+       git log --follow --pretty="format:%s" ichi >actual &&
+       printf "third\nsecond\ninitial" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git config log.follow works like --follow' '
        test_config log.follow true &&
-       actual=$(git log --pretty="format:%s" ichi) &&
-       expect=$(echo third ; echo second ; echo initial) &&
-       verbose test "$actual" = "$expect"
+       git log --pretty="format:%s" ichi >actual &&
+       printf "third\nsecond\ninitial" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git config log.follow does not die with multiple paths' '
@@ -176,9 +176,9 @@ test_expect_success 'git config log.follow does not die with no paths' '
 
 test_expect_success 'git config log.follow is overridden by --no-follow' '
        test_config log.follow true &&
-       actual=$(git log --no-follow --pretty="format:%s" ichi) &&
-       expect="third" &&
-       verbose test "$actual" = "$expect"
+       git log --no-follow --pretty="format:%s" ichi >actual &&
+       printf "third" >expect &&
+       test_cmp expect actual
 '
 
 # Note that these commits are intentionally listed out of order.
@@ -250,7 +250,7 @@ test_expect_success 'log --invert-grep --grep' '
        test_cmp expect actual &&
 
        # POSIX extended
-       git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+       git -c grep.patternType=extended log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
        test_cmp expect actual &&
 
        # PCRE
@@ -952,6 +952,43 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
        test_cmp expect.decorate actual
 '
 
+test_expect_success 'decorate-refs with implied decorate from format' '
+       cat >expect <<-\EOF &&
+       side-2 (tag: side-2)
+       side-1
+       EOF
+       git log --no-walk --format="%s%d" \
+               --decorate-refs="*side-2" side-1 side-2 \
+               >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'implied decorate does not override option' '
+       cat >expect <<-\EOF &&
+       side-2 (tag: refs/tags/side-2, refs/heads/side)
+       side-1 (tag: refs/tags/side-1)
+       EOF
+       git log --no-walk --format="%s%d" \
+               --decorate=full side-1 side-2 \
+               >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'decorate-refs and simplify-by-decoration without output' '
+       cat >expect <<-\EOF &&
+       side-2
+       initial
+       EOF
+       # Do not just use a --format without %d here; we want to
+       # make sure that we did not accidentally turn on displaying
+       # the decorations, too. And that requires one of the regular
+       # formats.
+       git log --decorate-refs="*side-2" --oneline \
+               --simplify-by-decoration >actual.raw &&
+       sed "s/^[0-9a-f]* //" <actual.raw >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'log.decorate config parsing' '
        git log --oneline --decorate=full >expect.full &&
        git log --oneline --decorate=short >expect.short &&
@@ -1677,6 +1714,24 @@ test_expect_success GPGSSH 'setup sshkey signed branch' '
        git commit -S -m signed_commit
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' '
+       test_config gpg.format ssh &&
+       touch file &&
+       git add file &&
+
+       echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+       git tag expired-signed &&
+
+       echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+       git tag notyetvalid-signed &&
+
+       echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+       git tag timeboxedvalid-signed &&
+
+       echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+       git tag timeboxedinvalid-signed
+'
+
 test_expect_success GPGSM 'log x509 fingerprint' '
        echo "F8BF62E0693D0694816377099909C779FA23FD65 | " >expect &&
        git log -n1 --format="%GF | %GP" signed-x509 >actual &&
@@ -1714,6 +1769,31 @@ test_expect_success GPGSSH 'log --graph --show-signature ssh' '
        grep "${GOOD_SIGNATURE_TRUSTED}" actual
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on expired signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git log --graph --show-signature -n1 expired-signed >actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure on not yet valid signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git log --graph --show-signature -n1 notyetvalid-signed >actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log show success with commit date and key validity matching' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git log --graph --show-signature -n1 timeboxedvalid-signed >actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'log shows failure with commit date outside of key validity' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git log --graph --show-signature -n1 timeboxedinvalid-signed >actual &&
+       ! grep "${GPGSSH_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 &&
@@ -2010,4 +2090,23 @@ test_expect_success 'log --end-of-options' '
        test_cmp expect actual
 '
 
+test_expect_success 'set up commits with different authors' '
+       git checkout --orphan authors &&
+       test_commit --author "Jim <jim@example.com>" jim_1 &&
+       test_commit --author "Val <val@example.com>" val_1 &&
+       test_commit --author "Val <val@example.com>" val_2 &&
+       test_commit --author "Jim <jim@example.com>" jim_2 &&
+       test_commit --author "Val <val@example.com>" val_3 &&
+       test_commit --author "Jim <jim@example.com>" jim_3
+'
+
+test_expect_success 'log --invert-grep --grep --author' '
+       cat >expect <<-\EOF &&
+       val_3
+       val_1
+       EOF
+       git log --format=%s --author=Val --grep 2 --invert-grep >actual &&
+       test_cmp expect actual
+'
+
 test_done
index e78d8097f39690ee094f601344d104a6e3a4ce09..80f4a65b285c55cfc7dce38fb115fb0190de97a2 100755 (executable)
@@ -5,7 +5,6 @@ test_description='git patch-id'
 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' '
@@ -28,7 +27,8 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'patch-id output is well-formed' '
-       git log -p -1 | git patch-id >output &&
+       git log -p -1 >log.output &&
+       git patch-id <log.output >output &&
        grep "^$OID_REGEX $(git rev-parse HEAD)$" output
 '
 
@@ -36,8 +36,8 @@ test_expect_success 'patch-id output is well-formed' '
 calc_patch_id () {
        patch_name="$1"
        shift
-       git patch-id "$@" |
-       sed "s/ .*//" >patch-id_"$patch_name" &&
+       git patch-id "$@" >patch-id.output &&
+       sed "s/ .*//" patch-id.output >patch-id_"$patch_name" &&
        test_line_count -gt 0 patch-id_"$patch_name"
 }
 
@@ -46,7 +46,8 @@ get_top_diff () {
 }
 
 get_patch_id () {
-       get_top_diff "$1" | calc_patch_id "$@"
+       get_top_diff "$1" >top-diff.output &&
+       calc_patch_id <top-diff.output "$@"
 }
 
 test_expect_success 'patch-id detects equality' '
@@ -64,16 +65,18 @@ test_expect_success 'patch-id detects inequality' '
 test_expect_success 'patch-id supports git-format-patch output' '
        get_patch_id main &&
        git checkout same &&
-       git format-patch -1 --stdout | calc_patch_id same &&
+       git format-patch -1 --stdout >format-patch.output &&
+       calc_patch_id same <format-patch.output &&
        test_cmp patch-id_main patch-id_same &&
-       set $(git format-patch -1 --stdout | git patch-id) &&
+       set $(git patch-id <format-patch.output) &&
        test "$2" = $(git rev-parse HEAD)
 '
 
 test_expect_success 'whitespace is irrelevant in footer' '
        get_patch_id main &&
        git checkout same &&
-       git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
+       git format-patch -1 --stdout >format-patch.output &&
+       sed "s/ \$//" format-patch.output | calc_patch_id same &&
        test_cmp patch-id_main patch-id_same
 '
 
@@ -92,10 +95,11 @@ test_patch_id_file_order () {
        shift
        name="order-${1}-$relevant"
        shift
-       get_top_diff "main" | calc_patch_id "$name" "$@" &&
+       get_top_diff "main" >top-diff.output &&
+       calc_patch_id <top-diff.output "$name" "$@" &&
        git checkout same &&
-       git format-patch -1 --stdout -O foo-then-bar |
-               calc_patch_id "ordered-$name" "$@" &&
+       git format-patch -1 --stdout -O foo-then-bar >format-patch.output &&
+       calc_patch_id <format-patch.output "ordered-$name" "$@" &&
        cmp_patch_id $relevant "$name" "ordered-$name"
 
 }
@@ -143,7 +147,8 @@ test_expect_success '--stable overrides patchid.stable = false' '
 test_expect_success 'patch-id supports git-format-patch MIME output' '
        get_patch_id main &&
        git checkout same &&
-       git format-patch -1 --attach --stdout | calc_patch_id same &&
+       git format-patch -1 --attach --stdout >format-patch.output &&
+       calc_patch_id <format-patch.output same &&
        test_cmp patch-id_main patch-id_same
 '
 
index 5865daa8f8d2234f0379d25bda47d5d3142f3fc8..e448ef2928a8261e7c991ee6eb0e96bf299d84ee 100755 (executable)
@@ -976,7 +976,7 @@ test_expect_success '%(describe) vs git describe' '
                else
                        : >expect-contains-bad
                fi &&
-               echo "$hash $desc"
+               echo "$hash $desc" || return 1
        done >expect &&
        test_path_exists expect-contains-good &&
        test_path_exists expect-contains-bad &&
@@ -1002,4 +1002,20 @@ test_expect_success '%(describe:exclude=...) vs git describe --exclude ...' '
        test_cmp expect actual
 '
 
+test_expect_success '%(describe:tags) vs git describe --tags' '
+       test_when_finished "git tag -d tagname" &&
+       git tag tagname &&
+       git describe --tags >expect &&
+       git log -1 --format="%(describe:tags)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '%(describe:abbrev=...) vs git describe --abbrev=...' '
+       test_when_finished "git tag -d tagname" &&
+       git tag -a -m tagged tagname &&
+       git describe --abbrev=15 >expect &&
+       git log -1 --format="%(describe:abbrev=15)" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 75795d0b492b4b3a32ab0539a1932bd9c38fcd6c..7f6bb27f141fe786811302f023366e6d15a5aa0e 100755 (executable)
@@ -63,21 +63,21 @@ test_expect_success 'usage' '
        test_i18ngrep "switch.*requires a value" err &&
 
        test_expect_code 128 git log -Gregex -Sstring 2>err &&
-       grep "mutually exclusive" err &&
+       grep "cannot be used together" err &&
 
        test_expect_code 128 git log -Gregex --find-object=HEAD 2>err &&
-       grep "mutually exclusive" err &&
+       grep "cannot be used together" err &&
 
        test_expect_code 128 git log -Sstring --find-object=HEAD 2>err &&
-       grep "mutually exclusive" err &&
+       grep "cannot be used together" err &&
 
        test_expect_code 128 git log --pickaxe-all --find-object=HEAD 2>err &&
-       grep "mutually exclusive" err
+       grep "cannot be used together" err
 '
 
 test_expect_success 'usage: --pickaxe-regex' '
        test_expect_code 128 git log -Gregex --pickaxe-regex 2>err &&
-       grep "mutually exclusive" err
+       grep "cannot be used together" err
 '
 
 test_expect_success 'usage: --no-pickaxe-regex' '
index 560127cc078ad09c7fd87e7d5dc0a4d65fcb22d3..ac9e4d0928593cd37b4a79e27f5fe99b6e4a3faf 100755 (executable)
@@ -137,7 +137,7 @@ test_expect_success 'range_set_union' '
        test_seq 1000 > c.c &&
        git add c.c &&
        git commit -m "modify many lines" &&
-       git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
+       git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c || return 1; done)
 '
 
 test_expect_success '-s shows only line-log commits' '
index 03b952c90d2fca011038e38075344530b5d64477..0244888a5a7a8939276b0425b9144a8e262a2c8f 100755 (executable)
@@ -20,10 +20,10 @@ test_expect_success 'fsck notices broken commit' '
 
 test_expect_success 'git log with broken author email' '
        {
-               echo commit $(cat broken_email.hash)
-               echo "Author: A U Thor <author@example.com>"
-               echo "Date:   Thu Apr 7 15:13:13 2005 -0700"
-               echo
+               echo commit $(cat broken_email.hash) &&
+               echo "Author: A U Thor <author@example.com>" &&
+               echo "Date:   Thu Apr 7 15:13:13 2005 -0700" &&
+               echo &&
                echo "    foo"
        } >expect.out &&
 
index 50f206db55043ff3481b4db4b23937b01bc43cab..cc3cebf672242a622e86b388ada63c9e0c1a9c04 100755 (executable)
@@ -175,13 +175,11 @@ test_expect_success 'persist filter settings' '
        test_when_finished rm -rf .git/objects/info/commit-graph* &&
        rm -rf .git/objects/info/commit-graph* &&
        GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
-               GIT_TRACE2_EVENT_NESTING=5 \
                GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=9 \
                GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY=15 \
                git commit-graph write --reachable --changed-paths &&
        grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2.txt &&
        GIT_TRACE2_EVENT="$(pwd)/trace2-auto.txt" \
-               GIT_TRACE2_EVENT_NESTING=5 \
                git commit-graph write --reachable --changed-paths &&
        grep "{\"hash_version\":1,\"num_hashes\":9,\"bits_per_entry\":15,\"max_changed_paths\":512" trace2-auto.txt
 '
@@ -376,7 +374,7 @@ test_expect_success 'Bloom generation backfills empty commits' '
                cd empty &&
                for i in $(test_seq 1 6)
                do
-                       git commit --allow-empty -m "$i"
+                       git commit --allow-empty -m "$i" || return 1
                done &&
 
                # Generate Bloom filters for empty commits 1-6, two at a time.
@@ -389,7 +387,7 @@ test_expect_success 'Bloom generation backfills empty commits' '
                        test_filter_computed 2 trace.event &&
                        test_filter_not_computed 4 trace.event &&
                        test_filter_trunc_empty 2 trace.event &&
-                       test_filter_trunc_large 0 trace.event
+                       test_filter_trunc_large 0 trace.event || return 1
                done &&
 
                # Finally, make sure that once all commits have filters, that
index 2c88d1c159623d6ba8a201352f7df26b4d2f23f7..7f8d2ab0a72dac716198a424556d1e9323f2f8c3 100755 (executable)
@@ -77,7 +77,7 @@ check_tar() {
                                        path=$(get_pax_header $header path) &&
                                        if test -n "$path"
                                        then
-                                               mv "$data" "$path"
+                                               mv "$data" "$path" || exit 1
                                        fi
                                fi
                        done
@@ -133,7 +133,7 @@ test_expect_success 'populate workdir' '
                for depth in 1 2 3 4 5
                do
                        mkdir $p &&
-                       cd $p
+                       cd $p || exit 1
                done &&
                echo text >file_with_long_path
        ) &&
index bda6d7d7e9e835213f9b1323407222850db47ad6..a66b5ba27e869e377a317258c03cde8953323d8f 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git archive attribute pattern tests'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_exists() {
index 1e6d18b140e5193d1eb046adbbbbba8c20cb63b1..d726964307ca89373eeaf206bbb2dc2d4c1c3008 100755 (executable)
@@ -106,7 +106,7 @@ test_expect_success \
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
      printf "A not substituted O" >a/substfile2 &&
      (p=long_path_to_a_file && cd a &&
-      for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+      for depth in 1 2 3 4 5; do mkdir $p && cd $p || exit 1; done &&
       echo text >file_with_long_path)
 '
 
index 2d32d0ed122277089966b44f015b5f8ff6765e1c..ae508e21623fb4af7b55c1c8afe27556b24bb232 100755 (executable)
@@ -131,7 +131,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
        do
                for b in 0 1 2 3 4 5 6 7 8 9 a b c d e f
                do
-                       : >00/$a$b
+                       : >00/$a$b || return 1
                done
        done &&
        git add 00 &&
@@ -143,7 +143,7 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
        do
                for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f
                do
-                       echo "040000 tree $subtree      $c$d"
+                       echo "040000 tree $subtree      $c$d" || return 1
                done
        done >tree &&
        tree=$(git mktree <tree) &&
@@ -171,7 +171,7 @@ test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \
        # create tree containing 65500 entries of that blob
        for i in $(test_seq 1 65500)
        do
-               echo "100644 blob $blob $i"
+               echo "100644 blob $blob $i" || return 1
        done >tree &&
        tree=$(git mktree <tree) &&
 
index 141b29f0319a606ceefce3a06cd26990a64c8f06..cebad1048cfca3a142bcaef361dfab69fe889ceb 100755 (executable)
@@ -122,7 +122,7 @@ test_expect_success 'mailinfo unescapes with --mboxrd' '
        do
                git mailinfo mboxrd/msg mboxrd/patch \
                  <mboxrd/$i >mboxrd/out &&
-               test_cmp "$DATA/${i}mboxrd" mboxrd/msg
+               test_cmp "$DATA/${i}mboxrd" mboxrd/msg || return 1
        done &&
        sp=" " &&
        echo "From " >expect &&
index 21a58eecb9b59a344e81f222b322a8ffe080e22e..ed9dfd624c754206e58b7eaaf0c023b573d8ffc1 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test git update-server-info'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' 'test_commit file'
index e13a884207589d3c804745acb9c1f91e731a6f1d..2fd845187e76bd2ca7269ae45441c1495eae5839 100755 (executable)
@@ -347,7 +347,7 @@ test_expect_success 'unpacking with --strict' '
                for i in 0 1 2 3 4 5 6 7 8 9
                do
                        o=$(echo $j$i | git hash-object -w --stdin) &&
-                       echo "100644 $o 0 $j$i"
+                       echo "100644 $o 0 $j$i" || return 1
                done
        done >LIST &&
        rm -f .git/index &&
@@ -361,11 +361,7 @@ test_expect_success 'unpacking with --strict' '
        ST=$(git write-tree) &&
        git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
        PACK5=$( git pack-objects test-5 <actual ) &&
-       PACK6=$( (
-                       echo "$LIST"
-                       echo "$LI"
-                       echo "$ST"
-                ) | git pack-objects test-6 ) &&
+       PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
        test_create_repo test-5 &&
        (
                cd test-5 &&
@@ -394,7 +390,7 @@ test_expect_success 'index-pack with --strict' '
                for i in 0 1 2 3 4 5 6 7 8 9
                do
                        o=$(echo $j$i | git hash-object -w --stdin) &&
-                       echo "100644 $o 0 $j$i"
+                       echo "100644 $o 0 $j$i" || return 1
                done
        done >LIST &&
        rm -f .git/index &&
@@ -408,11 +404,7 @@ test_expect_success 'index-pack with --strict' '
        ST=$(git write-tree) &&
        git rev-list --objects "$LIST" "$LI" "$ST" >actual &&
        PACK5=$( git pack-objects test-5 <actual ) &&
-       PACK6=$( (
-                       echo "$LIST"
-                       echo "$LI"
-                       echo "$ST"
-                ) | git pack-objects test-6 ) &&
+       PACK6=$( test_write_lines "$LIST" "$LI" "$ST" | git pack-objects test-6 ) &&
        test_create_repo test-7 &&
        (
                cd test-7 &&
@@ -594,7 +586,7 @@ test_expect_success 'setup for --stdin-packs tests' '
                for id in A B C
                do
                        git pack-objects .git/objects/pack/pack-$id \
-                               --incremental --revs <<-EOF
+                               --incremental --revs <<-EOF || exit 1
                        refs/tags/$id
                        EOF
                done &&
index 7c9d687367088346191434b1ceac6dd3726dff1d..8ee67df38f6c1c272834adcc3deef239eab6dd30 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'setup' '
        i=1 &&
        while test $i -le 100
        do
-               iii=$(printf "%03i" $i)
+               iii=$(printf "%03i" $i) &&
                test-tool genrandom "bar" 200 > wide_delta_$iii &&
                test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
                test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
index f4931c0c2a4050ddbf3896bba643d8091ee47e2a..51973f4a512bf914ee0fb50d5472d050f241a2ed 100755 (executable)
@@ -12,7 +12,7 @@ test_description='git-pack-object with missing base
 #
 test_expect_success \
     'setup base' \
-    'for a in a b c d e f g h i; do echo $a >>text; done &&
+    'test_write_lines a b c d e f g h i >text &&
      echo side >side &&
      git update-index --add text side &&
      A=$(echo A | git commit-tree $(git write-tree)) &&
index f4338abb78a83967a1bdc6b264600262a0f94d65..1e02c305c4fe5f910935463bbbb6724a3bde212d 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='pack should notice missing commit objects'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -11,7 +12,7 @@ test_expect_success setup '
                git add "file$i" &&
                test_tick &&
                git commit -m "$i" &&
-               git tag "tag$i"
+               git tag "tag$i" || return 1
        done &&
        obj=$(git rev-parse --verify tag3) &&
        fanout=$(expr "$obj" : "\(..\)") &&
index dcf03d324a25a4bdc14156c6b53c5442f9c3d83b..d05ab716f6aa1fd6f641f14e308e2e5b80e70af3 100755 (executable)
@@ -1,8 +1,6 @@
 #!/bin/sh
 
 test_description='exercise basic bitmap functionality'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
@@ -35,7 +33,7 @@ test_expect_success 'setup writing bitmaps during repack' '
 '
 
 test_expect_success 'full repack creates bitmaps' '
-       GIT_TRACE2_EVENT_NESTING=4 GIT_TRACE2_EVENT="$(pwd)/trace" \
+       GIT_TRACE2_EVENT="$(pwd)/trace" \
                git repack -ad &&
        ls .git/objects/pack/ | grep bitmap >output &&
        test_line_count = 1 output &&
@@ -230,7 +228,7 @@ test_expect_success 'pack reuse respects --honor-pack-keep' '
        test_when_finished "rm -f .git/objects/pack/*.keep" &&
        for i in .git/objects/pack/*.pack
        do
-               >${i%.pack}.keep
+               >${i%.pack}.keep || return 1
        done &&
        reusable_pack --honor-pack-keep >empty.pack &&
        git index-pack empty.pack &&
index 759169d0742c999adb33f91bc1ae1283877cb3db..df524f7b6dde1fa63d268bb88730dc766a003f4d 100755 (executable)
@@ -57,8 +57,11 @@ test_expect_success 'create series of packs' '
                git commit -m $i &&
                cur=$(git rev-parse HEAD^{tree}) &&
                {
-                       test -n "$prev" && echo "-$prev"
-                       echo $cur
+                       if test -n "$prev"
+                       then
+                               echo "-$prev"
+                       fi &&
+                       echo $cur &&
                        echo "$(git rev-parse :file) file"
                } | git pack-objects --stdout >tmp &&
                git index-pack --stdin --fix-thin <tmp || return 1
index 13ed3eb13652dcf07727f2a64b2b1f999604459c..33b740ce628f834a339154bd05d1acb5a1bbe4b7 100755 (executable)
@@ -16,9 +16,9 @@ test_expect_success 'setup r1' '
        git init r1 &&
        for n in 1 2 3 4 5
        do
-               echo "This is file: $n" > r1/file.$n
-               git -C r1 add file.$n
-               git -C r1 commit -m "$n"
+               echo "This is file: $n" > r1/file.$n &&
+               git -C r1 add file.$n &&
+               git -C r1 commit -m "$n" || return 1
        done
 '
 
@@ -116,9 +116,9 @@ test_expect_success 'setup r2' '
        git init r2 &&
        for n in 1000 10000
        do
-               printf "%"$n"s" X > r2/large.$n
-               git -C r2 add large.$n
-               git -C r2 commit -m "$n"
+               printf "%"$n"s" X > r2/large.$n &&
+               git -C r2 add large.$n &&
+               git -C r2 commit -m "$n" || return 1
        done
 '
 
@@ -278,10 +278,10 @@ test_expect_success 'setup r3' '
        mkdir r3/dir1 &&
        for n in sparse1 sparse2
        do
-               echo "This is file: $n" > r3/$n
-               git -C r3 add $n
-               echo "This is file: dir1/$n" > r3/dir1/$n
-               git -C r3 add dir1/$n
+               echo "This is file: $n" > r3/$n &&
+               git -C r3 add $n &&
+               echo "This is file: dir1/$n" > r3/dir1/$n &&
+               git -C r3 add dir1/$n || return 1
        done &&
        git -C r3 commit -m "sparse" &&
        echo dir1/ >pattern1 &&
@@ -331,10 +331,10 @@ test_expect_success 'setup r4' '
        mkdir r4/dir1 &&
        for n in sparse1 sparse2
        do
-               echo "This is file: $n" > r4/$n
-               git -C r4 add $n
-               echo "This is file: dir1/$n" > r4/dir1/$n
-               git -C r4 add dir1/$n
+               echo "This is file: $n" > r4/$n &&
+               git -C r4 add $n &&
+               echo "This is file: dir1/$n" > r4/dir1/$n &&
+               git -C r4 add dir1/$n || return 1
        done &&
        echo dir1/ >r4/pattern &&
        git -C r4 add pattern &&
@@ -409,7 +409,7 @@ test_expect_success 'setup r1 - delete loose blobs' '
 
        for id in `cat expected | sed "s|..|&/|"`
        do
-               rm r1/.git/objects/$id
+               rm r1/.git/objects/$id || return 1
        done
 '
 
index f516fda7cc934dc7672f0e0765206461a01df51e..edb728f77c3583cecde2d87555a4547464db7b96 100755 (executable)
@@ -64,7 +64,7 @@ test_expect_success 'create commits and repack' '
        for i in $(test_seq 3)
        do
                test_commit $i &&
-               git branch commits/$i
+               git branch commits/$i || return 1
        done &&
        git repack
 '
@@ -147,13 +147,13 @@ test_expect_success 'Add more commits' '
        for i in $(test_seq 4 5)
        do
                test_commit $i &&
-               git branch commits/$i
+               git branch commits/$i || return 1
        done &&
        git reset --hard commits/2 &&
        for i in $(test_seq 6 7)
        do
                test_commit $i &&
-               git branch commits/$i
+               git branch commits/$i || return 1
        done &&
        git reset --hard commits/2 &&
        git merge commits/4 &&
index a612e445472cd25c8e1bcc544f78595d44c9dd07..afbe93f162e854b462774cd5242ddfe40a64bf6c 100755 (executable)
@@ -93,7 +93,7 @@ test_expect_success 'create objects' '
        test_commit initial &&
        for i in $(test_seq 1 5)
        do
-               generate_objects $i
+               generate_objects $i || return 1
        done &&
        commit_and_list_objects
 '
@@ -155,7 +155,7 @@ test_expect_success 'corrupt idx reports errors' '
 test_expect_success 'add more objects' '
        for i in $(test_seq 6 10)
        do
-               generate_objects $i
+               generate_objects $i || return 1
        done &&
        commit_and_list_objects
 '
@@ -203,7 +203,7 @@ test_expect_success 'add more packs' '
        do
                generate_objects $j &&
                commit_and_list_objects &&
-               git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list
+               git pack-objects --index-version=2 $objdir/pack/test-pack <obj-list || return 1
        done
 '
 
@@ -596,7 +596,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '
        mkdir objects64/pack &&
        for i in $(test_seq 1 11)
        do
-               generate_objects 11
+               generate_objects 11 || return 1
        done &&
        commit_and_list_objects &&
        pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
@@ -640,7 +640,7 @@ test_expect_success 'setup expire tests' '
                git update-index --add large_file.txt &&
                for i in $(test_seq 1 20)
                do
-                       test_commit $i
+                       test_commit $i || exit 1
                done &&
                git branch A HEAD &&
                git branch B HEAD~8 &&
index 61cb907a9035219aecde0d425bfe73a7c98083ae..d39958c066de5e3739b48a4a3a64cb46f21f6700 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'setup repo' '
                for j in $(test_seq 1 3)
                do
                        mkdir f$i/f$j &&
-                       echo $j >f$i/f$j/data.txt
+                       echo $j >f$i/f$j/data.txt || return 1
                done
        done &&
        git add . &&
@@ -23,7 +23,7 @@ test_expect_success 'setup repo' '
        do
                git checkout -b topic$i main &&
                echo change-$i >f$i/f$i/data.txt &&
-               git commit -a -m "Changed f$i/f$i/data.txt"
+               git commit -a -m "Changed f$i/f$i/data.txt" || return 1
        done &&
        cat >packinput.txt <<-EOF &&
        topic1
index da453f68d67974327248563c25694adbe19154af..d042d26f2b393d52049ba3c9972e2516b03ed239 100755 (executable)
@@ -46,7 +46,7 @@ test_expect_success 'index-pack with --[no-]rev-index' '
                test_path_exists $rev &&
 
                test_index_pack "$conf" --no-rev-index &&
-               test_path_is_missing $rev
+               test_path_is_missing $rev || return 1
        done
 '
 
index 8a5d3492c713b8ca5eceef13ed55f7bd49edcae9..f0dc4e696860a4435a1be1182457325290bb1eb4 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'setup' '
        while [ $cur -le 10 ]; do
                add A$cur $(eval echo \$A$prev) &&
                prev=$cur &&
-               cur=$(($cur+1))
+               cur=$(($cur+1)) || return 1
        done &&
        add B1 $A1 &&
        git update-ref refs/heads/A "$ATIP" &&
@@ -112,7 +112,7 @@ test_expect_success 'post 1st pull setup' '
        while [ $cur -le 65 ]; do
                add B$cur $(eval echo \$B$prev) &&
                prev=$cur &&
-               cur=$(($cur+1))
+               cur=$(($cur+1)) || return 1
        done
 '
 
@@ -464,11 +464,11 @@ test_expect_success 'fetch creating new shallow root' '
 test_expect_success 'setup tests for the --stdin parameter' '
        for head in C D E F
        do
-               add $head
+               add $head || return 1
        done &&
        for head in A B C D E F
        do
-               git tag $head $head
+               git tag $head $head || return 1
        done &&
        cat >input <<-\EOF &&
        refs/heads/C
index 8c05c7715bff85c643bf8f09540a13ebe9312fb0..b160f8b7fb7e1f9973361cf3ed86d769d5610206 100755 (executable)
@@ -130,7 +130,7 @@ test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '
        for i in 0 1 2 3 4 5 6 7 8 9; do
                for j in 0 1 2 3 4 5 6 7 8 9; do
                        for k in 0 1 2 3 4 5 6 7 8 9; do
-                               echo "$branchprefix$i$j$k" >> .git/packed-refs
+                               echo "$branchprefix$i$j$k" >> .git/packed-refs || return 1
                        done
                done
        done &&
index 6e5a9c20e7fc036ef9fb5994931dba7b5cc9ef0f..b0b795aca97f8614dcae4a4a52f9b811386d7deb 100755 (executable)
@@ -292,7 +292,7 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '
                receive.fsck.missingEmail warn &&
        git push --porcelain dst bogus >act 2>&1 &&
        grep "missingEmail" act &&
-       test_i18ngrep "Skipping unknown msg id.*whatever" act &&
+       test_i18ngrep "skipping unknown msg id.*whatever" act &&
        git --git-dir=dst/.git branch -D bogus &&
        git --git-dir=dst/.git config --add \
                receive.fsck.missingEmail ignore &&
index e6e3c8f552cd66fedaccbadb49cb47facb3f3863..9ab315424c4b71e1a431c511bddc02b9c0b4e49b 100755 (executable)
@@ -2,9 +2,6 @@
 
 test_description='git remote porcelain-ish'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 setup_repository () {
@@ -1332,7 +1329,6 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
        (
                cd test &&
                git tag -a -m "Some tag" some-tag main &&
-               exit_with=true &&
                for type in commit tag tree blob
                do
                        if test "$type" = "blob"
@@ -1348,9 +1344,8 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
                                push origin $oid:dst 2>err &&
                        test_i18ngrep "error: The destination you" err &&
                        test_i18ngrep ! "hint: Did you mean" err ||
-                       exit_with=false
-               done &&
-               $exit_with
+                       exit 1
+               done
        )
 '
 
index a0faf0dd94909392d7fa3b263b30b2bb41d7ca35..20f7110ec108fda462f5e9a0a43221d2c66e7fe1 100755 (executable)
@@ -5,9 +5,6 @@ test_description='Per branch config variables affects "git fetch".
 
 '
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bundle.sh
 
@@ -40,11 +37,11 @@ test_expect_success "clone and setup child repos" '
                git config branch.main.remote two &&
                git config branch.main.merge refs/heads/one &&
                mkdir -p .git/remotes &&
-               {
-                       echo "URL: ../two/.git/"
-                       echo "Pull: refs/heads/main:refs/heads/two"
-                       echo "Pull: refs/heads/one:refs/heads/one"
-               } >.git/remotes/two
+               cat >.git/remotes/two <<-\EOF
+               URL: ../two/.git/
+               Pull: refs/heads/main:refs/heads/two
+               Pull: refs/heads/one:refs/heads/one
+               EOF
        ) &&
        git clone . bundle &&
        git clone . seven
@@ -71,7 +68,7 @@ test_expect_success "fetch test for-merge" '
        main_in_two=$(cd ../two && git rev-parse main) &&
        one_in_two=$(cd ../two && git rev-parse one) &&
        {
-               echo "$one_in_two       "
+               echo "$one_in_two       " &&
                echo "$main_in_two      not-for-merge"
        } >expected &&
        cut -f -2 .git/FETCH_HEAD >actual &&
@@ -550,7 +547,7 @@ test_expect_success 'bundle should record HEAD correctly' '
        git bundle list-heads bundle5 >actual &&
        for h in HEAD refs/heads/main
        do
-               echo "$(git rev-parse --verify $h) $h"
+               echo "$(git rev-parse --verify $h) $h" || return 1
        done >expect &&
        test_cmp expect actual
 
index 50f14101c532d67480cd9796c797df3932d33d56..320d26796d24d8a2281d37220a7bdf73cafaa503 100755 (executable)
@@ -105,19 +105,19 @@ test_expect_success setup '
        remotes="$remotes config-glob" &&
 
        mkdir -p .git/remotes &&
-       {
-               echo "URL: ../.git/"
-               echo "Pull: refs/heads/main:remotes/rem/main"
-               echo "Pull: refs/heads/one:remotes/rem/one"
-               echo "Pull: two:remotes/rem/two"
-               echo "Pull: refs/heads/three:remotes/rem/three"
-       } >.git/remotes/remote-explicit &&
+       cat >.git/remotes/remote-explicit <<-\EOF &&
+       URL: ../.git/
+       Pull: refs/heads/main:remotes/rem/main
+       Pull: refs/heads/one:remotes/rem/one
+       Pull: two:remotes/rem/two
+       Pull: refs/heads/three:remotes/rem/three
+       EOF
        remotes="$remotes remote-explicit" &&
 
-       {
-               echo "URL: ../.git/"
-               echo "Pull: refs/heads/*:refs/remotes/rem/*"
-       } >.git/remotes/remote-glob &&
+       cat >.git/remotes/remote-glob <<-\EOF &&
+       URL: ../.git/
+       Pull: refs/heads/*:refs/remotes/rem/*
+       EOF
        remotes="$remotes remote-glob" &&
 
        mkdir -p .git/branches &&
@@ -133,7 +133,7 @@ test_expect_success setup '
                git config branch.br-$remote-merge.merge refs/heads/three &&
                git config branch.br-$remote-octopus.remote $remote &&
                git config branch.br-$remote-octopus.merge refs/heads/one &&
-               git config --add branch.br-$remote-octopus.merge two
+               git config --add branch.br-$remote-octopus.merge two || return 1
        done &&
        build_script sed_script
 '
@@ -191,17 +191,17 @@ do
                cp "$expect_r" expect_r &&
                convert_expected expect_r sed_script &&
                {
-                       echo "# $cmd"
-                       set x $cmd; shift
-                       git symbolic-ref HEAD refs/heads/$1 ; shift
-                       rm -f .git/FETCH_HEAD
+                       echo "# $cmd" &&
+                       set x $cmd && shift &&
+                       git symbolic-ref HEAD refs/heads/$1 && shift &&
+                       rm -f .git/FETCH_HEAD &&
                        git for-each-ref \
                                refs/heads refs/remotes/rem refs/tags |
                        while read val type refname
                        do
-                               git update-ref -d "$refname" "$val"
-                       done
-                       git fetch "$@" >/dev/null
+                               git update-ref -d "$refname" "$val" || return 1
+                       done &&
+                       git fetch "$@" >/dev/null &&
                        cat .git/FETCH_HEAD
                } >"$actual_f" &&
                git show-ref >"$actual_r" &&
index 7831a38ddefdc52001b37c73f1087cda2462d182..2f04cf9a1c7700c599237cee93c3877a0f9f649e 100755 (executable)
@@ -1325,10 +1325,7 @@ test_expect_success 'fetch follows tags by default' '
                git pull ../testrepo main &&
                git tag -m "annotated" tag &&
                git for-each-ref >tmp1 &&
-               (
-                       cat tmp1
-                       sed -n "s|refs/heads/main$|refs/remotes/origin/main|p" tmp1
-               ) |
+               sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
                sort -k 3 >../expect
        ) &&
        git init dst &&
@@ -1778,6 +1775,38 @@ test_expect_success 'denyCurrentBranch and worktrees' '
        test_must_fail git -C cloned push origin HEAD:new-wt &&
        test_config receive.denyCurrentBranch updateInstead &&
        git -C cloned push origin HEAD:new-wt &&
+       test_path_exists new-wt/first.t &&
        test_must_fail git -C cloned push --delete origin new-wt
 '
+
+test_expect_success 'denyCurrentBranch and bare repository worktrees' '
+       test_when_finished "rm -fr bare.git" &&
+       git clone --bare . bare.git &&
+       git -C bare.git worktree add wt &&
+       test_commit grape &&
+       git -C bare.git config receive.denyCurrentBranch refuse &&
+       test_must_fail git push bare.git HEAD:wt &&
+       git -C bare.git config receive.denyCurrentBranch updateInstead &&
+       git push bare.git HEAD:wt &&
+       test_path_exists bare.git/wt/grape.t &&
+       test_must_fail git push --delete bare.git wt
+'
+
+test_expect_success 'refuse fetch to current branch of worktree' '
+       test_when_finished "git worktree remove --force wt && git branch -D wt" &&
+       git worktree add wt &&
+       test_commit apple &&
+       test_must_fail git fetch . HEAD:wt &&
+       git fetch -u . HEAD:wt
+'
+
+test_expect_success 'refuse fetch to current branch of bare repository worktree' '
+       test_when_finished "rm -fr bare.git" &&
+       git clone --bare . bare.git &&
+       git -C bare.git worktree add wt &&
+       test_commit banana &&
+       test_must_fail git -C bare.git fetch .. HEAD:wt &&
+       git -C bare.git fetch -u .. HEAD:wt
+'
+
 test_done
index 2dc75b80db80964a6bcdbc3977be98f815b40dd0..840c89cc8b9cbd10b769d4bf67a278293278cfef 100755 (executable)
@@ -3,9 +3,6 @@
 
 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
 
index 8b68bb38a442747bff7a8b416f5300a9e2135662..b0dbacf0b9b5ee8300368c1defe54cf87ce3ee05 100755 (executable)
@@ -18,6 +18,12 @@ then
        test_done
 fi
 
+if test_have_prereq !REFFILES
+then
+       skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+       test_done
+fi
+
 LIB_HTTPD_DAV=t
 . "$TEST_DIRECTORY"/lib-httpd.sh
 ROOT_PATH="$PWD"
index 6d9142afc3b24b39594ce869b48df9549fe603c4..259203926a95d0342a87cff42e00976e66631798 100755 (executable)
@@ -5,6 +5,13 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
+
+if test_have_prereq !REFFILES
+then
+       skip_all='skipping test; dumb HTTP protocol not supported with reftable.'
+       test_done
+fi
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 7b9fb4ff02c2145a6029dee781661dcab37c3bdc..165427d57e5cfb2c4ecd3826564bf7ea89fba629 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc
        git init client &&
        for i in $(test_seq 7)
        do
-               test_commit -C client c$i
+               test_commit -C client c$i || return 1
        done &&
 
        # We send: "c7" (skip 1) "c5" (skip 2) "c2" (skip 4). After that, since
@@ -68,7 +68,7 @@ test_expect_success 'when two skips collide, favor the larger one' '
        git init client &&
        for i in $(test_seq 11)
        do
-               test_commit -C client c$i
+               test_commit -C client c$i || return 1
        done &&
        git -C client checkout c5 &&
        test_commit -C client c5side &&
@@ -155,14 +155,14 @@ test_expect_success 'do not send "have" with ancestors of commits that server AC
        for i in $(test_seq 8)
        do
                git -C client checkout --orphan b$i &&
-               test_commit -C client b$i.c0
+               test_commit -C client b$i.c0 || return 1
        done &&
        for j in $(test_seq 19)
        do
                for i in $(test_seq 8)
                do
                        git -C client checkout b$i &&
-                       test_commit -C client b$i.c$j
+                       test_commit -C client b$i.c$j || return 1
                done
        done &&
 
@@ -201,7 +201,7 @@ test_expect_success 'do not send "have" with ancestors of commits that server AC
        # should still send the others (in this test, just check b2).
        for i in $(test_seq 0 8)
        do
-               have_not_sent b1.c$i
+               have_not_sent b1.c$i || return 1
        done &&
        have_sent b2.c1 b2.c0
 '
index 9c12c0f8c321def9e39d0887d463b5d227d3157d..48050162c27440dab8a0dbc5cd19a2781bee2d37 100755 (executable)
@@ -91,6 +91,17 @@ test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '
        check_config_missing other2
 '
 
+test_expect_success 'fetch --set-upstream with a detached HEAD' '
+       git checkout HEAD^0 &&
+       test_when_finished "git checkout -" &&
+       cat >expect <<-\EOF &&
+       warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+       EOF
+       git fetch --set-upstream upstream main 2>actual.raw &&
+       grep ^warning: actual.raw >actual &&
+       test_cmp expect actual
+'
+
 # tests for pull --set-upstream
 
 test_expect_success 'setup bare parent pull' '
@@ -178,4 +189,15 @@ test_expect_success 'pull --set-upstream with valid URL and branch sets branch'
        check_config_missing other2
 '
 
+test_expect_success 'pull --set-upstream with a detached HEAD' '
+       git checkout HEAD^0 &&
+       test_when_finished "git checkout -" &&
+       cat >expect <<-\EOF &&
+       warning: could not set upstream of HEAD to '"'"'main'"'"' from '"'"'upstream'"'"' when it does not point to any branch.
+       EOF
+       git pull --no-rebase --set-upstream upstream main 2>actual.raw &&
+       grep ^warning: actual.raw >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 49faf5e283bdb05b090f9682cd315e94eea4d217..b1cfe8b7dba816ddcaee85c0a3e19d0c958e6cd5 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test functionality common to smart fetch & push'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 05a58069b0cd41243f072c37b612c721ee88c2e1..b68ec22d3fdb0bab650b39642a61a11a6579fcb5 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success 'setup' '
        hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
        {
                printf "%s %s refs/heads/newbranch\\0report-status object-format=%s\\n" \
-                       "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw
+                       "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize_raw &&
                printf 0000 &&
                echo "$hash_next" | git pack-objects --stdout
        } >push_body &&
index b87ca06a585ca9791ca766dea212704dacddd7ff..1131503b760c48cbb6f6b0d10a5027d76ec40db5 100755 (executable)
@@ -194,7 +194,7 @@ test_expect_success 'hostname cannot break out of directory' '
 
 test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
        {
-               printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw
+               printf "git-upload-pack /interp.git\n\0host=localhost" | packetize_raw &&
                printf "0000"
        } >input &&
        fake_nc "$GIT_DAEMON_HOST_PORT" <input >output &&
index ad8d5804f7b7df3995643db0f30a6b87c524ce36..660f876eec2122f3b00e2dd087a03e2ffcd685e2 100755 (executable)
@@ -114,11 +114,11 @@ test_expect_success 'push to URL' '
 
 test_expect_success 'set up many-ref tests' '
        {
-               nr=1000
+               nr=1000 &&
                while test $nr -lt 2000
                do
-                       nr=$(( $nr + 1 ))
-                       echo "create refs/heads/b/$nr $COMMIT3"
+                       nr=$(( $nr + 1 )) &&
+                       echo "create refs/heads/b/$nr $COMMIT3" || return 1
                done
        } | git update-ref --stdin
 '
index cbcceab9d56b591ee851374c9030a23a4c65a462..56329aa160e7529eb2709be333919c9d172fabf3 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description=clone
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 13b5e5eb9b9fa2aa96e0496eb9fa3e7049fdfb51..8ca1f0942378d27e509e248fc1ea3346b26ef9db 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check output directory names used by git-clone'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # we use a fake ssh wrapper that ignores the arguments
index d822153e4d29240babbd2904af9527e6f93e6528..8f676d6b0c0e2f6f72b2c0285afaca5b16be3017 100755 (executable)
@@ -46,7 +46,7 @@ test_expect_success 'disallows --bare with --origin' '
 
        test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
        test_debug "cat err" &&
-       test_i18ngrep -e "--bare and --origin foo options are incompatible" err
+       test_i18ngrep -e "options .--bare. and .--origin foo. cannot be used together" err
 
 '
 
@@ -54,7 +54,7 @@ test_expect_success 'disallows --bare with --separate-git-dir' '
 
        test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
        test_debug "cat err" &&
-       test_i18ngrep -e "--bare and --separate-git-dir are incompatible" err
+       test_i18ngrep -e "options .--bare. and .--separate-git-dir. cannot be used together" err
 
 '
 
index f8625f915821b5db03a5667b548ed055d26ccca3..4b3877216ee4649a0a31986962740e0f37b5ad85 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'clone -c sets config in cloned repo' '
 test_expect_success 'clone -c can set multi-keys' '
        rm -rf child &&
        git clone -c core.foo=bar -c core.foo=baz . child &&
-       { echo bar; echo baz; } >expect &&
+       test_write_lines bar baz >expect &&
        git --git-dir=child/.git config --get-all core.foo >actual &&
        test_cmp expect actual
 '
index cf3e82bdf5cc1c823709963402eb3396ba0ba01a..34469b6ac10fef6dac4fb161f5377a8f47a6dd98 100755 (executable)
@@ -16,10 +16,10 @@ test_expect_success 'setup normal src repo' '
        git init src &&
        for n in 1 2 3 4
        do
-               echo "This is file: $n" > src/file.$n.txt
-               git -C src add file.$n.txt
-               git -C src commit -m "file $n"
-               git -C src ls-files -s file.$n.txt >>temp
+               echo "This is file: $n" > src/file.$n.txt &&
+               git -C src add file.$n.txt &&
+               git -C src commit -m "file $n" &&
+               git -C src ls-files -s file.$n.txt >>temp || return 1
        done &&
        awk -f print_2.awk <temp | sort >expect_1.oids &&
        test_line_count = 4 expect_1.oids
@@ -72,9 +72,9 @@ test_expect_success 'push new commits to server' '
        git -C src remote add srv "file://$(pwd)/srv.bare" &&
        for x in a b c d e
        do
-               echo "Mod file.1.txt $x" >>src/file.1.txt
-               git -C src add file.1.txt
-               git -C src commit -m "mod $x"
+               echo "Mod file.1.txt $x" >>src/file.1.txt &&
+               git -C src add file.1.txt &&
+               git -C src commit -m "mod $x" || return 1
        done &&
        git -C src blame main -- file.1.txt >expect.blame &&
        git -C src push -u srv main
@@ -114,9 +114,9 @@ test_expect_success 'verify blame causes dynamic object fetch' '
 test_expect_success 'push new commits to server for file.2.txt' '
        for x in a b c d e f
        do
-               echo "Mod file.2.txt $x" >>src/file.2.txt
-               git -C src add file.2.txt
-               git -C src commit -m "mod $x"
+               echo "Mod file.2.txt $x" >>src/file.2.txt &&
+               git -C src add file.2.txt &&
+               git -C src commit -m "mod $x" || return 1
        done &&
        git -C src push -u srv main
 '
@@ -135,9 +135,9 @@ test_expect_success 'override inherited filter-spec using --no-filter' '
 test_expect_success 'push new commits to server for file.3.txt' '
        for x in a b c d e f
        do
-               echo "Mod file.3.txt $x" >>src/file.3.txt
-               git -C src add file.3.txt
-               git -C src commit -m "mod $x"
+               echo "Mod file.3.txt $x" >>src/file.3.txt &&
+               git -C src add file.3.txt &&
+               git -C src commit -m "mod $x" || return 1
        done &&
        git -C src push -u srv main
 '
@@ -385,7 +385,7 @@ setup_triangle () {
        for i in $(test_seq 1 100)
        do
                echo "make the tree big" >server/file$i &&
-               git -C server add file$i
+               git -C server add file$i || return 1
        done &&
        git -C server commit -m "initial" &&
        git clone --bare --filter=tree:0 "file://$(pwd)/server" client &&
@@ -669,7 +669,7 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor
        for i in $(test_seq 10)
        do
                echo "this is a line" >>"$SERVER/foo.txt" &&
-               echo "this is another line" >>"$SERVER/have.txt"
+               echo "this is another line" >>"$SERVER/have.txt" || return 1
        done &&
        git -C "$SERVER" add foo.txt have.txt &&
        git -C "$SERVER" commit -m bar &&
index aa1827d841d47779ef3bb081aba8205802395fb2..1896f671cb37f916bc09e50f0f5a45df81da1327 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test protocol v2 server commands'
 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 'test capability advertisement' '
index 78f85b0714acbc9064199cec25f634370a58d36f..710f33e2aa0d1775c21c00a939d04cd129f5b3e5 100755 (executable)
@@ -747,7 +747,7 @@ test_expect_success 'clone big repository with http:// using protocol v2' '
                echo "data 0" &&
                echo "M 644 inline bla.txt" &&
                echo "data 4" &&
-               echo "bla"
+               echo "bla" || return 1
        done | git -C "$HTTPD_DOCUMENT_ROOT_PATH/big" fast-import &&
 
        GIT_TRACE_PACKET="$(pwd)/log" GIT_TRACE_CURL="$(pwd)/log" git \
@@ -942,7 +942,7 @@ test_expect_success 'part of packfile response provided as URI' '
                        then
                                >h2found
                        fi
-               fi
+               fi || return 1
        done &&
        test -f hfound &&
        test -f h2found &&
index 220098523a699451cbb3abaaa2850416561b981b..9d6cd7d98649c0fe0f40c474d5ed88528ac9ba8c 100755 (executable)
@@ -2,9 +2,6 @@
 
 test_description='upload-pack ref-in-want'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 get_actual_refs () {
index bc393d7c31939f6e592815614521741c3e8dc087..ae1a00afb09e2f38b272602a9c39d51f0fa72110 100755 (executable)
@@ -4,6 +4,8 @@ test_description='Test responses to violations of the network protocol. In most
 of these cases it will generally be acceptable for one side to break off
 communications if the other side says something unexpected. We are mostly
 making sure that we do not segfault or otherwise behave badly.'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'extra delim packet in v2 ls-refs args' '
index eb8c79aafdd6aa4d203ff6d8ab85a191c37a5d9b..ed38c76c29059d3f5363116db1b8231c31b0dc5a 100755 (executable)
@@ -32,7 +32,6 @@ do
                test_when_finished "git -C local push --delete origin new-branch" &&
                cp -r "$LOCAL_PRISTINE" local &&
                git -C local pull --no-rebase origin &&
-               GIT_TRACE2_EVENT_NESTING=5 \
                GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
                git -c protocol.version=$PROTO -C local push \
                        --receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
@@ -65,7 +64,6 @@ do
                test_when_finished "git -C local push --delete origin new-branch" &&
                cp -r "$LOCAL_PRISTINE" local &&
                git -C local pull --no-rebase origin &&
-               GIT_TRACE2_EVENT_NESTING=5 \
                GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
                git -c protocol.version=$PROTO -C local push \
                        --receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
index 0b64822bf621dee5c9544f76013c0342412eaee6..86542c650e241976943ca58c5496cf03965c630e 100755 (executable)
@@ -2,13 +2,14 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-    for n in 1 2 3 4 5 ; do \
-        echo $n > a ; \
-        git add a ; \
-        git commit -m "$n" ; \
+    for n in 1 2 3 4 5 ; do
+       echo $n > a &&
+       git add a &&
+       git commit -m "$n" || return 1
     done
 '
 
index 63fa7c83130ba371913ac07db1f95d3e57991d35..5a67bbc760fdc8c2b53dc7721a144e033bf8e934 100755 (executable)
@@ -124,7 +124,7 @@ test_expect_success 'dodecapus' '
                git checkout -b root$i five &&
                test_commit $i &&
                roots="$roots root$i" ||
-               return
+               return 1
        done &&
        git checkout main &&
        test_tick &&
@@ -142,8 +142,8 @@ test_expect_success 'ancestors with the same commit time' '
 
        test_tick_keep=$test_tick &&
        for i in 1 2 3 4 5 6 7 8; do
-               test_tick=$test_tick_keep
-               test_commit t$i
+               test_tick=$test_tick_keep &&
+               test_commit t$i || return 1
        done &&
        git rev-list t1^! --not t$i >result &&
        test_must_be_empty result
index 20adbece6588a70ab72ac44825e8f359bdcff014..af57a04b7ffa01644768ee507714ad5f5d5b79c6 100755 (executable)
@@ -51,7 +51,7 @@ test_expect_success setup '
 '
 
 test_expect_success 'rev-list D..M' '
-       for c in E F G H I J K L M; do echo $c; done >expect &&
+       test_write_lines E F G H I J K L M >expect &&
        git rev-list --format=%s D..M |
        sed -e "/^commit /d" |
        sort >actual &&
@@ -59,7 +59,7 @@ test_expect_success 'rev-list D..M' '
 '
 
 test_expect_success 'rev-list --ancestry-path D..M' '
-       for c in E F H I J L M; do echo $c; done >expect &&
+       test_write_lines E F H I J L M >expect &&
        git rev-list --ancestry-path --format=%s D..M |
        sed -e "/^commit /d" |
        sort >actual &&
@@ -81,7 +81,7 @@ test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
 '
 
 test_expect_success 'rev-list F...I' '
-       for c in F G H I; do echo $c; done >expect &&
+       test_write_lines F G H I >expect &&
        git rev-list --format=%s F...I |
        sed -e "/^commit /d" |
        sort >actual &&
@@ -89,7 +89,7 @@ test_expect_success 'rev-list F...I' '
 '
 
 test_expect_success 'rev-list --ancestry-path F...I' '
-       for c in F H I; do echo $c; done >expect &&
+       test_write_lines F H I >expect &&
        git rev-list --ancestry-path --format=%s F...I |
        sed -e "/^commit /d" |
        sort >actual &&
@@ -111,7 +111,7 @@ test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
 '
 
 test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
-       for c in G L; do echo $c; done >expect &&
+       test_write_lines G L >expect &&
        git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
        sed -e "/^commit /d" |
        sort >actual &&
index ddf34f0115b08b401a8981099459d10e1b1c4a02..ed449abe5520e59ddb0ad27def5711007f620650 100755 (executable)
@@ -4,9 +4,7 @@ test_description='basic git merge-index / git-merge-one-file tests'
 . ./test-lib.sh
 
 test_expect_success 'setup diverging branches' '
-       for i in 1 2 3 4 5 6 7 8 9 10; do
-               echo $i
-       done >file &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 10 >file &&
        git add file &&
        git commit -m base &&
        git tag base &&
index 78b585178051615a3712b58d1f87192aa678fb7f..c571fa517978818703e29c72e36faf52643a8ee5 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'setup' '
                test_tick &&
                git commit --allow-empty -m "$i" &&
                commit=$(git rev-parse --verify HEAD) &&
-               printf "$commit " >>.git/info/grafts
+               printf "$commit " >>.git/info/grafts || return 1
        done
 '
 
index 52cde097dd5c1c473df987cf74f51da714bd17b4..6f0902b86383191511116ee4fc5406e9dac6fb2e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git rev-list should handle unexpected object types'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup well-formed objects' '
index 4ade105db385c3458d4549d596923bb4197c605d..8d9d6604f052f6645e8760d3890f1ed29384fd49 100755 (executable)
@@ -16,9 +16,9 @@ test_expect_success 'setup r1' '
        git init r1 &&
        for n in 1 2 3 4 5
        do
-               echo "This is file: $n" > r1/file.$n
-               git -C r1 add file.$n
-               git -C r1 commit -m "$n"
+               echo "This is file: $n" > r1/file.$n &&
+               git -C r1 add file.$n &&
+               git -C r1 commit -m "$n" || return 1
        done
 '
 
@@ -73,9 +73,9 @@ test_expect_success 'setup r2' '
        git init r2 &&
        for n in 1000 10000
        do
-               printf "%"$n"s" X > r2/large.$n
-               git -C r2 add large.$n
-               git -C r2 commit -m "$n"
+               printf "%"$n"s" X > r2/large.$n &&
+               git -C r2 add large.$n &&
+               git -C r2 commit -m "$n" || return 1
        done
 '
 
@@ -245,10 +245,10 @@ test_expect_success 'setup r3' '
        mkdir r3/dir1 &&
        for n in sparse1 sparse2
        do
-               echo "This is file: $n" > r3/$n
-               git -C r3 add $n
-               echo "This is file: dir1/$n" > r3/dir1/$n
-               git -C r3 add dir1/$n
+               echo "This is file: $n" > r3/$n &&
+               git -C r3 add $n &&
+               echo "This is file: dir1/$n" > r3/dir1/$n &&
+               git -C r3 add dir1/$n || return 1
        done &&
        git -C r3 commit -m "sparse" &&
        echo dir1/ >pattern1 &&
@@ -672,7 +672,7 @@ test_expect_success 'rev-list W/ --missing=print' '
 
        for id in `cat expected | sed "s|..|&/|"`
        do
-               rm r1/.git/objects/$id
+               rm r1/.git/objects/$id || return 1
        done &&
 
        git -C r1 rev-list --quiet --missing=print --objects HEAD >revs &&
index bae2419150b8b52244dda726a4d2eaf10c1405ce..d8af2bb9d2b876c3277d1211468ce4717b83697e 100755 (executable)
@@ -262,7 +262,7 @@ test_expect_success 'name-rev --all' '
        >expect.unsorted &&
        for rev in $(git rev-list --all)
        do
-               git name-rev $rev >>expect.unsorted
+               git name-rev $rev >>expect.unsorted || return 1
        done &&
        sort <expect.unsorted >expect &&
        git name-rev --all >actual.unsorted &&
@@ -275,7 +275,7 @@ test_expect_success 'name-rev --stdin' '
        for rev in $(git rev-list --all)
        do
                name=$(git name-rev --name-only $rev) &&
-               echo "$rev ($name)" >>expect.unsorted
+               echo "$rev ($name)" >>expect.unsorted || return 1
        done &&
        sort <expect.unsorted >expect &&
        git rev-list --all | git name-rev --stdin >actual.unsorted &&
@@ -390,9 +390,12 @@ test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
 committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
 data <<EOF
 commit #$i
-EOF"
-               test $i = 1 && echo "from refs/heads/main^0"
-               i=$(($i + 1))
+EOF" &&
+               if test $i = 1
+               then
+                       echo "from refs/heads/main^0"
+               fi &&
+               i=$(($i + 1)) || return 1
        done | git fast-import &&
        git checkout main &&
        git tag far-far-away HEAD^ &&
index 30328b87f07657c899801304bf6e0331f569f1bb..8ff1d76f794783346c1b3597c55924e8acb4811d 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'setup' '
                fi &&
                : >$p &&
                git add $p &&
-               git commit -m $p
+               git commit -m $p || return 1
        done &&
        git log --oneline --format=%s >actual &&
        cat <<EOF >expect &&
index b117251366dd6fa1f3e6cd59c03bcefd88811405..ae8b5379e24d52c559e0d02da6f95fdbc40874c8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diagnosing out-of-scope pathspec'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a bare and non-bare repository' '
index 06c5fb56157f28359b47c04818e2a51f9098ee36..5a221f8ef1fd810731a4dd8b0d5cdab988084ed8 100755 (executable)
@@ -91,6 +91,26 @@ test_expect_success GPGSSH 'created ssh signed commit and tag' '
        git tag -s -u"${GPGSSH_KEY_UNTRUSTED}" -m signed-ssh-tag-msg-untrusted signed-untrusted-ssh-tag left
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed tags with keys having defined lifetimes' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+       git checkout -b signed-expiry-ssh &&
+       touch file &&
+       git add file &&
+
+       echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+       git tag -s -u "${GPGSSH_KEY_EXPIRED}" -m expired-signed expired-signed &&
+
+       echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_NOTYETVALID}" -m notyetvalid-signed notyetvalid-signed &&
+
+       echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_TIMEBOXEDVALID}" -m timeboxedvalid-signed timeboxedvalid-signed &&
+
+       echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_TIMEBOXEDINVALID}" -m timeboxedinvalid-signed timeboxedinvalid-signed
+'
+
 test_expect_success 'message for merging local branch' '
        echo "Merge branch ${apos}left${apos}" >expected &&
 
@@ -104,8 +124,9 @@ test_expect_success 'message for merging local branch' '
 test_expect_success GPG 'message for merging local tag signed by good key' '
        git checkout main &&
        git fetch . signed-good-tag &&
-       git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
+       grep "^signed-tag-msg" actual &&
        grep "^# gpg: Signature made" actual &&
        grep "^# gpg: Good signature from" actual
 '
@@ -113,8 +134,9 @@ test_expect_success GPG 'message for merging local tag signed by good key' '
 test_expect_success GPG 'message for merging local tag signed by unknown key' '
        git checkout main &&
        git fetch . signed-good-tag &&
-       GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+       GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
+       grep "^signed-tag-msg" actual &&
        grep "^# gpg: Signature made" actual &&
        grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual
 '
@@ -123,7 +145,9 @@ 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 &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}signed-good-ssh-tag${apos}" actual &&
+       grep "^signed-ssh-tag-msg" actual &&
        grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
        ! grep "${GPGSSH_BAD_SIGNATURE}" actual
 '
@@ -132,11 +156,55 @@ test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh
        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 &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}signed-untrusted-ssh-tag${apos}" actual &&
+       grep "^signed-ssh-tag-msg-untrusted" actual &&
        grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
        ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
        grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
 '
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by expired ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . expired-signed &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}expired-signed${apos}" actual &&
+       grep "^expired-signed" actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by not yet valid ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . notyetvalid-signed &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}notyetvalid-signed${apos}" actual &&
+       grep "^notyetvalid-signed" actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by valid timeboxed ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . timeboxedvalid-signed &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}timeboxedvalid-signed${apos}" actual &&
+       grep "^timeboxedvalid-signed" actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'message for merging local tag signed by invalid timeboxed ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . timeboxedinvalid-signed &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       grep "^Merge tag ${apos}timeboxedinvalid-signed${apos}" actual &&
+       grep "^timeboxedinvalid-signed" actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
 test_expect_success 'message for merging external branch' '
        echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
 
@@ -519,7 +587,7 @@ test_expect_success 'merge-msg lots of commits' '
                while test $i -gt 9
                do
                        echo "  $i" &&
-                       i=$(($i-1))
+                       i=$(($i-1)) || return 1
                done &&
                echo "  ..."
        } >expected &&
@@ -573,7 +641,35 @@ test_expect_success 'merge-msg with "merging" an annotated tag' '
        test_cmp expected .git/MERGE_MSG
 '
 
+test_expect_success 'merge --into-name=<name>' '
+       test_when_finished "git checkout main" &&
+       git checkout -B side main &&
+       git commit --allow-empty -m "One step ahead" &&
+
+       git checkout --detach main &&
+       git merge --no-ff side &&
+       git show -s --format="%s" >full.0 &&
+       head -n1 full.0 >actual &&
+       # expect that HEAD is shown as-is
+       grep -e "Merge branch .side. into HEAD$" actual &&
+
+       git reset --hard main &&
+       git merge --no-ff --into-name=main side &&
+       git show -s --format="%s" >full.1 &&
+       head -n1 full.1 >actual &&
+       # expect that we pretend to be merging to main, that is suppressed
+       grep -e "Merge branch .side.$" actual &&
+
+       git checkout -b throwaway main &&
+       git merge --no-ff --into-name=main side &&
+       git show -s --format="%s" >full.2 &&
+       head -n1 full.2 >actual &&
+       # expect that we pretend to be merging to main, that is suppressed
+       grep -e "Merge branch .side.$" actual
+'
+
 test_expect_success 'merge.suppressDest configuration' '
+       test_when_finished "git checkout main" &&
        git checkout -B side main &&
        git commit --allow-empty -m "One step ahead" &&
        git checkout main &&
@@ -590,7 +686,19 @@ test_expect_success 'merge.suppressDest configuration' '
        git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
        head -n1 full.3 >actual &&
        grep -e "Merge branch .side." actual &&
-       ! grep -e " into main$" actual
+       ! grep -e " into main$" actual &&
+
+       git checkout --detach HEAD &&
+       git -c merge.suppressDest="main" fmt-merge-msg <.git/FETCH_HEAD >full.4 &&
+       head -n1 full.4 >actual &&
+       grep -e "Merge branch .side. into HEAD$" actual &&
+
+       git -c merge.suppressDest="main" fmt-merge-msg \
+               --into-name=main <.git/FETCH_HEAD >full.5 &&
+       head -n1 full.5 >actual &&
+       grep -e "Merge branch .side." actual &&
+       ! grep -e " into main$" actual &&
+       ! grep -e " into HEAD$" actual
 '
 
 test_done
index 9f2c706c12a930ec716db6100063b2201c542353..dcaab7265f5c75a8be444db3e2d1766732192f8d 100755 (executable)
@@ -5,9 +5,6 @@
 
 test_description='for-each-ref test'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
@@ -955,10 +952,7 @@ test_expect_success '%(raw) with --shell and --sort=raw must fail' '
 '
 
 test_expect_success '%(raw:size) with --shell' '
-       git for-each-ref --format="%(raw:size)" | while read line
-       do
-               echo "'\''$line'\''" >>expect
-       done &&
+       git for-each-ref --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect &&
        git for-each-ref --format="%(raw:size)" --shell >actual &&
        test_cmp expect actual
 '
@@ -1341,7 +1335,7 @@ test_expect_success ':remotename and :remoteref' '
                        echo "${pair#*=}" >expect &&
                        git for-each-ref --format="${pair%=*}" \
                                refs/heads/main >actual &&
-                       test_cmp expect actual
+                       test_cmp expect actual || exit 1
                done &&
                git branch push-simple &&
                git config branch.push-simple.pushRemote from &&
index 1537aa21798b669e30c0dbdc0d72aa3465c141b6..1ce5f490e99d374e039d29e708ee98dfb8988eab 100755 (executable)
@@ -2,9 +2,6 @@
 
 test_description='test for-each-refs usage of ref-filter APIs'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
index 849464583713c40859d2473d2a4075367b1e4c09..57e6af5eaa09e52e9f867c957e2117cb66c46e4b 100755 (executable)
@@ -62,10 +62,10 @@ test_expect_success setup '
 
 test_expect_success merge '
 
-       {
-               echo "binary -merge"
-               echo "union merge=union"
-       } >.gitattributes &&
+       cat >.gitattributes <<-\EOF &&
+       binary -merge
+       union merge=union
+       EOF
 
        if git merge main
        then
index e34676c204b41a76b3c90519386e275db42c3b55..8e6241f92e6a6c10b8686900bf1ed61c2c96d4d3 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success resolve '
                echo Oops, should not have succeeded
                false
        else
-               git ls-files -s >current
+               git ls-files -s >current &&
                test_cmp expect current
        fi
 '
@@ -63,7 +63,7 @@ test_expect_success recursive '
                echo Oops, should not have succeeded
                false
        else
-               git ls-files -s >current
+               git ls-files -s >current &&
                test_cmp expect current
        fi
 '
index ba7890ec521f510cf9d389331bf12d7cf11be4fb..e9ba6f1690d015d06e13c69db2c1190ef8436443 100755 (executable)
@@ -10,7 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 test_expect_success setup '
 
        s="1 2 3 4 5 6 7 8" &&
-       for i in $s; do echo $i; done >hello &&
+       test_write_lines $s >hello &&
        git add hello &&
        git commit -m initial &&
        git checkout -b side &&
@@ -18,7 +18,7 @@ test_expect_success setup '
        git add hello &&
        git commit -m second &&
        git checkout main &&
-       for i in mundo $s; do echo $i; done >hello &&
+       test_write_lines mundo $s >hello &&
        git add hello &&
        git commit -m main
 
@@ -27,7 +27,7 @@ test_expect_success setup '
 test_expect_success 'subtree available and works like recursive' '
 
        git merge -s subtree side &&
-       for i in mundo $s world; do echo $i; done >expect &&
+       test_write_lines mundo $s world >expect &&
        test_cmp expect hello
 
 '
index f54c915d6a540719329277e7c9e77a8ed4536a93..6ae2489286c278f978c3a87f1015f16fe2bb005f 100755 (executable)
@@ -51,10 +51,10 @@ test_expect_success 'set up mode change in both branches' '
        : >file2 &&
        git add file2 &&
        git commit -m b2 &&
-       {
-               echo "100755 $H 2       file2"
-               echo "100644 $H 3       file2"
-       } >expect
+       cat >expect <<-EOF
+       100755 $H 2     file2
+       100644 $H 3     file2
+       EOF
 '
 
 do_both_modes () {
index c50d31572221eb934134f3ba5baa82577e03161d..ca018d11f547978bf66a41804c16bf24a0b614f8 100755 (executable)
@@ -37,18 +37,18 @@ test_rename() {
        test_might_fail git branch -D test$n &&
        git reset --hard initial &&
        for i in $(count $n); do
-               make_text $i initial initial >$i
+               make_text $i initial initial >$i || return 1
        done &&
        git add . &&
        git commit -m add=$n &&
        for i in $(count $n); do
-               make_text $i changed initial >$i
+               make_text $i changed initial >$i || return 1
        done &&
        git commit -a -m change=$n &&
        git checkout -b test$n HEAD^ &&
        for i in $(count $n); do
-               git rm $i
-               make_text $i initial changed >$i.moved
+               git rm $i &&
+               make_text $i initial changed >$i.moved || return 1
        done &&
        git add . &&
        git commit -m change+rename=$n &&
@@ -79,7 +79,7 @@ test_expect_success 'setup large simple rename' '
 
        git reset --hard initial &&
        for i in $(count 200); do
-               make_text foo bar baz >$i
+               make_text foo bar baz >$i || return 1
        done &&
        git add . &&
        git commit -m create-files &&
index 84f50823666671e54328670dbc7e370600fd1a51..690c8482b13a3763b223be19799ce22197cd383c 100755 (executable)
@@ -24,14 +24,8 @@ test_expect_success 'setup basic criss-cross + rename with no modifications' '
                cd basic-rename &&
 
                ten="0 1 2 3 4 5 6 7 8 9" &&
-               for i in $ten
-               do
-                       echo line $i in a sample file
-               done >one &&
-               for i in $ten
-               do
-                       echo line $i in another sample file
-               done >two &&
+               printf "line %d in a sample file\n" $ten >one &&
+               printf "line %d in another sample file\n" $ten >two &&
                git add one two &&
                test_tick && git commit -m initial &&
 
@@ -96,14 +90,8 @@ test_expect_success 'setup criss-cross + rename merges with basic modification'
                cd rename-modify &&
 
                ten="0 1 2 3 4 5 6 7 8 9" &&
-               for i in $ten
-               do
-                       echo line $i in a sample file
-               done >one &&
-               for i in $ten
-               do
-                       echo line $i in another sample file
-               done >two &&
+               printf "line %d in a sample file\n" $ten >one &&
+               printf "line %d in another sample file\n" $ten >two &&
                git add one two &&
                test_tick && git commit -m initial &&
 
@@ -1588,10 +1576,7 @@ test_expect_success 'setup nested conflicts' '
                cd nested_conflicts &&
 
                # Create some related files now
-               for i in $(test_seq 1 10)
-               do
-                       echo Random base content line $i
-               done >initial &&
+               printf "Random base content line %d\n" $(test_seq 1 10) >initial &&
 
                cp initial b_L1 &&
                cp initial b_R1 &&
@@ -1777,10 +1762,7 @@ test_expect_success 'setup virtual merge base with nested conflicts' '
                cd virtual_merge_base_has_nested_conflicts &&
 
                # Create some related files now
-               for i in $(test_seq 1 10)
-               do
-                       echo Random base content line $i
-               done >content &&
+               printf "Random base content line %d\n" $(test_seq 1 10) >content &&
 
                # Setup original commit
                git add content &&
index ec065d6a6581dc368fa9f03edfa9cfdcb0483eb2..62d1406119e8c2b08a1a93e3d8fb95167ad16e7d 100755 (executable)
@@ -7,10 +7,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 
 test_expect_success setup '
-       for i in 1 2 3 4 5 6 7 8 9
-       do
-               echo "$i"
-       done >file &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 >file &&
        git add file &&
        cp file elif &&
        git commit -m initial &&
index 1e0296dd17263e3d436dd4bc6d759d095ecccd50..41288a60ceb549295699f2efd9cb8e187d3e297b 100755 (executable)
@@ -204,4 +204,30 @@ test_expect_success 'Test delete/normalize conflict' '
        test_path_is_missing file
 '
 
+test_expect_success 'rename/delete vs. renormalization' '
+       git init subrepo &&
+       (
+               cd subrepo &&
+               echo foo >oldfile &&
+               git add oldfile &&
+               git commit -m original &&
+
+               git branch rename &&
+               git branch nuke &&
+
+               git checkout rename &&
+               git mv oldfile newfile &&
+               git commit -m renamed &&
+
+               git checkout nuke &&
+               git rm oldfile &&
+               git commit -m deleted &&
+
+               git checkout rename^0 &&
+               test_must_fail git -c merge.renormalize=true merge nuke >out &&
+
+               grep "rename/delete" out
+       )
+'
+
 test_done
index 25c4b720e72712d07b344aebc8d797136cda83be..a9ee4cb207a140eb3c93614f25b9625f43553d59 100755 (executable)
@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '
        )
 '
 
+test_setup_zdiff3 () {
+       test_create_repo zdiff3 &&
+       (
+               cd zdiff3 &&
+
+               test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&
+               test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&
+               test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&
+               test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&
+
+               git add basic middle-common interesting evil &&
+               git commit -m base &&
+
+               git branch left &&
+               git branch right &&
+
+               git checkout left &&
+               test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&
+               test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&
+               test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&
+               test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&
+               git add -u &&
+               git commit -m letters &&
+
+               git checkout right &&
+               test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&
+               test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&
+               test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&
+               test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&
+               git add -u &&
+               git commit -m permuted
+       )
+}
+
+test_expect_success 'check zdiff3 markers' '
+       test_setup_zdiff3 &&
+       (
+               cd zdiff3 &&
+
+               git checkout left^0 &&
+
+               base=$(git rev-parse --short HEAD^1) &&
+               test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&
+
+               test_write_lines 1 2 3 4 A \
+                                "<<<<<<< HEAD" B C D \
+                                "||||||| $base" 5 6 \
+                                ======= X C Y \
+                                ">>>>>>> right^0" \
+                                E 7 8 9 \
+                                >expect &&
+               test_cmp expect basic &&
+
+               test_write_lines 1 2 3 \
+                                "<<<<<<< HEAD" CC \
+                                "||||||| $base" AA \
+                                ======= EE \
+                                ">>>>>>> right^0" \
+                                4 5 \
+                                "<<<<<<< HEAD" DD \
+                                "||||||| $base" BB \
+                                ======= FF \
+                                ">>>>>>> right^0" \
+                                6 7 8 \
+                                >expect &&
+               test_cmp expect middle-common &&
+
+               test_write_lines 1 2 3 4 A B C \
+                                "<<<<<<< HEAD" D E F \
+                                "||||||| $base" 5 6 \
+                                ======= 5 6 \
+                                ">>>>>>> right^0" \
+                                G H I J 7 8 9 \
+                                >expect &&
+               test_cmp expect interesting &&
+
+               # Not passing this one yet; the common "B C" lines is still
+               # being left in the conflict blocks on the left and right
+               # sides.
+               test_write_lines 1 2 3 4 \
+                                "<<<<<<< HEAD" X A \
+                                "||||||| $base" 5 6 \
+                                ======= Y A B C \
+                                ">>>>>>> right^0" \
+                                B C 7 8 9 \
+                                >expect &&
+               test_cmp expect evil
+       )
+'
+
 test_done
index a0efe7cb6dbe77215f0b2bd220f6974ad0c17ec0..07067bb347955b146b9654b353e9db2b8b2cd323 100755 (executable)
@@ -706,7 +706,7 @@ test_expect_success 'merge-recursive remembers the names of all base trees' '
        # more trees than static slots used by oid_to_hex()
        for commit in $c0 $c2 $c4 $c5 $c6 $c7
        do
-               git rev-parse "$commit^{tree}"
+               git rev-parse "$commit^{tree}" || return 1
        done >trees &&
 
        # ignore the return code; it only fails because the input is weird...
index 3d7a62ddab61729d37c183fe327a9d8997f96c1c..338a9c46a24b02caf471da4afb1cff14a75b6f2f 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'setup' '
        do
                test_commit "1-$i" &&
                git branch -f commit-1-$i &&
-               git tag -a -m "1-$i" tag-1-$i commit-1-$i
+               git tag -a -m "1-$i" tag-1-$i commit-1-$i || return 1
        done &&
        for j in $(test_seq 1 9)
        do
@@ -46,7 +46,7 @@ test_expect_success 'setup' '
                do
                        git merge commit-$j-$i -m "$x-$i" &&
                        git branch -f commit-$x-$i &&
-                       git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i
+                       git tag -a -m "$x-$i" tag-$x-$i commit-$x-$i || return 1
                done
        done &&
        git commit-graph write --reachable &&
index 082be85dffc7b1765471e11d2c580c853cf0965b..9aa1660651b8a96397ce2a83cb3d107c8b7d43dc 100755 (executable)
@@ -94,10 +94,10 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' '
        git log -1 \
                --format="format:tag: tagging %h (%s, %cd)%n" \
                --date=format:%Y-%m-%d >expected &&
-       test_when_finished "git tag -d tag_with_reflog" &&
-       git tag --create-reflog tag_with_reflog &&
-       git reflog exists refs/tags/tag_with_reflog &&
-       sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
+       test_when_finished "git tag -d tag_with_reflog1" &&
+       git tag --create-reflog tag_with_reflog1 &&
+       git reflog exists refs/tags/tag_with_reflog1 &&
+       test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog1 | sed -e "s/^.* //" >actual &&
        test_cmp expected actual
 '
 
@@ -105,10 +105,10 @@ test_expect_success 'annotated tag with --create-reflog has correct message' '
        git log -1 \
                --format="format:tag: tagging %h (%s, %cd)%n" \
                --date=format:%Y-%m-%d >expected &&
-       test_when_finished "git tag -d tag_with_reflog" &&
-       git tag -m "annotated tag" --create-reflog tag_with_reflog &&
-       git reflog exists refs/tags/tag_with_reflog &&
-       sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
+       test_when_finished "git tag -d tag_with_reflog2" &&
+       git tag -m "annotated tag" --create-reflog tag_with_reflog2 &&
+       git reflog exists refs/tags/tag_with_reflog2 &&
+       test-tool ref-store main for-each-reflog-ent refs/tags/tag_with_reflog2 | sed -e "s/^.* //" >actual &&
        test_cmp expected actual
 '
 
@@ -118,10 +118,10 @@ test_expect_success '--create-reflog does not create reflog on failure' '
 '
 
 test_expect_success 'option core.logAllRefUpdates=always creates reflog' '
-       test_when_finished "git tag -d tag_with_reflog" &&
+       test_when_finished "git tag -d tag_with_reflog3" &&
        test_config core.logAllRefUpdates always &&
-       git tag tag_with_reflog &&
-       git reflog exists refs/tags/tag_with_reflog
+       git tag tag_with_reflog3 &&
+       git reflog exists refs/tags/tag_with_reflog3
 '
 
 test_expect_success 'listing all tags if one exists should succeed' '
@@ -1976,9 +1976,12 @@ test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a de
 committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
 data <<EOF
 commit #$i
-EOF"
-               test $i = 1 && echo "from refs/heads/main^0"
-               i=$(($i + 1))
+EOF" &&
+               if test $i = 1
+               then
+                       echo "from refs/heads/main^0"
+               fi &&
+               i=$(($i + 1)) || return 1
        done | git fast-import &&
        git checkout main &&
        git tag far-far-away HEAD^ &&
index 0335a9a158ab507b2e37e4d7642a69ba4344c0ad..520f96d09fb71778704119929012a40aa7c0072a 100755 (executable)
@@ -137,7 +137,7 @@ test_expect_success 'setup deeper work tree' '
 
 test_expect_success 'add a directory outside the work tree' '(
        cd tester &&
-       d1="$(cd .. ; pwd)" &&
+       d1="$(cd .. && pwd)" &&
        test_must_fail git add "$d1"
 )'
 
index 06c9dd6c9339f24e81fc81757daf00d5b1a63fa3..1cb36b9ab83cdba01a554a94c982c968cd572f8b 100755 (executable)
@@ -48,6 +48,23 @@ test_expect_success GPGSSH 'create signed tags ssh' '
        git tag -u"${GPGSSH_KEY_UNTRUSTED}" -m eighth eighth-signed-alt
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed tags with keys having defined lifetimes' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+
+       echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+       git tag -s -u "${GPGSSH_KEY_EXPIRED}" -m expired-signed expired-signed &&
+
+       echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_NOTYETVALID}" -m notyetvalid-signed notyetvalid-signed &&
+
+       echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_TIMEBOXEDVALID}" -m timeboxedvalid-signed timeboxedvalid-signed &&
+
+       echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+       git tag -s -u "${GPGSSH_KEY_TIMEBOXEDINVALID}" -m timeboxedinvalid-signed timeboxedinvalid-signed
+'
+
 test_expect_success GPGSSH 'verify and show ssh signatures' '
        test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
        (
@@ -80,6 +97,31 @@ test_expect_success GPGSSH 'verify and show ssh signatures' '
        )
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on expired signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-tag expired-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag exits failure on not yet valid signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-tag notyetvalid-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag succeeds with tag date and key validity matching' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git verify-tag timeboxedvalid-signed 2>actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-tag failes with tag date outside of key validity' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-tag timeboxedinvalid-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
 test_expect_success GPGSSH 'detect fudged ssh signature' '
        test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
        git cat-file tag seventh-signed >raw &&
index 47fc21d96232ae4ae46e2e7a9db63723bdc66ff0..20a0d2afc2a54d9cd446d2a70168641b7167a15a 100755 (executable)
@@ -4,10 +4,6 @@ test_description='git status --porcelain=v2
 
 This test exercises porcelain V2 output for git status.'
 
-
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./test-lib.sh
 
 
index 5530651eea492cacf106a89a88c261bc101707d7..638bb04e217600387fdf3f46fb8ba1caacba0f94 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='git reset should cull empty subdirs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff-data.sh
 
index afe36a533c4bc603d5acced8de1d607a4a7d5fc8..0de83e36199ec44eec0ea1ca568c730da759d5d3 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git reset in a bare repository'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup non-bare' '
index 15ccb14f7e26faa41ba07d7901e498a0f75f726d..523efbecde1dff517ffebf3c04cfcf74ea280f15 100755 (executable)
@@ -160,13 +160,13 @@ test_expect_success 'error conditions' '
        git rm fileA.t &&
 
        test_must_fail git reset --pathspec-from-file=list --patch 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--patch. cannot be used together" err &&
 
        test_must_fail git reset --pathspec-from-file=list -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git reset --pathspec-file-nul 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git reset --soft --pathspec-from-file=list 2>err &&
        test_i18ngrep -e "fatal: Cannot do soft reset with paths" err &&
index a82a07a04a8500cdac2cfb7ef39fb3f5981f437f..3d62e10b53fe16fd2eae0d0fa0363d8839f97b95 100755 (executable)
@@ -8,7 +8,7 @@ test_description='Tests for "git reset" with "--merge" and "--keep" options'
 . ./test-lib.sh
 
 test_expect_success setup '
-    for i in 1 2 3; do echo line $i; done >file1 &&
+    printf "line %d\n" 1 2 3 >file1 &&
     cat file1 >file2 &&
     git add file1 file2 &&
     test_tick &&
index b7ba1c3268e32935ca62f0e3f562a80c99f84481..61ad47b0c18d231c60327acda04348895947809c 100755 (executable)
@@ -658,4 +658,21 @@ test_expect_success 'custom merge driver with checkout -m' '
        test_cmp expect arm
 '
 
+test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
+       git reset --hard main &&
+       # default config does not copy tracking info
+       git checkout -b foo-no-inherit koala/bear &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.remote &&
+       test_cmp_config "" --default "" branch.foo-no-inherit.merge &&
+       # with autoSetupMerge=inherit, we copy tracking info from koala/bear
+       test_config branch.autoSetupMerge inherit &&
+       git checkout -b foo koala/bear &&
+       test_cmp_config origin branch.foo.remote &&
+       test_cmp_config refs/heads/koala/bear branch.foo.merge &&
+       # no tracking info to inherit from main
+       git checkout -b main2 main &&
+       test_cmp_config "" --default "" branch.main2.remote &&
+       test_cmp_config "" --default "" branch.main2.merge
+'
+
 test_done
index 8dd0f988129d5f35c3b0aa93ba2f441c20d5afe9..91964653a0b6937cf41c3ac2b687523f482abdb2 100755 (executable)
@@ -359,14 +359,14 @@ test_expect_success '--fixup=reword: ignores staged changes' '
 
 test_expect_success '--fixup=reword: error out with -m option' '
        commit_for_rebase_autosquash_setup &&
-       echo "fatal: cannot combine -m with --fixup:reword" >expect &&
+       echo "fatal: options '\''-m'\'' and '\''--fixup:reword'\'' cannot be used together" >expect &&
        test_must_fail git commit --fixup=reword:HEAD~ -m "reword commit message" 2>actual &&
        test_cmp expect actual
 '
 
 test_expect_success '--fixup=amend: error out with -m option' '
        commit_for_rebase_autosquash_setup &&
-       echo "fatal: cannot combine -m with --fixup:amend" >expect &&
+       echo "fatal: options '\''-m'\'' and '\''--fixup:amend'\'' cannot be used together" >expect &&
        test_must_fail git commit --fixup=amend:HEAD~ -m "amend commit message" 2>actual &&
        test_cmp expect actual
 '
@@ -421,8 +421,9 @@ test_expect_success 'amend! commit allows empty commit msg body with --allow-emp
 
 test_fixup_reword_opt () {
        test_expect_success "--fixup=reword: incompatible with $1" "
-               echo 'fatal: reword option of --fixup is mutually exclusive with'\
-                       '--patch/--interactive/--all/--include/--only' >expect &&
+               echo 'fatal: reword option of '\''--fixup'\'' and' \
+                       ''\''--patch/--interactive/--all/--include/--only'\' \
+                       'cannot be used together' >expect &&
                test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
                test_cmp expect actual
        "
@@ -435,7 +436,7 @@ done
 
 test_expect_success '--fixup=reword: give error with pathsec' '
        commit_for_rebase_autosquash_setup &&
-       echo "fatal: cannot combine reword option of --fixup with path '\''foo'\''" >expect &&
+       echo "fatal: reword option of '\''--fixup'\'' and path '\''foo'\'' cannot be used together" >expect &&
        test_must_fail git commit --fixup=reword:HEAD~ -- foo 2>actual &&
        test_cmp expect actual
 '
index 512ae2781fe2c9b02a5a37f5230ec14992114ae7..fb5417d5e7e044f5bfbbe1b4ef8f6b42857c4e73 100755 (executable)
@@ -667,10 +667,7 @@ test_expect_success 'amend can copy notes' '
 
 test_expect_success 'commit a file whose name is a dash' '
        git reset --hard &&
-       for i in 1 2 3 4 5
-       do
-               echo $i
-       done >./- &&
+       test_write_lines 1 2 3 4 5 >./- &&
        git add ./- &&
        test_tick &&
        git commit -m "add dash" >output </dev/null &&
index 2a07c70867966ccfa901d91667358a7d86f34e5e..e39c809ca42f04dbda578689e627b4aceb67d044 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success 'set up commits for rebasing' '
        test_commit rebase-b b bb &&
        for i in $(test_seq 1 13)
        do
-               test_commit rebase-$i c $i
+               test_commit rebase-$i c $i || return 1
        done &&
        git checkout main &&
 
index d65a0171f29c85b437721d823737189a9d72df81..8593b7e3cb8d9aa0c4badeef4273ed406cecc9ca 100755 (executable)
@@ -71,25 +71,7 @@ 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) &&
-
-       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
+       git tag twelfth-signed-alt $(cat oid)
 '
 
 test_expect_success GPG 'verify and show signatures' '
@@ -129,7 +111,7 @@ 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 &&
+       test_must_fail env GNUPGHOME="$GNUPGHOME_NOT_USED" git verify-commit initial 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
@@ -228,7 +210,7 @@ test_expect_success GPG 'detect fudged signature with NUL' '
 '
 
 test_expect_success GPG 'amending already signed commit' '
-       git checkout fourth-signed^0 &&
+       git checkout -f fourth-signed^0 &&
        git commit --amend -S --no-edit &&
        git verify-commit HEAD &&
        git show -s --show-signature HEAD >actual &&
index b5fdc048a54a151f78b10e9d379383a421cf3726..4ffa45a7bf3599b78624fbdaa39b828f4d7a0643 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git status with certain file name lengths'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 files="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z"
index 7f2956d77ad033a57981a9569db3ef803ef93961..2f16d5787edfb16956312a3be70fb9da1ffadc46 100755 (executable)
@@ -659,6 +659,7 @@ On branch am_empty
 You are in the middle of an am session.
 The current patch is empty.
   (use "git am --skip" to skip this patch)
+  (use "git am --allow-empty" to record this patch as an empty commit)
   (use "git am --abort" to restore the original branch)
 
 nothing to commit (use -u to show untracked files)
index 04885d0a5e5c2697409395698af15b48002cabe2..97f10905d23fd3077aa9dd253fa079eb9c5be73d 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success 'with config option on the command line' '
                Acked-by: Johan
                Reviewed-by: Peff
        EOF
-       { echo; echo "Acked-by: Johan"; } |
+       { echo && echo "Acked-by: Johan"; } |
        git -c "trailer.Acked-by.ifexists=addifdifferent" interpret-trailers \
                --trailer "Reviewed-by: Peff" --trailer "Acked-by: Johan" >actual &&
        test_cmp expected actual
index 9f989be01b9f107e145519fd7a7fd8ee31800a47..e3d6bb67bf95a666f2268e412db4301513746b2f 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git status and symlinks'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index f488d930dfd73f37b1c01b52d4ce5732b38e1d8a..a6308acf006c9e4d35e47578465e348bb213356f 100755 (executable)
@@ -248,7 +248,7 @@ do
                git config core.preloadIndex $preload_val &&
                if test $preload_val = true
                then
-                       GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
+                       GIT_TEST_PRELOAD_INDEX=$preload_val && export GIT_TEST_PRELOAD_INDEX
                else
                        sane_unset GIT_TEST_PRELOAD_INDEX
                fi
@@ -390,7 +390,7 @@ test_expect_success 'status succeeds after staging/unstaging' '
 # during a call to 'git status'. Otherwise, we verify that we _do_ call it.
 check_sparse_index_behavior () {
        git -C full status --porcelain=v2 >expect &&
-       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" GIT_TRACE2_EVENT_NESTING=10 \
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
                git -C sparse status --porcelain=v2 >actual &&
        test_region $1 index ensure_full_index trace2.txt &&
        test_region fsm_hook query trace2.txt &&
index a62736dce09f6b8623929b20cb3a70f56dfeabc0..22bf5c7e5dc108fabfc165e948733d6221855785 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git status rename detection options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 5fbe47ebcd02714b8bf2e0cdf5f0d32064b55fd8..ad011bb9f158034171c04e76722b183eece45576 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='commit --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
@@ -140,19 +141,19 @@ test_expect_success 'error conditions' '
        >empty_list &&
 
        test_must_fail git commit --pathspec-from-file=list --interactive -m "Commit" 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list --patch -m "Commit" 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .--interactive/--patch. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list --all -m "Commit" 2>err &&
-       test_i18ngrep -e "--pathspec-from-file with -a does not make sense" err &&
+       test_i18ngrep -e "options .--pathspec-from-file. and .-a. cannot be used together" err &&
 
        test_must_fail git commit --pathspec-from-file=list -m "Commit" -- fileA.t 2>err &&
-       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+       test_i18ngrep -e ".--pathspec-from-file. and pathspec arguments cannot be used together" err &&
 
        test_must_fail git commit --pathspec-file-nul -m "Commit" 2>err &&
-       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+       test_i18ngrep -e "the option .--pathspec-file-nul. requires .--pathspec-from-file." err &&
 
        test_must_fail git commit --pathspec-from-file=empty_list --include -m "Commit" 2>err &&
        test_i18ngrep -e "No paths with --include/--only does not make sense." err &&
index badf3ed320406ea5550e7b24b82216d147a48e80..f47e99517983163e0bbb3fce9c35800270fe4e28 100755 (executable)
@@ -73,7 +73,46 @@ test_expect_success GPGSSH 'create signed commits' '
        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)
+       git tag twelfth-signed-alt $(cat oid) &&
+
+       echo 13>file && test_tick && git commit -a -m thirteenth -S"${GPGSSH_KEY_ECDSA}" &&
+       git tag thirteenth-signed-ecdsa
+'
+
+test_expect_success GPGSSH 'sign commits using literal public keys with ssh-agent' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+       eval $(ssh-agent) &&
+       test_when_finished "kill ${SSH_AGENT_PID}" &&
+       ssh-add "${GPGSSH_KEY_PRIMARY}" &&
+       echo 1 >file && git add file &&
+       git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
+       echo 2 >file &&
+       test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" &&
+       git commit -a -m rsa-config -S &&
+       ssh-add "${GPGSSH_KEY_ECDSA}" &&
+       echo 3 >file &&
+       git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
+       echo 4 >file &&
+       test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" &&
+       git commit -a -m ecdsa-config -S
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+
+       echo expired >file && test_tick && git commit -a -m expired -S"${GPGSSH_KEY_EXPIRED}" &&
+       git tag expired-signed &&
+
+       echo notyetvalid >file && test_tick && git commit -a -m notyetvalid -S"${GPGSSH_KEY_NOTYETVALID}" &&
+       git tag notyetvalid-signed &&
+
+       echo timeboxedvalid >file && test_tick && git commit -a -m timeboxedvalid -S"${GPGSSH_KEY_TIMEBOXEDVALID}" &&
+       git tag timeboxedvalid-signed &&
+
+       echo timeboxedinvalid >file && test_tick && git commit -a -m timeboxedinvalid -S"${GPGSSH_KEY_TIMEBOXEDINVALID}" &&
+       git tag timeboxedinvalid-signed
 '
 
 test_expect_success GPGSSH 'verify and show signatures' '
@@ -122,6 +161,31 @@ test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature'
        grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
 '
 
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on expired signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-commit expired-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure on not yet valid signature key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-commit notyetvalid-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit succeeds with commit date and key validity matching' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git verify-commit timeboxedvalid-signed 2>actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'verify-commit exits failure with commit date outside of key validity' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-commit timeboxedinvalid-signed 2>actual &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_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 &&
@@ -217,7 +281,7 @@ 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 checkout -f fourth-signed^0 &&
        git commit --amend -S --no-edit &&
        git verify-commit HEAD &&
        git show -s --show-signature HEAD >actual &&
index c773e30b3fa17bc129ca78afe3aef9194d77cb1d..f0f6fda150bc29695dc8b56cb3c204de251e6e1d 100755 (executable)
@@ -967,7 +967,7 @@ test_expect_success 'set up mod-256 conflict scenario' '
        # 256 near-identical stanzas...
        for i in $(test_seq 1 256); do
                for j in 1 2 3 4 5; do
-                       echo $i-$j
+                       echo $i-$j || return 1
                done
        done >file &&
        git add file &&
index a9c816b47f269ad0bf073f97f50e03a6ba3890d7..ff085b086cc38f36a180e22ab02bbea12a29cc0c 100755 (executable)
@@ -29,8 +29,8 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
        refs="" &&
        while test $i -le 30
        do
-               refs="$refs c$i"
-               i=$(expr $i + 1)
+               refs="$refs c$i" &&
+               i=$(expr $i + 1) || return 1
        done &&
        git merge $refs &&
        test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
index 27cd94ad6f7770f93c84bad8a0a966e6d026ea78..4887ca705b330e8cbf6f25595cbcadfdfd67f9f2 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'setup' '
                echo $i > $i.c &&
                git add $i.c &&
                git commit -m $i &&
-               git tag $i
+               git tag $i || return 1
        done &&
        git reset --hard A &&
        for i in F G H I
@@ -103,7 +103,7 @@ test_expect_success 'setup' '
                echo $i > $i.c &&
                git add $i.c &&
                git commit -m $i &&
-               git tag $i
+               git tag $i || return 1
        done
 '
 
index 0260ad6f0e06ec3cbd4d6515a4f893e219b42b0a..e489869dd94daf98f900d14d75a3da97f5f7ab34 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git repack works correctly'
 . ./test-lib.sh
 . "${TEST_DIRECTORY}/lib-bitmap.sh"
 . "${TEST_DIRECTORY}/lib-midx.sh"
+. "${TEST_DIRECTORY}/lib-terminal.sh"
 
 commit_and_pack () {
        test_commit "$@" 1>&2 &&
@@ -117,7 +118,7 @@ test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
                        rm alt_objects/pack/$base_name.keep
                else
                        touch alt_objects/pack/$base_name.keep
-               fi
+               fi || return 1
        done &&
        git repack -a -d &&
        test_no_missing_in_packs
@@ -372,4 +373,16 @@ test_expect_success '--write-midx with preferred bitmap tips' '
        )
 '
 
+test_expect_success '--write-midx -b packs non-kept objects' '
+       GIT_TRACE2_EVENT="$(pwd)/trace.txt" \
+               git repack --write-midx -a -b &&
+       test_subcommand_inexact git pack-objects --honor-pack-keep <trace.txt
+'
+
+test_expect_success TTY '--quiet disables progress' '
+       test_terminal env GIT_PROGRESS_DELAY=0 \
+               git -C midx repack -ad --quiet --write-midx 2>stderr &&
+       test_must_be_empty stderr
+'
+
 test_done
index 6b6423a07c3a6e0b497e3fbd109679b38dbe3856..424c31c3287d352e135faf1754e221cc17eb8247 100755 (executable)
@@ -31,28 +31,28 @@ int main(int argc, const char **argv)
 EOF
 
 test_expect_success setup '
-       {
-               echo foo mmap bar
-               echo foo_mmap bar
-               echo foo_mmap bar mmap
-               echo foo mmap bar_mmap
-               echo foo_mmap bar mmap baz
-       } >file &&
-       {
-               echo Hello world
-               echo HeLLo world
-               echo Hello_world
-               echo HeLLo_world
-       } >hello_world &&
-       {
-               echo "a+b*c"
-               echo "a+bc"
-               echo "abc"
-       } >ab &&
-       {
-               echo d &&
-               echo 0
-       } >d0 &&
+       cat >file <<-\EOF &&
+       foo mmap bar
+       foo_mmap bar
+       foo_mmap bar mmap
+       foo mmap bar_mmap
+       foo_mmap bar mmap baz
+       EOF
+       cat >hello_world <<-\EOF &&
+       Hello world
+       HeLLo world
+       Hello_world
+       HeLLo_world
+       EOF
+       cat >ab <<-\EOF &&
+       a+b*c
+       a+bc
+       abc
+       EOF
+       cat >d0 <<-\EOF &&
+       d
+       0
+       EOF
        echo vvv >v &&
        echo ww w >w &&
        echo x x xx x >x &&
@@ -63,13 +63,13 @@ test_expect_success setup '
        echo vvv >t/v &&
        mkdir t/a &&
        echo vvv >t/a/v &&
-       {
-               echo "line without leading space1"
-               echo " line with leading space1"
-               echo " line with leading space2"
-               echo " line with leading space3"
-               echo "line without leading space2"
-       } >space &&
+       qz_to_tab_space >space <<-\EOF &&
+       line without leading space1
+       Zline with leading space1
+       Zline with leading space2
+       Zline with leading space3
+       line without leading space2
+       EOF
        cat >hello.ps1 <<-\EOF &&
        # No-op.
        function dummy() {}
@@ -106,129 +106,129 @@ do
        esac
 
        test_expect_success "grep -w $L" '
-               {
-                       echo ${HC}file:1:foo mmap bar
-                       echo ${HC}file:3:foo_mmap bar mmap
-                       echo ${HC}file:4:foo mmap bar_mmap
-                       echo ${HC}file:5:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:foo mmap bar
+               ${HC}file:3:foo_mmap bar mmap
+               ${HC}file:4:foo mmap bar_mmap
+               ${HC}file:5:foo_mmap bar mmap baz
+               EOF
                git -c grep.linenumber=false grep -n -w -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with --column)" '
-               {
-                       echo ${HC}file:5:foo mmap bar
-                       echo ${HC}file:14:foo_mmap bar mmap
-                       echo ${HC}file:5:foo mmap bar_mmap
-                       echo ${HC}file:14:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:5:foo mmap bar
+               ${HC}file:14:foo_mmap bar mmap
+               ${HC}file:5:foo mmap bar_mmap
+               ${HC}file:14:foo_mmap bar mmap baz
+               EOF
                git grep --column -w -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with --column, extended OR)" '
-               {
-                       echo ${HC}file:14:foo_mmap bar mmap
-                       echo ${HC}file:19:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:14:foo_mmap bar mmap
+               ${HC}file:19:foo_mmap bar mmap baz
+               EOF
                git grep --column -w -e mmap$ --or -e baz $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with --column, --invert-match)" '
-               {
-                       echo ${HC}file:1:foo mmap bar
-                       echo ${HC}file:1:foo_mmap bar
-                       echo ${HC}file:1:foo_mmap bar mmap
-                       echo ${HC}file:1:foo mmap bar_mmap
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:foo mmap bar
+               ${HC}file:1:foo_mmap bar
+               ${HC}file:1:foo_mmap bar mmap
+               ${HC}file:1:foo mmap bar_mmap
+               EOF
                git grep --column --invert-match -w -e baz $H -- file >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep $L (with --column, --invert-match, extended OR)" '
-               {
-                       echo ${HC}hello_world:6:HeLLo_world
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}hello_world:6:HeLLo_world
+               EOF
                git grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \
                        >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep $L (with --column, --invert-match, extended AND)" '
-               {
-                       echo ${HC}hello_world:3:Hello world
-                       echo ${HC}hello_world:3:Hello_world
-                       echo ${HC}hello_world:6:HeLLo_world
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}hello_world:3:Hello world
+               ${HC}hello_world:3:Hello_world
+               ${HC}hello_world:6:HeLLo_world
+               EOF
                git grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \
                        >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep $L (with --column, double-negation)" '
-               {
-                       echo ${HC}file:1:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:foo_mmap bar mmap baz
+               EOF
                git grep --column --not \( --not -e foo --or --not -e baz \) $H -- file \
                        >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with --column, -C)" '
-               {
-                       echo ${HC}file:5:foo mmap bar
-                       echo ${HC}file-foo_mmap bar
-                       echo ${HC}file:14:foo_mmap bar mmap
-                       echo ${HC}file:5:foo mmap bar_mmap
-                       echo ${HC}file:14:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:5:foo mmap bar
+               ${HC}file-foo_mmap bar
+               ${HC}file:14:foo_mmap bar mmap
+               ${HC}file:5:foo mmap bar_mmap
+               ${HC}file:14:foo_mmap bar mmap baz
+               EOF
                git grep --column -w -C1 -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with --line-number, --column)" '
-               {
-                       echo ${HC}file:1:5:foo mmap bar
-                       echo ${HC}file:3:14:foo_mmap bar mmap
-                       echo ${HC}file:4:5:foo mmap bar_mmap
-                       echo ${HC}file:5:14:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:5:foo mmap bar
+               ${HC}file:3:14:foo_mmap bar mmap
+               ${HC}file:4:5:foo mmap bar_mmap
+               ${HC}file:5:14:foo_mmap bar mmap baz
+               EOF
                git grep -n --column -w -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (with non-extended patterns, --column)" '
-               {
-                       echo ${HC}file:5:foo mmap bar
-                       echo ${HC}file:10:foo_mmap bar
-                       echo ${HC}file:10:foo_mmap bar mmap
-                       echo ${HC}file:5:foo mmap bar_mmap
-                       echo ${HC}file:10:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:5:foo mmap bar
+               ${HC}file:10:foo_mmap bar
+               ${HC}file:10:foo_mmap bar mmap
+               ${HC}file:5:foo mmap bar_mmap
+               ${HC}file:10:foo_mmap bar mmap baz
+               EOF
                git grep --column -w -e bar -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L" '
-               {
-                       echo ${HC}file:1:foo mmap bar
-                       echo ${HC}file:3:foo_mmap bar mmap
-                       echo ${HC}file:4:foo mmap bar_mmap
-                       echo ${HC}file:5:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:foo mmap bar
+               ${HC}file:3:foo_mmap bar mmap
+               ${HC}file:4:foo mmap bar_mmap
+               ${HC}file:5:foo_mmap bar mmap baz
+               EOF
                git -c grep.linenumber=true grep -w -e mmap $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L" '
-               {
-                       echo ${HC}file:foo mmap bar
-                       echo ${HC}file:foo_mmap bar mmap
-                       echo ${HC}file:foo mmap bar_mmap
-                       echo ${HC}file:foo_mmap bar mmap baz
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:foo mmap bar
+               ${HC}file:foo_mmap bar mmap
+               ${HC}file:foo mmap bar_mmap
+               ${HC}file:foo_mmap bar mmap baz
+               EOF
                git -c grep.linenumber=true grep --no-line-number -w -e mmap $H >actual &&
                test_cmp expected actual
        '
@@ -239,17 +239,17 @@ do
        '
 
        test_expect_success "grep -w $L (x)" '
-               {
-                       echo ${HC}x:1:x x xx x
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}x:1:x x xx x
+               EOF
                git grep -n -w -e "x xx* x" $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep -w $L (y-1)" '
-               {
-                       echo ${HC}y:1:y yy
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}y:1:y yy
+               EOF
                git grep -n -w -e "^y" $H >actual &&
                test_cmp expected actual
        '
@@ -277,16 +277,16 @@ do
        '
 
        test_expect_success "grep $L (with --column, --only-matching)" '
-               {
-                       echo ${HC}file:1:5:mmap
-                       echo ${HC}file:2:5:mmap
-                       echo ${HC}file:3:5:mmap
-                       echo ${HC}file:3:13:mmap
-                       echo ${HC}file:4:5:mmap
-                       echo ${HC}file:4:13:mmap
-                       echo ${HC}file:5:5:mmap
-                       echo ${HC}file:5:13:mmap
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}file:1:5:mmap
+               ${HC}file:2:5:mmap
+               ${HC}file:3:5:mmap
+               ${HC}file:3:13:mmap
+               ${HC}file:4:5:mmap
+               ${HC}file:4:13:mmap
+               ${HC}file:5:5:mmap
+               ${HC}file:5:13:mmap
+               EOF
                git grep --column -n -o -e mmap $H >actual &&
                test_cmp expected actual
        '
@@ -320,11 +320,11 @@ do
        '
 
        test_expect_success "grep --max-depth -1 $L" '
-               {
-                       echo ${HC}t/a/v:1:vvv
-                       echo ${HC}t/v:1:vvv
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/a/v:1:vvv
+               ${HC}t/v:1:vvv
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth -1 -n -e vvv $H >actual &&
                test_cmp expected actual &&
                git grep --recursive -n -e vvv $H >actual &&
@@ -332,9 +332,9 @@ do
        '
 
        test_expect_success "grep --max-depth 0 $L" '
-               {
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth 0 -n -e vvv $H >actual &&
                test_cmp expected actual &&
                git grep --no-recursive -n -e vvv $H >actual &&
@@ -342,11 +342,11 @@ do
        '
 
        test_expect_success "grep --max-depth 0 -- '*' $L" '
-               {
-                       echo ${HC}t/a/v:1:vvv
-                       echo ${HC}t/v:1:vvv
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/a/v:1:vvv
+               ${HC}t/v:1:vvv
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
                test_cmp expected actual &&
                git grep --no-recursive -n -e vvv $H -- "*" >actual &&
@@ -354,18 +354,18 @@ do
        '
 
        test_expect_success "grep --max-depth 1 $L" '
-               {
-                       echo ${HC}t/v:1:vvv
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/v:1:vvv
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth 1 -n -e vvv $H >actual &&
                test_cmp expected actual
        '
 
        test_expect_success "grep --max-depth 0 -- t $L" '
-               {
-                       echo ${HC}t/v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/v:1:vvv
+               EOF
                git grep --max-depth 0 -n -e vvv $H -- t >actual &&
                test_cmp expected actual &&
                git grep --no-recursive -n -e vvv $H -- t >actual &&
@@ -373,10 +373,10 @@ do
        '
 
        test_expect_success "grep --max-depth 0 -- . t $L" '
-               {
-                       echo ${HC}t/v:1:vvv
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/v:1:vvv
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
                test_cmp expected actual &&
                git grep --no-recursive -n -e vvv $H -- . t >actual &&
@@ -384,10 +384,10 @@ do
        '
 
        test_expect_success "grep --max-depth 0 -- t . $L" '
-               {
-                       echo ${HC}t/v:1:vvv
-                       echo ${HC}v:1:vvv
-               } >expected &&
+               cat >expected <<-EOF &&
+               ${HC}t/v:1:vvv
+               ${HC}v:1:vvv
+               EOF
                git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
                test_cmp expected actual &&
                git grep --no-recursive -n -e vvv $H -- t . >actual &&
@@ -1314,10 +1314,10 @@ test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '
 '
 
 test_expect_success PCRE 'grep -P -v pattern' '
-       {
-               echo "ab:a+b*c"
-               echo "ab:a+bc"
-       } >expected &&
+       cat >expected <<-\EOF &&
+       ab:a+b*c
+       ab:a+bc
+       EOF
        git grep -P -v "abc" ab >actual &&
        test_cmp expected actual
 '
@@ -1331,10 +1331,10 @@ test_expect_success PCRE 'grep -P -i pattern' '
 '
 
 test_expect_success PCRE 'grep -P -w pattern' '
-       {
-               echo "hello_world:Hello world"
-               echo "hello_world:HeLLo world"
-       } >expected &&
+       cat >expected <<-\EOF &&
+       hello_world:Hello world
+       hello_world:HeLLo world
+       EOF
        git grep -P -w "He((?i)ll)o" hello_world >actual &&
        test_cmp expected actual
 '
@@ -1469,10 +1469,10 @@ test_expect_success 'grep -F pattern with grep.patternType=basic' '
 '
 
 test_expect_success 'grep -G pattern with grep.patternType=fixed' '
-       {
-               echo "ab:a+b*c"
-               echo "ab:a+bc"
-       } >expected &&
+       cat >expected <<-\EOF &&
+       ab:a+b*c
+       ab:a+bc
+       EOF
        git \
                -c grep.patterntype=fixed \
                grep -G "a+b" ab >actual &&
@@ -1480,11 +1480,11 @@ test_expect_success 'grep -G pattern with grep.patternType=fixed' '
 '
 
 test_expect_success 'grep -E pattern with grep.patternType=fixed' '
-       {
-               echo "ab:a+b*c"
-               echo "ab:a+bc"
-               echo "ab:abc"
-       } >expected &&
+       cat >expected <<-\EOF &&
+       ab:a+b*c
+       ab:a+bc
+       ab:abc
+       EOF
        git \
                -c grep.patterntype=fixed \
                grep -E "a+" ab >actual &&
index e5d1e4ea6862694b0392415807d37ef4d3efc71b..ca3f24f8079b7acf491f8e8b0cb20c14e43272d3 100755 (executable)
@@ -123,4 +123,10 @@ test_expect_success GETTEXT_LOCALE,LIBPCRE2,PCRE2_MATCH_INVALID_UTF 'PCRE v2: gr
        test_cmp invalid-0xe5 actual
 '
 
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-literal ASCII from UTF-8' '
+       git grep --perl-regexp -h -o -e ll. file >actual &&
+       echo "lló" >expected &&
+       test_cmp expected actual
+'
+
 test_done
index 5bb302b1ba0f36eff7c3bd64a4a0dd690a135559..ee4fdd8f18d572f82392b6ed2e4b4da9837b63b5 100755 (executable)
@@ -97,7 +97,7 @@ test_expect_success 'set up abbrev tests' '
        test_commit abbrev &&
        sha1=$(git rev-parse --verify HEAD) &&
        check_abbrev () {
-               expect=$1; shift
+               expect=$1 && shift &&
                echo $sha1 | cut -c 1-$expect >expect &&
                git blame "$@" abbrev.t >actual &&
                perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha &&
index da80f815ce9d38f109df07a8dcbe803f8a794309..d751d48b7dae6a3e007d5eb19f92cd41eb445065 100755 (executable)
@@ -13,14 +13,8 @@ test_expect_success setup '
        echo B B B B B >two &&
        echo C C C C C >tres &&
        echo ABC >mouse &&
-       for i in 1 2 3 4 5 6 7 8 9
-       do
-               echo $i
-       done >nine_lines &&
-       for i in 1 2 3 4 5 6 7 8 9 a
-       do
-               echo $i
-       done >ten_lines &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 >nine_lines &&
+       test_write_lines 1 2 3 4 5 6 7 8 9 a >ten_lines &&
        git add one two tres mouse nine_lines ten_lines &&
        test_tick &&
        GIT_AUTHOR_NAME=Initial git commit -m Initial &&
index e68e6115a66d3722413a1982aa86cc2a5d5932eb..0bd034130189db8561d824449de646f2bfcef61f 100755 (executable)
@@ -310,7 +310,7 @@ test_expect_success setup '
                        echo "$line" >>"$i" &&
                        git add "$i" &&
                        test_tick &&
-                       GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count"
+                       GIT_AUTHOR_NAME="$line_count" git commit -m "$line_count" || return 1
                done <"a$i"
        done &&
 
@@ -318,7 +318,7 @@ test_expect_success setup '
        do
                # Overwrite the files with the final content.
                cp b$i $i &&
-               git add $i
+               git add $i || return 1
        done &&
        test_tick &&
 
index 67eed2fefcec974f7f166b3221150392323211c5..c7d8e0bf00f6344d1216f117f57587cda3116fc2 100755 (executable)
@@ -117,7 +117,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
        mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
        for i in a b c ; do \
          echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
-         echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
+         echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t || return 1
        done &&
          echo "bad delete test" > \
           import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
@@ -134,7 +134,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' '
                svn mv t native/t &&
                for i in a b c
                do
-                       svn mv $i.pm native/$i.pm
+                       svn mv $i.pm native/$i.pm || return 1
                done &&
                echo z >>native/t/c.t &&
                poke native/t/c.t &&
index ceaa5bad105e52eca3d193255766548e41d84f31..aa908bbc2f7dad953a3ee35c2fe32b027826f9fc 100755 (executable)
@@ -98,10 +98,10 @@ test_expect_success 'migrate --minimize on old inited layout' '
        rm -rf "$GIT_DIR"/svn &&
        for i in $(cat fetch.out)
        do
-               path=$(expr $i : "\([^:]*\):.*$")
-               ref=$(expr $i : "[^:]*:\(refs/remotes/.*\)$")
-               if test -z "$ref"; then continue; fi
-               if test -n "$path"; then path="/$path"; fi
+               path=${i%%:*} &&
+               ref=${i#*:} &&
+               if test "$ref" = "${ref#refs/remotes/}"; then continue; fi &&
+               if test -n "$path"; then path="/$path"; fi &&
                mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
                echo "$svnrepo"$path >"$GIT_DIR"/svn/$ref/info/url ||
                return 1
index cb764bcadc72cd954d3727f6fbc4d1e0d1fe6c46..90325db909e43c09b13cdf0c731d5e4260566313 100755 (executable)
@@ -15,7 +15,7 @@ EOF
 test_expect_success 'setup svnrepo' '
        for i in aa bb cc dd
        do
-               svn_cmd mkdir -m $i --username $i "$svnrepo"/$i
+               svn_cmd mkdir -m $i --username $i "$svnrepo"/$i || return 1
        done
        '
 
@@ -59,8 +59,8 @@ test_expect_success 'authors-file against globs' '
        git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
        for i in bb ee cc
        do
-               branch="aa/branches/$i"
-               svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch"
+               branch="aa/branches/$i" &&
+               svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch" || return 1
        done
        '
 
index fff49c4100852b28899c6694a33acd3b96b188b8..4a77eb9f60da3ade87ed701ecb0dca7f7ad13d87 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success 'setup test repository' '
 test_expect_success 'clone an SVN repository with ignored www directory' '
        git svn clone --ignore-paths="^www" "$svnrepo" g &&
        echo test_qqq > expect &&
-       for i in g/*/*.txt; do cat $i >> expect2; done &&
+       for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
        test_cmp expect expect2
 '
 
@@ -36,7 +36,7 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '
        ( cd c && git svn fetch --ignore-paths="^www" ) &&
        rm expect2 &&
        echo test_qqq > expect &&
-       for i in c/*/*.txt; do cat $i >> expect2; done &&
+       for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
        test_cmp expect expect2
 '
 
@@ -62,7 +62,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -73,7 +73,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
                cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -94,7 +94,7 @@ test_expect_success 'update git svn-cloned repo (config ignore)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -105,7 +105,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
                cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -127,7 +127,7 @@ test_expect_success 'update git svn-cloned repo again (config ignore)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\nygg\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -138,7 +138,7 @@ test_expect_success 'update git svn-cloned repo again (option ignore)' '
                cd c &&
                git svn rebase --ignore-paths="^www" &&
                printf "test_qqq\nb\nygg\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
index 027b416720ddf774ffd52e9bd3267b009bd70899..784ec7fc2d6e4d22feaad6dfc17120e24856b407 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success 'svn-authors setup' '
 test_expect_success 'setup svnrepo' '
        for i in aa bb cc-sub dd-sub ee-foo ff
        do
-               svn mkdir -m $i --username $i "$svnrepo"/$i
+               svn mkdir -m $i --username $i "$svnrepo"/$i || return 1
        done
 '
 
index 5f91c0d68b4582958e2ba5e20e888b9369e6a3f4..80cb55fee70e2affd719c0b6812e01f3125ce42e 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git svn creates empty directories'
 test_expect_success 'initialize repo' '
        for i in a b c d d/e d/e/f "weird file name"
        do
-               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
        done
 '
 
@@ -102,7 +102,7 @@ test_expect_success 'git svn mkdirs -r works' '
 test_expect_success 'initialize trunk' '
        for i in trunk trunk/a trunk/"weird file name"
        do
-               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
        done
 '
 
index d292bf9f55cdbedefca4e3e0777dd1f701dea9be..257fc8f2f8d194bbf1a0bb151da1b80d85874e28 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'setup test repository' '
 test_expect_success 'clone an SVN repository with filter to include qqq directory' '
        git svn clone --include-paths="qqq" "$svnrepo" g &&
        echo test_qqq > expect &&
-       for i in g/*/*.txt; do cat $i >> expect2; done &&
+       for i in g/*/*.txt; do cat $i >> expect2 || return 1; done &&
        test_cmp expect expect2
 '
 
@@ -38,7 +38,7 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '
        ( cd c && git svn fetch --include-paths="qqq" ) &&
        rm expect2 &&
        echo test_qqq > expect &&
-       for i in c/*/*.txt; do cat $i >> expect2; done &&
+       for i in c/*/*.txt; do cat $i >> expect2 || return 1; done &&
        test_cmp expect expect2
 '
 
@@ -64,7 +64,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -75,7 +75,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
                cd c &&
                git svn rebase --include-paths="qqq" &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -96,7 +96,7 @@ test_expect_success 'update git svn-cloned repo (config include)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -107,7 +107,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
                cd c &&
                git svn rebase --include-paths="qqq" &&
                printf "test_qqq\nb\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -129,7 +129,7 @@ test_expect_success 'update git svn-cloned repo again (config include)' '
                cd g &&
                git svn rebase &&
                printf "test_qqq\nb\nygg\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
@@ -140,7 +140,7 @@ test_expect_success 'update git svn-cloned repo again (option include)' '
                cd c &&
                git svn rebase --include-paths="qqq" &&
                printf "test_qqq\nb\nygg\n" > expect &&
-               for i in */*.txt; do cat $i >> expect2; done &&
+               for i in */*.txt; do cat $i >> expect2 || exit 1; done &&
                test_cmp expect2 expect &&
                rm expect expect2
        )
index 1fbe84feb1689cc413725f49189bcdd7f1379139..c93a5beab25703a935e22d012420e044a8c0ed8e 100755 (executable)
@@ -5,9 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
index 89f285d082965c0b8f0d8cbee226f3c8e2a75cfd..a597c42f77de667047c94a02534f11f884995ed7 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git svn creates empty directories, calls git gc, makes sure th
 test_expect_success 'initialize repo' '
        for i in a b c d d/e d/e/f "weird file name"
        do
-               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+               svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i" || return 1
        done
 '
 
index f519e4f1bfe62f9ec550333d400ff4fa888d1bc3..d8b1f9442e8f6e020d2e3ff736cf4853f3b47036 100755 (executable)
@@ -1,5 +1,7 @@
 #!/bin/sh
 test_description='test git fast-import unpack limit'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create loose objects on import' '
index 57d916524ecd4125717f8b70b310e965b765571e..4f5bf40587cb03aeed5b564c21780010431008ba 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='compression setting of fast-import utility'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 import_large () {
index d4359dba21c9cff29da7f96541e7c2293da759f3..bed01c99ea704244ace8c189dc428cd432eb6205 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success 'setup large marks file' '
        blob=$(git rev-parse HEAD:one.t) &&
        for i in $(test_seq 1024 16384)
        do
-               echo ":$i $blob"
+               echo ":$i $blob" || return 1
        done >>marks
 '
 
index 409b48e244224629a11ced5d9cbd99fd81f167ae..7b7a18dd2c1e07afe2a129b868bf878020882a1f 100755 (executable)
@@ -750,4 +750,36 @@ test_expect_success 'merge commit gets exported with --import-marks' '
        )
 '
 
+
+test_expect_success 'fast-export --first-parent outputs all revisions output by revision walk' '
+       git init first-parent &&
+       (
+               cd first-parent &&
+               test_commit A &&
+               git checkout -b topic1 &&
+               test_commit B &&
+               git checkout main &&
+               git merge --no-ff topic1 &&
+
+               git checkout -b topic2 &&
+               test_commit C &&
+               git checkout main &&
+               git merge --no-ff topic2 &&
+
+               test_commit D &&
+
+               git fast-export main -- --first-parent >first-parent-export &&
+               git fast-export main -- --first-parent --reverse >first-parent-reverse-export &&
+               test_cmp first-parent-export first-parent-reverse-export &&
+
+               git init import &&
+               git -C import fast-import <first-parent-export &&
+
+               git log --format="%ad %s" --first-parent main >expected &&
+               git -C import log --format="%ad %s" --all >actual &&
+               test_cmp expected actual &&
+               test_line_count = 4 actual
+       )
+'
+
 test_done
index 17f988edd268d60e4b4a7dd2074c5566b290554f..210ddf09e3094ebe5fa00599db22106813f4229e 100755 (executable)
@@ -338,7 +338,7 @@ test_expect_success 'cvs update (subdirectories)' \
   '(for dir in A A/B A/B/C A/D E; do
       mkdir $dir &&
       echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
-      git add $dir
+      git add $dir || exit 1
    done) &&
    git commit -q -m "deep sub directory structure" &&
    git push gitcvs.git >/dev/null &&
@@ -350,10 +350,9 @@ test_expect_success 'cvs update (subdirectories)' \
        test_cmp "$dir/$filename" "../$dir/$filename"; then
         :
       else
-        echo >failure
+       exit 1
       fi
-    done) &&
-   test ! -f failure'
+    done)'
 
 cd "$WORKDIR"
 test_expect_success 'cvs update (delete file)' \
@@ -382,7 +381,7 @@ test_expect_success 'cvs update (merge)' \
    for i in 1 2 3 4 5 6 7
    do
      echo Line $i >>merge &&
-     echo Line $i >>expected
+     echo Line $i >>expected || return 1
    done &&
    echo Line 8 >>expected &&
    git add merge &&
@@ -592,7 +591,7 @@ test_expect_success 'cvs annotate' '
     cd cvswork &&
     GIT_CONFIG="$git_config" cvs annotate merge >../out &&
     sed -e "s/ .*//" ../out >../actual &&
-    for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&
+    printf "1.%d\n" 3 1 1 1 1 1 1 1 2 4 >../expect &&
     test_cmp ../expect ../actual
 '
 
index 0e9daa5768c4c1cfd650bb2b552d4d6694bbe014..19f38f78f2b9350271c7bc6b16bd1f8a4f6eea5f 100755 (executable)
@@ -12,9 +12,6 @@
 # bug.
 
 test_description='git cvsimport testing for correct patchset estimation'
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./lib-cvs.sh
 
 setup_cvs_test_repository t9603
index 81bc8e8da1aa3feeec001b9f479998d76e899dd8..806005a793a3ed70492d08878d99fae499464ca9 100755 (executable)
@@ -171,7 +171,7 @@ test_expect_success 'clone using non-numeric revision ranges' '
                        cd "$git" &&
                        git ls-files >lines &&
                        test_line_count = 8 lines
-               )
+               ) || return 1
        done
 '
 
index e3836888ec8b322cf1032d9a1804fec915c10b11..5fe83315ecd57a81055bdcf2d055122ec53fd3b8 100755 (executable)
@@ -4,6 +4,8 @@ test_description='git p4 rcs keywords'
 
 . ./lib-git-p4.sh
 
+CP1252="\223\224"
+
 test_expect_success 'start p4d' '
        start_p4d
 '
@@ -32,6 +34,9 @@ test_expect_success 'init depot' '
                p4 submit -d "filek" &&
                p4 add -t text+ko fileko &&
                p4 submit -d "fileko" &&
+               printf "$CP1252" >fileko_cp1252 &&
+               p4 add -t text+ko fileko_cp1252 &&
+               p4 submit -d "fileko_cp1252" &&
                p4 add -t text file_text &&
                p4 submit -d "file_text"
        )
@@ -359,4 +364,14 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
        )
 '
 
+test_expect_success 'check cp1252 smart quote are preserved through RCS keyword processing' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               printf "$CP1252" >expect &&
+               test_cmp_bin expect fileko_cp1252
+       )
+'
+
 test_done
index 0db7ab99184add2c500f821659b09cc833c6a225..de591d875c2bbc94fd1c9b093867d23f3ce1b00c 100755 (executable)
@@ -92,11 +92,11 @@ test_expect_success 'Add some more files' '
        for i in $(test_seq 0 10)
        do
                p4_add_file "included/x$i" &&
-               p4_add_file "excluded/x$i"
+               p4_add_file "excluded/x$i" || return 1
        done &&
        for i in $(test_seq 0 10)
        do
-               p4_add_file "excluded/y$i"
+               p4_add_file "excluded/y$i" || return 1
        done
 '
 
@@ -123,7 +123,7 @@ test_expect_success 'Create a repo with multiple depot paths' '
        do
                for i in $(test_seq 1 10)
                do
-                       p4_add_file "$p/file$p$i"
+                       p4_add_file "$p/file$p$i" || return 1
                done
        done
 '
index 518203fbe07399f794651d2bb81878ed0321d922..98c628063288bca40f08664abf329340f05a28fa 100755 (executable)
@@ -5,9 +5,6 @@
 
 test_description='test bash completion'
 
-GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
-export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
-
 . ./lib-bash.sh
 
 complete ()
@@ -879,7 +876,7 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
                refs/remotes/remote/branch-in-remote
        do
                git update-ref $remote_ref main &&
-               test_when_finished "git update-ref -d $remote_ref"
+               test_when_finished "git update-ref -d $remote_ref" || return 1
        done &&
        (
                cur= &&
@@ -1052,7 +1049,7 @@ test_expect_success '__git_refs - only matching refs - checkout DWIMery' '
                refs/remotes/remote/branch-in-remote
        do
                git update-ref $remote_ref main &&
-               test_when_finished "git update-ref -d $remote_ref"
+               test_when_finished "git update-ref -d $remote_ref" || return 1
        done &&
        (
                cur=mat &&
index eef2262a3608aaf44786f0481b1c1739ae17ea6f..c3d38aaccbd526be398416de3a942f6335aa9bb4 100644 (file)
@@ -680,6 +680,17 @@ test_have_prereq () {
                        # Keep a list of missing prerequisites; restore
                        # the negative marker if necessary.
                        prerequisite=${negative_prereq:+!}$prerequisite
+
+                       # Abort if this prereq was marked as required
+                       if test -n "$GIT_TEST_REQUIRE_PREREQ"
+                       then
+                               case " $GIT_TEST_REQUIRE_PREREQ " in
+                               *" $prerequisite "*)
+                                       BAIL_OUT "required prereq $prerequisite failed"
+                                       ;;
+                               esac
+                       fi
+
                        if test -z "$missing_prereq"
                        then
                                missing_prereq=$prerequisite
@@ -1748,6 +1759,40 @@ test_subcommand () {
        fi
 }
 
+# Check that the given command was invoked as part of the
+# trace2-format trace on stdin, but without an exact set of
+# arguments.
+#
+#      test_subcommand [!] <command> <args>... < <trace>
+#
+# For example, to look for an invocation of "git pack-objects"
+# with the "--honor-pack-keep" argument, use
+#
+#      GIT_TRACE2_EVENT=event.log git repack ... &&
+#      test_subcommand git pack-objects --honor-pack-keep <event.log
+#
+# If the first parameter passed is !, this instead checks that
+# the given command was not called.
+#
+test_subcommand_inexact () {
+       local negate=
+       if test "$1" = "!"
+       then
+               negate=t
+               shift
+       fi
+
+       local expr=$(printf '"%s".*' "$@")
+       expr="${expr%,}"
+
+       if test -n "$negate"
+       then
+               ! grep "\"event\":\"child_start\".*\[$expr\]"
+       else
+               grep "\"event\":\"child_start\".*\[$expr\]"
+       fi
+}
+
 # Check that the given command was invoked as part of the
 # trace2-format trace on stdin.
 #
index 57efcc5e97a8f714a4539f1aef71253ff1e725ee..0f7a137c7d8d1ba15cc88f04c471aff4fcf7ecf6 100644 (file)
@@ -476,6 +476,13 @@ export GIT_TEST_MERGE_ALGORITHM
 GIT_TRACE_BARE=1
 export GIT_TRACE_BARE
 
+# Some tests scan the GIT_TRACE2_EVENT feed for events, but the
+# default depth is 2, which frequently causes issues when the
+# events are wrapped in new regions. Set it to a sufficiently
+# large depth to avoid custom changes in the test suite.
+GIT_TRACE2_EVENT_NESTING=100
+export GIT_TRACE2_EVENT_NESTING
+
 # Use specific version of the index file format
 if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
 then
@@ -489,6 +496,13 @@ then
        export GIT_PERL_FATAL_WARNINGS
 fi
 
+case $GIT_TEST_FSYNC in
+'')
+       GIT_TEST_FSYNC=0
+       export GIT_TEST_FSYNC
+       ;;
+esac
+
 # Add libc MALLOC and MALLOC_PERTURB test
 # only if we are not executing the test with valgrind
 if test -n "$valgrind" ||
@@ -589,6 +603,15 @@ USER_TERM="$TERM"
 TERM=dumb
 export TERM USER_TERM
 
+# What is written by tests to stdout and stderr is sent to different places
+# depending on the test mode (e.g. /dev/null in non-verbose mode, piped to tee
+# with --tee option, etc.). We save the original stdin to FD #6 and stdout and
+# stderr to #5 and #7, so that the test framework can use them (e.g. for
+# printing errors within the test framework) independently of the test mode.
+exec 5>&1
+exec 6<&0
+exec 7>&2
+
 _error_exit () {
        finalize_junit_xml
        GIT_EXIT_OK=t
@@ -612,7 +635,7 @@ BAIL_OUT () {
        local bail_out="Bail out! "
        local message="$1"
 
-       say_color error $bail_out "$message"
+       say_color >&5 error $bail_out "$message"
        _error_exit
 }
 
@@ -637,9 +660,6 @@ then
        exit 0
 fi
 
-exec 5>&1
-exec 6<&0
-exec 7>&2
 if test "$verbose_log" = "t"
 then
        exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
@@ -669,6 +689,8 @@ test_fixed=0
 test_broken=0
 test_success=0
 
+test_missing_prereq=
+
 test_external_has_tap=0
 
 die () {
@@ -1069,6 +1091,14 @@ test_skip () {
                        of_prereq=" of $test_prereq"
                fi
                skipped_reason="missing $missing_prereq${of_prereq}"
+
+               # Keep a list of all the missing prereq for result aggregation
+               if test -z "$missing_prereq"
+               then
+                       test_missing_prereq=$missing_prereq
+               else
+                       test_missing_prereq="$test_missing_prereq,$missing_prereq"
+               fi
        fi
 
        case "$to_skip" in
@@ -1175,6 +1205,7 @@ test_done () {
                fixed $test_fixed
                broken $test_broken
                failed $test_failure
+               missing_prereq $test_missing_prereq
 
                EOF
        fi
diff --git a/tag.c b/tag.c
index 3e18a41841485e8138d7ccaf36e24cc99e5d81cd..dfbcd7fcc244ac2500d41655afac3136758bb331 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -25,8 +25,9 @@ static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
                return error("no signature found");
        }
 
-       ret = check_signature(payload.buf, payload.len, signature.buf,
-                               signature.len, &sigc);
+       sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+       sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+       ret = check_signature(&sigc, signature.buf, signature.len);
 
        if (!(flags & GPG_VERIFY_OMIT_STATUS))
                print_signature_buffer(&sigc, flags);
index b8d880e3626a9cdbc818cf69bda67fcd256194b1..3d38eeab66bfb048c20dd3490f92f2a6e15e040b 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tmp-objdir.h"
+#include "chdir-notify.h"
 #include "dir.h"
 #include "sigchain.h"
 #include "string-list.h"
@@ -11,6 +12,8 @@
 struct tmp_objdir {
        struct strbuf path;
        struct strvec env;
+       struct object_directory *prev_odb;
+       int will_destroy;
 };
 
 /*
@@ -38,6 +41,9 @@ static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
        if (t == the_tmp_objdir)
                the_tmp_objdir = NULL;
 
+       if (!on_signal && t->prev_odb)
+               restore_primary_odb(t->prev_odb, t->path.buf);
+
        /*
         * This may use malloc via strbuf_grow(), but we should
         * have pre-grown t->path sufficiently so that this
@@ -52,6 +58,7 @@ static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
         */
        if (!on_signal)
                tmp_objdir_free(t);
+
        return err;
 }
 
@@ -121,7 +128,7 @@ static int setup_tmp_objdir(const char *root)
        return ret;
 }
 
-struct tmp_objdir *tmp_objdir_create(void)
+struct tmp_objdir *tmp_objdir_create(const char *prefix)
 {
        static int installed_handlers;
        struct tmp_objdir *t;
@@ -129,11 +136,16 @@ struct tmp_objdir *tmp_objdir_create(void)
        if (the_tmp_objdir)
                BUG("only one tmp_objdir can be used at a time");
 
-       t = xmalloc(sizeof(*t));
+       t = xcalloc(1, sizeof(*t));
        strbuf_init(&t->path, 0);
        strvec_init(&t->env);
 
-       strbuf_addf(&t->path, "%s/incoming-XXXXXX", get_object_directory());
+       /*
+        * Use a string starting with tmp_ so that the builtin/prune.c code
+        * can recognize any stale objdirs left behind by a crash and delete
+        * them.
+        */
+       strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);
 
        /*
         * Grow the strbuf beyond any filename we expect to be placed in it.
@@ -269,6 +281,13 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
        if (!t)
                return 0;
 
+       if (t->prev_odb) {
+               if (the_repository->objects->odb->will_destroy)
+                       BUG("migrating an ODB that was marked for destruction");
+               restore_primary_odb(t->prev_odb, t->path.buf);
+               t->prev_odb = NULL;
+       }
+
        strbuf_addbuf(&src, &t->path);
        strbuf_addstr(&dst, get_object_directory());
 
@@ -292,3 +311,33 @@ void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)
 {
        add_to_alternates_memory(t->path.buf);
 }
+
+void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy)
+{
+       if (t->prev_odb)
+               BUG("the primary object database is already replaced");
+       t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy);
+       t->will_destroy = will_destroy;
+}
+
+struct tmp_objdir *tmp_objdir_unapply_primary_odb(void)
+{
+       if (!the_tmp_objdir || !the_tmp_objdir->prev_odb)
+               return NULL;
+
+       restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf);
+       the_tmp_objdir->prev_odb = NULL;
+       return the_tmp_objdir;
+}
+
+void tmp_objdir_reapply_primary_odb(struct tmp_objdir *t, const char *old_cwd,
+               const char *new_cwd)
+{
+       char *path;
+
+       path = reparent_relative_path(old_cwd, new_cwd, t->path.buf);
+       strbuf_reset(&t->path);
+       strbuf_addstr(&t->path, path);
+       free(path);
+       tmp_objdir_replace_primary_odb(t, t->will_destroy);
+}
index b1e45b4c75d2d8877dce507a7f2134aa93da8e17..cda5ec7677881c5a47bb5f2ebf3edc3a3140a73c 100644 (file)
@@ -10,7 +10,7 @@
  *
  * Example:
  *
- *     struct tmp_objdir *t = tmp_objdir_create();
+ *     struct tmp_objdir *t = tmp_objdir_create("incoming");
  *     if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&
  *         !tmp_objdir_migrate(t))
  *             printf("success!\n");
 struct tmp_objdir;
 
 /*
- * Create a new temporary object directory; returns NULL on failure.
+ * Create a new temporary object directory with the specified prefix;
+ * returns NULL on failure.
  */
-struct tmp_objdir *tmp_objdir_create(void);
+struct tmp_objdir *tmp_objdir_create(const char *prefix);
 
 /*
  * Return a list of environment strings, suitable for use with
@@ -51,4 +52,26 @@ int tmp_objdir_destroy(struct tmp_objdir *);
  */
 void tmp_objdir_add_as_alternate(const struct tmp_objdir *);
 
+/*
+ * Replaces the writable object store in the current process with the temporary
+ * object directory and makes the former main object store an alternate.
+ * If will_destroy is nonzero, the object directory may not be migrated.
+ */
+void tmp_objdir_replace_primary_odb(struct tmp_objdir *, int will_destroy);
+
+/*
+ * If the primary object database was replaced by a temporary object directory,
+ * restore it to its original value while keeping the directory contents around.
+ * Returns NULL if the primary object database was not replaced.
+ */
+struct tmp_objdir *tmp_objdir_unapply_primary_odb(void);
+
+/*
+ * Reapplies the former primary temporary object database, after potentially
+ * changing its relative path.
+ */
+void tmp_objdir_reapply_primary_odb(struct tmp_objdir *, const char *old_cwd,
+               const char *new_cwd);
+
+
 #endif /* TMP_OBJDIR_H */
index 3a0014417cc0c3b0e3a5995773550681880842e5..bd17ecdc32162e88eb8f43c663e3f3745f08b34a 100644 (file)
@@ -354,7 +354,7 @@ static void fn_child_start_fl(const char *file, int line,
        jw_object_inline_begin_array(&jw, "argv");
        if (cmd->git_cmd)
                jw_array_string(&jw, "git");
-       jw_array_argv(&jw, cmd->argv);
+       jw_array_argv(&jw, cmd->args.v);
        jw_end(&jw);
        jw_end(&jw);
 
index 58d9e430f05c96cf8886e09b93e92403e1ad1ef1..6e429a3fb9e6d8fefe200e24180e0f1d966cf9a1 100644 (file)
@@ -232,7 +232,7 @@ static void fn_child_start_fl(const char *file, int line,
        strbuf_addch(&buf_payload, ' ');
        if (cmd->git_cmd)
                strbuf_addstr(&buf_payload, "git ");
-       sq_append_quote_argv_pretty(&buf_payload, cmd->argv);
+       sq_append_quote_argv_pretty(&buf_payload, cmd->args.v);
 
        normal_io_write_fl(file, line, &buf_payload);
        strbuf_release(&buf_payload);
index e4acca13d64b7e1a8f2c25c15f61dc35384ce9e1..2ff9cf708355b4f5e64a9fa802c04c93f5b81728 100644 (file)
@@ -335,10 +335,10 @@ static void fn_child_start_fl(const char *file, int line,
        strbuf_addstr(&buf_payload, " argv:[");
        if (cmd->git_cmd) {
                strbuf_addstr(&buf_payload, "git");
-               if (cmd->argv[0])
+               if (cmd->args.nr)
                        strbuf_addch(&buf_payload, ' ');
        }
-       sq_append_quote_argv_pretty(&buf_payload, cmd->argv);
+       sq_append_quote_argv_pretty(&buf_payload, cmd->args.v);
        strbuf_addch(&buf_payload, ']');
 
        perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
index 7c7cb61a945c7ba2634685d6d49e3ad7ac23204a..1b12f77d945f423aae5b8141d9494c06424f7180 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -236,7 +236,7 @@ static char *apply_command(struct conf_info *conf, const char *arg)
                        strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
                strvec_push(&cp.args, cmd.buf);
        }
-       cp.env = local_repo_env;
+       strvec_pushv(&cp.env_array, (const char **)local_repo_env);
        cp.no_stdin = 1;
        cp.use_shell = 1;
 
index e4f1decae2063ce8981344c19626141c8bcd866c..2a3e32415455baf09f165837cb169e31ab6876f5 100644 (file)
@@ -1204,16 +1204,15 @@ static int run_pre_push_hook(struct transport *transport,
        struct ref *r;
        struct child_process proc = CHILD_PROCESS_INIT;
        struct strbuf buf;
-       const char *argv[4];
+       const char *hook_path = find_hook("pre-push");
 
-       if (!(argv[0] = find_hook("pre-push")))
+       if (!hook_path)
                return 0;
 
-       argv[1] = transport->remote->name;
-       argv[2] = transport->url;
-       argv[3] = NULL;
+       strvec_push(&proc.args, hook_path);
+       strvec_push(&proc.args, transport->remote->name);
+       strvec_push(&proc.args, transport->url);
 
-       proc.argv = argv;
        proc.in = -1;
        proc.trace2_hook_name = "pre-push";
 
@@ -1457,13 +1456,18 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        return rc;
 }
 
-void transport_unlock_pack(struct transport *transport)
+void transport_unlock_pack(struct transport *transport, unsigned int flags)
 {
+       int in_signal_handler = !!(flags & TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
        int i;
 
        for (i = 0; i < transport->pack_lockfiles.nr; i++)
-               unlink_or_warn(transport->pack_lockfiles.items[i].string);
-       string_list_clear(&transport->pack_lockfiles, 0);
+               if (in_signal_handler)
+                       unlink(transport->pack_lockfiles.items[i].string);
+               else
+                       unlink_or_warn(transport->pack_lockfiles.items[i].string);
+       if (!in_signal_handler)
+               string_list_clear(&transport->pack_lockfiles, 0);
 }
 
 int transport_connect(struct transport *transport, const char *name,
index 8bb4c8bbc8cae2059060ae25044a160192a4354e..3f16e50c1965db222173a2330b12c26aee3d6d25 100644 (file)
@@ -279,7 +279,19 @@ const struct ref *transport_get_remote_refs(struct transport *transport,
  */
 const struct git_hash_algo *transport_get_hash_algo(struct transport *transport);
 int transport_fetch_refs(struct transport *transport, struct ref *refs);
-void transport_unlock_pack(struct transport *transport);
+
+/*
+ * If this flag is set, unlocking will avoid to call non-async-signal-safe
+ * functions. This will necessarily leave behind some data structures which
+ * cannot be cleaned up.
+ */
+#define TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER (1 << 0)
+
+/*
+ * Unlock all packfiles locked by the transport.
+ */
+void transport_unlock_pack(struct transport *transport, unsigned int flags);
+
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
 void transport_take_over(struct transport *transport,
index 437c98a70e47ebc89d258e281f16207b9dfa990d..69031d7cbae6a88c1fca3242e974d9cd435a4c78 100644 (file)
@@ -603,8 +603,7 @@ static void try_to_follow_renames(const struct object_id *old_oid,
         * about dry-run mode and returns wildcard info.
         */
        if (opt->pathspec.has_wildcard)
-               die("BUG:%s:%d: wildcards are not supported",
-                   __FILE__, __LINE__);
+               BUG("wildcards are not supported");
 #endif
 
        /* Remove the file creation entry from the diff queue, and remember it */
index b71fdab5e34f2d36a7fb341213801bf1dfd8207e..360844bda3ab976c73e9443b318b58dfc400f475 100644 (file)
@@ -36,6 +36,9 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
        /* ERROR_NOT_UPTODATE_DIR */
        "Updating '%s' would lose untracked files in it",
 
+       /* ERROR_CWD_IN_THE_WAY */
+       "Refusing to remove '%s' since it is the current working directory.",
+
        /* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
        "Untracked working tree file '%s' would be overwritten by merge.",
 
@@ -131,6 +134,9 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
        msgs[ERROR_NOT_UPTODATE_DIR] =
                _("Updating the following directories would lose untracked files in them:\n%s");
 
+       msgs[ERROR_CWD_IN_THE_WAY] =
+               _("Refusing to remove the current working directory:\n%s");
+
        if (!strcmp(cmd, "checkout"))
                msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
                      ? _("The following untracked working tree files would be removed by checkout:\n%%s"
@@ -1238,7 +1244,9 @@ static int find_cache_pos(struct traverse_info *info,
 
 /*
  * Given a sparse directory entry 'ce', compare ce->name to
- * info->name + '/' + p->path + '/' if info->name is non-empty.
+ * info->traverse_path + p->path + '/' if info->traverse_path
+ * is non-empty.
+ *
  * Compare ce->name to p->path + '/' otherwise. Note that
  * ce->name must end in a trailing '/' because it is a sparse
  * directory entry.
@@ -1250,11 +1258,11 @@ static int sparse_dir_matches_path(const struct cache_entry *ce,
        assert(S_ISSPARSEDIR(ce->ce_mode));
        assert(ce->name[ce->ce_namelen - 1] == '/');
 
-       if (info->namelen)
-               return ce->ce_namelen == info->namelen + p->pathlen + 2 &&
-                      ce->name[info->namelen] == '/' &&
-                      !strncmp(ce->name, info->name, info->namelen) &&
-                      !strncmp(ce->name + info->namelen + 1, p->path, p->pathlen);
+       if (info->pathlen)
+               return ce->ce_namelen == info->pathlen + p->pathlen + 1 &&
+                      ce->name[info->pathlen - 1] == '/' &&
+                      !strncmp(ce->name, info->traverse_path, info->pathlen) &&
+                      !strncmp(ce->name + info->pathlen, p->path, p->pathlen);
        return ce->ce_namelen == p->pathlen + 1 &&
               !strncmp(ce->name, p->path, p->pathlen);
 }
@@ -2157,10 +2165,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
                cnt++;
        }
 
-       /*
-        * Then we need to make sure that we do not lose a locally
-        * present file that is not ignored.
-        */
+       /* Do not lose a locally present file that is not ignored. */
        pathbuf = xstrfmt("%.*s/", namelen, ce->name);
 
        memset(&d, 0, sizeof(d));
@@ -2171,6 +2176,12 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
        free(pathbuf);
        if (i)
                return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
+
+       /* Do not lose startup_info->original_cwd */
+       if (startup_info->original_cwd &&
+           !strcmp(startup_info->original_cwd, ce->name))
+               return add_rejected_path(o, ERROR_CWD_IN_THE_WAY, ce->name);
+
        return cnt;
 }
 
@@ -2263,10 +2274,19 @@ static int verify_absent_1(const struct cache_entry *ce,
        int len;
        struct stat st;
 
-       if (o->index_only || !o->update ||
-           o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED)
+       if (o->index_only || !o->update)
                return 0;
 
+       if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED) {
+               /* Avoid nuking startup_info->original_cwd... */
+               if (startup_info->original_cwd &&
+                   !strcmp(startup_info->original_cwd, ce->name))
+                       return add_rejected_path(o, ERROR_CWD_IN_THE_WAY,
+                                                ce->name);
+               /* ...but nuke anything else. */
+               return 0;
+       }
+
        len = check_leading_path(ce->name, ce_namelen(ce), 0);
        if (!len)
                return 0;
index 71ffb7eeb0c0d1df8539cdff4d6383116d4ff1b4..efb9edfbb2717b4739247ecdd58a104e1d44cfd2 100644 (file)
@@ -19,6 +19,7 @@ enum unpack_trees_error_types {
        ERROR_WOULD_OVERWRITE = 0,
        ERROR_NOT_UPTODATE_FILE,
        ERROR_NOT_UPTODATE_DIR,
+       ERROR_CWD_IN_THE_WAY,
        ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
        ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
        ERROR_BIND_OVERLAP,
index c78d55bc674ed2b22b7f4417173242ad4e53bfd0..8acc98741bbb83db90dc6b0901d5be817cacd0a3 100644 (file)
@@ -194,7 +194,13 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
 }
 
 struct output_state {
-       char buffer[8193];
+       /*
+        * We do writes no bigger than LARGE_PACKET_DATA_MAX - 1, because with
+        * sideband-64k the band designator takes up 1 byte of space. Because
+        * relay_pack_data keeps the last byte to itself, we make the buffer 1
+        * byte bigger than the intended maximum write size.
+        */
+       char buffer[(LARGE_PACKET_DATA_MAX - 1) + 1];
        int used;
        unsigned packfile_uris_started : 1;
        unsigned packfile_started : 1;
@@ -269,7 +275,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
                             const struct string_list *uri_protocols)
 {
        struct child_process pack_objects = CHILD_PROCESS_INIT;
-       struct output_state output_state = { { 0 } };
+       struct output_state *output_state = xcalloc(1, sizeof(struct output_state));
        char progress[128];
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
@@ -404,7 +410,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
                }
                if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
                        int result = relay_pack_data(pack_objects.out,
-                                                    &output_state,
+                                                    output_state,
                                                     pack_data->use_sideband,
                                                     !!uri_protocols);
 
@@ -438,11 +444,12 @@ static void create_pack_file(struct upload_pack_data *pack_data,
        }
 
        /* flush the data */
-       if (output_state.used > 0) {
-               send_client_data(1, output_state.buffer, output_state.used,
+       if (output_state->used > 0) {
+               send_client_data(1, output_state->buffer, output_state->used,
                                 pack_data->use_sideband);
                fprintf(stderr, "flushed.\n");
        }
+       free(output_state);
        if (pack_data->use_sideband)
                packet_flush(1);
        return;
@@ -596,14 +603,11 @@ static int do_reachable_revlist(struct child_process *cmd,
                                struct object_array *reachable,
                                enum allow_uor allow_uor)
 {
-       static const char *argv[] = {
-               "rev-list", "--stdin", NULL,
-       };
        struct object *o;
        FILE *cmd_in = NULL;
        int i;
 
-       cmd->argv = argv;
+       strvec_pushl(&cmd->args, "rev-list", "--stdin", NULL);
        cmd->git_cmd = 1;
        cmd->no_stderr = 1;
        cmd->in = -1;
diff --git a/usage.c b/usage.c
index c7d233b0de9538951c5f3d47aa165d900034c02b..9943dd8742e8adb2d283be453d7cb9da478a0a7e 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -6,7 +6,7 @@
 #include "git-compat-util.h"
 #include "cache.h"
 
-void vreportf(const char *prefix, const char *err, va_list params)
+static void vreportf(const char *prefix, const char *err, va_list params)
 {
        char msg[4096];
        char *p, *pend = msg + sizeof(msg);
@@ -55,6 +55,12 @@ static NORETURN void usage_builtin(const char *err, va_list params)
        exit(129);
 }
 
+static void die_message_builtin(const char *err, va_list params)
+{
+       trace2_cmd_error_va(err, params);
+       vreportf("fatal: ", err, params);
+}
+
 /*
  * We call trace2_cmd_error_va() in the below functions first and
  * expect it to va_copy 'params' before using it (because an 'ap' can
@@ -62,10 +68,9 @@ static NORETURN void usage_builtin(const char *err, va_list params)
  */
 static NORETURN void die_builtin(const char *err, va_list params)
 {
-       trace2_cmd_error_va(err, params);
-
-       vreportf("fatal: ", err, params);
+       report_fn die_message_fn = get_die_message_routine();
 
+       die_message_fn(err, params);
        exit(128);
 }
 
@@ -109,6 +114,7 @@ static int die_is_recursing_builtin(void)
  * (ugh), so keep things static. */
 static NORETURN_PTR report_fn usage_routine = usage_builtin;
 static NORETURN_PTR report_fn die_routine = die_builtin;
+static report_fn die_message_routine = die_message_builtin;
 static report_fn error_routine = error_builtin;
 static report_fn warn_routine = warn_builtin;
 static int (*die_is_recursing)(void) = die_is_recursing_builtin;
@@ -118,6 +124,11 @@ void set_die_routine(NORETURN_PTR report_fn routine)
        die_routine = routine;
 }
 
+report_fn get_die_message_routine(void)
+{
+       return die_message_routine;
+}
+
 void set_error_routine(report_fn routine)
 {
        error_routine = routine;
@@ -211,6 +222,29 @@ void NORETURN die_errno(const char *fmt, ...)
        va_end(params);
 }
 
+#undef die_message
+int die_message(const char *err, ...)
+{
+       va_list params;
+
+       va_start(params, err);
+       die_message_routine(err, params);
+       va_end(params);
+       return 128;
+}
+
+#undef die_message_errno
+int die_message_errno(const char *fmt, ...)
+{
+       char buf[1024];
+       va_list params;
+
+       va_start(params, fmt);
+       die_message_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
+       va_end(params);
+       return 128;
+}
+
 #undef error_errno
 int error_errno(const char *fmt, ...)
 {
index 2c155b1015082e98634e4a5c80563973e2b29cc4..6f598dcfcdf96fa7b3e4f59bb6a028abef89fdf5 100644 (file)
@@ -404,17 +404,13 @@ int is_worktree_being_bisected(const struct worktree *wt,
  * bisect). New commands that do similar things should update this
  * function as well.
  */
-const struct worktree *find_shared_symref(const char *symref,
+const struct worktree *find_shared_symref(struct worktree **worktrees,
+                                         const char *symref,
                                          const char *target)
 {
        const struct worktree *existing = NULL;
-       static struct worktree **worktrees;
        int i = 0;
 
-       if (worktrees)
-               free_worktrees(worktrees);
-       worktrees = get_worktrees();
-
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
                const char *symref_target;
index 8b7c408132d1f7c7a81824f635488955a2df3cd6..9e06fcbdf3d53f724ba6746db04db1ffd40e800f 100644 (file)
@@ -143,9 +143,10 @@ void free_worktrees(struct worktree **);
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
  * or any linked worktree, and return the worktree that holds the ref,
- * or NULL otherwise. The result may be destroyed by the next call.
+ * or NULL otherwise.
  */
-const struct worktree *find_shared_symref(const char *symref,
+const struct worktree *find_shared_symref(struct worktree **worktrees,
+                                         const char *symref,
                                          const char *target);
 
 /*
index 0b1ec8190b622d18751aef3d0e640bf3d0e63b97..a3d5784cec94230d18747612bfe59e8db8248ff9 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "config.h"
 #include "run-command.h"
 
 /*
@@ -57,6 +58,10 @@ void fprintf_or_die(FILE *f, const char *fmt, ...)
 
 void fsync_or_die(int fd, const char *msg)
 {
+       if (use_fsync < 0)
+               use_fsync = git_env_bool("GIT_TEST_FSYNC", 1);
+       if (!use_fsync)
+               return;
        while (fsync(fd) < 0) {
                if (errno != EINTR)
                        die_errno("fsync error on '%s'", msg);
index 5d215f4e4f1ea862a169bab5c2cb2039ad0d50d3..335e723a71e2537095359d3d4ee6cb77daea1e16 100644 (file)
@@ -1218,17 +1218,23 @@ static void show_merge_in_progress(struct wt_status *s,
 static void show_am_in_progress(struct wt_status *s,
                                const char *color)
 {
+       int am_empty_patch;
+
        status_printf_ln(s, color,
                _("You are in the middle of an am session."));
        if (s->state.am_empty_patch)
                status_printf_ln(s, color,
                        _("The current patch is empty."));
        if (s->hints) {
-               if (!s->state.am_empty_patch)
+               am_empty_patch = s->state.am_empty_patch;
+               if (!am_empty_patch)
                        status_printf_ln(s, color,
                                _("  (fix conflicts and then run \"git am --continue\")"));
                status_printf_ln(s, color,
                        _("  (use \"git am --skip\" to skip this patch)"));
+               if (am_empty_patch)
+                       status_printf_ln(s, color,
+                               _("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
                status_printf_ln(s, color,
                        _("  (use \"git am --abort\" to restore the original branch)"));
        }
index 75b32aef51dabf9e9ae8384d0376287e3a8495d0..2e3a5a2943e7fc28e79a425fcbdb01da0b0567b0 100644 (file)
@@ -313,6 +313,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)
                        die("'%s' is not a boolean", var);
                if (!strcmp(value, "diff3"))
                        git_xmerge_style = XDL_MERGE_DIFF3;
+               else if (!strcmp(value, "zdiff3"))
+                       git_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;
                else if (!strcmp(value, "merge"))
                        git_xmerge_style = 0;
                /*
index b29deca5de84babed44bce2ff4934745c90ffc70..72e25a9ffa56fbeebefbd2e9904b3957d0a8610d 100644 (file)
@@ -66,6 +66,7 @@ extern "C" {
 
 /* merge output styles */
 #define XDL_MERGE_DIFF3 1
+#define XDL_MERGE_ZEALOUS_DIFF3 2
 
 typedef struct s_mmfile {
        char *ptr;
index a4542c05b617d748e6e97e4990e38e0afe7f5eb4..69689fab2478c8fed40b63e9a7cefb43c13e7ae7 100644 (file)
@@ -390,12 +390,9 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
-static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags)
+static int recs_match(xrecord_t *rec1, xrecord_t *rec2)
 {
-       return (rec1->ha == rec2->ha &&
-               xdl_recmatch(rec1->ptr, rec1->size,
-                            rec2->ptr, rec2->size,
-                            flags));
+       return (rec1->ha == rec2->ha);
 }
 
 /*
@@ -759,10 +756,10 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)
  * following group, expand this group to include it. Return 0 on success or -1
  * if g cannot be slid down.
  */
-static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags)
+static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)
 {
        if (g->end < xdf->nrec &&
-           recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) {
+           recs_match(xdf->recs[g->start], xdf->recs[g->end])) {
                xdf->rchg[g->start++] = 0;
                xdf->rchg[g->end++] = 1;
 
@@ -780,10 +777,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags)
  * into a previous group, expand this group to include it. Return 0 on success
  * or -1 if g cannot be slid up.
  */
-static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags)
+static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)
 {
        if (g->start > 0 &&
-           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) {
+           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {
                xdf->rchg[--g->start] = 1;
                xdf->rchg[--g->end] = 0;
 
@@ -833,7 +830,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                        end_matching_other = -1;
 
                        /* Shift the group backward as much as possible: */
-                       while (!group_slide_up(xdf, &g, flags))
+                       while (!group_slide_up(xdf, &g))
                                if (group_previous(xdfo, &go))
                                        BUG("group sync broken sliding up");
 
@@ -848,7 +845,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
 
                        /* Now shift the group forward as far as possible: */
                        while (1) {
-                               if (group_slide_down(xdf, &g, flags))
+                               if (group_slide_down(xdf, &g))
                                        break;
                                if (group_next(xdfo, &go))
                                        BUG("group sync broken sliding down");
@@ -875,7 +872,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                         * other file that it can align with.
                         */
                        while (go.end == go.start) {
-                               if (group_slide_up(xdf, &g, flags))
+                               if (group_slide_up(xdf, &g))
                                        BUG("match disappeared");
                                if (group_previous(xdfo, &go))
                                        BUG("group sync broken sliding to match");
@@ -918,7 +915,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
                        }
 
                        while (g.end > best_shift) {
-                               if (group_slide_up(xdf, &g, flags))
+                               if (group_slide_up(xdf, &g))
                                        BUG("best shift unreached");
                                if (group_previous(xdfo, &go))
                                        BUG("group sync broken sliding to blank line");
index e694bfd9e31d54f1925a730a75b0ef6d9a4e6d95..80794748b0de6bb9176ce088c472d44c62b91e6d 100644 (file)
@@ -88,19 +88,14 @@ struct region {
 #define REC(env, s, l) \
        (env->xdf##s.recs[l - 1])
 
-static int cmp_recs(xpparam_t const *xpp,
-       xrecord_t *r1, xrecord_t *r2)
+static int cmp_recs(xrecord_t *r1, xrecord_t *r2)
 {
-       return r1->ha == r2->ha &&
-               xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
-                           xpp->flags);
-}
+       return r1->ha == r2->ha;
 
-#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
-       (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+}
 
 #define CMP(i, s1, l1, s2, l2) \
-       (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+       (cmp_recs(REC(i->env, s1, l1), REC(i->env, s2, l2)))
 
 #define TABLE_HASH(index, side, line) \
        XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
index 1659edb45393a6b57ca70654e67784e5d0025c65..fff0b594f9a851a8e1a6fca961692614ff0c822a 100644 (file)
@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
        size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,
                              dest ? dest + size : NULL);
 
-       if (style == XDL_MERGE_DIFF3) {
+       if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
                /* Shared preimage */
                if (!dest) {
                        size += marker_size + 1 + needs_cr + marker3_size;
@@ -322,6 +322,40 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
        return size;
 }
 
+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
+{
+       return xdl_recmatch(rec1->ptr, rec1->size,
+                           rec2->ptr, rec2->size, flags);
+}
+
+/*
+ * Remove any common lines from the beginning and end of the conflicted region.
+ */
+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+               xpparam_t const *xpp)
+{
+       xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;
+       for (; m; m = m->next) {
+               /* let's handle just the conflicts */
+               if (m->mode)
+                       continue;
+
+               while(m->chg1 && m->chg2 &&
+                     recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {
+                       m->chg1--;
+                       m->chg2--;
+                       m->i1++;
+                       m->i2++;
+               }
+               while (m->chg1 && m->chg2 &&
+                      recmatch(rec1[m->i1 + m->chg1 - 1],
+                               rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
+                       m->chg1--;
+                       m->chg2--;
+               }
+       }
+}
+
 /*
  * Sometimes, changes are not quite identical, but differ in only a few
  * lines. Try hard to show only these few lines as conflicting.
@@ -482,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
        int style = xmp->style;
        int favor = xmp->favor;
 
-       if (style == XDL_MERGE_DIFF3) {
+       /*
+        * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking
+        * at common areas of sides 1 & 2, because the base (side 0) does
+        * not match and is being shown.  Similarly, simplification of
+        * non-conflicts is also skipped due to the skipping of conflict
+        * refinement.
+        *
+        * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to
+        * refine conflicts looking for common areas of sides 1 & 2.
+        * However, since the base is being shown and does not match,
+        * it will only look for common areas at the beginning or end
+        * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's
+        * conflict refinement is much more limited in this fashion, the
+        * conflict simplification will be skipped.
+        */
+       if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
                /*
                 * "diff3 -m" output does not make sense for anything
                 * more aggressive than XDL_MERGE_EAGER.
@@ -603,10 +652,12 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
        if (!changes)
                changes = c;
        /* refine conflicts */
-       if (XDL_MERGE_ZEALOUS <= level &&
-           (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
-            xdl_simplify_non_conflicts(xe1, changes,
-                                       XDL_MERGE_ZEALOUS < level) < 0)) {
+       if (style == XDL_MERGE_ZEALOUS_DIFF3) {
+               xdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);
+       } else if (XDL_MERGE_ZEALOUS <= level &&
+                  (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+                   xdl_simplify_non_conflicts(xe1, changes,
+                                              XDL_MERGE_ZEALOUS < level) < 0)) {
                xdl_cleanup_merge(changes);
                return -1;
        }
index abeb8fb84e6d73086d612b831963a227e35743b8..4527a4a07c4e0986cb377015d09f9a13309c04d8 100644 (file)
@@ -181,15 +181,11 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
        if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
                goto abort;
 
-       if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
-               hbits = hsize = 0;
-       else {
-               hbits = xdl_hashbits((unsigned int) narec);
-               hsize = 1 << hbits;
-               if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
-                       goto abort;
-               memset(rhash, 0, hsize * sizeof(xrecord_t *));
-       }
+       hbits = xdl_hashbits((unsigned int) narec);
+       hsize = 1 << hbits;
+       if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+               goto abort;
+       memset(rhash, 0, hsize * sizeof(xrecord_t *));
 
        nrec = 0;
        if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
@@ -208,9 +204,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
                        crec->size = (long) (cur - prev);
                        crec->ha = hav;
                        recs[nrec++] = crec;
-
-                       if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
-                           xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+                       if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
                                goto abort;
                }
        }
@@ -219,10 +213,13 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
                goto abort;
        memset(rchg, 0, (nrec + 2) * sizeof(char));
 
-       if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
-               goto abort;
-       if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
-               goto abort;
+       if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+           (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
+               if (!(rindex = xdl_malloc((nrec + 1) * sizeof(*rindex))))
+                       goto abort;
+               if (!(ha = xdl_malloc((nrec + 1) * sizeof(*ha))))
+                       goto abort;
+       }
 
        xdf->nrec = nrec;
        xdf->recs = recs;
@@ -279,8 +276,7 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        enl1 = xdl_guess_lines(mf1, sample) + 1;
        enl2 = xdl_guess_lines(mf2, sample) + 1;
 
-       if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
-           xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
+       if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
                return -1;
 
        if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
@@ -305,8 +301,7 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
 
-       if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)
-               xdl_free_classifier(&cf);
+       xdl_free_classifier(&cf);
 
        return 0;
 }