]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ma/doc-folder-to-directory'
authorJunio C Hamano <gitster@pobox.com>
Fri, 29 Oct 2021 22:43:15 +0000 (15:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 29 Oct 2021 22:43:15 +0000 (15:43 -0700)
Consistently use 'directory', not 'folder', to call the filesystem
entity that collects a group of files and, eh, directories.

* ma/doc-folder-to-directory:
  gitweb.txt: change "folder" to "directory"
  gitignore.txt: change "folder" to "directory"
  git-multi-pack-index.txt: change "folder" to "directory"

205 files changed:
Documentation/.gitignore
Documentation/Makefile
Documentation/RelNotes/2.34.0.txt
Documentation/config.txt
Documentation/config/color.txt
Documentation/config/gpg.txt
Documentation/config/user.txt
Documentation/git-archive.txt
Documentation/git.txt
Documentation/lint-gitlink.perl
Documentation/lint-man-end-blurb.perl
Documentation/lint-man-section-order.perl
Documentation/technical/signature-format.txt
Makefile
builtin/blame.c
builtin/branch.c
builtin/checkout--worker.c
builtin/clone.c
builtin/commit-graph.c
builtin/fast-export.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/grep.c
builtin/index-pack.c
builtin/ls-files.c
builtin/merge.c
builtin/mktag.c
builtin/receive-pack.c
builtin/reflog.c
builtin/send-pack.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/tag.c
cache-tree.c
cache.h
command-list.txt
config.c
daemon.c
fmt-merge-msg.c
git-sh-i18n.sh
git-sh-setup.sh
gpg-interface.c
gpg-interface.h
log-tree.c
merge-ort.c
merge-recursive.c
mergetools/xxdiff
midx.c
object-file.c
object-store.h
object.c
pack-check.c
parallel-checkout.c
parse-options.c
parse-options.h
pkt-line.c
pkt-line.h
pretty.c
read-cache.c
ref-filter.c
ref-filter.h
refs.c
refs/files-backend.c
refs/packed-backend.c
refs/packed-backend.h
refs/ref-cache.c
refs/ref-cache.h
refs/refs-internal.h
remote-curl.c
send-pack.c
sequencer.c
strbuf.c
strbuf.h
streaming.c
submodule.c
t/.gitattributes
t/README
t/helper/test-oid-array.c
t/helper/test-oidtree.c
t/helper/test-parse-options.c
t/helper/test-prio-queue.c
t/lib-diff-data.sh [new file with mode: 0644]
t/lib-diff.sh
t/lib-diff/COPYING [deleted file]
t/lib-diff/README [deleted file]
t/lib-gpg.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/error-no-report.sh [new file with mode: 0644]
t/oid-info/oid
t/perf/config [new file with mode: 0644]
t/perf/perf-lib.sh
t/t0001-init.sh
t/t0002-gitfile.sh
t/t0003-attributes.sh
t/t0004-unwritable.sh
t/t0005-signals.sh
t/t0007-git-var.sh
t/t0008-ignores.sh
t/t0009-prio-queue.sh
t/t0010-racy-git.sh
t/t0013-sha1dc.sh
t/t0022-crlf-rename.sh
t/t0024-crlf-archive.sh
t/t0025-crlf-renormalize.sh
t/t0026-eol-config.sh
t/t0029-core-unsetenvvars.sh
t/t0040-parse-options.sh
t/t0052-simple-ipc.sh
t/t0055-beyond-symlinks.sh
t/t0061-run-command.sh
t/t0064-oid-array.sh
t/t0065-strcmp-offset.sh
t/t0066-dir-iterator.sh
t/t0067-parse_pathspec_file.sh
t/t0069-oidtree.sh
t/t0210-trace2-normal.sh
t/t0211-trace2-perf.sh
t/t0212-trace2-event.sh
t/t1000-read-tree-m-3way.sh
t/t1001-read-tree-m-2way.sh
t/t1003-read-tree-prefix.sh
t/t1006-cat-file.sh
t/t1009-read-tree-new-index.sh
t/t1010-mktree.sh
t/t1012-read-tree-df.sh
t/t1014-read-tree-confusing.sh
t/t1092-sparse-checkout-compatibility.sh
t/t1100-commit-tree-options.sh
t/t1305-config-include.sh
t/t1430-bad-ref-name.sh
t/t1450-fsck.sh
t/t1504-ceiling-dirs.sh
t/t1510-repo-setup.sh
t/t2002-checkout-cache-u.sh
t/t2003-checkout-cache-mkdir.sh
t/t2004-checkout-cache-temp.sh
t/t2005-checkout-index-symlinks.sh
t/t2050-git-dir-relative.sh
t/t2081-parallel-checkout-collisions.sh
t/t2200-add-update.sh
t/t2300-cd-to-toplevel.sh
t/t3000-ls-files-others.sh
t/t3001-ls-files-others-exclude.sh
t/t3002-ls-files-dashpath.sh
t/t3003-ls-files-exclude.sh
t/t3004-ls-files-basic.sh
t/t3005-ls-files-relative.sh
t/t3006-ls-files-long.sh
t/t3008-ls-files-lazy-init-name-hash.sh
t/t3020-ls-files-error-unmatch.sh
t/t3070-wildmatch.sh
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
t/t3102-ls-tree-wildcards.sh
t/t3103-ls-tree-misc.sh
t/t3205-branch-color.sh
t/t3211-peel-ref.sh
t/t3300-funny-names.sh
t/t3601-rm-pathspec-file.sh
t/t3700-add.sh
t/t3902-quoted.sh
t/t4002-diff-basic.sh
t/t4003-diff-rename-1.sh
t/t4005-diff-rename-2.sh
t/t4007-rename-3.sh
t/t4008-diff-break-rewrite.sh
t/t4009-diff-rename-4.sh
t/t4016-diff-quote.sh
t/t4019-diff-wserror.sh
t/t4022-diff-rewrite.sh
t/t4023-diff-rename-typechange.sh
t/t4025-hunk-header.sh
t/t4026-color.sh
t/t4034/cpp/expect
t/t4034/cpp/post
t/t4034/cpp/pre
t/t4202-log.sh
t/t4300-merge-tree.sh
t/t5526-fetch-submodules.sh
t/t5531-deep-submodule-push.sh
t/t5534-push-signed.sh
t/t5541-http-push-smart.sh
t/t5545-push-options.sh
t/t5572-pull-submodule.sh
t/t5580-unc-paths.sh
t/t5615-alternate-env.sh
t/t6200-fmt-merge-msg.sh
t/t6437-submodule-merge.sh
t/t7001-mv.sh
t/t7031-verify-tag-signed-ssh.sh [new file with mode: 0755]
t/t7101-reset-empty-subdirs.sh
t/t7104-reset-hard.sh
t/t7418-submodule-sparse-gitmodules.sh
t/t7510-signed-commit.sh
t/t7518-ident-corner-cases.sh
t/t7528-signed-commit-ssh.sh [new file with mode: 0755]
t/t7604-merge-custom-message.sh
t/t7811-grep-open.sh
t/t7813-grep-icase-iso.sh
t/t7816-grep-binary-pattern.sh
t/test-lib.sh
transport-helper.c
unpack-trees.c
userdiff.c

index 9022d4835545cbf40c9537efa8ca9a7678e42673..1c3771e7d72f690d07f4a4ce49275a3a74709dfd 100644 (file)
@@ -14,4 +14,5 @@ manpage-base-url.xsl
 SubmittingPatches.txt
 tmp-doc-diff/
 GIT-ASCIIDOCFLAGS
+/.build/
 /GIT-EXCLUDED-PROGRAMS
index 2021568cd5a5cf314a3c9f77164c3c94e025aa1e..ed656db2ae90c1f5e0498867ec170395f6898b1f 100644 (file)
@@ -226,6 +226,7 @@ endif
 
 ifneq ($(findstring $(MAKEFLAGS),s),s)
 ifndef V
+       QUIET           = @
        QUIET_ASCIIDOC  = @echo '   ' ASCIIDOC $@;
        QUIET_XMLTO     = @echo '   ' XMLTO $@;
        QUIET_DB2TEXI   = @echo '   ' DB2TEXI $@;
@@ -233,11 +234,15 @@ ifndef V
        QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
        QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
        QUIET_GEN       = @echo '   ' GEN $@;
-       QUIET_LINT      = @echo '   ' LINT $@;
        QUIET_STDERR    = 2> /dev/null
        QUIET_SUBDIR0   = +@subdir=
        QUIET_SUBDIR1   = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                          $(MAKE) $(PRINT_DIR) -C $$subdir
+
+       QUIET_LINT_GITLINK      = @echo '   ' LINT GITLINK $<;
+       QUIET_LINT_MANSEC       = @echo '   ' LINT MAN SEC $<;
+       QUIET_LINT_MANEND       = @echo '   ' LINT MAN END $<;
+
        export V
 endif
 endif
@@ -285,7 +290,7 @@ install-html: html
 ../GIT-VERSION-FILE: FORCE
        $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
 
-ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(filter-out lint-docs clean,$(MAKECMDGOALS)),)
 -include ../GIT-VERSION-FILE
 endif
 
@@ -344,6 +349,7 @@ GIT-ASCIIDOCFLAGS: FORCE
             fi
 
 clean:
+       $(RM) -rf .build/
        $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
        $(RM) *.texi *.texi+ *.texi++ git.info gitman.info
        $(RM) *.pdf
@@ -457,14 +463,61 @@ quick-install-html: require-htmlrepo
 print-man1:
        @for i in $(MAN1_TXT); do echo $$i; done
 
-lint-docs::
-       $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl \
+## Lint: Common
+.build:
+       $(QUIET)mkdir $@
+.build/lint-docs: | .build
+       $(QUIET)mkdir $@
+
+## Lint: gitlink
+.build/lint-docs/gitlink: | .build/lint-docs
+       $(QUIET)mkdir $@
+.build/lint-docs/gitlink/howto: | .build/lint-docs/gitlink
+       $(QUIET)mkdir $@
+.build/lint-docs/gitlink/config: | .build/lint-docs/gitlink
+       $(QUIET)mkdir $@
+LINT_DOCS_GITLINK = $(patsubst %.txt,.build/lint-docs/gitlink/%.ok,$(HOWTO_TXT) $(DOC_DEP_TXT))
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/howto
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/config
+$(LINT_DOCS_GITLINK): lint-gitlink.perl
+$(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt
+       $(QUIET_LINT_GITLINK)$(PERL_PATH) lint-gitlink.perl \
+               $< \
                $(HOWTO_TXT) $(DOC_DEP_TXT) \
                --section=1 $(MAN1_TXT) \
                --section=5 $(MAN5_TXT) \
-               --section=7 $(MAN7_TXT); \
-       $(PERL_PATH) lint-man-end-blurb.perl $(MAN_TXT); \
-       $(PERL_PATH) lint-man-section-order.perl $(MAN_TXT);
+               --section=7 $(MAN7_TXT) >$@
+.PHONY: lint-docs-gitlink
+lint-docs-gitlink: $(LINT_DOCS_GITLINK)
+
+## Lint: man-end-blurb
+.build/lint-docs/man-end-blurb: | .build/lint-docs
+       $(QUIET)mkdir $@
+LINT_DOCS_MAN_END_BLURB = $(patsubst %.txt,.build/lint-docs/man-end-blurb/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_END_BLURB): | .build/lint-docs/man-end-blurb
+$(LINT_DOCS_MAN_END_BLURB): lint-man-end-blurb.perl
+$(LINT_DOCS_MAN_END_BLURB): .build/lint-docs/man-end-blurb/%.ok: %.txt
+       $(QUIET_LINT_MANEND)$(PERL_PATH) lint-man-end-blurb.perl $< >$@
+.PHONY: lint-docs-man-end-blurb
+lint-docs-man-end-blurb: $(LINT_DOCS_MAN_END_BLURB)
+
+## Lint: man-section-order
+.build/lint-docs/man-section-order: | .build/lint-docs
+       $(QUIET)mkdir $@
+LINT_DOCS_MAN_SECTION_ORDER = $(patsubst %.txt,.build/lint-docs/man-section-order/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_SECTION_ORDER): | .build/lint-docs/man-section-order
+$(LINT_DOCS_MAN_SECTION_ORDER): lint-man-section-order.perl
+$(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt
+       $(QUIET_LINT_MANSEC)$(PERL_PATH) lint-man-section-order.perl $< >$@
+.PHONY: lint-docs-man-section-order
+lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER)
+
+## Lint: list of targets above
+.PHONY: lint-docs
+lint-docs: lint-docs-gitlink
+lint-docs: lint-docs-man-end-blurb
+lint-docs: lint-docs-man-section-order
 
 ifeq ($(wildcard po/Makefile),po/Makefile)
 doc-l10n install-l10n::
index c85385dc03228450cb7fb6d306252038a91b47e6..fdf7992193e306423d4ae72fd7590aba993400cc 100644 (file)
@@ -74,6 +74,11 @@ UI, Workflows & Features
  * "git repack" has been taught to generate multi-pack reachability
    bitmaps.
 
+ * "git fsck" has been taught to report mismatch between expected and
+   actual types of an object better.
+
+ * Use ssh public crypto for object and push-cert signing.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -173,6 +178,11 @@ Performance, Internal Implementation, Development Support etc.
  * Prevent "make sparse" from running for the source files that
    haven't been modified.
 
+ * The codepath to write a new version of .midx multi-pack index files
+   has learned to release the mmaped memory holding the current
+   version of .midx before removing them from the disk, as some
+   platforms do not allow removal of a file that still has mapping.
+
 
 Fixes since v2.33
 -----------------
@@ -334,6 +344,30 @@ Fixes since v2.33
    to be grabbed, which can cause the build&test to fail.  Tighten it.
    (merge 7491ef6198 js/windows-ci-path-fix later to maint).
 
+ * Avoid performance measurements from getting ruined by gc and other
+   housekeeping pauses interfering in the middle.
+   (merge be79131a53 rs/disable-gc-during-perf-tests later to maint).
+
+ * Stop "git add --dry-run" from creating new blob and tree objects.
+   (merge e578d0311d rs/add-dry-run-without-objects later to maint).
+
+ * "git commit" gave duplicated error message when the object store
+   was unwritable, which has been corrected.
+   (merge 4ef91a2d79 ab/fix-commit-error-message-upon-unwritable-object-store later to maint).
+
+ * Recent sparse-index addition, namely any use of index_name_pos(),
+   can expand sparse index entries and breaks any code that walks
+   cache-tree or existing index entries.  One such instance of such a
+   breakage has been corrected.
+
+ * The xxdiff difftool backend can exit with status 128, which the
+   difftool-helper that launches the backend takes as a significant
+   failure, when it is not significant at all.  Work it around.
+   (merge 571f4348dd da/mergetools-special-case-xxdiff-exit-128 later to maint).
+
+ * Improve test framework around unwritable directories.
+   (merge 5d22e18965 ab/test-cleanly-recreate-trash-directory later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge f188160be9 ab/bundle-remove-verbose-option later to maint).
    (merge 8c6b4332b4 rs/close-pack-leakfix later to maint).
@@ -343,3 +377,7 @@ Fixes since v2.33
    (merge 100c2da2d3 rs/p3400-lose-tac later to maint).
    (merge 76f3b69896 tb/aggregate-ignore-leading-whitespaces later to maint).
    (merge 6e4fd8bfcd tz/doc-link-to-bundle-format-fix later to maint).
+   (merge f6c013dfa1 jc/doc-commit-header-continuation-line later to maint).
+   (merge ec9a37d69b ab/pkt-line-cleanup later to maint).
+   (merge 8650c6298c ab/fix-make-lint-docs later to maint).
+   (merge 1c720357ce ab/test-lib-diff-cleanup later to maint).
index 0c0e6b859f1ed28753bd9d1bea9d2969388bd39f..1167e88e341bc38c3f8b5a60ea7b476cd561e340 100644 (file)
@@ -304,7 +304,7 @@ path relative to Git's "runtime prefix", i.e. relative to the location
 where Git itself was installed. For example, `%(prefix)/bin/` refers to
 the directory in which the Git executable itself lives. If Git was
 compiled without runtime prefix support, the compiled-in prefix will be
-subsituted instead. In the unlikely event that a literal path needs to
+substituted instead. In the unlikely event that a literal path needs to
 be specified that should _not_ be expanded, it needs to be prefixed by
 `./`, like so: `./%(prefix)/bin`.
 
index dd2d2e0d84e7cdd657c4b196a9a7a84829081de8..6e817f60476a8c597fcef08f10918d8f225c6715 100644 (file)
@@ -17,11 +17,9 @@ date settings, starting and ending with a color, the dates should be
 set from oldest to newest. The metadata will be colored with the
 specified colors if the line was introduced before the given
 timestamp, overwriting older timestamped colors.
-
 +
 Instead of an absolute timestamp relative timestamps work as well,
 e.g. `2.weeks.ago` is valid to address anything older than 2 weeks.
-
 +
 It defaults to `blue,12 month ago,white,1 month ago,red`, which
 colors everything older than one year blue, recent changes between
index d94025cb3684d8e20f2875716cc16dfad47cf6ff..4f30c7dbdd9f88480ab5ba54ba71f016c6571114 100644 (file)
@@ -11,13 +11,13 @@ gpg.program::
 
 gpg.format::
        Specifies which key format to use when signing with `--gpg-sign`.
-       Default is "openpgp" and another possible value is "x509".
+       Default is "openpgp". Other possible values are "x509", "ssh".
 
 gpg.<format>.program::
        Use this to customize the program used for the signing format you
        chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
        be used as a legacy synonym for `gpg.openpgp.program`. The default
-       value for `gpg.x509.program` is "gpgsm".
+       value for `gpg.x509.program` is "gpgsm" and `gpg.ssh.program` is "ssh-keygen".
 
 gpg.minTrustLevel::
        Specifies a minimum trust level for signature verification.  If
@@ -33,3 +33,42 @@ gpg.minTrustLevel::
 * `marginal`
 * `fully`
 * `ultimate`
+
+gpg.ssh.defaultKeyCommand:
+       This command that will be run when user.signingkey is not set and a ssh
+       signature is requested. On successful exit a valid ssh public key is
+       expected in the first line of its output. To automatically use the first
+       available key from your ssh-agent set this to "ssh-add -L".
+
+gpg.ssh.allowedSignersFile::
+       A file containing ssh public keys which you are willing to trust.
+       The file consists of one or more lines of principals followed by an ssh
+       public key.
+       e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1...
+       See ssh-keygen(1) "ALLOWED SIGNERS" for details.
+       The principal is only used to identify the key and is available when
+       verifying a signature.
++
+SSH has no concept of trust levels like gpg does. To be able to differentiate
+between valid signatures and trusted signatures the trust level of a signature
+verification is set to `fully` when the public key is present in the allowedSignersFile.
+Otherwise the trust level is `undefined` and git verify-commit/tag will fail.
++
+This file can be set to a location outside of the repository and every developer
+maintains their own trust store. A central repository server could generate this
+file automatically from ssh keys with push access to verify the code against.
+In a corporate setting this file is probably generated at a global location
+from automation that already handles developer ssh keys.
++
+A repository that only allows signed commits can store the file
+in the repository itself using a path relative to the top-level of the working tree.
+This way only committers with an already valid key can add or change keys in the keyring.
++
+Using a SSH CA key with the cert-authority option
+(see ssh-keygen(1) "CERTIFICATES") is also valid.
+
+gpg.ssh.revocationFile::
+       Either a SSH KRL or a list of revoked public keys (without the principal prefix).
+       See ssh-keygen(1) for details.
+       If a public key is found in this file then it will always be treated
+       as having trust level "never" and signatures will show as invalid.
index 59aec7c3aed32aac0a8d7e015c0602705e4ccc6d..ad78dce9ecbfc6faf633be133f8f88218844bd9a 100644 (file)
@@ -36,3 +36,10 @@ user.signingKey::
        commit, you can override the default selection with this variable.
        This option is passed unchanged to gpg's --local-user parameter,
        so you may specify a key using any method that gpg supports.
+       If gpg.format is set to "ssh" this can contain the literal ssh public
+       key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
+       corresponds to the private key used for signing. The private key
+       needs to be available via ssh-agent. Alternatively it can be set to
+       a file containing a private key directly. If not set git will call
+       gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
+       key available.
index 9f8172828d70142de652bb90297e912a8e0805c7..bc4e76a7834a59a7ecfd9880d5a51bf794ca1b6c 100644 (file)
@@ -93,12 +93,19 @@ BACKEND EXTRA OPTIONS
 
 zip
 ~~~
--0::
-       Store the files instead of deflating them.
--9::
-       Highest and slowest compression level.  You can specify any
-       number from 1 to 9 to adjust compression speed and ratio.
+-<digit>::
+       Specify compression level.  Larger values allow the command
+       to spend more time to compress to smaller size.  Supported
+       values are from `-0` (store-only) to `-9` (best ratio).
+       Default is `-6` if not given.
 
+tar
+~~~
+-<number>::
+       Specify compression level. The value will be passed to the
+       compression command configured in `tar.<format>.command`. See
+       manual page of the configured command for the list of supported
+       levels and the default level if this option isn't specified.
 
 CONFIGURATION
 -------------
index d63c65e67d825d6aac6181de79689557bd76a019..281c5f8caefdafbcd6c41955cb4d54aa85a8be82 100644 (file)
@@ -42,7 +42,7 @@ OPTIONS
 --version::
        Prints the Git suite version that the 'git' program came from.
 +
-This option is internaly converted to `git version ...` and accepts
+This option is internally converted to `git version ...` and accepts
 the same options as the linkgit:git-version[1] command. If `--help` is
 also given, it takes precedence over `--version`.
 
index b22a367844a219a4206d5a96371eb60920268385..1c61dd9512b9e3db1768416ede49686020d38662 100755 (executable)
@@ -5,11 +5,12 @@ use warnings;
 
 # Parse arguments, a simple state machine for input like:
 #
-# howto/*.txt config/*.txt --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
+# <file-to-check.txt> <valid-files-to-link-to> --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
 my %TXT;
 my %SECTION;
 my $section;
 my $lint_these = 0;
+my $to_check = shift @ARGV;
 for my $arg (@ARGV) {
        if (my ($sec) = $arg =~ /^--section=(\d+)$/s) {
                $section = $sec;
@@ -30,13 +31,14 @@ sub report {
        my ($pos, $line, $target, $msg) = @_;
        substr($line, $pos) = "' <-- HERE";
        $line =~ s/^\s+//;
-       print "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
-       print "$ARGV:$.:\t'$line\n";
+       print STDERR "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
+       print STDERR "$ARGV:$.:\t'$line\n";
        $exit_code = 1;
 }
 
 @ARGV = sort values %TXT;
-die "BUG: Nothing to process!" unless @ARGV;
+die "BUG: No list of valid linkgit:* files given" unless @ARGV;
+@ARGV = $to_check;
 while (<>) {
        my $line = $_;
        while ($line =~ m/linkgit:((.*?)\[(\d)\])/g) {
index d69312e5db585ca98dc05d892fcccb0d2b7446cb..6bdb13ad9fdb36b8c8f5244fa66fb29c0f619cc7 100755 (executable)
@@ -6,7 +6,7 @@ use warnings;
 my $exit_code = 0;
 sub report {
        my ($target, $msg) = @_;
-       print "error: $target: $msg\n";
+       print STDERR "error: $target: $msg\n";
        $exit_code = 1;
 }
 
index b05f9156dd9800466d79d3178f388b7fc26a9397..425377dfeb7fc0856773fd5eaaac7dd3b7b1c999 100755 (executable)
@@ -46,7 +46,7 @@ my $SECTION_RX = do {
 my $exit_code = 0;
 sub report {
        my ($msg) = @_;
-       print "$ARGV:$.: $msg\n";
+       print STDERR "$ARGV:$.: $msg\n";
        $exit_code = 1;
 }
 
index 2c9406a56a881541aba389b4a576fee80d6e734d..166721be6f648cdac8d0fc960e121bafc02cd226 100644 (file)
@@ -13,6 +13,22 @@ Signatures always begin with `-----BEGIN PGP SIGNATURE-----`
 and end with `-----END PGP SIGNATURE-----`, unless gpg is told to
 produce RFC1991 signatures which use `MESSAGE` instead of `SIGNATURE`.
 
+Signatures sometimes appear as a part of the normal payload
+(e.g. a signed tag has the signature block appended after the payload
+that the signature applies to), and sometimes appear in the value of
+an object header (e.g. a merge commit that merged a signed tag would
+have the entire tag contents on its "mergetag" header).  In the case
+of the latter, the usual multi-line formatting rule for object
+headers applies.  I.e. the second and subsequent lines are prefixed
+with a SP to signal that the line is continued from the previous
+line.
+
+This is even true for an originally empty line.  In the following
+examples, the end of line that ends with a whitespace letter is
+highlighted with a `$` sign; if you are trying to recreate these
+example by hand, do not cut and paste them---they are there
+primarily to highlight extra whitespace at the end of some lines.
+
 The signed payload and the way the signature is embedded depends
 on the type of the object resp. transaction.
 
@@ -78,7 +94,7 @@ author A U Thor <author@example.com> 1465981137 +0000
 committer C O Mitter <committer@example.com> 1465981137 +0000
 gpgsig -----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1
-
+ $
  iQEcBAABAgAGBQJXYRjRAAoJEGEJLoW3InGJ3IwIAIY4SA6GxY3BjL60YyvsJPh/
  HRCJwH+w7wt3Yc/9/bW2F+gF72kdHOOs2jfv+OZhq0q4OAN6fvVSczISY/82LpS7
  DVdMQj2/YcHDT4xrDNBnXnviDO9G7am/9OE77kEbXrp7QPxvhjkicHNwy2rEflAA
@@ -128,13 +144,13 @@ mergetag object 04b871796dc0420f8e7561a895b52484b701d51a
  type commit
  tag signedtag
  tagger C O Mitter <committer@example.com> 1465981006 +0000
-
+ $
  signed tag
-
+ $
  signed tag message body
  -----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1
-
+ $
  iQEcBAABAgAGBQJXYRhOAAoJEGEJLoW3InGJklkIAIcnhL7RwEb/+QeX9enkXhxn
  rxfdqrvWd1K80sl2TOt8Bg/NYwrUBw/RWJ+sg/hhHp4WtvE1HDGHlkEz3y11Lkuh
  8tSxS3qKTxXUGozyPGuE90sJfExhZlW4knIQ1wt/yWqM+33E9pN4hzPqLwyrdods
index 381bed2c1d23b4bcc9d739b6fc34cc2cf80d87f3..12be39ac49789898227d025c5249ce5d2842ae47 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2901,7 +2901,7 @@ check-sha1:: t/helper/test-tool$X
 
 SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
 
-$(SP_OBJ): %.sp: %.c %.o GIT-CFLAGS
+$(SP_OBJ): %.sp: %.c %.o
        $(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
                -Wsparse-error \
                $(SPARSE_FLAGS) $(SP_EXTRA_FLAGS) $< && \
index 1c31a996403903f94235ca3ea488c4edf5e11ef0..f9ee3f8c688d472804f63d0702e92747ab39067f 100644 (file)
@@ -913,6 +913,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+               case PARSE_OPT_NON_OPTION:
+               case PARSE_OPT_UNKNOWN:
+                       break;
                case PARSE_OPT_HELP:
                case PARSE_OPT_ERROR:
                        exit(129);
index 0b7ed82654af1fd52170e7a27fcc3f69fd2f4825..7a1d1eeb070c5bd3fe2f01eff40796b15f889632 100644 (file)
@@ -407,7 +407,8 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
        return strbuf_detach(&fmt, NULL);
 }
 
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting,
+                          struct ref_format *format, struct string_list *output)
 {
        int i;
        struct ref_array array;
@@ -449,7 +450,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
                if (column_active(colopts)) {
                        assert(!filter->verbose && "--column and --verbose are incompatible");
                         /* format to a string_list to let print_columns() do its job */
-                       string_list_append(&output, out.buf);
+                       string_list_append(output, out.buf);
                } else {
                        fwrite(out.buf, 1, out.len, stdout);
                        putchar('\n');
@@ -753,9 +754,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
                ref_sorting_set_sort_flags_all(
                        sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
-               print_ref_list(&filter, sorting, &format);
+               print_ref_list(&filter, sorting, &format, &output);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
+               ref_sorting_release(sorting);
                return 0;
        } else if (edit_description) {
                const char *branch_name;
index fb9fd13b73c4f8af3f5c9e745ab3e20f30a5f77f..ede7dc32a43c01a78a0074e1bddd5ba18b487568 100644 (file)
@@ -82,8 +82,8 @@ static void worker_loop(struct checkout *state)
        size_t i, nr = 0, alloc = 0;
 
        while (1) {
-               int len = packet_read(0, NULL, NULL, packet_buffer,
-                                     sizeof(packet_buffer), 0);
+               int len = packet_read(0, packet_buffer, sizeof(packet_buffer),
+                                     0);
 
                if (len < 0)
                        BUG("packet_read() returned negative value");
index 559acf9e03685e5b7f1fa4936685cc6570d716fc..fb377b27657c4048a04c85ddb82e15a950db5a7d 100644 (file)
@@ -1040,8 +1040,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
                INIT_DB_QUIET);
 
-       if (real_git_dir)
+       if (real_git_dir) {
+               free((char *)git_dir);
                git_dir = real_git_dir;
+       }
 
        /*
         * additional config can be injected with -c, make sure it's included
index 3c3de3a156f1e2afc2b4952cf38e8d1136ad4716..ab8e5cd59af3ea03bc100279a24dc8aec4478260 100644 (file)
@@ -172,8 +172,8 @@ static int write_option_max_new_filters(const struct option *opt,
                const char *s;
                *to = strtol(arg, (char **)&s, 10);
                if (*s)
-                       return error(_("%s expects a numerical value"),
-                                    optname(opt, opt->flags));
+                       return error(_("option `%s' expects a numerical value"),
+                                    "max-new-filters");
        }
        return 0;
 }
index 95e8e89e81f0ec5ac2993ea5dc0cd8d4ce095e9d..8e2caf7281970cfb1bd3cf3bad07b488e61b6f07 100644 (file)
@@ -312,7 +312,7 @@ static void export_blob(const struct object_id *oid)
                if (!buf)
                        die("could not read blob %s", oid_to_hex(oid));
                if (check_object_signature(the_repository, oid, buf, size,
-                                          type_name(type)) < 0)
+                                          type_name(type), NULL) < 0)
                        die("oid mismatch in blob %s", oid_to_hex(oid));
                object = parse_object_buffer(the_repository, oid, type,
                                             size, buf, &eaten);
index 642b4b888fbe88eaa3d1b28a7bd6eaed092540f1..16a2c7d57ca40a891f6fac7e5674ad8a045536e4 100644 (file)
@@ -96,6 +96,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        ref_array_clear(&array);
        free_commit_list(filter.with_commit);
        free_commit_list(filter.no_commit);
-       UNLEAK(sorting);
+       ref_sorting_release(sorting);
        return 0;
 }
index b42b6fe21f7347b546d53e8f6a250e50659befe3..30a516da29eab319f4b5b404c6b397b4637668d5 100644 (file)
@@ -593,18 +593,43 @@ static void get_default_heads(void)
        }
 }
 
+struct for_each_loose_cb
+{
+       struct progress *progress;
+       struct strbuf obj_type;
+};
+
 static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 {
+       struct for_each_loose_cb *cb_data = data;
        struct object *obj;
-       enum object_type type;
+       enum object_type type = OBJ_NONE;
        unsigned long size;
        void *contents;
        int eaten;
+       struct object_info oi = OBJECT_INFO_INIT;
+       struct object_id real_oid = *null_oid();
+       int err = 0;
 
-       if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+       strbuf_reset(&cb_data->obj_type);
+       oi.type_name = &cb_data->obj_type;
+       oi.sizep = &size;
+       oi.typep = &type;
+
+       if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) {
+               if (contents && !oideq(&real_oid, oid))
+                       err = error(_("%s: hash-path mismatch, found at: %s"),
+                                   oid_to_hex(&real_oid), path);
+               else
+                       err = error(_("%s: object corrupt or missing: %s"),
+                                   oid_to_hex(oid), path);
+       }
+       if (type != OBJ_NONE && type < 0)
+               err = error(_("%s: object is of unknown type '%s': %s"),
+                           oid_to_hex(&real_oid), cb_data->obj_type.buf,
+                           path);
+       if (err < 0) {
                errors_found |= ERROR_OBJECT;
-               error(_("%s: object corrupt or missing: %s"),
-                     oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
 
@@ -640,8 +665,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
        return 0;
 }
 
-static int fsck_subdir(unsigned int nr, const char *path, void *progress)
+static int fsck_subdir(unsigned int nr, const char *path, void *data)
 {
+       struct for_each_loose_cb *cb_data = data;
+       struct progress *progress = cb_data->progress;
        display_progress(progress, nr + 1);
        return 0;
 }
@@ -649,6 +676,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress)
 static void fsck_object_dir(const char *path)
 {
        struct progress *progress = NULL;
+       struct for_each_loose_cb cb_data = {
+               .obj_type = STRBUF_INIT,
+               .progress = progress,
+       };
 
        if (verbose)
                fprintf_ln(stderr, _("Checking object directory"));
@@ -657,9 +688,10 @@ static void fsck_object_dir(const char *path)
                progress = start_progress(_("Checking object directories"), 256);
 
        for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
-                                     progress);
+                                     &cb_data);
        display_progress(progress, 256);
        stop_progress(&progress);
+       strbuf_release(&cb_data.obj_type);
 }
 
 static int fsck_head_link(const char *head_ref_name,
index 8af5249a7bb118333da4eede706d3fd2de8a287b..9e34a820ad4d837937a0ecb2bc712de972506b50 100644 (file)
@@ -199,8 +199,8 @@ static void *run(void *arg)
                grep_source_clear_data(&w->source);
                work_done(w);
        }
-       free_grep_patterns(arg);
-       free(arg);
+       free_grep_patterns(opt);
+       free(opt);
 
        return (void*) (intptr_t) hit;
 }
@@ -401,7 +401,7 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len)
 
        if (len == 1 && *(const char *)data == '\0')
                return;
-       string_list_append(path_list, xstrndup(data, len));
+       string_list_append_nodup(path_list, xstrndup(data, len));
 }
 
 static void run_pager(struct grep_opt *opt, const char *prefix)
@@ -839,7 +839,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        struct grep_opt opt;
        struct object_array list = OBJECT_ARRAY_INIT;
        struct pathspec pathspec;
-       struct string_list path_list = STRING_LIST_INIT_NODUP;
+       struct string_list path_list = STRING_LIST_INIT_DUP;
        int i;
        int dummy;
        int use_index = 1;
@@ -1159,8 +1159,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        strbuf_addf(&buf, "+/%s%s",
                                        strcmp("less", pager) ? "" : "*",
                                        opt.pattern_list->pattern);
-                       string_list_append(&path_list,
-                                          strbuf_detach(&buf, NULL));
+                       string_list_append_nodup(&path_list,
+                                                strbuf_detach(&buf, NULL));
                }
        }
 
@@ -1195,7 +1195,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
        clear_pathspec(&pathspec);
+       string_list_clear(&path_list, 0);
        free_grep_patterns(&opt);
+       object_array_clear(&list);
        free_repos();
        return !hit;
 }
index 7ce69c087ecd1cc925780b6668a18d0ab1cf00d8..15ae406e6b7e221f720070f3abbea951e2b914a9 100644 (file)
@@ -1415,7 +1415,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
 
                if (check_object_signature(the_repository, &d->oid,
                                           data, size,
-                                          type_name(type)))
+                                          type_name(type), NULL))
                        die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
 
                /*
index a2000ed6bf2578ae248be18cb00623eb2b8d1fae..031fef1bcaa9d08bdad41b56be6e81c18eae485d 100644 (file)
@@ -672,6 +672,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                         N_("suppress duplicate entries")),
                OPT_END()
        };
+       int ret = 0;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(ls_files_usage, builtin_ls_files_options);
@@ -775,16 +776,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        if (show_resolve_undo)
                show_ru_info(the_repository->index);
 
-       if (ps_matched) {
-               int bad;
-               bad = report_path_error(ps_matched, &pathspec);
-               if (bad)
-                       fprintf(stderr, "Did you forget to 'git add'?\n");
-
-               return bad ? 1 : 0;
+       if (ps_matched && report_path_error(ps_matched, &pathspec)) {
+               fprintf(stderr, "Did you forget to 'git add'?\n");
+               ret = 1;
        }
 
+       string_list_clear(&exclude_list, 0);
        dir_clear(&dir);
        free(max_prefix);
-       return 0;
+       return ret;
 }
index cc4a910c69bb17769f88076a6f4459d399d5a996..ea3112e0c0b3acc9f4439430fb19e596a9fc9093 100644 (file)
@@ -1578,6 +1578,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
                remove_merge_branch_state(the_repository);
+               strbuf_release(&msg);
                goto done;
        } else if (!remoteheads->next && common->next)
                ;
@@ -1748,6 +1749,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                ret = suggest_conflicts();
 
 done:
+       strbuf_release(&buf);
        free(branch_to_free);
        return ret;
 }
index dddcccdd368328261db51d47b3b5a7b444fbaca5..3b2dbbb37e6fe1afc11af79aae774ca526496413 100644 (file)
@@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
 
        repl = lookup_replace_object(the_repository, tagged_oid);
        ret = check_object_signature(the_repository, repl,
-                                    buffer, size, type_name(*tagged_type));
+                                    buffer, size, type_name(*tagged_type),
+                                    NULL);
        free(buffer);
 
        return ret;
index 25cc0c907e133650ed55d173353155d35df33f89..49b846d960522ad1a5f29f7394bca4fa24e5b622 100644 (file)
@@ -132,6 +132,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
 {
        int status = parse_hide_refs_config(var, value, "receive");
 
+       if (status)
+               return status;
+
+       status = git_gpg_config(var, value, NULL);
        if (status)
                return status;
 
index bd4c669918d3d0884c015e9e2650bb65ea507d7a..175c83e7cc2804ae841e72ea487cb64fe3d537ae 100644 (file)
@@ -653,6 +653,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                                        should_expire_reflog_ent,
                                        reflog_expiry_cleanup,
                                        &cb);
+               free(ref);
        }
        return status;
 }
index 89321423125f94574150258a3d9f6d1a34bf0c10..69c432ef1a6272c12f6c71558c973eecd3796c90 100644 (file)
@@ -87,6 +87,10 @@ static void print_helper_status(struct ref *ref)
                        break;
 
                case REF_STATUS_EXPECTING_REPORT:
+                       res = "error";
+                       msg = "expecting report";
+                       break;
+
                default:
                        continue;
                }
index 3e7ab1ca821584cce2a71e8d7fa5e18648a1a5a6..e7f7af5de3ff005150680d19761828c7b9984ecf 100644 (file)
@@ -374,6 +374,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
        for (;;) {
                switch (parse_options_step(&ctx, options, shortlog_usage)) {
+               case PARSE_OPT_NON_OPTION:
+               case PARSE_OPT_UNKNOWN:
+                       break;
                case PARSE_OPT_HELP:
                case PARSE_OPT_ERROR:
                        exit(129);
index 6298cbdd4e5f23aba5e47b5863737379bd60135e..e630f0c730eae7235191957cff3fb814dac33f62 100644 (file)
@@ -2999,7 +2999,7 @@ struct add_data {
 };
 #define ADD_DATA_INIT { .depth = -1 }
 
-static void show_fetch_remotes(FILE *output, const char *git_dir_path)
+static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
 {
        struct child_process cp_remote = CHILD_PROCESS_INIT;
        struct strbuf sb_remote_out = STRBUF_INIT;
@@ -3015,7 +3015,7 @@ static void show_fetch_remotes(FILE *output, const char *git_dir_path)
                while ((next_line = strchr(line, '\n')) != NULL) {
                        size_t len = next_line - line;
                        if (strip_suffix_mem(line, &len, " (fetch)"))
-                               fprintf(output, "  %.*s\n", (int)len, line);
+                               strbuf_addf(msg, "  %.*s\n", (int)len, line);
                        line = next_line + 1;
                }
        }
@@ -3047,19 +3047,27 @@ static int add_submodule(const struct add_data *add_data)
 
                if (is_directory(submod_gitdir_path)) {
                        if (!add_data->force) {
-                               fprintf(stderr, _("A git directory for '%s' is found "
-                                                 "locally with remote(s):"),
-                                       add_data->sm_name);
-                               show_fetch_remotes(stderr, submod_gitdir_path);
+                               struct strbuf msg = STRBUF_INIT;
+                               char *die_msg;
+
+                               strbuf_addf(&msg, _("A git directory for '%s' is found "
+                                                   "locally with remote(s):\n"),
+                                           add_data->sm_name);
+
+                               append_fetch_remotes(&msg, submod_gitdir_path);
                                free(submod_gitdir_path);
-                               die(_("If you want to reuse this local git "
-                                     "directory instead of cloning again from\n"
-                                     "  %s\n"
-                                     "use the '--force' option. If the local git "
-                                     "directory is not the correct repo\n"
-                                     "or if you are unsure what this means, choose "
-                                     "another name with the '--name' option.\n"),
-                                   add_data->realrepo);
+
+                               strbuf_addf(&msg, _("If you want to reuse this local git "
+                                                   "directory instead of cloning again from\n"
+                                                   "  %s\n"
+                                                   "use the '--force' option. If the local git "
+                                                   "directory is not the correct repo\n"
+                                                   "or you are unsure what this means choose "
+                                                   "another name with the '--name' option."),
+                                           add_data->realrepo);
+
+                               die_msg = strbuf_detach(&msg, NULL);
+                               die("%s", die_msg);
                        } else {
                                printf(_("Reactivating local git directory for "
                                         "submodule '%s'\n"), add_data->sm_name);
@@ -3220,6 +3228,7 @@ static void die_on_index_match(const char *path, int force)
                }
                free(ps_matched);
        }
+       clear_pathspec(&ps);
 }
 
 static void die_on_repo_without_commits(const char *path)
@@ -3231,6 +3240,7 @@ static void die_on_repo_without_commits(const char *path)
                if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
                        die(_("'%s' does not have a commit checked out"), path);
        }
+       strbuf_release(&sb);
 }
 
 static int module_add(int argc, const char **argv, const char *prefix)
index 6535ed27ee9e6b419c67349dd470af02265586b3..6fe646710d6ffbf242dda3bf7a5c27742acc0ef3 100644 (file)
@@ -432,7 +432,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        int annotate = 0, force = 0;
        int cmdmode = 0, create_tag_object = 0;
        const char *msgfile = NULL, *keyid = NULL;
-       struct msg_arg msg = { 0, STRBUF_INIT };
+       struct msg_arg msg = { .buf = STRBUF_INIT };
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
        struct ref_filter filter;
@@ -482,6 +482,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END()
        };
+       int ret = 0;
 
        setup_ref_filter_porcelain_msg();
 
@@ -529,7 +530,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
        filter.ignore_case = icase;
        if (cmdmode == 'l') {
-               int ret;
                if (column_active(colopts)) {
                        struct column_options copts;
                        memset(&copts, 0, sizeof(copts));
@@ -540,7 +540,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                ret = list_tags(&filter, sorting, &format);
                if (column_active(colopts))
                        stop_column_filter();
-               return ret;
+               goto cleanup;
        }
        if (filter.lines != -1)
                die(_("-n option is only allowed in list mode"));
@@ -552,12 +552,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                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"));
-       if (cmdmode == 'd')
-               return delete_tags(argv);
+       if (cmdmode == 'd') {
+               ret = delete_tags(argv);
+               goto cleanup;
+       }
        if (cmdmode == 'v') {
                if (format.format && verify_ref_format(&format))
                        usage_with_options(git_tag_usage, options);
-               return for_each_tag_name(argv, verify_tag, &format);
+               ret = for_each_tag_name(argv, verify_tag, &format);
+               goto cleanup;
        }
 
        if (msg.given || msgfile) {
@@ -626,10 +629,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                printf(_("Updated tag '%s' (was %s)\n"), tag,
                       find_unique_abbrev(&prev, DEFAULT_ABBREV));
 
-       UNLEAK(buf);
-       UNLEAK(ref);
-       UNLEAK(reflog_msg);
-       UNLEAK(msg);
-       UNLEAK(err);
-       return 0;
+cleanup:
+       ref_sorting_release(sorting);
+       strbuf_release(&buf);
+       strbuf_release(&ref);
+       strbuf_release(&reflog_msg);
+       strbuf_release(&msg.buf);
+       strbuf_release(&err);
+       return ret;
 }
index 90919f9e3454791e2132236a2319b5f51c9c1d41..79d168192d74b829f4cd80cf45407074032a9593 100644 (file)
@@ -440,8 +440,9 @@ static int update_one(struct cache_tree *it,
        } else if (dryrun) {
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
                                 tree_type, &it->oid);
-       } else if (write_object_file(buffer.buf, buffer.len, tree_type,
-                                    &it->oid)) {
+       } else if (write_object_file_flags(buffer.buf, buffer.len, tree_type,
+                                          &it->oid, flags & WRITE_TREE_SILENT
+                                          ? HASH_SILENT : 0)) {
                strbuf_release(&buffer);
                return -1;
        }
@@ -826,10 +827,17 @@ static void verify_one_sparse(struct repository *r,
                    path->buf);
 }
 
-static void verify_one(struct repository *r,
-                      struct index_state *istate,
-                      struct cache_tree *it,
-                      struct strbuf *path)
+/*
+ * Returns:
+ *  0 - Verification completed.
+ *  1 - Restart verification - a call to ensure_full_index() freed the cache
+ *      tree that is being verified and verification needs to be restarted from
+ *      the new toplevel cache tree.
+ */
+static int verify_one(struct repository *r,
+                     struct index_state *istate,
+                     struct cache_tree *it,
+                     struct strbuf *path)
 {
        int i, pos, len = path->len;
        struct strbuf tree_buf = STRBUF_INIT;
@@ -837,21 +845,30 @@ static void verify_one(struct repository *r,
 
        for (i = 0; i < it->subtree_nr; i++) {
                strbuf_addf(path, "%s/", it->down[i]->name);
-               verify_one(r, istate, it->down[i]->cache_tree, path);
+               if (verify_one(r, istate, it->down[i]->cache_tree, path))
+                       return 1;
                strbuf_setlen(path, len);
        }
 
        if (it->entry_count < 0 ||
            /* no verification on tests (t7003) that replace trees */
            lookup_replace_object(r, &it->oid) != &it->oid)
-               return;
+               return 0;
 
        if (path->len) {
+               /*
+                * If the index is sparse and the cache tree is not
+                * index_name_pos() may trigger ensure_full_index() which will
+                * free the tree that is being verified.
+                */
+               int is_sparse = istate->sparse_index;
                pos = index_name_pos(istate, path->buf, path->len);
+               if (is_sparse && !istate->sparse_index)
+                       return 1;
 
                if (pos >= 0) {
                        verify_one_sparse(r, istate, it, path, pos);
-                       return;
+                       return 0;
                }
 
                pos = -pos - 1;
@@ -899,6 +916,7 @@ static void verify_one(struct repository *r,
                    oid_to_hex(&new_oid), oid_to_hex(&it->oid));
        strbuf_setlen(path, len);
        strbuf_release(&tree_buf);
+       return 0;
 }
 
 void cache_tree_verify(struct repository *r, struct index_state *istate)
@@ -907,6 +925,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate)
 
        if (!istate->cache_tree)
                return;
-       verify_one(r, istate, istate->cache_tree, &path);
+       if (verify_one(r, istate, istate->cache_tree, &path)) {
+               strbuf_reset(&path);
+               if (verify_one(r, istate, istate->cache_tree, &path))
+                       BUG("ensure_full_index() called twice while verifying cache tree");
+       }
        strbuf_release(&path);
 }
diff --git a/cache.h b/cache.h
index d092820c94337064b49de930e01995b14b767c11..eba12487b99caae71cbd4367e609e156ea9867ff 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -887,6 +887,7 @@ int ie_modified(struct index_state *, const struct cache_entry *, struct stat *,
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
 #define HASH_RENORMALIZE  4
+#define HASH_SILENT 8
 int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 
@@ -1268,11 +1269,50 @@ char *xdg_cache_home(const char *filename);
 
 int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
-int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
-int parse_loose_header(const char *hdr, unsigned long *sizep);
+
+/**
+ * unpack_loose_header() initializes the data stream needed to unpack
+ * a loose object header.
+ *
+ * Returns:
+ *
+ * - ULHR_OK on success
+ * - ULHR_BAD on error
+ * - ULHR_TOO_LONG if the header was too long
+ *
+ * It will only parse up to MAX_HEADER_LEN bytes unless an optional
+ * "hdrbuf" argument is non-NULL. This is intended for use with
+ * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
+ * reporting. The full header will be extracted to "hdrbuf" for use
+ * with parse_loose_header(), ULHR_TOO_LONG will still be returned
+ * from this function to indicate that the header was too long.
+ */
+enum unpack_loose_header_result {
+       ULHR_OK,
+       ULHR_BAD,
+       ULHR_TOO_LONG,
+};
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+                                                   unsigned char *map,
+                                                   unsigned long mapsize,
+                                                   void *buffer,
+                                                   unsigned long bufsiz,
+                                                   struct strbuf *hdrbuf);
+
+/**
+ * parse_loose_header() parses the starting "<type> <len>\0" of an
+ * object. If it doesn't follow that format -1 is returned. To check
+ * the validity of the <type> populate the "typep" in the "struct
+ * object_info". It will be OBJ_BAD if the object type is unknown. The
+ * parsed <len> can be retrieved via "oi->sizep", and from there
+ * passed to unpack_loose_rest().
+ */
+struct object_info;
+int parse_loose_header(const char *hdr, struct object_info *oi);
 
 int check_object_signature(struct repository *r, const struct object_id *oid,
-                          void *buf, unsigned long size, const char *type);
+                          void *buf, unsigned long size, const char *type,
+                          struct object_id *real_oidp);
 
 int finalize_object_file(const char *tmpfile, const char *filename);
 
index a289f09ed6fbf9a117468ae69c7ee2e8ab48f0a8..eb9cee8dee9a6baa9fb8ed031f1f6de29983c2a7 100644 (file)
@@ -168,7 +168,7 @@ git-show-index                          plumbinginterrogators
 git-show-ref                            plumbinginterrogators
 git-sh-i18n                             purehelpers
 git-sh-setup                            purehelpers
-git-sparse-checkout                     mainporcelain           worktree
+git-sparse-checkout                     mainporcelain
 git-stash                               mainporcelain
 git-stage                                                               complete
 git-status                              mainporcelain           info
index 2dcbe901b6b7a05f56a66056ac79d9b729454382..c5873f3a70643a9c03feeec4981a06a439e78118 100644 (file)
--- a/config.c
+++ b/config.c
@@ -148,8 +148,10 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!is_absolute_path(path)) {
                char *slash;
 
-               if (!cf || !cf->path)
-                       return error(_("relative config includes must come from files"));
+               if (!cf || !cf->path) {
+                       ret = error(_("relative config includes must come from files"));
+                       goto cleanup;
+               }
 
                slash = find_last_dir_sep(cf->path);
                if (slash)
@@ -167,6 +169,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                ret = git_config_from_file(git_config_include, path, inc);
                inc->depth--;
        }
+cleanup:
        strbuf_release(&buf);
        free(expanded);
        return ret;
index d80d009d1a1b63815858d8fb9a78c1a058f4610b..b1fcbe0d6fa847dd936467c0d8d1aeffbf90d1cc 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -765,7 +765,7 @@ static int execute(void)
 
        set_keep_alive(0);
        alarm(init_timeout ? init_timeout : timeout);
-       pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
+       pktlen = packet_read(0, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
 
        len = strlen(line);
index b969dc6ebb6800addbafe72adcfbabdadb6af121..5216191488e20115949418aab527988ba7b81e7c 100644 (file)
@@ -9,6 +9,7 @@
 #include "branch.h"
 #include "fmt-merge-msg.h"
 #include "commit-reach.h"
+#include "gpg-interface.h"
 
 static int use_branch_desc;
 static int suppress_dest_pattern_seen;
@@ -16,6 +17,8 @@ static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
 
 int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 {
+       int status = 0;
+
        if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
                int is_bool;
                merge_log_config = git_config_bool_or_int(key, value, &is_bool);
@@ -34,6 +37,9 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
                        string_list_append(&suppress_dest_patterns, value);
                suppress_dest_pattern_seen = 1;
        } else {
+               status = git_gpg_config(key, value, NULL);
+               if (status)
+                       return status;
                return git_default_config(key, value, cb);
        }
        return 0;
@@ -528,11 +534,11 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                        buf = payload.buf;
                        len = payload.len;
                        if (check_signature(payload.buf, payload.len, sig.buf,
-                                        sig.len, &sigc) &&
-                               !sigc.gpg_output)
+                                           sig.len, &sigc) &&
+                           !sigc.output)
                                strbuf_addstr(&sig, "gpg verification failed.\n");
                        else
-                               strbuf_addstr(&sig, sigc.gpg_output);
+                               strbuf_addstr(&sig, sigc.output);
                }
                signature_check_clear(&sigc);
 
index e3d9f4836db14e5834372a8bb61ee3c27c82f34f..a15c0620db6893898152b1c9e363a0af7f1b304e 100644 (file)
@@ -51,12 +51,6 @@ gettext_without_eval_gettext)
                )
        }
 
-       eval_ngettext () {
-               ngettext "$1" "$2" "$3" | (
-                       export PATH $(git sh-i18n--envsubst --variables "$2");
-                       git sh-i18n--envsubst "$2"
-               )
-       }
        ;;
 *)
        gettext () {
@@ -70,12 +64,6 @@ gettext_without_eval_gettext)
                )
        }
 
-       eval_ngettext () {
-               (test "$3" = 1 && printf "%s" "$1" || printf "%s" "$2") | (
-                       export PATH $(git sh-i18n--envsubst --variables "$2");
-                       git sh-i18n--envsubst "$2"
-               )
-       }
        ;;
 esac
 
index cee053cdc388a9390ef4c23730d334e0bba376db..960982f9d534f4984f4e372a1883899293c536ff 100644 (file)
@@ -217,9 +217,6 @@ require_clean_work_tree () {
        then
                action=$1
                case "$action" in
-               rebase)
-                       gettextln "Cannot rebase: You have unstaged changes." >&2
-                       ;;
                "rewrite branches")
                        gettextln "Cannot rewrite branches: You have unstaged changes." >&2
                        ;;
@@ -235,14 +232,7 @@ require_clean_work_tree () {
                if test $err = 0
                then
                        action=$1
-                       case "$action" in
-                       rebase)
-                               gettextln "Cannot rebase: Your index contains uncommitted changes." >&2
-                               ;;
-                       *)
-                               eval_gettextln "Cannot \$action: Your index contains uncommitted changes." >&2
-                               ;;
-                       esac
+                       eval_gettextln "Cannot \$action: Your index contains uncommitted changes." >&2
                else
                    gettextln "Additionally, your index contains uncommitted changes." >&2
                fi
index 127aecfc2b071f9a745a871a9ea205931eeb672f..800d8caa677d4d2baf4fcd3c8aebf9649650a8b3 100644 (file)
@@ -3,11 +3,14 @@
 #include "config.h"
 #include "run-command.h"
 #include "strbuf.h"
+#include "dir.h"
 #include "gpg-interface.h"
 #include "sigchain.h"
 #include "tempfile.h"
+#include "alias.h"
 
 static char *configured_signing_key;
+static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file;
 static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
 
 struct gpg_format {
@@ -15,6 +18,14 @@ struct gpg_format {
        const char *program;
        const char **verify_args;
        const char **sigs;
+       int (*verify_signed_buffer)(struct signature_check *sigc,
+                                   struct gpg_format *fmt, const char *payload,
+                                   size_t payload_size, const char *signature,
+                                   size_t signature_size);
+       int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
+                          const char *signing_key);
+       const char *(*get_default_key)(void);
+       const char *(*get_key_id)(void);
 };
 
 static const char *openpgp_verify_args[] = {
@@ -35,14 +46,59 @@ static const char *x509_sigs[] = {
        NULL
 };
 
+static const char *ssh_verify_args[] = { NULL };
+static const char *ssh_sigs[] = {
+       "-----BEGIN SSH SIGNATURE-----",
+       NULL
+};
+
+static int verify_gpg_signed_buffer(struct signature_check *sigc,
+                                   struct gpg_format *fmt, const char *payload,
+                                   size_t payload_size, const char *signature,
+                                   size_t signature_size);
+static int verify_ssh_signed_buffer(struct signature_check *sigc,
+                                   struct gpg_format *fmt, const char *payload,
+                                   size_t payload_size, const char *signature,
+                                   size_t signature_size);
+static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
+                          const char *signing_key);
+static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
+                          const char *signing_key);
+
+static const char *get_default_ssh_signing_key(void);
+
+static const char *get_ssh_key_id(void);
+
 static struct gpg_format gpg_format[] = {
-       { .name = "openpgp", .program = "gpg",
-         .verify_args = openpgp_verify_args,
-         .sigs = openpgp_sigs
+       {
+               .name = "openpgp",
+               .program = "gpg",
+               .verify_args = openpgp_verify_args,
+               .sigs = openpgp_sigs,
+               .verify_signed_buffer = verify_gpg_signed_buffer,
+               .sign_buffer = sign_buffer_gpg,
+               .get_default_key = NULL,
+               .get_key_id = NULL,
+       },
+       {
+               .name = "x509",
+               .program = "gpgsm",
+               .verify_args = x509_verify_args,
+               .sigs = x509_sigs,
+               .verify_signed_buffer = verify_gpg_signed_buffer,
+               .sign_buffer = sign_buffer_gpg,
+               .get_default_key = NULL,
+               .get_key_id = NULL,
        },
-       { .name = "x509", .program = "gpgsm",
-         .verify_args = x509_verify_args,
-         .sigs = x509_sigs
+       {
+               .name = "ssh",
+               .program = "ssh-keygen",
+               .verify_args = ssh_verify_args,
+               .sigs = ssh_sigs,
+               .verify_signed_buffer = verify_ssh_signed_buffer,
+               .sign_buffer = sign_buffer_ssh,
+               .get_default_key = get_default_ssh_signing_key,
+               .get_key_id = get_ssh_key_id,
        },
 };
 
@@ -72,7 +128,7 @@ static struct gpg_format *get_format_by_sig(const char *sig)
 void signature_check_clear(struct signature_check *sigc)
 {
        FREE_AND_NULL(sigc->payload);
-       FREE_AND_NULL(sigc->gpg_output);
+       FREE_AND_NULL(sigc->output);
        FREE_AND_NULL(sigc->gpg_status);
        FREE_AND_NULL(sigc->signer);
        FREE_AND_NULL(sigc->key);
@@ -257,16 +313,16 @@ error:
        FREE_AND_NULL(sigc->key);
 }
 
-static int verify_signed_buffer(const char *payload, size_t payload_size,
-                               const char *signature, size_t signature_size,
-                               struct strbuf *gpg_output,
-                               struct strbuf *gpg_status)
+static int verify_gpg_signed_buffer(struct signature_check *sigc,
+                                   struct gpg_format *fmt, const char *payload,
+                                   size_t payload_size, const char *signature,
+                                   size_t signature_size)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
-       struct gpg_format *fmt;
        struct tempfile *temp;
        int ret;
-       struct strbuf buf = STRBUF_INIT;
+       struct strbuf gpg_stdout = STRBUF_INIT;
+       struct strbuf gpg_stderr = STRBUF_INIT;
 
        temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
        if (!temp)
@@ -279,10 +335,6 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
                return -1;
        }
 
-       fmt = get_format_by_sig(signature);
-       if (!fmt)
-               BUG("bad signature '%s'", signature);
-
        strvec_push(&gpg.args, fmt->program);
        strvec_pushv(&gpg.args, fmt->verify_args);
        strvec_pushl(&gpg.args,
@@ -290,18 +342,220 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
                     "--verify", temp->filename.buf, "-",
                     NULL);
 
-       if (!gpg_status)
-               gpg_status = &buf;
-
        sigchain_push(SIGPIPE, SIG_IGN);
-       ret = pipe_command(&gpg, payload, payload_size,
-                          gpg_status, 0, gpg_output, 0);
+       ret = pipe_command(&gpg, payload, payload_size, &gpg_stdout, 0,
+                          &gpg_stderr, 0);
        sigchain_pop(SIGPIPE);
 
        delete_tempfile(&temp);
 
-       ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG ");
-       strbuf_release(&buf); /* no matter it was used or not */
+       ret |= !strstr(gpg_stdout.buf, "\n[GNUPG:] GOODSIG ");
+       sigc->payload = xmemdupz(payload, payload_size);
+       sigc->output = strbuf_detach(&gpg_stderr, NULL);
+       sigc->gpg_status = strbuf_detach(&gpg_stdout, NULL);
+
+       parse_gpg_output(sigc);
+
+       strbuf_release(&gpg_stdout);
+       strbuf_release(&gpg_stderr);
+
+       return ret;
+}
+
+static void parse_ssh_output(struct signature_check *sigc)
+{
+       const char *line, *principal, *search;
+       char *to_free;
+       char *key = NULL;
+
+       /*
+        * ssh-keygen output should be:
+        * Good "git" signature for PRINCIPAL with RSA key SHA256:FINGERPRINT
+        *
+        * or for valid but unknown keys:
+        * Good "git" signature with RSA key SHA256:FINGERPRINT
+        *
+        * Note that "PRINCIPAL" can contain whitespace, "RSA" and
+        * "SHA256" part could be a different token that names of
+        * the algorithms used, and "FINGERPRINT" is a hexadecimal
+        * string.  By finding the last occurence of " with ", we can
+        * reliably parse out the PRINCIPAL.
+        */
+       sigc->result = 'B';
+       sigc->trust_level = TRUST_NEVER;
+
+       line = to_free = xmemdupz(sigc->output, strcspn(sigc->output, "\n"));
+
+       if (skip_prefix(line, "Good \"git\" signature for ", &line)) {
+               /* Valid signature and known principal */
+               sigc->result = 'G';
+               sigc->trust_level = TRUST_FULLY;
+
+               /* Search for the last "with" to get the full principal */
+               principal = line;
+               do {
+                       search = strstr(line, " with ");
+                       if (search)
+                               line = search + 1;
+               } while (search != NULL);
+               sigc->signer = xmemdupz(principal, line - principal - 1);
+       } else if (skip_prefix(line, "Good \"git\" signature with ", &line)) {
+               /* Valid signature, but key unknown */
+               sigc->result = 'G';
+               sigc->trust_level = TRUST_UNDEFINED;
+       } else {
+               goto cleanup;
+       }
+
+       key = strstr(line, "key");
+       if (key) {
+               sigc->fingerprint = xstrdup(strstr(line, "key") + 4);
+               sigc->key = xstrdup(sigc->fingerprint);
+       } else {
+               /*
+                * Output did not match what we expected
+                * Treat the signature as bad
+                */
+               sigc->result = 'B';
+       }
+
+cleanup:
+       free(to_free);
+}
+
+static int verify_ssh_signed_buffer(struct signature_check *sigc,
+                                   struct gpg_format *fmt, const char *payload,
+                                   size_t payload_size, const char *signature,
+                                   size_t signature_size)
+{
+       struct child_process ssh_keygen = CHILD_PROCESS_INIT;
+       struct tempfile *buffer_file;
+       int ret = -1;
+       const char *line;
+       size_t trust_size;
+       char *principal;
+       struct strbuf ssh_principals_out = STRBUF_INIT;
+       struct strbuf ssh_principals_err = STRBUF_INIT;
+       struct strbuf ssh_keygen_out = STRBUF_INIT;
+       struct strbuf ssh_keygen_err = STRBUF_INIT;
+
+       if (!ssh_allowed_signers) {
+               error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));
+               return -1;
+       }
+
+       buffer_file = mks_tempfile_t(".git_vtag_tmpXXXXXX");
+       if (!buffer_file)
+               return error_errno(_("could not create temporary file"));
+       if (write_in_full(buffer_file->fd, signature, signature_size) < 0 ||
+           close_tempfile_gently(buffer_file) < 0) {
+               error_errno(_("failed writing detached signature to '%s'"),
+                           buffer_file->filename.buf);
+               delete_tempfile(&buffer_file);
+               return -1;
+       }
+
+       /* Find the principal from the signers */
+       strvec_pushl(&ssh_keygen.args, fmt->program,
+                    "-Y", "find-principals",
+                    "-f", ssh_allowed_signers,
+                    "-s", buffer_file->filename.buf,
+                    NULL);
+       ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
+                          &ssh_principals_err, 0);
+       if (ret && strstr(ssh_principals_err.buf, "usage:")) {
+               error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)"));
+               goto out;
+       }
+       if (ret || !ssh_principals_out.len) {
+               /*
+                * We did not find a matching principal in the allowedSigners
+                * Check without validation
+                */
+               child_process_init(&ssh_keygen);
+               strvec_pushl(&ssh_keygen.args, fmt->program,
+                            "-Y", "check-novalidate",
+                            "-n", "git",
+                            "-s", buffer_file->filename.buf,
+                            NULL);
+               pipe_command(&ssh_keygen, payload, payload_size,
+                                  &ssh_keygen_out, 0, &ssh_keygen_err, 0);
+
+               /*
+                * Fail on unknown keys
+                * we still call check-novalidate to display the signature info
+                */
+               ret = -1;
+       } else {
+               /* Check every principal we found (one per line) */
+               for (line = ssh_principals_out.buf; *line;
+                    line = strchrnul(line + 1, '\n')) {
+                       while (*line == '\n')
+                               line++;
+                       if (!*line)
+                               break;
+
+                       trust_size = strcspn(line, "\n");
+                       principal = xmemdupz(line, trust_size);
+
+                       child_process_init(&ssh_keygen);
+                       strbuf_release(&ssh_keygen_out);
+                       strbuf_release(&ssh_keygen_err);
+                       strvec_push(&ssh_keygen.args, fmt->program);
+                       /*
+                        * We found principals
+                        * Try with each until we find a match
+                        */
+                       strvec_pushl(&ssh_keygen.args, "-Y", "verify",
+                                    "-n", "git",
+                                    "-f", ssh_allowed_signers,
+                                    "-I", principal,
+                                    "-s", buffer_file->filename.buf,
+                                    NULL);
+
+                       if (ssh_revocation_file) {
+                               if (file_exists(ssh_revocation_file)) {
+                                       strvec_pushl(&ssh_keygen.args, "-r",
+                                                    ssh_revocation_file, NULL);
+                               } else {
+                                       warning(_("ssh signing revocation file configured but not found: %s"),
+                                               ssh_revocation_file);
+                               }
+                       }
+
+                       sigchain_push(SIGPIPE, SIG_IGN);
+                       ret = pipe_command(&ssh_keygen, payload, payload_size,
+                                          &ssh_keygen_out, 0, &ssh_keygen_err, 0);
+                       sigchain_pop(SIGPIPE);
+
+                       FREE_AND_NULL(principal);
+
+                       if (!ret)
+                               ret = !starts_with(ssh_keygen_out.buf, "Good");
+
+                       if (!ret)
+                               break;
+               }
+       }
+
+       sigc->payload = xmemdupz(payload, payload_size);
+       strbuf_stripspace(&ssh_keygen_out, 0);
+       strbuf_stripspace(&ssh_keygen_err, 0);
+       /* Add stderr outputs to show the user actual ssh-keygen errors */
+       strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
+       strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
+       sigc->output = strbuf_detach(&ssh_keygen_out, NULL);
+       sigc->gpg_status = xstrdup(sigc->output);
+
+       parse_ssh_output(sigc);
+
+out:
+       if (buffer_file)
+               delete_tempfile(&buffer_file);
+       strbuf_release(&ssh_principals_out);
+       strbuf_release(&ssh_principals_err);
+       strbuf_release(&ssh_keygen_out);
+       strbuf_release(&ssh_keygen_err);
 
        return ret;
 }
@@ -309,35 +563,32 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
 int check_signature(const char *payload, size_t plen, const char *signature,
        size_t slen, struct signature_check *sigc)
 {
-       struct strbuf gpg_output = STRBUF_INIT;
-       struct strbuf gpg_status = STRBUF_INIT;
+       struct gpg_format *fmt;
        int status;
 
        sigc->result = 'N';
        sigc->trust_level = -1;
 
-       status = verify_signed_buffer(payload, plen, signature, slen,
-                                     &gpg_output, &gpg_status);
-       if (status && !gpg_output.len)
-               goto out;
-       sigc->payload = xmemdupz(payload, plen);
-       sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
-       sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
-       parse_gpg_output(sigc);
+       fmt = get_format_by_sig(signature);
+       if (!fmt)
+               die(_("bad/incompatible signature '%s'"), signature);
+
+       status = fmt->verify_signed_buffer(sigc, fmt, payload, plen, signature,
+                                          slen);
+
+       if (status && !sigc->output)
+               return !!status;
+
        status |= sigc->result != 'G';
        status |= sigc->trust_level < configured_min_trust_level;
 
- out:
-       strbuf_release(&gpg_status);
-       strbuf_release(&gpg_output);
-
        return !!status;
 }
 
 void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
 {
-       const char *output = flags & GPG_VERIFY_RAW ?
-               sigc->gpg_status : sigc->gpg_output;
+       const char *output = flags & GPG_VERIFY_RAW ? sigc->gpg_status :
+                                                           sigc->output;
 
        if (flags & GPG_VERIFY_VERBOSE && sigc->payload)
                fputs(sigc->payload, stdout);
@@ -419,12 +670,33 @@ int git_gpg_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return git_config_string(&ssh_default_key_command, var, value);
+       }
+
+       if (!strcmp(var, "gpg.ssh.allowedsignersfile")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return git_config_pathname(&ssh_allowed_signers, var, value);
+       }
+
+       if (!strcmp(var, "gpg.ssh.revocationfile")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return git_config_pathname(&ssh_revocation_file, var, value);
+       }
+
        if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
                fmtname = "openpgp";
 
        if (!strcmp(var, "gpg.x509.program"))
                fmtname = "x509";
 
+       if (!strcmp(var, "gpg.ssh.program"))
+               fmtname = "ssh";
+
        if (fmtname) {
                fmt = get_format_by_name(fmtname);
                return git_config_string(&fmt->program, var, value);
@@ -433,18 +705,148 @@ int git_gpg_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
+static char *get_ssh_key_fingerprint(const char *signing_key)
+{
+       struct child_process ssh_keygen = CHILD_PROCESS_INIT;
+       int ret = -1;
+       struct strbuf fingerprint_stdout = STRBUF_INIT;
+       struct strbuf **fingerprint;
+       char *fingerprint_ret;
+
+       /*
+        * With SSH Signing this can contain a filename or a public key
+        * For textual representation we usually want a fingerprint
+        */
+       if (starts_with(signing_key, "ssh-")) {
+               strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL);
+               ret = pipe_command(&ssh_keygen, signing_key,
+                                  strlen(signing_key), &fingerprint_stdout, 0,
+                                  NULL, 0);
+       } else {
+               strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf",
+                            configured_signing_key, NULL);
+               ret = pipe_command(&ssh_keygen, NULL, 0, &fingerprint_stdout, 0,
+                                  NULL, 0);
+       }
+
+       if (!!ret)
+               die_errno(_("failed to get the ssh fingerprint for key '%s'"),
+                         signing_key);
+
+       fingerprint = strbuf_split_max(&fingerprint_stdout, ' ', 3);
+       if (!fingerprint[1])
+               die_errno(_("failed to get the ssh fingerprint for key '%s'"),
+                         signing_key);
+
+       fingerprint_ret = strbuf_detach(fingerprint[1], NULL);
+       strbuf_list_free(fingerprint);
+       strbuf_release(&fingerprint_stdout);
+       return fingerprint_ret;
+}
+
+/* Returns the first public key from an ssh-agent to use for signing */
+static const char *get_default_ssh_signing_key(void)
+{
+       struct child_process ssh_default_key = CHILD_PROCESS_INIT;
+       int ret = -1;
+       struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT;
+       struct strbuf **keys;
+       char *key_command = NULL;
+       const char **argv;
+       int n;
+       char *default_key = NULL;
+
+       if (!ssh_default_key_command)
+               die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
+
+       key_command = xstrdup(ssh_default_key_command);
+       n = split_cmdline(key_command, &argv);
+
+       if (n < 0)
+               die("malformed build-time gpg.ssh.defaultKeyCommand: %s",
+                   split_cmdline_strerror(n));
+
+       strvec_pushv(&ssh_default_key.args, argv);
+       ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0,
+                          &key_stderr, 0);
+
+       if (!ret) {
+               keys = strbuf_split_max(&key_stdout, '\n', 2);
+               if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
+                       default_key = strbuf_detach(keys[0], NULL);
+               } else {
+                       warning(_("gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s"),
+                               key_stderr.buf, key_stdout.buf);
+               }
+
+               strbuf_list_free(keys);
+       } else {
+               warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"),
+                       key_stderr.buf, key_stdout.buf);
+       }
+
+       free(key_command);
+       free(argv);
+       strbuf_release(&key_stdout);
+
+       return default_key;
+}
+
+static const char *get_ssh_key_id(void) {
+       return get_ssh_key_fingerprint(get_signing_key());
+}
+
+/* Returns a textual but unique representation of the signing key */
+const char *get_signing_key_id(void)
+{
+       if (use_format->get_key_id) {
+               return use_format->get_key_id();
+       }
+
+       /* GPG/GPGSM only store a key id on this variable */
+       return get_signing_key();
+}
+
 const char *get_signing_key(void)
 {
        if (configured_signing_key)
                return configured_signing_key;
-       return git_committer_info(IDENT_STRICT|IDENT_NO_DATE);
+       if (use_format->get_default_key) {
+               return use_format->get_default_key();
+       }
+
+       return git_committer_info(IDENT_STRICT | IDENT_NO_DATE);
 }
 
 int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key)
+{
+       return use_format->sign_buffer(buffer, signature, signing_key);
+}
+
+/*
+ * Strip CR from the line endings, in case we are on Windows.
+ * NEEDSWORK: make it trim only CRs before LFs and rename
+ */
+static void remove_cr_after(struct strbuf *buffer, size_t offset)
+{
+       size_t i, j;
+
+       for (i = j = offset; i < buffer->len; i++) {
+               if (buffer->buf[i] != '\r') {
+                       if (i != j)
+                               buffer->buf[j] = buffer->buf[i];
+                       j++;
+               }
+       }
+       strbuf_setlen(buffer, j);
+}
+
+static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
+                         const char *signing_key)
 {
        struct child_process gpg = CHILD_PROCESS_INIT;
        int ret;
-       size_t i, j, bottom;
+       size_t bottom;
        struct strbuf gpg_status = STRBUF_INIT;
 
        strvec_pushl(&gpg.args,
@@ -470,13 +872,98 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
                return error(_("gpg failed to sign the data"));
 
        /* Strip CR from the line endings, in case we are on Windows. */
-       for (i = j = bottom; i < signature->len; i++)
-               if (signature->buf[i] != '\r') {
-                       if (i != j)
-                               signature->buf[j] = signature->buf[i];
-                       j++;
-               }
-       strbuf_setlen(signature, j);
+       remove_cr_after(signature, bottom);
 
        return 0;
 }
+
+static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
+                          const char *signing_key)
+{
+       struct child_process signer = CHILD_PROCESS_INIT;
+       int ret = -1;
+       size_t bottom, keylen;
+       struct strbuf signer_stderr = STRBUF_INIT;
+       struct tempfile *key_file = NULL, *buffer_file = NULL;
+       char *ssh_signing_key_file = NULL;
+       struct strbuf ssh_signature_filename = STRBUF_INIT;
+
+       if (!signing_key || signing_key[0] == '\0')
+               return error(
+                       _("user.signingkey needs to be set for ssh signing"));
+
+       if (starts_with(signing_key, "ssh-")) {
+               /* A literal ssh key */
+               key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
+               if (!key_file)
+                       return error_errno(
+                               _("could not create temporary file"));
+               keylen = strlen(signing_key);
+               if (write_in_full(key_file->fd, signing_key, keylen) < 0 ||
+                   close_tempfile_gently(key_file) < 0) {
+                       error_errno(_("failed writing ssh signing key to '%s'"),
+                                   key_file->filename.buf);
+                       goto out;
+               }
+               ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL);
+       } else {
+               /* We assume a file */
+               ssh_signing_key_file = expand_user_path(signing_key, 1);
+       }
+
+       buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");
+       if (!buffer_file) {
+               error_errno(_("could not create temporary file"));
+               goto out;
+       }
+
+       if (write_in_full(buffer_file->fd, buffer->buf, buffer->len) < 0 ||
+           close_tempfile_gently(buffer_file) < 0) {
+               error_errno(_("failed writing ssh signing key buffer to '%s'"),
+                           buffer_file->filename.buf);
+               goto out;
+       }
+
+       strvec_pushl(&signer.args, use_format->program,
+                    "-Y", "sign",
+                    "-n", "git",
+                    "-f", ssh_signing_key_file,
+                    buffer_file->filename.buf,
+                    NULL);
+
+       sigchain_push(SIGPIPE, SIG_IGN);
+       ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
+       sigchain_pop(SIGPIPE);
+
+       if (ret) {
+               if (strstr(signer_stderr.buf, "usage:"))
+                       error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)"));
+
+               error("%s", signer_stderr.buf);
+               goto out;
+       }
+
+       bottom = signature->len;
+
+       strbuf_addbuf(&ssh_signature_filename, &buffer_file->filename);
+       strbuf_addstr(&ssh_signature_filename, ".sig");
+       if (strbuf_read_file(signature, ssh_signature_filename.buf, 0) < 0) {
+               error_errno(
+                       _("failed reading ssh signing data buffer from '%s'"),
+                       ssh_signature_filename.buf);
+       }
+       unlink_or_warn(ssh_signature_filename.buf);
+
+       /* Strip CR from the line endings, in case we are on Windows. */
+       remove_cr_after(signature, bottom);
+
+out:
+       if (key_file)
+               delete_tempfile(&key_file);
+       if (buffer_file)
+               delete_tempfile(&buffer_file);
+       strbuf_release(&signer_stderr);
+       strbuf_release(&ssh_signature_filename);
+       FREE_AND_NULL(ssh_signing_key_file);
+       return ret;
+}
index 80567e4894868d5d7a192cab0afb9ca9c09cb70d..beefacbb1e9025b8d65a83aea74c6ce3913535cd 100644 (file)
@@ -17,7 +17,7 @@ enum signature_trust_level {
 
 struct signature_check {
        char *payload;
-       char *gpg_output;
+       char *output;
        char *gpg_status;
 
        /*
@@ -64,6 +64,12 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
 int git_gpg_config(const char *, const char *, void *);
 void set_signing_key(const char *);
 const char *get_signing_key(void);
+
+/*
+ * Returns a textual unique representation of the signing key in use
+ * Either a GPG KeyID or a SSH Key Fingerprint
+ */
+const char *get_signing_key_id(void);
 int check_signature(const char *payload, size_t plen,
                    const char *signature, size_t slen,
                    struct signature_check *sigc);
index 6dc4412268b8ae34f215e066bd94a1f2ddad1ce8..644893fd8cfff6a9ee9cda0b512c2adb9c8a6953 100644 (file)
@@ -515,10 +515,10 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
 
        status = check_signature(payload.buf, payload.len, signature.buf,
                                 signature.len, &sigc);
-       if (status && !sigc.gpg_output)
+       if (status && !sigc.output)
                show_sig_lines(opt, status, "No signature\n");
        else
-               show_sig_lines(opt, status, sigc.gpg_output);
+               show_sig_lines(opt, status, sigc.output);
        signature_check_clear(&sigc);
 
  out:
@@ -585,8 +585,8 @@ static int show_one_mergetag(struct commit *commit,
                /* could have a good signature */
                status = check_signature(payload.buf, payload.len,
                                         signature.buf, signature.len, &sigc);
-               if (sigc.gpg_output)
-                       strbuf_addstr(&verify_message, sigc.gpg_output);
+               if (sigc.output)
+                       strbuf_addstr(&verify_message, sigc.output);
                else
                        strbuf_addstr(&verify_message, "No signature\n");
                signature_check_clear(&sigc);
index e5456f4722894a20f09c6255d921306ab5884ccd..0342f104836b69a7889b3fa686c7c359c27e5dd6 100644 (file)
@@ -609,6 +609,7 @@ static int err(struct merge_options *opt, const char *err, ...)
 
 static void format_commit(struct strbuf *sb,
                          int indent,
+                         struct repository *repo,
                          struct commit *commit)
 {
        struct merge_remote_desc *desc;
@@ -622,7 +623,7 @@ static void format_commit(struct strbuf *sb,
                return;
        }
 
-       format_commit_message(commit, "%h %s", sb, &ctx);
+       repo_format_commit_message(repo, commit, "%h %s", sb, &ctx);
        strbuf_addch(sb, '\n');
 }
 
@@ -1578,17 +1579,6 @@ static int merge_submodule(struct merge_options *opt,
        if (is_null_oid(b))
                return 0;
 
-       /*
-        * NEEDSWORK: Remove this when all submodule object accesses are
-        * through explicitly specified repositores.
-        */
-       if (add_submodule_odb(path)) {
-               path_msg(opt, path, 0,
-                        _("Failed to merge submodule %s (not checked out)"),
-                        path);
-               return 0;
-       }
-
        if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
                path_msg(opt, path, 0,
                                _("Failed to merge submodule %s (not checked out)"),
@@ -1653,7 +1643,7 @@ static int merge_submodule(struct merge_options *opt,
                break;
 
        case 1:
-               format_commit(&sb, 4,
+               format_commit(&sb, 4, &subrepo,
                              (struct commit *)merges.objects[0].item);
                path_msg(opt, path, 0,
                         _("Failed to merge submodule %s, but a possible merge "
@@ -1670,7 +1660,7 @@ static int merge_submodule(struct merge_options *opt,
                break;
        default:
                for (i = 0; i < merges.nr; i++)
-                       format_commit(&sb, 4,
+                       format_commit(&sb, 4, &subrepo,
                                      (struct commit *)merges.objects[i].item);
                path_msg(opt, path, 0,
                         _("Failed to merge submodule %s, but multiple "
index c5537518899684b2fa4809211fee4309b9b57290..d9457797dbb73bfed720ac9ca0b9bcf56575c62a 100644 (file)
@@ -334,7 +334,9 @@ static void output(struct merge_options *opt, int v, const char *fmt, ...)
                flush_output(opt);
 }
 
-static void output_commit_title(struct merge_options *opt, struct commit *commit)
+static void repo_output_commit_title(struct merge_options *opt,
+                                    struct repository *repo,
+                                    struct commit *commit)
 {
        struct merge_remote_desc *desc;
 
@@ -343,23 +345,29 @@ static void output_commit_title(struct merge_options *opt, struct commit *commit
        if (desc)
                strbuf_addf(&opt->obuf, "virtual %s\n", desc->name);
        else {
-               strbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,
-                                        DEFAULT_ABBREV);
+               strbuf_repo_add_unique_abbrev(&opt->obuf, repo,
+                                             &commit->object.oid,
+                                             DEFAULT_ABBREV);
                strbuf_addch(&opt->obuf, ' ');
-               if (parse_commit(commit) != 0)
+               if (repo_parse_commit(repo, commit) != 0)
                        strbuf_addstr(&opt->obuf, _("(bad commit)\n"));
                else {
                        const char *title;
-                       const char *msg = get_commit_buffer(commit, NULL);
+                       const char *msg = repo_get_commit_buffer(repo, commit, NULL);
                        int len = find_commit_subject(msg, &title);
                        if (len)
                                strbuf_addf(&opt->obuf, "%.*s\n", len, title);
-                       unuse_commit_buffer(commit, msg);
+                       repo_unuse_commit_buffer(repo, commit, msg);
                }
        }
        flush_output(opt);
 }
 
+static void output_commit_title(struct merge_options *opt, struct commit *commit)
+{
+       repo_output_commit_title(opt, the_repository, commit);
+}
+
 static int add_cacheinfo(struct merge_options *opt,
                         const struct diff_filespec *blob,
                         const char *path, int stage, int refresh, int options)
@@ -1152,14 +1160,14 @@ static int find_first_merges(struct repository *repo,
        return result->nr;
 }
 
-static void print_commit(struct commit *commit)
+static void print_commit(struct repository *repo, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
        ctx.date_mode.type = DATE_NORMAL;
        /* FIXME: Merge this with output_commit_title() */
        assert(!merge_remote_util(commit));
-       format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+       repo_format_commit_message(repo, commit, " %h: %m %s", &sb, &ctx);
        fprintf(stderr, "%s\n", sb.buf);
        strbuf_release(&sb);
 }
@@ -1199,15 +1207,6 @@ static int merge_submodule(struct merge_options *opt,
        if (is_null_oid(b))
                return 0;
 
-       /*
-        * NEEDSWORK: Remove this when all submodule object accesses are
-        * through explicitly specified repositores.
-        */
-       if (add_submodule_odb(path)) {
-               output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
-               return 0;
-       }
-
        if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
                output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
                return 0;
@@ -1232,7 +1231,7 @@ static int merge_submodule(struct merge_options *opt,
                oidcpy(result, b);
                if (show(opt, 3)) {
                        output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-                       output_commit_title(opt, commit_b);
+                       repo_output_commit_title(opt, &subrepo, commit_b);
                } else if (show(opt, 2))
                        output(opt, 2, _("Fast-forwarding submodule %s"), path);
                else
@@ -1245,7 +1244,7 @@ static int merge_submodule(struct merge_options *opt,
                oidcpy(result, a);
                if (show(opt, 3)) {
                        output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-                       output_commit_title(opt, commit_a);
+                       repo_output_commit_title(opt, &subrepo, commit_a);
                } else if (show(opt, 2))
                        output(opt, 2, _("Fast-forwarding submodule %s"), path);
                else
@@ -1277,7 +1276,7 @@ static int merge_submodule(struct merge_options *opt,
        case 1:
                output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
                output(opt, 2, _("Found a possible merge resolution for the submodule:\n"));
-               print_commit((struct commit *) merges.objects[0].item);
+               print_commit(&subrepo, (struct commit *) merges.objects[0].item);
                output(opt, 2, _(
                       "If this is correct simply add it to the index "
                       "for example\n"
@@ -1290,7 +1289,7 @@ static int merge_submodule(struct merge_options *opt,
        default:
                output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
                for (i = 0; i < merges.nr; i++)
-                       print_commit((struct commit *) merges.objects[i].item);
+                       print_commit(&subrepo, (struct commit *) merges.objects[i].item);
        }
 
        object_array_clear(&merges);
index ce5b8e9f296233470d18feae929772235e4f12f2..d5ce467995653e07be726c307fcf3b61fc5ddea4 100644 (file)
@@ -3,6 +3,13 @@ diff_cmd () {
                -R 'Accel.Search: "Ctrl+F"' \
                -R 'Accel.SearchForward: "Ctrl+G"' \
                "$LOCAL" "$REMOTE"
+
+       # xxdiff can segfault on binary files which are often uninteresting.
+       # Do not allow segfaults to stop us from continuing on to the next file.
+       if test $? = 128
+       then
+               return 1
+       fi
 }
 
 merge_cmd () {
diff --git a/midx.c b/midx.c
index 7e06e8597561fe1341a53983a36b4a8a0eafe378..8433086ac13c91a5c00fd4c97ef72341b2c06e3a 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -1107,6 +1107,22 @@ cleanup:
        return ret;
 }
 
+static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
+                                                       const char *object_dir)
+{
+       struct multi_pack_index *cur;
+
+       /* Ensure the given object_dir is local, or a known alternate. */
+       find_odb(r, object_dir);
+
+       for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
+               if (!strcmp(object_dir, cur->object_dir))
+                       return cur;
+       }
+
+       return NULL;
+}
+
 static int write_midx_internal(const char *object_dir,
                               struct string_list *packs_to_include,
                               struct string_list *packs_to_drop,
@@ -1120,15 +1136,11 @@ static int write_midx_internal(const char *object_dir,
        struct hashfile *f = NULL;
        struct lock_file lk;
        struct write_midx_context ctx = { 0 };
-       struct multi_pack_index *cur;
        int pack_name_concat_len = 0;
        int dropped_packs = 0;
        int result = 0;
        struct chunkfile *cf;
 
-       /* Ensure the given object_dir is local, or a known alternate. */
-       find_odb(the_repository, object_dir);
-
        midx_name = get_midx_filename(object_dir);
        if (safe_create_leading_directories(midx_name))
                die_errno(_("unable to create leading directories of %s"),
@@ -1140,12 +1152,7 @@ static int write_midx_internal(const char *object_dir,
                 * packs to include, since all packs and objects are copied
                 * blindly from an existing MIDX if one is present.
                 */
-               for (cur = get_multi_pack_index(the_repository); cur; cur = cur->next) {
-                       if (!strcmp(object_dir, cur->object_dir)) {
-                               ctx.m = cur;
-                               break;
-                       }
-               }
+               ctx.m = lookup_multi_pack_index(the_repository, object_dir);
        }
 
        if (ctx.m && !midx_checksum_valid(ctx.m)) {
@@ -1416,7 +1423,8 @@ static int write_midx_internal(const char *object_dir,
        if (ctx.m)
                close_object_store(the_repository->objects);
 
-       commit_lock_file(&lk);
+       if (commit_lock_file(&lk) < 0)
+               die_errno(_("could not write multi-pack-index"));
 
        clear_midx_files_ext(object_dir, ".bitmap", midx_hash);
        clear_midx_files_ext(object_dir, ".rev", midx_hash);
@@ -1689,7 +1697,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
 {
        uint32_t i, *count, result = 0;
        struct string_list packs_to_drop = STRING_LIST_INIT_DUP;
-       struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+       struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
        struct progress *progress = NULL;
 
        if (!m)
@@ -1734,12 +1742,11 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
 
        free(count);
 
-       if (packs_to_drop.nr) {
+       if (packs_to_drop.nr)
                result = write_midx_internal(object_dir, NULL, &packs_to_drop, NULL, NULL, flags);
-               m = NULL;
-       }
 
        string_list_clear(&packs_to_drop, 0);
+
        return result;
 }
 
@@ -1855,7 +1862,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
        struct child_process cmd = CHILD_PROCESS_INIT;
        FILE *cmd_in;
        struct strbuf base_name = STRBUF_INIT;
-       struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+       struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir);
 
        /*
         * When updating the default for these configuration
@@ -1927,11 +1934,8 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
        }
 
        result = write_midx_internal(object_dir, NULL, NULL, NULL, NULL, flags);
-       m = NULL;
 
 cleanup:
-       if (m)
-               close_midx(m);
        free(include_pack);
        return result;
 }
index 112d9b4badcfdeb2667aea1f2ea07c3adb6ea8e7..02b79702748d1f163bb84f925fde45e49c3bd4b6 100644 (file)
@@ -1016,9 +1016,11 @@ void *xmmap(void *start, size_t length,
  * the streaming interface and rehash it to do the same.
  */
 int check_object_signature(struct repository *r, const struct object_id *oid,
-                          void *map, unsigned long size, const char *type)
+                          void *map, unsigned long size, const char *type,
+                          struct object_id *real_oidp)
 {
-       struct object_id real_oid;
+       struct object_id tmp;
+       struct object_id *real_oid = real_oidp ? real_oidp : &tmp;
        enum object_type obj_type;
        struct git_istream *st;
        git_hash_ctx c;
@@ -1026,8 +1028,8 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
        int hdrlen;
 
        if (map) {
-               hash_object_file(r->hash_algo, map, size, type, &real_oid);
-               return !oideq(oid, &real_oid) ? -1 : 0;
+               hash_object_file(r->hash_algo, map, size, type, real_oid);
+               return !oideq(oid, real_oid) ? -1 : 0;
        }
 
        st = open_istream(r, oid, &obj_type, &size, NULL);
@@ -1052,9 +1054,9 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
                        break;
                r->hash_algo->update_fn(&c, buf, readlen);
        }
-       r->hash_algo->final_oid_fn(&real_oid, &c);
+       r->hash_algo->final_oid_fn(real_oid, &c);
        close_istream(st);
-       return !oideq(oid, &real_oid) ? -1 : 0;
+       return !oideq(oid, real_oid) ? -1 : 0;
 }
 
 int git_open_cloexec(const char *name, int flags)
@@ -1187,11 +1189,14 @@ void *map_loose_object(struct repository *r,
        return map_loose_object_1(r, NULL, oid, size);
 }
 
-static int unpack_loose_short_header(git_zstream *stream,
-                                    unsigned char *map, unsigned long mapsize,
-                                    void *buffer, unsigned long bufsiz)
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+                                                   unsigned char *map,
+                                                   unsigned long mapsize,
+                                                   void *buffer,
+                                                   unsigned long bufsiz,
+                                                   struct strbuf *header)
 {
-       int ret;
+       int status;
 
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
@@ -1202,43 +1207,24 @@ static int unpack_loose_short_header(git_zstream *stream,
 
        git_inflate_init(stream);
        obj_read_unlock();
-       ret = git_inflate(stream, 0);
+       status = git_inflate(stream, 0);
        obj_read_lock();
-
-       return ret;
-}
-
-int unpack_loose_header(git_zstream *stream,
-                       unsigned char *map, unsigned long mapsize,
-                       void *buffer, unsigned long bufsiz)
-{
-       int status = unpack_loose_short_header(stream, map, mapsize,
-                                              buffer, bufsiz);
-
        if (status < Z_OK)
-               return status;
-
-       /* Make sure we have the terminating NUL */
-       if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
-               return -1;
-       return 0;
-}
-
-static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map,
-                                        unsigned long mapsize, void *buffer,
-                                        unsigned long bufsiz, struct strbuf *header)
-{
-       int status;
-
-       status = unpack_loose_short_header(stream, map, mapsize, buffer, bufsiz);
-       if (status < Z_OK)
-               return -1;
+               return ULHR_BAD;
 
        /*
         * Check if entire header is unpacked in the first iteration.
         */
        if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
-               return 0;
+               return ULHR_OK;
+
+       /*
+        * We have a header longer than MAX_HEADER_LEN. The "header"
+        * here is only non-NULL when we run "cat-file
+        * --allow-unknown-type".
+        */
+       if (!header)
+               return ULHR_TOO_LONG;
 
        /*
         * buffer[0..bufsiz] was not large enough.  Copy the partial
@@ -1259,7 +1245,7 @@ static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map
                stream->next_out = buffer;
                stream->avail_out = bufsiz;
        } while (status != Z_STREAM_END);
-       return -1;
+       return ULHR_TOO_LONG;
 }
 
 static void *unpack_loose_rest(git_zstream *stream,
@@ -1317,8 +1303,7 @@ static void *unpack_loose_rest(git_zstream *stream,
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
-                                      unsigned int flags)
+int parse_loose_header(const char *hdr, struct object_info *oi)
 {
        const char *type_buf = hdr;
        unsigned long size;
@@ -1340,15 +1325,6 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
        type = type_from_string_gently(type_buf, type_len, 1);
        if (oi->type_name)
                strbuf_add(oi->type_name, type_buf, type_len);
-       /*
-        * Set type to 0 if its an unknown object and
-        * we're obtaining the type using '--allow-unknown-type'
-        * option.
-        */
-       if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0))
-               type = 0;
-       else if (type < 0)
-               die(_("invalid object type"));
        if (oi->typep)
                *oi->typep = type;
 
@@ -1375,15 +1351,14 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
        /*
         * The length must be followed by a zero byte
         */
-       return *hdr ? -1 : type;
-}
-
-int parse_loose_header(const char *hdr, unsigned long *sizep)
-{
-       struct object_info oi = OBJECT_INFO_INIT;
+       if (*hdr)
+               return -1;
 
-       oi.sizep = sizep;
-       return parse_loose_header_extended(hdr, &oi, 0);
+       /*
+        * The format is valid, but the type may still be bogus. The
+        * Caller needs to check its oi->typep.
+        */
+       return 0;
 }
 
 static int loose_object_info(struct repository *r,
@@ -1397,6 +1372,8 @@ static int loose_object_info(struct repository *r,
        char hdr[MAX_HEADER_LEN];
        struct strbuf hdrbuf = STRBUF_INIT;
        unsigned long size_scratch;
+       enum object_type type_scratch;
+       int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
 
        if (oi->delta_base_oid)
                oidclr(oi->delta_base_oid);
@@ -1427,43 +1404,48 @@ static int loose_object_info(struct repository *r,
 
        if (!oi->sizep)
                oi->sizep = &size_scratch;
+       if (!oi->typep)
+               oi->typep = &type_scratch;
 
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
-       if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) {
-               if (unpack_loose_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
-                       status = error(_("unable to unpack %s header with --allow-unknown-type"),
-                                      oid_to_hex(oid));
-       } else if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
+
+       switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
+                                   allow_unknown ? &hdrbuf : NULL)) {
+       case ULHR_OK:
+               if (parse_loose_header(hdrbuf.len ? hdrbuf.buf : hdr, oi) < 0)
+                       status = error(_("unable to parse %s header"), oid_to_hex(oid));
+               else if (!allow_unknown && *oi->typep < 0)
+                       die(_("invalid object type"));
+
+               if (!oi->contentp)
+                       break;
+               *oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
+               if (*oi->contentp)
+                       goto cleanup;
+
+               status = -1;
+               break;
+       case ULHR_BAD:
                status = error(_("unable to unpack %s header"),
                               oid_to_hex(oid));
-       if (status < 0)
-               ; /* Do nothing */
-       else if (hdrbuf.len) {
-               if ((status = parse_loose_header_extended(hdrbuf.buf, oi, flags)) < 0)
-                       status = error(_("unable to parse %s header with --allow-unknown-type"),
-                                      oid_to_hex(oid));
-       } else if ((status = parse_loose_header_extended(hdr, oi, flags)) < 0)
-               status = error(_("unable to parse %s header"), oid_to_hex(oid));
-
-       if (status >= 0 && oi->contentp) {
-               *oi->contentp = unpack_loose_rest(&stream, hdr,
-                                                 *oi->sizep, oid);
-               if (!*oi->contentp) {
-                       git_inflate_end(&stream);
-                       status = -1;
-               }
-       } else
-               git_inflate_end(&stream);
+               break;
+       case ULHR_TOO_LONG:
+               status = error(_("header for %s too long, exceeds %d bytes"),
+                              oid_to_hex(oid), MAX_HEADER_LEN);
+               break;
+       }
 
+       git_inflate_end(&stream);
+cleanup:
        munmap(map, mapsize);
-       if (status && oi->typep)
-               *oi->typep = status;
        if (oi->sizep == &size_scratch)
                oi->sizep = NULL;
        strbuf_release(&hdrbuf);
+       if (oi->typep == &type_scratch)
+               oi->typep = NULL;
        oi->whence = OI_LOOSE;
-       return (status < 0) ? status : 0;
+       return status;
 }
 
 int obj_read_use_lock = 0;
@@ -1546,7 +1528,14 @@ static int do_oid_object_info_extended(struct repository *r,
                                break;
                }
 
-               if (register_all_submodule_odb_as_alternates())
+               /*
+                * If r is the_repository, this might be an attempt at
+                * accessing a submodule object as if it were in the_repository
+                * (having called add_submodule_odb() on that submodule's ODB).
+                * If any such ODBs exist, register them and try again.
+                */
+               if (r == the_repository &&
+                   register_all_submodule_odb_as_alternates())
                        /* We added some alternates; retry */
                        continue;
 
@@ -1873,7 +1862,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
 
 static int write_loose_object(const struct object_id *oid, char *hdr,
                              int hdrlen, const void *buf, unsigned long len,
-                             time_t mtime)
+                             time_t mtime, unsigned flags)
 {
        int fd, ret;
        unsigned char compressed[4096];
@@ -1887,7 +1876,9 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
 
        fd = create_tmpfile(&tmp_file, filename.buf);
        if (fd < 0) {
-               if (errno == EACCES)
+               if (flags & HASH_SILENT)
+                       return -1;
+               else if (errno == EACCES)
                        return error(_("insufficient permission for adding an object to repository database %s"), get_object_directory());
                else
                        return error_errno(_("unable to create temporary file"));
@@ -1937,7 +1928,8 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
                struct utimbuf utb;
                utb.actime = mtime;
                utb.modtime = mtime;
-               if (utime(tmp_file.buf, &utb) < 0)
+               if (utime(tmp_file.buf, &utb) < 0 &&
+                   !(flags & HASH_SILENT))
                        warning_errno(_("failed utime() on %s"), tmp_file.buf);
        }
 
@@ -1962,8 +1954,9 @@ static int freshen_packed_object(const struct object_id *oid)
        return 1;
 }
 
-int write_object_file(const void *buf, unsigned long len, const char *type,
-                     struct object_id *oid)
+int write_object_file_flags(const void *buf, unsigned long len,
+                           const char *type, struct object_id *oid,
+                           unsigned flags)
 {
        char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
@@ -1975,7 +1968,7 @@ int write_object_file(const void *buf, unsigned long len, const char *type,
                                  &hdrlen);
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                return 0;
-       return write_loose_object(oid, hdr, hdrlen, buf, len, 0);
+       return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
 }
 
 int hash_object_file_literally(const void *buf, unsigned long len,
@@ -1995,7 +1988,7 @@ int hash_object_file_literally(const void *buf, unsigned long len,
                goto cleanup;
        if (freshen_packed_object(oid) || freshen_loose_object(oid))
                goto cleanup;
-       status = write_loose_object(oid, header, hdrlen, buf, len, 0);
+       status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
 
 cleanup:
        free(header);
@@ -2017,7 +2010,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
        if (!buf)
                return error(_("cannot read object for %s"), oid_to_hex(oid));
        hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
-       ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime);
+       ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
        free(buf);
 
        return ret;
@@ -2524,17 +2517,16 @@ static int check_stream_oid(git_zstream *stream,
 
 int read_loose_object(const char *path,
                      const struct object_id *expected_oid,
-                     enum object_type *type,
-                     unsigned long *size,
-                     void **contents)
+                     struct object_id *real_oid,
+                     void **contents,
+                     struct object_info *oi)
 {
        int ret = -1;
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
        char hdr[MAX_HEADER_LEN];
-
-       *contents = NULL;
+       unsigned long *size = oi->sizep;
 
        map = map_loose_object_1(the_repository, path, NULL, &mapsize);
        if (!map) {
@@ -2542,19 +2534,19 @@ int read_loose_object(const char *path,
                goto out;
        }
 
-       if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) {
+       if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
+                               NULL) < 0) {
                error(_("unable to unpack header of %s"), path);
                goto out;
        }
 
-       *type = parse_loose_header(hdr, size);
-       if (*type < 0) {
+       if (parse_loose_header(hdr, oi) < 0) {
                error(_("unable to parse header of %s"), path);
                git_inflate_end(&stream);
                goto out;
        }
 
-       if (*type == OBJ_BLOB && *size > big_file_threshold) {
+       if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
                if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
                        goto out;
        } else {
@@ -2565,10 +2557,7 @@ int read_loose_object(const char *path,
                        goto out;
                }
                if (check_object_signature(the_repository, expected_oid,
-                                          *contents, *size,
-                                          type_name(*type))) {
-                       error(_("hash mismatch for %s (expected %s)"), path,
-                             oid_to_hex(expected_oid));
+                                          *contents, *size, oi->type_name->buf, real_oid)) {
                        free(*contents);
                        goto out;
                }
index 1e647a5be3033ab34239545674cc668b4b0969aa..952efb6a4be25bbf6947b789246eedc0a09bdcd5 100644 (file)
@@ -223,8 +223,14 @@ int hash_object_file(const struct git_hash_algo *algo, const void *buf,
                     unsigned long len, const char *type,
                     struct object_id *oid);
 
-int write_object_file(const void *buf, unsigned long len,
-                     const char *type, struct object_id *oid);
+int write_object_file_flags(const void *buf, unsigned long len,
+                           const char *type, struct object_id *oid,
+                           unsigned flags);
+static inline int write_object_file(const void *buf, unsigned long len,
+                                   const char *type, struct object_id *oid)
+{
+       return write_object_file_flags(buf, len, type, oid, 0);
+}
 
 int hash_object_file_literally(const void *buf, unsigned long len,
                               const char *type, struct object_id *oid,
@@ -245,6 +251,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
 
 /*
  * Open the loose object at path, check its hash, and return the contents,
+ * use the "oi" argument to assert things about the object, or e.g. populate its
  * type, and size. If the object is a blob, then "contents" may return NULL,
  * to allow streaming of large blobs.
  *
@@ -252,9 +259,9 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
  */
 int read_loose_object(const char *path,
                      const struct object_id *expected_oid,
-                     enum object_type *type,
-                     unsigned long *size,
-                     void **contents);
+                     struct object_id *real_oid,
+                     void **contents,
+                     struct object_info *oi);
 
 /* Retry packed storage after checking packed and loose storage */
 #define HAS_OBJECT_RECHECK_PACKED 1
index 4e85955a941168bc4bcd0f97ee98353402561dd1..23a24e678a8e33f82b417fa6f005aae7de7bc29f 100644 (file)
--- a/object.c
+++ b/object.c
@@ -279,7 +279,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
        if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
            (!obj && repo_has_object_file(r, oid) &&
             oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
-               if (check_object_signature(r, repl, NULL, 0, NULL) < 0) {
+               if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) {
                        error(_("hash mismatch %s"), oid_to_hex(oid));
                        return NULL;
                }
@@ -290,7 +290,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
        buffer = repo_read_object_file(r, oid, &type, &size);
        if (buffer) {
                if (check_object_signature(r, repl, buffer, size,
-                                          type_name(type)) < 0) {
+                                          type_name(type), NULL) < 0) {
                        free(buffer);
                        error(_("hash mismatch %s"), oid_to_hex(repl));
                        return NULL;
index c8e560d71ab7e558da1a332d0640e6a71df5ac8c..3f418e3a6afb8ec7a8b401dcca89d5550813748f 100644 (file)
@@ -142,7 +142,8 @@ static int verify_packfile(struct repository *r,
                        err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
                                    oid_to_hex(&oid), p->pack_name,
                                    (uintmax_t)entries[i].offset);
-               else if (check_object_signature(r, &oid, data, size, type_name(type)))
+               else if (check_object_signature(r, &oid, data, size,
+                                               type_name(type), NULL))
                        err = error("packed %s from %s is corrupt",
                                    oid_to_hex(&oid), p->pack_name);
                else if (fn) {
index ddc0ff3c0646aed54724fe1b39753c28e50da3fc..ed9c999520703be35f3e022966b496943ef9c3f5 100644 (file)
@@ -603,8 +603,7 @@ static void gather_results_from_workers(struct pc_worker *workers,
                                continue;
 
                        if (pfd->revents & POLLIN) {
-                               int len = packet_read(pfd->fd, NULL, NULL,
-                                                     packet_buffer,
+                               int len = packet_read(pfd->fd, packet_buffer,
                                                      sizeof(packet_buffer), 0);
 
                                if (len < 0) {
index 6e0535bdaadda9ecf040f3cf0baec64ed593c848..9a0484c883145b2f056172d84f8a549b65a4bb3e 100644 (file)
@@ -8,10 +8,13 @@
 
 static int disallow_abbreviated_options;
 
-#define OPT_SHORT 1
-#define OPT_UNSET 2
+enum opt_parsed {
+       OPT_LONG  = 0,
+       OPT_SHORT = 1<<0,
+       OPT_UNSET = 1<<1,
+};
 
-int optbug(const struct option *opt, const char *reason)
+static int optbug(const struct option *opt, const char *reason)
 {
        if (opt->long_name) {
                if (opt->short_name)
@@ -22,9 +25,26 @@ int optbug(const struct option *opt, const char *reason)
        return error("BUG: switch '%c' %s", opt->short_name, reason);
 }
 
+static const char *optname(const struct option *opt, enum opt_parsed flags)
+{
+       static struct strbuf sb = STRBUF_INIT;
+
+       strbuf_reset(&sb);
+       if (flags & OPT_SHORT)
+               strbuf_addf(&sb, "switch `%c'", opt->short_name);
+       else if (flags & OPT_UNSET)
+               strbuf_addf(&sb, "option `no-%s'", opt->long_name);
+       else if (flags == OPT_LONG)
+               strbuf_addf(&sb, "option `%s'", opt->long_name);
+       else
+               BUG("optname() got unknown flags %d", flags);
+
+       return sb.buf;
+}
+
 static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
                                     const struct option *opt,
-                                    int flags, const char **arg)
+                                    enum opt_parsed flags, const char **arg)
 {
        if (p->opt) {
                *arg = p->opt;
@@ -50,7 +70,7 @@ static void fix_filename(const char *prefix, const char **file)
 static enum parse_opt_result opt_command_mode_error(
        const struct option *opt,
        const struct option *all_opts,
-       int flags)
+       enum opt_parsed flags)
 {
        const struct option *that;
        struct strbuf that_name = STRBUF_INIT;
@@ -82,7 +102,7 @@ static enum parse_opt_result opt_command_mode_error(
 static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
                                       const struct option *opt,
                                       const struct option *all_opts,
-                                      int flags)
+                                      enum opt_parsed flags)
 {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
@@ -298,11 +318,11 @@ static enum parse_opt_result parse_long_opt(
        const struct option *all_opts = options;
        const char *arg_end = strchrnul(arg, '=');
        const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
-       int abbrev_flags = 0, ambiguous_flags = 0;
+       enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
 
        for (; options->type != OPTION_END; options++) {
                const char *rest, *long_name = options->long_name;
-               int flags = 0, opt_flags = 0;
+               enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG;
 
                if (!long_name)
                        continue;
@@ -481,7 +501,8 @@ static void parse_options_check(const struct option *opts)
 
 static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
                                  int argc, const char **argv, const char *prefix,
-                                 const struct option *options, int flags)
+                                 const struct option *options,
+                                 enum parse_opt_flags flags)
 {
        ctx->argc = argc;
        ctx->argv = argv;
@@ -506,7 +527,8 @@ static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
                         int argc, const char **argv, const char *prefix,
-                        const struct option *options, int flags)
+                        const struct option *options,
+                        enum parse_opt_flags flags)
 {
        memset(ctx, 0, sizeof(*ctx));
        parse_options_start_1(ctx, argc, argv, prefix, options, flags);
@@ -697,13 +719,14 @@ static void free_preprocessed_options(struct option *options)
        free(options);
 }
 
-static int usage_with_options_internal(struct parse_opt_ctx_t *,
-                                      const char * const *,
-                                      const struct option *, int, int);
+static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *,
+                                                        const char * const *,
+                                                        const struct option *,
+                                                        int, int);
 
-int parse_options_step(struct parse_opt_ctx_t *ctx,
-                      const struct option *options,
-                      const char * const usagestr[])
+enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
+                                        const struct option *options,
+                                        const char * const usagestr[])
 {
        int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
 
@@ -837,9 +860,11 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
        return ctx->cpidx + ctx->argc;
 }
 
-int parse_options(int argc, const char **argv, const char *prefix,
-                 const struct option *options, const char * const usagestr[],
-                 int flags)
+enum parse_opt_result parse_options(int argc, const char **argv,
+                                   const char *prefix,
+                                   const struct option *options,
+                                   const char * const usagestr[],
+                                   enum parse_opt_flags flags)
 {
        struct parse_opt_ctx_t ctx;
        struct option *real_options;
@@ -861,7 +886,7 @@ int parse_options(int argc, const char **argv, const char *prefix,
        case PARSE_OPT_NON_OPTION:
        case PARSE_OPT_DONE:
                break;
-       default: /* PARSE_OPT_UNKNOWN */
+       case PARSE_OPT_UNKNOWN:
                if (ctx.argv[0][1] == '-') {
                        error(_("unknown option `%s'"), ctx.argv[0] + 2);
                } else if (isascii(*ctx.opt)) {
@@ -897,9 +922,10 @@ static int usage_argh(const struct option *opts, FILE *outfile)
 #define USAGE_OPTS_WIDTH 24
 #define USAGE_GAP         2
 
-static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
-                                      const char * const *usagestr,
-                                      const struct option *opts, int full, int err)
+static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
+                                                        const char * const *usagestr,
+                                                        const struct option *opts,
+                                                        int full, int err)
 {
        FILE *outfile = err ? stderr : stdout;
        int need_newline;
@@ -1052,18 +1078,3 @@ void NORETURN usage_msg_opt(const char *msg,
        fprintf(stderr, "fatal: %s\n\n", msg);
        usage_with_options(usagestr, options);
 }
-
-const char *optname(const struct option *opt, int flags)
-{
-       static struct strbuf sb = STRBUF_INIT;
-
-       strbuf_reset(&sb);
-       if (flags & OPT_SHORT)
-               strbuf_addf(&sb, "switch `%c'", opt->short_name);
-       else if (flags & OPT_UNSET)
-               strbuf_addf(&sb, "option `no-%s'", opt->long_name);
-       else
-               strbuf_addf(&sb, "option `%s'", opt->long_name);
-
-       return sb.buf;
-}
index 13405472ee82c9028cb441e20535631bf2993f33..bdea052c399ddb64e25010ec93e46ba1350c4649 100644 (file)
@@ -33,6 +33,7 @@ enum parse_opt_flags {
        PARSE_OPT_KEEP_UNKNOWN = 1 << 3,
        PARSE_OPT_NO_INTERNAL_HELP = 1 << 4,
        PARSE_OPT_ONE_SHOT = 1 << 5,
+       PARSE_OPT_SHELL_EVAL = 1 << 6,
 };
 
 enum parse_opt_option_flags {
@@ -44,7 +45,6 @@ enum parse_opt_option_flags {
        PARSE_OPT_NODASH = 1 << 5,
        PARSE_OPT_LITERAL_ARGHELP = 1 << 6,
        PARSE_OPT_FROM_ALIAS = 1 << 7,
-       PARSE_OPT_SHELL_EVAL = 1 << 8,
        PARSE_OPT_NOCOMPLETE = 1 << 9,
        PARSE_OPT_COMP_ARG = 1 << 10,
        PARSE_OPT_CMDMODE = 1 << 11,
@@ -134,7 +134,7 @@ struct option {
        const char *argh;
        const char *help;
 
-       int flags;
+       enum parse_opt_option_flags flags;
        parse_opt_cb *callback;
        intptr_t defval;
        parse_opt_ll_cb *ll_callback;
@@ -213,9 +213,11 @@ struct option {
  * untouched and parse_options() returns the number of options
  * processed.
  */
-int parse_options(int argc, const char **argv, const char *prefix,
-                 const struct option *options,
-                 const char * const usagestr[], int flags);
+enum parse_opt_result parse_options(int argc, const char **argv,
+                                   const char *prefix,
+                                   const struct option *options,
+                                   const char * const usagestr[],
+                                   enum parse_opt_flags flags);
 
 NORETURN void usage_with_options(const char * const *usagestr,
                                 const struct option *options);
@@ -224,9 +226,6 @@ NORETURN void usage_msg_opt(const char *msg,
                            const char * const *usagestr,
                            const struct option *options);
 
-int optbug(const struct option *opt, const char *reason);
-const char *optname(const struct option *opt, int flags);
-
 /*
  * Use these assertions for callbacks that expect to be called with NONEG and
  * NOARG respectively, and do not otherwise handle the "unset" and "arg"
@@ -264,7 +263,7 @@ struct parse_opt_ctx_t {
        const char **out;
        int argc, cpidx, total;
        const char *opt;
-       int flags;
+       enum parse_opt_flags flags;
        const char *prefix;
        const char **alias_groups; /* must be in groups of 3 elements! */
        struct option *updated_options;
@@ -272,11 +271,12 @@ struct parse_opt_ctx_t {
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
                         int argc, const char **argv, const char *prefix,
-                        const struct option *options, int flags);
+                        const struct option *options,
+                        enum parse_opt_flags flags);
 
-int parse_options_step(struct parse_opt_ctx_t *ctx,
-                      const struct option *options,
-                      const char * const usagestr[]);
+enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
+                                        const struct option *options,
+                                        const char * const usagestr[]);
 
 int parse_options_end(struct parse_opt_ctx_t *ctx);
 
index de4a94b437e1d573ef9395314fbab2acfc38b887..2dc8ac274bd0c03b5612e73fb587dde911865f0d 100644 (file)
@@ -289,22 +289,6 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
        va_end(args);
 }
 
-void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len)
-{
-       size_t orig_len, n;
-
-       orig_len = buf->len;
-       strbuf_addstr(buf, "0000");
-       strbuf_add(buf, data, len);
-       n = buf->len - orig_len;
-
-       if (n > LARGE_PACKET_MAX)
-               die(_("protocol error: impossibly long line"));
-
-       set_packet_header(&buf->buf[orig_len], n);
-       packet_trace(data, len, 1);
-}
-
 int write_packetized_from_fd_no_flush(int fd_in, int fd_out)
 {
        char *buf = xmalloc(LARGE_PACKET_DATA_MAX);
@@ -453,38 +437,28 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
        return PACKET_READ_NORMAL;
 }
 
-int packet_read(int fd, char **src_buffer, size_t *src_len,
-               char *buffer, unsigned size, int options)
+int packet_read(int fd, char *buffer, unsigned size, int options)
 {
        int pktlen = -1;
 
-       packet_read_with_status(fd, src_buffer, src_len, buffer, size,
-                               &pktlen, options);
+       packet_read_with_status(fd, NULL, NULL, buffer, size, &pktlen,
+                               options);
 
        return pktlen;
 }
 
-static char *packet_read_line_generic(int fd,
-                                     char **src, size_t *src_len,
-                                     int *dst_len)
+char *packet_read_line(int fd, int *dst_len)
 {
-       int len = packet_read(fd, src, src_len,
-                             packet_buffer, sizeof(packet_buffer),
+       int len = packet_read(fd, packet_buffer, sizeof(packet_buffer),
                              PACKET_READ_CHOMP_NEWLINE);
        if (dst_len)
                *dst_len = len;
        return (len > 0) ? packet_buffer : NULL;
 }
 
-char *packet_read_line(int fd, int *len_p)
-{
-       return packet_read_line_generic(fd, NULL, NULL, len_p);
-}
-
 int packet_read_line_gently(int fd, int *dst_len, char **dst_line)
 {
-       int len = packet_read(fd, NULL, NULL,
-                             packet_buffer, sizeof(packet_buffer),
+       int len = packet_read(fd, packet_buffer, sizeof(packet_buffer),
                              PACKET_READ_CHOMP_NEWLINE|PACKET_READ_GENTLE_ON_EOF);
        if (dst_len)
                *dst_len = len;
@@ -493,11 +467,6 @@ int packet_read_line_gently(int fd, int *dst_len, char **dst_line)
        return len;
 }
 
-char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
-{
-       return packet_read_line_generic(-1, src, src_len, dst_len);
-}
-
 ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options)
 {
        int packet_len;
@@ -507,7 +476,7 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options)
 
        for (;;) {
                strbuf_grow(sb_out, LARGE_PACKET_DATA_MAX);
-               packet_len = packet_read(fd_in, NULL, NULL,
+               packet_len = packet_read(fd_in,
                        /* strbuf_grow() above always allocates one extra byte to
                         * store a '\0' at the end of the string. packet_read()
                         * writes a '\0' extra byte at the end, too. Let it know
index 82b95e4bdd3b775ad6b75e4c5cea1e1cc704546e..467ae01357301ee503c6f8a49430c7dd08a9ccdd 100644 (file)
@@ -29,7 +29,6 @@ void packet_buf_delim(struct strbuf *buf);
 void set_packet_header(char *buf, int size);
 void packet_write(int fd_out, const char *buf, size_t size);
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
-void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len);
 int packet_flush_gently(int fd);
 int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 int write_packetized_from_fd_no_flush(int fd_in, int fd_out);
@@ -88,8 +87,7 @@ void packet_fflush(FILE *f);
 #define PACKET_READ_CHOMP_NEWLINE        (1u<<1)
 #define PACKET_READ_DIE_ON_ERR_PACKET    (1u<<2)
 #define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
-int packet_read(int fd, char **src_buffer, size_t *src_len, char
-               *buffer, unsigned size, int options);
+int packet_read(int fd, char *buffer, unsigned size, int options);
 
 /*
  * Convert a four hex digit packet line length header into its numeric
@@ -138,12 +136,6 @@ char *packet_read_line(int fd, int *size);
  */
 int packet_read_line_gently(int fd, int *size, char **dst_line);
 
-/*
- * Same as packet_read_line, but read from a buf rather than a descriptor;
- * see packet_read for details on how src_* is used.
- */
-char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
-
 /*
  * Reads a stream of variable sized packets until a flush packet is detected.
  */
index 73b5ead5099d3efdb3e1999865ab868da5053ea6..fe95107ae5a4150797be620e254466f8dce6423f 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1436,8 +1436,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        check_commit_signature(c->commit, &(c->signature_check));
                switch (placeholder[1]) {
                case 'G':
-                       if (c->signature_check.gpg_output)
-                               strbuf_addstr(sb, c->signature_check.gpg_output);
+                       if (c->signature_check.output)
+                               strbuf_addstr(sb, c->signature_check.output);
                        break;
                case '?':
                        switch (c->signature_check.result) {
index 8a50ff66b3010a736db85f769007c64208590503..f398659662325d728c2cfe8efc95acf781e5d049 100644 (file)
@@ -738,7 +738,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        int intent_only = flags & ADD_CACHE_INTENT;
        int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
                          (intent_only ? ADD_CACHE_NEW_ONLY : 0));
-       int hash_flags = HASH_WRITE_OBJECT;
+       unsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;
        struct object_id oid;
 
        if (flags & ADD_CACHE_RENORMALIZE)
index add429be7973634ef99c26847b90e15b146d9f4d..282cdad1036e24eb1de57b97c810bbdbfda558fb 100644 (file)
@@ -2705,6 +2705,15 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+void ref_sorting_release(struct ref_sorting *sorting)
+{
+       while (sorting) {
+               struct ref_sorting *next = sorting->next;
+               free(sorting);
+               sorting = next;
+       }
+}
+
 int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
 {
        struct ref_filter *rf = opt->value;
index b636f4389d0507d870ece2bba0f0e6ceb5d951e7..6228458d30673c21a2b2eb6bf2537de4beea01fb 100644 (file)
@@ -127,6 +127,8 @@ void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *atom);
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
 /*  Default sort option based on refname */
 struct ref_sorting *ref_default_sorting(void);
+/* Release a "struct ref_sorting" */
+void ref_sorting_release(struct ref_sorting *);
 /*  Function to parse --merged and --no-merged options */
 int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
 /*  Get the current HEAD's description */
diff --git a/refs.c b/refs.c
index 7f019c2377effa49a90427ffec0451c400941e0c..d7cc0a23a3b65502242650cce698179e3cca27d4 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -251,12 +251,13 @@ int refname_is_safe(const char *refname)
  * does not exist, emit a warning and return false.
  */
 int ref_resolves_to_object(const char *refname,
+                          struct repository *repo,
                           const struct object_id *oid,
                           unsigned int flags)
 {
        if (flags & REF_ISBROKEN)
                return 0;
-       if (!has_object_file(oid)) {
+       if (!repo_has_object_file(repo, oid)) {
                error(_("%s does not point to a valid object!"), refname);
                return 0;
        }
@@ -1870,7 +1871,8 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,
  * Create, record, and return a ref_store instance for the specified
  * gitdir.
  */
-static struct ref_store *ref_store_init(const char *gitdir,
+static struct ref_store *ref_store_init(struct repository *repo,
+                                       const char *gitdir,
                                        unsigned int flags)
 {
        const char *be_name = "files";
@@ -1880,7 +1882,7 @@ static struct ref_store *ref_store_init(const char *gitdir,
        if (!be)
                BUG("reference backend %s is unknown", be_name);
 
-       refs = be->init(gitdir, flags);
+       refs = be->init(repo, gitdir, flags);
        return refs;
 }
 
@@ -1892,7 +1894,7 @@ struct ref_store *get_main_ref_store(struct repository *r)
        if (!r->gitdir)
                BUG("attempting to get main_ref_store outside of repository");
 
-       r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+       r->refs_private = ref_store_init(r, r->gitdir, REF_STORE_ALL_CAPS);
        r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
        return r->refs_private;
 }
@@ -1922,6 +1924,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
        struct ref_store *refs;
        char *to_free = NULL;
        size_t len;
+       struct repository *subrepo;
 
        if (!submodule)
                return NULL;
@@ -1947,8 +1950,19 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
        if (submodule_to_gitdir(&submodule_sb, submodule))
                goto done;
 
-       /* assume that add_submodule_odb() has been called */
-       refs = ref_store_init(submodule_sb.buf,
+       subrepo = xmalloc(sizeof(*subrepo));
+       /*
+        * NEEDSWORK: Make get_submodule_ref_store() work with arbitrary
+        * superprojects other than the_repository. This probably should be
+        * done by making it take a struct repository * parameter instead of a
+        * submodule path.
+        */
+       if (repo_submodule_init(subrepo, the_repository, submodule,
+                               null_oid())) {
+               free(subrepo);
+               goto done;
+       }
+       refs = ref_store_init(subrepo, submodule_sb.buf,
                              REF_STORE_READ | REF_STORE_ODB);
        register_ref_store_map(&submodule_ref_stores, "submodule",
                               refs, submodule);
@@ -1974,10 +1988,12 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
                return refs;
 
        if (wt->id)
-               refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
+               refs = ref_store_init(the_repository,
+                                     git_common_path("worktrees/%s", wt->id),
                                      REF_STORE_ALL_CAPS);
        else
-               refs = ref_store_init(get_git_common_dir(),
+               refs = ref_store_init(the_repository,
+                                     get_git_common_dir(),
                                      REF_STORE_ALL_CAPS);
 
        if (refs)
index 6a6ead0b99bbd8b0326081516e3b6cac45366012..151b0056fe57d41acdacdcf4e8dfc16a43d82b96 100644 (file)
@@ -79,13 +79,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
  * Create a new submodule ref cache and add it to the internal
  * set of caches.
  */
-static struct ref_store *files_ref_store_create(const char *gitdir,
+static struct ref_store *files_ref_store_create(struct repository *repo,
+                                               const char *gitdir,
                                                unsigned int flags)
 {
        struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
        struct strbuf sb = STRBUF_INIT;
 
+       ref_store->repo = repo;
        ref_store->gitdir = xstrdup(gitdir);
        base_ref_store_init(ref_store, &refs_be_files);
        refs->store_flags = flags;
@@ -93,7 +95,7 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
        get_common_dir_noenv(&sb, gitdir);
        refs->gitcommondir = strbuf_detach(&sb, NULL);
        strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
-       refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+       refs->packed_ref_store = packed_ref_store_create(repo, sb.buf, flags);
        strbuf_release(&sb);
 
        chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
@@ -730,6 +732,7 @@ struct files_ref_iterator {
        struct ref_iterator base;
 
        struct ref_iterator *iter0;
+       struct repository *repo;
        unsigned int flags;
 };
 
@@ -751,6 +754,7 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
                    !ref_resolves_to_object(iter->iter0->refname,
+                                           iter->repo,
                                            iter->iter0->oid,
                                            iter->iter0->flags))
                        continue;
@@ -829,7 +833,7 @@ static struct ref_iterator *files_ref_iterator_begin(
         */
 
        loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
-                                             prefix, 1);
+                                             prefix, ref_store->repo, 1);
 
        /*
         * The packed-refs file might contain broken references, for
@@ -853,6 +857,7 @@ static struct ref_iterator *files_ref_iterator_begin(
        base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
                               overlay_iter->ordered);
        iter->iter0 = overlay_iter;
+       iter->repo = ref_store->repo;
        iter->flags = flags;
 
        return ref_iterator;
@@ -1169,7 +1174,7 @@ static int should_pack_ref(const char *refname,
                return 0;
 
        /* Do not pack broken refs: */
-       if (!ref_resolves_to_object(refname, oid, ref_flags))
+       if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
                return 0;
 
        return 1;
@@ -1192,7 +1197,8 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
 
        packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
 
-       iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
+       iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL,
+                                       the_repository, 0);
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
                /*
                 * If the loose reference can be packed, add an entry
index 47247a149178f364423f84572c5b2912772d10be..1c5211b03e48cf23fd3a4d02e8aa6278b24ce26f 100644 (file)
@@ -193,13 +193,15 @@ static int release_snapshot(struct snapshot *snapshot)
        }
 }
 
-struct ref_store *packed_ref_store_create(const char *path,
+struct ref_store *packed_ref_store_create(struct repository *repo,
+                                         const char *path,
                                          unsigned int store_flags)
 {
        struct packed_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
 
        base_ref_store_init(ref_store, &refs_be_packed);
+       ref_store->repo = repo;
        ref_store->gitdir = xstrdup(path);
        refs->store_flags = store_flags;
 
@@ -776,6 +778,7 @@ struct packed_ref_iterator {
        struct object_id oid, peeled;
        struct strbuf refname_buf;
 
+       struct repository *repo;
        unsigned int flags;
 };
 
@@ -864,8 +867,8 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
                        continue;
 
                if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
-                   !ref_resolves_to_object(iter->base.refname, &iter->oid,
-                                           iter->flags))
+                   !ref_resolves_to_object(iter->base.refname, iter->repo,
+                                           &iter->oid, iter->flags))
                        continue;
 
                return ITER_OK;
@@ -883,6 +886,9 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
        struct packed_ref_iterator *iter =
                (struct packed_ref_iterator *)ref_iterator;
 
+       if (iter->repo != the_repository)
+               BUG("peeling for non-the_repository is not supported");
+
        if ((iter->base.flags & REF_KNOWS_PEELED)) {
                oidcpy(peeled, &iter->peeled);
                return is_null_oid(&iter->peeled) ? -1 : 0;
@@ -954,6 +960,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
 
        iter->base.oid = &iter->oid;
 
+       iter->repo = ref_store->repo;
        iter->flags = flags;
 
        if (prefix && *prefix)
index a01a0aff9c77280d7b3e605230af822fdab24ca9..f61a73ec25b4cb55d96bac23ada11a53e2781f95 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef REFS_PACKED_BACKEND_H
 #define REFS_PACKED_BACKEND_H
 
+struct repository;
 struct ref_transaction;
 
 /*
@@ -12,7 +13,8 @@ struct ref_transaction;
  * even among packed refs.
  */
 
-struct ref_store *packed_ref_store_create(const char *path,
+struct ref_store *packed_ref_store_create(struct repository *repo,
+                                         const char *path,
                                          unsigned int store_flags);
 
 /*
index a5ad8a39fb405430f7d5f9ffd18af6472c3b215f..be4aa5e09818fad082a7c15303746edd8a5ddcaf 100644 (file)
@@ -378,6 +378,8 @@ struct cache_ref_iterator {
         * on from there.)
         */
        struct cache_ref_iterator_level *levels;
+
+       struct repository *repo;
 };
 
 static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -434,6 +436,11 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
                                   struct object_id *peeled)
 {
+       struct cache_ref_iterator *iter =
+               (struct cache_ref_iterator *)ref_iterator;
+
+       if (iter->repo != the_repository)
+               BUG("peeling for non-the_repository is not supported");
        return peel_object(ref_iterator->oid, peeled) ? -1 : 0;
 }
 
@@ -456,6 +463,7 @@ static struct ref_iterator_vtable cache_ref_iterator_vtable = {
 
 struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
                                              const char *prefix,
+                                             struct repository *repo,
                                              int prime_dir)
 {
        struct ref_dir *dir;
@@ -490,5 +498,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
                level->prefix_state = PREFIX_CONTAINS_DIR;
        }
 
+       iter->repo = repo;
+
        return ref_iterator;
 }
index 5c042ae718cb370ddba39e27f9f972636384bace..850d9d3744e94270fb5cd445b4f6b7b18aac8128 100644 (file)
@@ -214,6 +214,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname);
  */
 struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
                                              const char *prefix,
+                                             struct repository *repo,
                                              int prime_dir);
 
 #endif /* REFS_REF_CACHE_H */
index 72746407fc3dd4df57d17894967cece6804bf0d5..12224742ede8f01d4f8d88ceaaa2e326413bf674 100644 (file)
@@ -66,6 +66,7 @@ int refname_is_safe(const char *refname);
  * referred-to object does not exist, emit a warning and return false.
  */
 int ref_resolves_to_object(const char *refname,
+                          struct repository *repo,
                           const struct object_id *oid,
                           unsigned int flags);
 
@@ -539,7 +540,8 @@ struct ref_store;
  * should call base_ref_store_init() to initialize the shared part of
  * the ref_store and to record the ref_store for later lookup.
  */
-typedef struct ref_store *ref_store_init_fn(const char *gitdir,
+typedef struct ref_store *ref_store_init_fn(struct repository *repo,
+                                           const char *gitdir,
                                            unsigned int flags);
 
 typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
@@ -701,7 +703,12 @@ struct ref_store {
        /* The backend describing this ref_store's storage scheme: */
        const struct ref_storage_be *be;
 
-       /* The gitdir that this ref_store applies to: */
+       struct repository *repo;
+
+       /*
+        * The gitdir that this ref_store applies to. Note that this is not
+        * necessarily repo->gitdir if the repo has multiple worktrees.
+        */
        char *gitdir;
 };
 
index 5975103b96af34959713697f18de8ededf9272c5..d69156312bda65c7f081a0bfd8c91b043f676487 100644 (file)
@@ -1088,7 +1088,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
                rpc->protocol_header = NULL;
 
        while (!err) {
-               int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
+               int n = packet_read(rpc->out, rpc->buf, rpc->alloc, 0);
                if (!n)
                        break;
                rpc->pos = 0;
index b3a495b7b1998f992b69a4b6a37613a8a7a61dba..bc0fcdbb000769935e9f080164d8e02e81dbb8b3 100644 (file)
@@ -341,13 +341,13 @@ static int generate_push_cert(struct strbuf *req_buf,
 {
        const struct ref *ref;
        struct string_list_item *item;
-       char *signing_key = xstrdup(get_signing_key());
+       char *signing_key_id = xstrdup(get_signing_key_id());
        const char *cp, *np;
        struct strbuf cert = STRBUF_INIT;
        int update_seen = 0;
 
        strbuf_addstr(&cert, "certificate version 0.1\n");
-       strbuf_addf(&cert, "pusher %s ", signing_key);
+       strbuf_addf(&cert, "pusher %s ", signing_key_id);
        datestamp(&cert);
        strbuf_addch(&cert, '\n');
        if (args->url && *args->url) {
@@ -374,7 +374,7 @@ static int generate_push_cert(struct strbuf *req_buf,
        if (!update_seen)
                goto free_return;
 
-       if (sign_buffer(&cert, &cert, signing_key))
+       if (sign_buffer(&cert, &cert, get_signing_key()))
                die(_("failed to sign the push certificate"));
 
        packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);
@@ -386,7 +386,7 @@ static int generate_push_cert(struct strbuf *req_buf,
        packet_buf_write(req_buf, "push-cert-end\n");
 
 free_return:
-       free(signing_key);
+       free(signing_key_id);
        strbuf_release(&cert);
        return update_seen;
 }
index 74ecb23d20e730b2716ba0f9b17d7c8d494c1779..cd2aabf1f765508b13e748dcffda8073da6b9d55 100644 (file)
@@ -3645,9 +3645,9 @@ static int do_reset(struct repository *r,
        struct strbuf ref_name = STRBUF_INIT;
        struct object_id oid;
        struct lock_file lock = LOCK_INIT;
-       struct tree_desc desc;
+       struct tree_desc desc = { 0 };
        struct tree *tree;
-       struct unpack_trees_options unpack_tree_opts;
+       struct unpack_trees_options unpack_tree_opts = { 0 };
        int ret = 0;
 
        if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0)
@@ -3679,14 +3679,11 @@ static int do_reset(struct repository *r,
                strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
                if (get_oid(ref_name.buf, &oid) &&
                    get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) {
-                       error(_("could not read '%s'"), ref_name.buf);
-                       rollback_lock_file(&lock);
-                       strbuf_release(&ref_name);
-                       return -1;
+                       ret = error(_("could not read '%s'"), ref_name.buf);
+                       goto cleanup;
                }
        }
 
-       memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
        setup_unpack_trees_porcelain(&unpack_tree_opts, "reset");
        unpack_tree_opts.head_idx = 1;
        unpack_tree_opts.src_index = r->index;
@@ -3698,24 +3695,18 @@ static int do_reset(struct repository *r,
        init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL);
 
        if (repo_read_index_unmerged(r)) {
-               rollback_lock_file(&lock);
-               strbuf_release(&ref_name);
-               return error_resolve_conflict(_(action_name(opts)));
+               ret = error_resolve_conflict(_(action_name(opts)));
+               goto cleanup;
        }
 
        if (!fill_tree_descriptor(r, &desc, &oid)) {
-               error(_("failed to find tree of %s"), oid_to_hex(&oid));
-               rollback_lock_file(&lock);
-               free((void *)desc.buffer);
-               strbuf_release(&ref_name);
-               return -1;
+               ret = error(_("failed to find tree of %s"), oid_to_hex(&oid));
+               goto cleanup;
        }
 
        if (unpack_trees(1, &desc, &unpack_tree_opts)) {
-               rollback_lock_file(&lock);
-               free((void *)desc.buffer);
-               strbuf_release(&ref_name);
-               return -1;
+               ret = -1;
+               goto cleanup;
        }
 
        tree = parse_tree_indirect(&oid);
@@ -3723,14 +3714,17 @@ static int do_reset(struct repository *r,
 
        if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
                ret = error(_("could not write index"));
-       free((void *)desc.buffer);
 
        if (!ret)
                ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
                                                len, name), "HEAD", &oid,
                                 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-
+cleanup:
+       free((void *)desc.buffer);
+       if (ret < 0)
+               rollback_lock_file(&lock);
        strbuf_release(&ref_name);
+       clear_unpack_trees_porcelain(&unpack_tree_opts);
        return ret;
 }
 
index c8a5789694cf805799a288b66e517bedf786e479..b22e9816559cabc4f3d94f7f42cde5a50f32495a 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1059,15 +1059,21 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
-                             int abbrev_len)
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+                                  const struct object_id *oid, int abbrev_len)
 {
        int r;
        strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
-       r = find_unique_abbrev_r(sb->buf + sb->len, oid, abbrev_len);
+       r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
        strbuf_setlen(sb, sb->len + r);
 }
 
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+                             int abbrev_len)
+{
+       strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
+}
+
 /*
  * Returns the length of a line, without trailing spaces.
  *
index 3b36bbc49f08b2fa38fb97994053ed74b44ef766..96512f85b316e7f3b56df94cd1e7912dbb6020f0 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -634,8 +634,10 @@ void strbuf_list_free(struct strbuf **list);
  * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
  * the strbuf `sb`.
  */
-void strbuf_add_unique_abbrev(struct strbuf *sb,
-                             const struct object_id *oid,
+struct repository;
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+                                  const struct object_id *oid, int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
                              int abbrev_len);
 
 /**
index 5f480ad50c415cc48a591adb29b46f00b67468d7..fe54665d86e72a3400a6f60f610a5264bc0f0667 100644 (file)
@@ -223,19 +223,24 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
                              const struct object_id *oid,
                              enum object_type *type)
 {
+       struct object_info oi = OBJECT_INFO_INIT;
+       oi.sizep = &st->size;
+       oi.typep = type;
+
        st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
        if (!st->u.loose.mapped)
                return -1;
-       if ((unpack_loose_header(&st->z,
-                                st->u.loose.mapped,
-                                st->u.loose.mapsize,
-                                st->u.loose.hdr,
-                                sizeof(st->u.loose.hdr)) < 0) ||
-           (parse_loose_header(st->u.loose.hdr, &st->size) < 0)) {
-               git_inflate_end(&st->z);
-               munmap(st->u.loose.mapped, st->u.loose.mapsize);
-               return -1;
+       switch (unpack_loose_header(&st->z, st->u.loose.mapped,
+                                   st->u.loose.mapsize, st->u.loose.hdr,
+                                   sizeof(st->u.loose.hdr), NULL)) {
+       case ULHR_OK:
+               break;
+       case ULHR_BAD:
+       case ULHR_TOO_LONG:
+               goto error;
        }
+       if (parse_loose_header(st->u.loose.hdr, &oi) < 0 || *type < 0)
+               goto error;
 
        st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
        st->u.loose.hdr_avail = st->z.total_out;
@@ -244,6 +249,10 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
        st->read = read_istream_loose;
 
        return 0;
+error:
+       git_inflate_end(&st->z);
+       munmap(st->u.loose.mapped, st->u.loose.mapsize);
+       return -1;
 }
 
 
index f3c99634a974a5e3f2f4b68836782ad74a2fbd1d..c689070524171b8e6cfae3349cb317c18f693664 100644 (file)
@@ -201,6 +201,8 @@ int register_all_submodule_odb_as_alternates(void)
                add_to_alternates_memory(added_submodule_odb_paths.items[i].string);
        if (ret) {
                string_list_clear(&added_submodule_odb_paths, 0);
+               trace2_data_intmax("submodule", the_repository,
+                                  "register_all_submodule_odb_as_alternates/registered", ret);
                if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0))
                        BUG("register_all_submodule_odb_as_alternates() called");
        }
@@ -928,23 +930,33 @@ struct has_commit_data {
 static int check_has_commit(const struct object_id *oid, void *data)
 {
        struct has_commit_data *cb = data;
+       struct repository subrepo;
+       enum object_type type;
 
-       enum object_type type = oid_object_info(cb->repo, oid, NULL);
+       if (repo_submodule_init(&subrepo, cb->repo, cb->path, null_oid())) {
+               cb->result = 0;
+               goto cleanup;
+       }
+
+       type = oid_object_info(&subrepo, oid, NULL);
 
        switch (type) {
        case OBJ_COMMIT:
-               return 0;
+               goto cleanup;
        case OBJ_BAD:
                /*
                 * Object is missing or invalid. If invalid, an error message
                 * has already been printed.
                 */
                cb->result = 0;
-               return 0;
+               goto cleanup;
        default:
                die(_("submodule entry '%s' (%s) is a %s, not a commit"),
                    cb->path, oid_to_hex(oid), type_name(type));
        }
+cleanup:
+       repo_clear(&subrepo);
+       return 0;
 }
 
 static int submodule_has_commits(struct repository *r,
index dafa17c3e616756d38dd9c0dba45de978c8088ff..9930e283512b663133161fd474fc2101c19ec283 100644 (file)
@@ -1,6 +1,5 @@
 t[0-9][0-9][0-9][0-9]/* -whitespace
 /chainlint/*.expect eol=lf
-/lib-diff/* eol=lf
 /t0110/url-* binary
 /t3206/* eol=lf
 /t3900/*.txt eol=lf
index b92155a822ea962cc4d7c899e89681884f25914d..29f72354bf17e959abcc2e14243816b1a0262e52 100644 (file)
--- a/t/README
+++ b/t/README
@@ -463,11 +463,8 @@ GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes
 registering submodule ODBs as alternates a fatal action. Support for
 this environment variable can be removed once the migration to
 explicitly providing repositories when accessing submodule objects is
-complete (in which case we might want to replace this with a trace2
-call so that users can make it visible if accessing submodule objects
-without an explicit repository still happens) or needs to be abandoned
-for whatever reason (in which case the migrated codepaths still retain
-their performance benefits).
+complete or needs to be abandoned for whatever reason (in which case the
+migrated codepaths still retain their performance benefits).
 
 Naming Tests
 ------------
index b16cd0b11b147559e01f4413213148a70f4cb9e1..d1324d086a24c0575e6beadaeef2ce1e8b6a80f0 100644 (file)
@@ -35,5 +35,9 @@ int cmd__oid_array(int argc, const char **argv)
                else
                        die("unknown command: %s", line.buf);
        }
+
+       strbuf_release(&line);
+       oid_array_clear(&array);
+
        return 0;
 }
index 180ee28dd935fc3aa94f513e72131abc4f523c96..d48a409f4e4fb377a7cbc4684fa05737ea32f2c1 100644 (file)
@@ -45,5 +45,8 @@ int cmd__oidtree(int argc, const char **argv)
                        die("unknown command: %s", line.buf);
                }
        }
+
+       strbuf_release(&line);
+
        return 0;
 }
index a282b6ff13e56196a36b951b4a9f7540f5ba2666..48d3cf6692da0612eff2f8c61051780ef944def6 100644 (file)
@@ -14,7 +14,6 @@ static int dry_run = 0, quiet = 0;
 static char *string = NULL;
 static char *file = NULL;
 static int ambiguous;
-static struct string_list list = STRING_LIST_INIT_NODUP;
 
 static struct {
        int called;
@@ -107,6 +106,8 @@ int cmd__parse_options(int argc, const char **argv)
                NULL
        };
        struct string_list expect = STRING_LIST_INIT_NODUP;
+       struct string_list list = STRING_LIST_INIT_NODUP;
+
        struct option options[] = {
                OPT_BOOL(0, "yes", &boolean, "get a boolean"),
                OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"),
@@ -185,5 +186,9 @@ int cmd__parse_options(int argc, const char **argv)
        for (i = 0; i < argc; i++)
                show(&expect, &ret, "arg %02d: %s", i, argv[i]);
 
+       expect.strdup_strings = 1;
+       string_list_clear(&expect, 0);
+       string_list_clear(&list, 0);
+
        return ret;
 }
index f4028442e37e39d7ec35abf00c237d4d2cbac892..133b5e6f4ae5fdbd90f6594223df60f918a036f4 100644 (file)
@@ -46,5 +46,7 @@ int cmd__prio_queue(int argc, const char **argv)
                }
        }
 
+       clear_prio_queue(&pq);
+
        return 0;
 }
diff --git a/t/lib-diff-data.sh b/t/lib-diff-data.sh
new file mode 100644 (file)
index 0000000..c64ec18
--- /dev/null
@@ -0,0 +1,22 @@
+COPYING_test_data () {
+       cat <<\EOF
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ HOWEVER, in order to allow a migration to GPLv3 if that seems like
+ a good idea, I also ask that people involved with the project make
+ their preferences known. In particular, if you trust me to make that
+ decision, you might note so in your copyright message, ie something
+ like
+
+       This file is licensed under the GPL v2, or a later version
+       at the discretion of Linus.
+
+  might avoid issues. But we can also just decide to synchronize and
+  contact all copyright holders on record if/when the occasion arises.
+
+                       Linus Torvalds
+EOF
+}
index 2de880f7a5b0642e0bcfb6fad97a8ff462c29392..c4606bd4b7f0aa3f64335569cd5c379f6617f59e 100644 (file)
@@ -1,3 +1,5 @@
+. "$TEST_DIRECTORY"/lib-diff-data.sh
+
 :
 
 sanitize_diff_raw='/^:/s/ '"\($OID_REGEX\)"' '"\($OID_REGEX\)"' \([A-Z]\)[0-9]*        / \1 \2 \3#     /'
diff --git a/t/lib-diff/COPYING b/t/lib-diff/COPYING
deleted file mode 100644 (file)
index 6ff87c4..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
-
- HOWEVER, in order to allow a migration to GPLv3 if that seems like
- a good idea, I also ask that people involved with the project make
- their preferences known. In particular, if you trust me to make that
- decision, you might note so in your copyright message, ie something
- like
-
-       This file is licensed under the GPL v2, or a later version
-       at the discretion of Linus.
-
-  might avoid issues. But we can also just decide to synchronize and
-  contact all copyright holders on record if/when the occasion arises.
-
-                       Linus Torvalds
-
-----------------------------------------
-
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-\f
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-\f
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                    END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/t/lib-diff/README b/t/lib-diff/README
deleted file mode 100644 (file)
index 548142c..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-////////////////////////////////////////////////////////////////
-
-       GIT - the stupid content tracker
-
-////////////////////////////////////////////////////////////////
-
-"git" can mean anything, depending on your mood.
-
- - random three-letter combination that is pronounceable, and not
-   actually used by any common UNIX command.  The fact that it is a
-   mispronunciation of "get" may or may not be relevant.
- - stupid. contemptible and despicable. simple. Take your pick from the
-   dictionary of slang.
- - "global information tracker": you're in a good mood, and it actually
-   works for you. Angels sing, and a light suddenly fills the room.
- - "goddamn idiotic truckload of sh*t": when it breaks
-
-Git is a fast, scalable, distributed revision control system with an
-unusually rich command set that provides both high-level operations
-and full access to internals.
-
-Git is an Open Source project covered by the GNU General Public License.
-It was originally written by Linus Torvalds with help of a group of
-hackers around the net. It is currently maintained by Junio C Hamano.
-
-Please read the file INSTALL for installation instructions.
-See Documentation/tutorial.txt to get started, then see
-Documentation/everyday.txt for a useful minimum set of commands,
-and "man git-commandname" for documentation of each command.
-CVS users may also want to read Documentation/cvs-migration.txt.
-
-Many Git online resources are accessible from http://git.or.cz/
-including full documentation and Git related tools.
-
-The user discussion and development of Git take place on the Git
-mailing list -- everyone is welcome to post bug reports, feature
-requests, comments and patches to git@vger.kernel.org. To subscribe
-to the list, send an email with just "subscribe git" in the body to
-majordomo@vger.kernel.org. The mailing list archives are available at
-http://marc.theaimsgroup.com/?l=git and other archival sites.
-
-The messages titled "A note from the maintainer", "What's in
-git.git (stable)" and "What's cooking in git.git (topics)" and
-the discussion following them on the mailing list give a good
-reference for project status, development direction and
-remaining tasks.
index 9fc5241228e800bd237d48018989c6aed3ef942b..f99ef3e859dd433b720c789cc1f26aae1796bbb6 100644 (file)
@@ -87,6 +87,34 @@ test_lazy_prereq RFC1991 '
        echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null
 '
 
+GPGSSH_KEY_PRIMARY="${GNUPGHOME}/ed25519_ssh_signing_key"
+GPGSSH_KEY_SECONDARY="${GNUPGHOME}/rsa_2048_ssh_signing_key"
+GPGSSH_KEY_UNTRUSTED="${GNUPGHOME}/untrusted_ssh_signing_key"
+GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key"
+GPGSSH_KEY_PASSPHRASE="super_secret"
+GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile"
+
+GPGSSH_GOOD_SIGNATURE_TRUSTED='Good "git" signature for'
+GPGSSH_GOOD_SIGNATURE_UNTRUSTED='Good "git" signature with'
+GPGSSH_KEY_NOT_TRUSTED="No principal matched"
+GPGSSH_BAD_SIGNATURE="Signature verification failed"
+
+test_lazy_prereq GPGSSH '
+       ssh_version=$(ssh-keygen -Y find-principals -n "git" 2>&1)
+       test $? != 127 || exit 1
+       echo $ssh_version | grep -q "find-principals:missing signature file"
+       test $? = 0 || exit 1;
+       mkdir -p "${GNUPGHOME}" &&
+       chmod 0700 "${GNUPGHOME}" &&
+       ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null &&
+       echo "\"principal with number 1\" $(cat "${GPGSSH_KEY_PRIMARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+       ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null &&
+       echo "\"principal with number 2\" $(cat "${GPGSSH_KEY_SECONDARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+       ssh-keygen -t ed25519 -N "${GPGSSH_KEY_PASSPHRASE}" -C "git ed25519 encrypted key" -f "${GPGSSH_KEY_WITH_PASSPHRASE}" >/dev/null &&
+       echo "\"principal with number 3\" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" &&
+       ssh-keygen -t ed25519 -N "" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null
+'
+
 sanitize_pgp() {
        perl -ne '
                /^-----END PGP/ and $in_pgp = 0;
index d2edfa4c503af07f72a282dddb8cd4cf1d8e868e..782891908d729b6cab46ae16775331bfca6e9add 100644 (file)
@@ -131,6 +131,7 @@ prepare_httpd() {
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
        install_script incomplete-length-upload-pack-v2-http.sh
        install_script incomplete-body-upload-pack-v2-http.sh
+       install_script error-no-report.sh
        install_script broken-smart-http.sh
        install_script error-smart-http.sh
        install_script error.sh
index 180a41fe9615c8023f063146a952f607c4d117d2..497b9b9d9272ea72796aa644e67f0ab9f4392d8c 100644 (file)
@@ -122,6 +122,7 @@ Alias /auth/dumb/ www/auth/dumb/
 </LocationMatch>
 ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/
 ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/
+ScriptAlias /smart/no_report/git-receive-pack error-no-report.sh/
 ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
@@ -137,6 +138,9 @@ ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
 <Files incomplete-body-upload-pack-v2-http.sh>
        Options ExecCGI
 </Files>
+<Files error-no-report.sh>
+       Options ExecCGI
+</Files>
 <Files broken-smart-http.sh>
        Options ExecCGI
 </Files>
diff --git a/t/lib-httpd/error-no-report.sh b/t/lib-httpd/error-no-report.sh
new file mode 100644 (file)
index 0000000..39ff75b
--- /dev/null
@@ -0,0 +1,6 @@
+echo "Content-Type: application/x-git-receive-pack-result"
+echo
+printf '0013\001000eunpack ok\n'
+printf '0015\002skipping report\n'
+printf '0009\0010000'
+printf '0000'
index a754970523ceaf4ff70dc9003d78a19e59d2268a..7547d2c790395a6cf67169ebfa832cc5ecfe2180 100644 (file)
@@ -27,3 +27,5 @@ numeric               sha1:0123456789012345678901234567890123456789
 numeric                sha256:0123456789012345678901234567890123456789012345678901234567890123
 deadbeef       sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
 deadbeef       sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+deadbeef_short sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbee
+deadbeef_short sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee
diff --git a/t/perf/config b/t/perf/config
new file mode 100644 (file)
index 0000000..b92768b
--- /dev/null
@@ -0,0 +1,2 @@
+[gc]
+       auto = 0
index f5ed092ee591ca3c350e274495acf1e37ce868c6..780a7402d5191f72f7b50a48da4adf665090349f 100644 (file)
@@ -27,6 +27,10 @@ TEST_NO_MALLOC_CHECK=t
 
 . ../test-lib.sh
 
+unset GIT_CONFIG_NOSYSTEM
+GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
+export GIT_CONFIG_SYSTEM
+
 if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
 then
        error "Do not use GIT_TEST_INSTALLED with the perf tests.
@@ -230,6 +234,7 @@ test_perf_ () {
                test_ok_ "$1"
        fi
        "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result
+       rm test_time.*
 }
 
 test_perf () {
index df544bb321fee8eb4537d32dbea11de82cb65b28..7603ad2f82b28264ce19ae77d6bbc250fb9a94fa 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git init'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_config () {
index 8440e6add1237e7d08f6b0010950b0d96b9e920d..76052cb5620bebd4965370077a303d3f633154fc 100755 (executable)
@@ -7,6 +7,7 @@ Verify that plumbing commands work when .git is a file
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 objpath() {
index 1e4c672b84aa366de04969b3c3f073c1f02ce92f..b9ed612af13262c0467aed2be927bd4704a221e4 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description=gitattributes
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 attr_check_basic () {
index 37d68ef03be4a468a4926d37f3cfab40f78ac9bb..2e9d652d826af230fc90166c9ef53da3f6e7aa7e 100755 (executable)
@@ -19,27 +19,66 @@ test_expect_success setup '
 test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable repository' '
        test_when_finished "chmod 775 .git/objects .git/objects/??" &&
        chmod a-w .git/objects .git/objects/?? &&
-       test_must_fail git write-tree
+       test_must_fail git write-tree 2>out.write-tree
+'
+
+test_lazy_prereq WRITE_TREE_OUT 'test -e "$TRASH_DIRECTORY"/out.write-tree'
+test_expect_success WRITE_TREE_OUT 'write-tree output on unwritable repository' '
+       cat >expect <<-\EOF &&
+       error: insufficient permission for adding an object to repository database .git/objects
+       fatal: git-write-tree: error building trees
+       EOF
+       test_cmp expect out.write-tree
 '
 
 test_expect_success POSIXPERM,SANITY,!SANITIZE_LEAK 'commit should notice unwritable repository' '
        test_when_finished "chmod 775 .git/objects .git/objects/??" &&
        chmod a-w .git/objects .git/objects/?? &&
-       test_must_fail git commit -m second
+       test_must_fail git commit -m second 2>out.commit
+'
+
+test_lazy_prereq COMMIT_OUT 'test -e "$TRASH_DIRECTORY"/out.commit'
+test_expect_success COMMIT_OUT 'commit output on unwritable repository' '
+       cat >expect <<-\EOF &&
+       error: insufficient permission for adding an object to repository database .git/objects
+       error: Error building trees
+       EOF
+       test_cmp expect out.commit
 '
 
 test_expect_success POSIXPERM,SANITY 'update-index should notice unwritable repository' '
        test_when_finished "chmod 775 .git/objects .git/objects/??" &&
        echo 6O >file &&
        chmod a-w .git/objects .git/objects/?? &&
-       test_must_fail git update-index file
+       test_must_fail git update-index file 2>out.update-index
+'
+
+test_lazy_prereq UPDATE_INDEX_OUT 'test -e "$TRASH_DIRECTORY"/out.update-index'
+test_expect_success UPDATE_INDEX_OUT 'update-index output on unwritable repository' '
+       cat >expect <<-\EOF &&
+       error: insufficient permission for adding an object to repository database .git/objects
+       error: file: failed to insert into database
+       fatal: Unable to process path file
+       EOF
+       test_cmp expect out.update-index
 '
 
 test_expect_success POSIXPERM,SANITY 'add should notice unwritable repository' '
        test_when_finished "chmod 775 .git/objects .git/objects/??" &&
        echo b >file &&
        chmod a-w .git/objects .git/objects/?? &&
-       test_must_fail git add file
+       test_must_fail git add file 2>out.add
+'
+
+test_lazy_prereq ADD_OUT 'test -e "$TRASH_DIRECTORY"/out.add'
+test_expect_success ADD_OUT 'add output on unwritable repository' '
+       cat >expect <<-\EOF &&
+       error: insufficient permission for adding an object to repository database .git/objects
+       error: file: failed to insert into database
+       error: unable to index file '\''file'\''
+       fatal: updating files failed
+       EOF
+       test_cmp expect out.add
 '
 
 test_done
index 4c214bd11c48859f0c4e64dbbe2d783e744eb1d1..a5ec6a0315ca09893e5c2b7403e91f2a0be0109c 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='signals work as we expect'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >expect <<EOF
index 88b9ae81588d1cd8581c29faf67ee8fe4a4e9a6d..53af92d571a92b2dccd37300e71ea030dde947c8 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic sanity checks for git var'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'get GIT_AUTHOR_IDENT' '
index a594b4aa7d045cb0c03c0eea8591912ee052dca8..532637de882c200b71b538ee56b839856d575f22 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description=check-ignore
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 init_vars () {
index 3941ad25286562a57faa63d4ba37604d1fd708bd..eea99107a488dcf5d14bbc42cb4222abf0c8cb2c 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic tests for priority queue implementation'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >expect <<'EOF'
index 5657c5a87b6e253b8c8ca7d18dfa7535c295dc83..837c8b7228b98e8e7b3dbfecd5046673554ef464 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='racy GIT'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test can give false success if your machine is sufficiently
index 419f31a8f7d4cb20041f054d25ddb25c7d43e3fb..9ad76080aa49d46d03e2bb805d039e6a88d87e5c 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test sha1 collision detection'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 TEST_DATA="$TEST_DIRECTORY/t0013"
 
index 7af3fbcc7b9e5e61e41205accaa6d81e85cae6cf..c1a331e9e939fa975ba038934e885bb2fe93b63c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='ignore CR in CRLF sequence while computing similiarity'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 4e9fa3cd68426942357d8f89ffa97ed3099e1084..a34de5642073d1323b37b7c82f56edfe798fcfdd 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='respect crlf in git archive'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index e13363ade5cf500e6f57677eb961c5ca01197a12..81447978b7f66a7a2169341f0b6da649e106e8bd 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='CRLF renormalization'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index c5203e232c8b6e480e2c3a16dca78d625f364aac..cdcafcdff724e2c13ee056fb59e915a5371ec13c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='CRLF conversion'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 has_cr() {
index 24ce46a6ea15cfcbf99d4affac625c61b2dd7286..b138e1d9cbcd9c459e8e1ce3849a199cb177f57b 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='test the Windows-only core.unsetenvvars setting'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if ! test_have_prereq MINGW
index da310ed29b15b320916a0eea8181e3b4c5211f64..ed2fb620a9d62d80b6fadb7fdbce76aff40cccc4 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='our own option parser'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >expect <<\EOF
@@ -168,9 +169,45 @@ test_expect_success 'long options' '
 '
 
 test_expect_success 'missing required value' '
-       test_expect_code 129 test-tool parse-options -s &&
-       test_expect_code 129 test-tool parse-options --string &&
-       test_expect_code 129 test-tool parse-options --file
+       cat >expect <<-\EOF &&
+       error: switch `s'\'' requires a value
+       EOF
+       test_expect_code 129 test-tool parse-options -s 2>actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       error: option `string'\'' requires a value
+       EOF
+       test_expect_code 129 test-tool parse-options --string 2>actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       error: option `file'\'' requires a value
+       EOF
+       test_expect_code 129 test-tool parse-options --file 2>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: boolean' '
+       cat >expect <<-\EOF &&
+       error: option `yes'\'' takes no value
+       EOF
+       test_expect_code 129 test-tool parse-options --yes=hi 2>actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-\EOF &&
+       error: option `no-yes'\'' takes no value
+       EOF
+       test_expect_code 129 test-tool parse-options --no-yes=hi 2>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'superfluous value provided: cmdmode' '
+       cat >expect <<-\EOF &&
+       error: option `mode1'\'' takes no value
+       EOF
+       test_expect_code 129 test-tool parse-options --mode1=hi 2>actual &&
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
index ff98be31a51b360ef917aba97dbda0e53719b58a..1a36a535743c14c29b993d77ed9bf6482c0b16a7 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='simple command server'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test-tool simple-ipc SUPPORTS_SIMPLE_IPC || {
index 0c6ff567a1d47f52492dd89bd098b25bae737bad..6bada3702257649551558884237f71bea3fdadc2 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='update-index and add refuse to add beyond symlinks'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success SYMLINKS setup '
index 7d599675e35a75637a75ab6bfed19ff7f784a312..ee281909bc37267458fa0ea47480cf7cbbf537c8 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test run command'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >hello-script <<-EOF
index 2e5438ccdacdeb31d60d3ffab22dcbc8151134fd..88c89e8f48ac1082892734c5bdb0e73a796f74f0 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic tests for the oid array implementation'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 echoid () {
index 91fa639c4a74ed69ed69ff7360728b2f1ab08a66..94e34c83ed96d86b096385610565a3179f7a9f1c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test strcmp_offset functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 while read s1 s2 expect
index 92910e4e6c13ce381dddefcf197dea26a07efd88..63a1a45cd30921a5b0d28b8d5db63046aeb2a3ea 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test the dir-iterator functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 7bab49f361a9bfea7cf72042d60e65b25698e5a9..0188d0423a0aa246d40b0735a31470f1bb97e8a6 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test parse_pathspec_file()'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'one item from stdin' '
index bfb1397d7b23610539803bb5c40484df6b02c836..74cc59bf8a7de8dfe1f4605c5cd1059d889bc4c6 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='basic tests for the oidtree implementation'
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 maxhexsz=$(test_oid hexsz)
index 0cf3a63b75b7c1e5c121787b5d612cd49a89ef71..37c359bd5a27a93c92d465a961583143ae484d93 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test trace2 facility (normal target)'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
index 6ee8ee3b6729f05f2d553086738f1fb797d833b7..22d0845544e97a8ae960759ffe027313d1b5acdc 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test trace2 facility (perf target)'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
index 1529155cf01629a2c82bc66ebc37c3617086f422..6d3374ff773c1ef3b612b2a0df9aa9df4525790e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test trace2 facility'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
index 013c5a7bc32849eaa1c2fe7dea007d2788e3ea8a..0e8c0dfbbee643c80d2164b6dc8e80fe6522f2e0 100755 (executable)
@@ -71,6 +71,8 @@ In addition:
  DF: a special case, where A makes a directory and B makes a file.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
index 1057a96b2498d1a8abf87f21f90d108eee2c2f96..d1115528cb9461495f3ad1e934beb1cd3682f97c 100755 (executable)
@@ -20,6 +20,8 @@ In the test, these paths are used:
        rezrov  - in H, deleted in M
        yomin   - not in H or M
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
index b6111cd150fdd69973142808cf4a86a4fa6b7e76..e0db2066f3194b7084f25ea249156c30711f3e2b 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='git read-tree --prefix test.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 4a753705ec2c392f1b15f1015557b405e38c0648..658628375c85b17e44aa501c7624daddff4053f6 100755 (executable)
@@ -315,46 +315,236 @@ test_expect_success '%(deltabase) reports packed delta bases' '
        }
 '
 
-bogus_type="bogus"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+test_expect_success 'setup bogus data' '
+       bogus_short_type="bogus" &&
+       bogus_short_content="bogus" &&
+       bogus_short_size=$(strlen "$bogus_short_content") &&
+       bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
+
+       bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
+       bogus_long_content="bogus" &&
+       bogus_long_size=$(strlen "$bogus_long_content") &&
+       bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
+'
+
+for arg1 in '' --allow-unknown-type
+do
+       for arg2 in -s -t -p
+       do
+               if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p"
+               then
+                       continue
+               fi
+
+
+               test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" '
+                       cat >expect <<-\EOF &&
+                       fatal: invalid object type
+                       EOF
+
+                       if test "$arg1" = "--allow-unknown-type"
+                       then
+                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                       else
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
+                               test_must_be_empty out &&
+                               test_cmp expect actual
+                       fi
+               '
+
+               test_expect_success "cat-file $arg1 $arg2 error on bogus full OID" '
+                       if test "$arg2" = "-p"
+                       then
+                               cat >expect <<-EOF
+                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+                               fatal: Not a valid object name $bogus_long_sha1
+                               EOF
+                       else
+                               cat >expect <<-EOF
+                               error: header for $bogus_long_sha1 too long, exceeds 32 bytes
+                               fatal: git cat-file: could not get object info
+                               EOF
+                       fi &&
+
+                       if test "$arg1" = "--allow-unknown-type"
+                       then
+                               git cat-file $arg1 $arg2 $bogus_short_sha1
+                       else
+                               test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
+                               test_must_be_empty out &&
+                               test_cmp expect actual
+                       fi
+               '
+
+               test_expect_success "cat-file $arg1 $arg2 error on missing short OID" '
+                       cat >expect.err <<-EOF &&
+                       fatal: Not a valid object name $(test_oid deadbeef_short)
+                       EOF
+                       test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
+                       test_must_be_empty out
+               '
+
+               test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
+                       if test "$arg2" = "-p"
+                       then
+                               cat >expect.err <<-EOF
+                               fatal: Not a valid object name $(test_oid deadbeef)
+                               EOF
+                       else
+                               cat >expect.err <<-\EOF
+                               fatal: git cat-file: could not get object info
+                               EOF
+                       fi &&
+                       test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef) >out 2>err.actual &&
+                       test_must_be_empty out &&
+                       test_cmp expect.err err.actual
+               '
+       done
+done
+
+test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
+       git cat-file -e $bogus_short_sha1
+'
+
+test_expect_success '-e can not be combined with --allow-unknown-type' '
+       test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
+'
+
+test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
+       test_must_fail git cat-file -p $bogus_short_sha1 &&
+       test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
+'
+
+test_expect_success '<type> <hash> does not work with objects of broken types' '
+       cat >err.expect <<-\EOF &&
+       fatal: invalid object type "bogus"
+       EOF
+       test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
+       test_cmp err.expect err.actual
+'
+
+test_expect_success 'broken types combined with --batch and --batch-check' '
+       echo $bogus_short_sha1 >bogus-oid &&
+
+       cat >err.expect <<-\EOF &&
+       fatal: invalid object type
+       EOF
+
+       test_must_fail git cat-file --batch <bogus-oid 2>err.actual &&
+       test_cmp err.expect err.actual &&
+
+       test_must_fail git cat-file --batch-check <bogus-oid 2>err.actual &&
+       test_cmp err.expect err.actual
+'
+
+test_expect_success 'the --batch and --batch-check options do not combine with --allow-unknown-type' '
+       test_expect_code 128 git cat-file --batch --allow-unknown-type <bogus-oid &&
+       test_expect_code 128 git cat-file --batch-check --allow-unknown-type <bogus-oid
+'
+
+test_expect_success 'the --allow-unknown-type option does not consider replacement refs' '
+       cat >expect <<-EOF &&
+       $bogus_short_type
+       EOF
+       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       test_cmp expect actual &&
+
+       # Create it manually, as "git replace" will die on bogus
+       # types.
+       head=$(git rev-parse --verify HEAD) &&
+       test_when_finished "rm -rf .git/refs/replace" &&
+       mkdir -p .git/refs/replace &&
+       echo $head >.git/refs/replace/$bogus_short_sha1 &&
+
+       cat >expect <<-EOF &&
+       commit
+       EOF
+       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
+       test_cmp expect actual
+'
 
 test_expect_success "Type of broken object is correct" '
-       echo $bogus_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+       echo $bogus_short_type >expect &&
+       git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of broken object is correct" '
-       echo $bogus_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+       echo $bogus_short_size >expect &&
+       git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
 '
 
-bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
-bogus_content="bogus"
-bogus_size=$(strlen "$bogus_content")
-bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
-
 test_expect_success "Type of broken object is correct when type is large" '
-       echo $bogus_type >expect &&
-       git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+       echo $bogus_long_type >expect &&
+       git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
        test_cmp expect actual
 '
 
 test_expect_success "Size of large broken object is correct when type is large" '
-       echo $bogus_size >expect &&
-       git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+       echo $bogus_long_size >expect &&
+       git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'clean up broken object' '
-       rm .git/objects/$(test_oid_to_path $bogus_sha1)
+       rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
+'
+
+test_expect_success 'cat-file -t and -s on corrupt loose object' '
+       git init --bare corrupt-loose.git &&
+       (
+               cd corrupt-loose.git &&
+
+               # Setup and create the empty blob and its path
+               empty_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$EMPTY_BLOB")) &&
+               git hash-object -w --stdin </dev/null &&
+
+               # Create another blob and its path
+               echo other >other.blob &&
+               other_blob=$(git hash-object -w --stdin <other.blob) &&
+               other_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$other_blob")) &&
+
+               # Before the swap the size is 0
+               cat >out.expect <<-EOF &&
+               0
+               EOF
+               git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+               test_must_be_empty err.actual &&
+               test_cmp out.expect out.actual &&
+
+               # Swap the two to corrupt the repository
+               mv -f "$other_path" "$empty_path" &&
+               test_must_fail git fsck 2>err.fsck &&
+               grep "hash-path mismatch" err.fsck &&
+
+               # confirm that cat-file is reading the new swapped-in
+               # blob...
+               cat >out.expect <<-EOF &&
+               blob
+               EOF
+               git cat-file -t "$EMPTY_BLOB" >out.actual 2>err.actual &&
+               test_must_be_empty err.actual &&
+               test_cmp out.expect out.actual &&
+
+               # ... since it has a different size now.
+               cat >out.expect <<-EOF &&
+               6
+               EOF
+               git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
+               test_must_be_empty err.actual &&
+               test_cmp out.expect out.actual &&
+
+               # So far "cat-file" has been happy to spew the found
+               # content out as-is. Try to make it zlib-invalid.
+               mv -f other.blob "$empty_path" &&
+               test_must_fail git fsck 2>err.fsck &&
+               grep "^error: inflate: data stream error (" err.fsck
+       )
 '
 
 # Tests for git cat-file --follow-symlinks
index 2935f68f8d215203ee1d2a9813de0039a89cb58f..fc179ac5dd604a085c9a19a491f0abb7ac4c066f 100755 (executable)
@@ -5,6 +5,7 @@ test_description='test read-tree into a fresh index file'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index b946f8768649dd76d8a175877c63d49244e00ffb..48bfad07abca6f3ee8430078372108451517aaed 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git mktree'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 57f0770df1410ea2005d8637323ff0eeeb82d880..cde93d22cde4ab09fe9fa594a58813f1d3f276af 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='read-tree D/F conflict corner cases'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
index da3376b3bb274b3632d204f225908fec701fa644..8ea8d36818bea10b227640b4974cc57f2157fa2e 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check that read-tree rejects confusing paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create base tree' '
index ca91c6a67f8fb7223b05870be0c40c013a51f5dc..16fbd2c6db9db18456b6a02d443b2b518aafc9e0 100755 (executable)
@@ -514,7 +514,7 @@ test_expect_success 'checkout and reset (mixed) [sparse]' '
 test_expect_success 'merge, cherry-pick, and rebase' '
        init_repos &&
 
-       for OPERATION in "merge -m merge" cherry-pick rebase
+       for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
        do
                test_all_match git checkout -B temp update-deep &&
                test_all_match git $OPERATION update-folder1 &&
index ae66ba5babf347f12f2c4cb213b0de72ea980da2..0f37a43fd3c5b2638fe41408a3860ce2facd6401 100755 (executable)
@@ -12,6 +12,7 @@ Also make sure that command line parser understands the normal
 "flags first and then non flag arguments" command line.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >expected <<EOF
index ccbb116c0161d99d75f26cfa77d68e2ad7909ce8..5cde79ef8c4fa34b61eba75f98e827f05e2a27ab 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='test config file include directives'
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Force setup_explicit_git_dir() to run until the end. This is needed
index fa3aeb80f2c8146b7df23daaf5d81d3015267935..4c77cf89a6ce63496393b6715a4e69d6081f62e5 100755 (executable)
@@ -4,6 +4,7 @@ test_description='Test handling of ref names that check-ref-format rejects'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 5071ac63a5b51b89c973456211ce3aaac3587553..6337236fd8226b1902f11ebf54ff9e6d02f537e2 100755 (executable)
@@ -48,24 +48,70 @@ remove_object () {
        rm "$(sha1_file "$1")"
 }
 
-test_expect_success 'object with bad sha1' '
-       sha=$(echo blob | git hash-object -w --stdin) &&
-       old=$(test_oid_to_path "$sha") &&
-       new=$(dirname $old)/$(test_oid ff_2) &&
-       sha="$(dirname $new)$(basename $new)" &&
-       mv .git/objects/$old .git/objects/$new &&
-       test_when_finished "remove_object $sha" &&
-       git update-index --add --cacheinfo 100644 $sha foo &&
-       test_when_finished "git read-tree -u --reset HEAD" &&
-       tree=$(git write-tree) &&
-       test_when_finished "remove_object $tree" &&
-       cmt=$(echo bogus | git commit-tree $tree) &&
-       test_when_finished "remove_object $cmt" &&
-       git update-ref refs/heads/bogus $cmt &&
-       test_when_finished "git update-ref -d refs/heads/bogus" &&
+test_expect_success 'object with hash mismatch' '
+       git init --bare hash-mismatch &&
+       (
+               cd hash-mismatch &&
 
-       test_must_fail git fsck 2>out &&
-       test_i18ngrep "$sha.*corrupt" out
+               oid=$(echo blob | git hash-object -w --stdin) &&
+               oldoid=$oid &&
+               old=$(test_oid_to_path "$oid") &&
+               new=$(dirname $old)/$(test_oid ff_2) &&
+               oid="$(dirname $new)$(basename $new)" &&
+
+               mv objects/$old objects/$new &&
+               git update-index --add --cacheinfo 100644 $oid foo &&
+               tree=$(git write-tree) &&
+               cmt=$(echo bogus | git commit-tree $tree) &&
+               git update-ref refs/heads/bogus $cmt &&
+
+               test_must_fail git fsck 2>out &&
+               grep "$oldoid: hash-path mismatch, found at: .*$new" out
+       )
+'
+
+test_expect_success 'object with hash and type mismatch' '
+       git init --bare hash-type-mismatch &&
+       (
+               cd hash-type-mismatch &&
+
+               oid=$(echo blob | git hash-object -w --stdin -t garbage --literally) &&
+               oldoid=$oid &&
+               old=$(test_oid_to_path "$oid") &&
+               new=$(dirname $old)/$(test_oid ff_2) &&
+               oid="$(dirname $new)$(basename $new)" &&
+
+               mv objects/$old objects/$new &&
+               git update-index --add --cacheinfo 100644 $oid foo &&
+               tree=$(git write-tree) &&
+               cmt=$(echo bogus | git commit-tree $tree) &&
+               git update-ref refs/heads/bogus $cmt &&
+
+
+               test_must_fail git fsck 2>out &&
+               grep "^error: $oldoid: hash-path mismatch, found at: .*$new" out &&
+               grep "^error: $oldoid: object is of unknown type '"'"'garbage'"'"'" out
+       )
+'
+
+test_expect_success POSIXPERM 'zlib corrupt loose object output ' '
+       git init --bare corrupt-loose-output &&
+       (
+               cd corrupt-loose-output &&
+               oid=$(git hash-object -w --stdin --literally </dev/null) &&
+               oidf=objects/$(test_oid_to_path "$oid") &&
+               chmod 755 $oidf &&
+               echo extra garbage >>$oidf &&
+
+               cat >expect.error <<-EOF &&
+               error: garbage at end of loose object '\''$oid'\''
+               error: unable to unpack contents of ./$oidf
+               error: $oid: object corrupt or missing: ./$oidf
+               EOF
+               test_must_fail git fsck 2>actual &&
+               grep ^error: actual >error &&
+               test_cmp expect.error error
+       )
 '
 
 test_expect_success 'branch pointing to non-commit' '
@@ -865,4 +911,21 @@ test_expect_success 'detect corrupt index file in fsck' '
        test_i18ngrep "bad index file" errors
 '
 
+test_expect_success 'fsck error and recovery on invalid object type' '
+       git init --bare garbage-type &&
+       (
+               cd garbage-type &&
+
+               garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
+
+               cat >err.expect <<-\EOF &&
+               fatal: invalid object type
+               EOF
+               test_must_fail git fsck >out 2>err &&
+               grep -e "^error" -e "^fatal" err >errors &&
+               test_line_count = 1 errors &&
+               grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
+       )
+'
+
 test_done
index 3d51615e42d53a0c7794533eb27049606981fbf3..0fafcf9dde385f2c2bac7fbd846098908a72f09b 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test GIT_CEILING_DIRECTORIES'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_prefix() {
index bbfe05b8e4a5c4c13e531647bfa460c0a552de2e..591505a39c02786962a02161d533f28dca16951e 100755 (executable)
@@ -43,6 +43,7 @@ A few rules for repo setup:
 # This test heavily relies on the standard error of nested function calls.
 test_untraceable=UnfortunatelyYes
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 here=$(pwd)
index 70361c806e1baf1b26810983374c53eb49ea2f2d..fc95cf90485366314dcebb7fe77f18b1d8d4ef1b 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git checkout-index -u test.
 With -u flag, git checkout-index internally runs the equivalent of
 git update-index --refresh on the checked out entry.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
index ff163cf6750f6d82c36964eb694efbcfae06135f..f0fd441d810d52c909476a76107098d16ecc9f35 100755 (executable)
@@ -10,6 +10,7 @@ also verifies that such leading path may contain symlinks, unlike
 the GIT controlled paths.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index a9352b08a8b80e4a58131f31f98eb7481914ecbe..9bb503a97578c1d43a01409a93e2dd4f7c207a43 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git checkout-index --temp test.
 With --temp flag, git checkout-index writes to temporary merge files
 rather than the tracked path.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 9fa561047430ae40a1860131a024e4fd7744f5cb..112682a45a1df3d5c19eab03ec79c1202d2cab71 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git checkout-index on filesystem w/o symlinks test.
 This tests that git checkout-index creates a symbolic link as a plain
 file if core.symlinks is false.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
index 21f4659a9d1c22fcc8c9eb3261315d67988dad2f..1f193cde965303a8f2362e824e0a86e99f746548 100755 (executable)
@@ -12,6 +12,7 @@ into the subdir while keeping the worktree location,
 and tries commits from the top and the subdir, checking
 that the commit-hook still gets called.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 COMMIT_FILE="$(pwd)/output"
index f6fcfc0c1e403930a602e694fe3bf7632b81192a..6acdb89d12bd046674c27aba12119a231dbb0f08 100755 (executable)
@@ -11,6 +11,7 @@ The tests in this file exercise parallel checkout's collision detection code in
 both these mechanics.
 "
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-parallel-checkout.sh"
 
index 45ca35d60ac5b750a635ef42f96275c4bb197692..94c4cb0672126c6ae0440598a68e99182a4cffbf 100755 (executable)
@@ -129,12 +129,15 @@ test_expect_success 'add -n -u should not add but just report' '
                echo "remove '\''top'\''"
        ) >expect &&
        before=$(git ls-files -s check top) &&
+       git count-objects -v >objects_before &&
        echo changed >>check &&
        rm -f top &&
        git add -n -u >actual &&
        after=$(git ls-files -s check top) &&
+       git count-objects -v >objects_after &&
 
        test "$before" = "$after" &&
+       test_cmp objects_before objects_after &&
        test_cmp expect actual
 
 '
index c8de6d8a1902209ef98ec0832296ed9060310429..b40eeb263fe896a9553ef93f82e8a78b1dc2fb60 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='cd_to_toplevel'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 EXEC_PATH="$(git --exec-path)"
index 740ce56eab5c78b18a85834e9dc9c091d195c2d1..11af4552f7461e1091e71407517504a6497867e5 100755 (executable)
@@ -15,6 +15,8 @@ filesystem.
     path3/file3 - a file in a directory
     path4       - an empty directory
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup ' '
index 516c95ea0e82e6d65b913cbddd647c939fbaea93..48cec4e5f88501db3287a47063ad2788807e205a 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git ls-files --others --exclude
 This test runs git ls-files --others and tests --exclude patterns.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 rm -fr one three
@@ -102,7 +103,7 @@ test_expect_success \
        >output &&
      test_cmp expect output'
 
-test_expect_success 'restore gitignore' '
+test_expect_success !SANITIZE_LEAK 'restore gitignore' '
        git checkout --ignore-skip-worktree-bits $allignores &&
        rm .git/index
 '
@@ -125,7 +126,7 @@ cat > expect << EOF
 #      three/
 EOF
 
-test_expect_success 'git status honors core.excludesfile' \
+test_expect_success !SANITIZE_LEAK 'git status honors core.excludesfile' \
        'test_cmp expect output'
 
 test_expect_success 'trailing slash in exclude allows directory match(1)' '
index 8704b04e1b4150357a7a01c91ac59bb1f22cbb8e..54d22a45dfbc4b7ca1080fc7bac27284fd3484f9 100755 (executable)
@@ -12,6 +12,8 @@ filesystem.
     -foo       - a file with a funny name.
     --         - another file with a funny name.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
index c41c4f046abf3c79ae54f33d56945726332b25ec..7933dff9b3849181b4c168096d166f1054a2a374 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='ls-files --exclude does not affect index files'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create repo with file' '
index 9fd5a1f188aae0507c335de65a742977c6976d69..a16e25c79bdde748cbb9c1d5419960e0e5f8f3f5 100755 (executable)
@@ -6,6 +6,7 @@ This test runs git ls-files with various unusual or malformed
 command-line arguments.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'ls-files in empty repository' '
index 727e9ae1a449ee4f105fe234b5041c6fbc397b45..6ba8b589cd00d3ad401f4018dc0eed7be1b54e05 100755 (executable)
@@ -5,6 +5,7 @@ test_description='ls-files tests with relative paths
 This test runs git ls-files with various relative path arguments.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'prepare' '
index e109c3fbfb5f57c35044299502d1ad406210c998..2aaf91ebc8c323a5ce4475bb8a7e03543dc319ad 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='overly long paths'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 85f370495876f51691322f3556b944a023ad8388..51d3dffaa66528ff36ebe7b6660f823a2fe56371 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='Test the lazy init name hash with various folder structures'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test 1 -eq $(test-tool online-cpus)
index 124e73b8e601ed3c714fe2857d2b227ee04e2ffc..2cbcbc0721b926dc7117ba680af7002466ed24a2 100755 (executable)
@@ -9,6 +9,8 @@ This test runs git ls-files --error-unmatch to ensure it correctly
 returns an error when a non-existent path is provided on the command
 line.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 56ea4bda133d7443ba0d67c082d157cd491bd9cf..72d5b014d82c6c3fbdbf693a791e8599138594c0 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='wildmatch tests'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Disable expensive chain-lint tests; all of the tests in this script
index 18baf49a49c792952f3c8028cafc2d193d88cb5d..436de44971eca5d7cbbe79d45ff2a59cf51329eb 100755 (executable)
@@ -16,6 +16,8 @@ This test runs git ls-tree with the following in a tree.
 The new path restriction code should do the right thing for path2 and
 path2/baz.  Also path0/ should snow nothing.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
index 12bf31022a871c4d2e43866ec6053c55205a3476..05fde64225963c119b7f2139681ccf278023c717 100755 (executable)
@@ -19,6 +19,8 @@ This test runs git ls-tree with the following in a tree.
 Test the handling of multiple directories which have matching file
 entries.  Also test odd filename and missing entries handling.
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 1e16c6b8ea610c4582e63950b8939205fc228b25..3942db229000e0c367a93b200f30c57bba95e7ee 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='ls-tree with(out) globs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 14520913afca8d1c980e4cd58d0b943a1a498611..d18ba1bd84bb473888c5e24191d79f9d0a792358 100755 (executable)
@@ -7,6 +7,7 @@ Miscellaneous tests for git ls-tree.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 08bd906173bd6f1c45103ee69608472a0631064d..6a521c1a3e5225716c14656aa9230afaf5a7829d 100755 (executable)
@@ -4,6 +4,7 @@ test_description='basic branch output coloring'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'set up some sample branches' '
index 37b9d26f4b6ba88396a2aaaecd4675470cfaa581..9cbc34fc5838b4d4a886135352e92d613ca8146a 100755 (executable)
@@ -4,6 +4,7 @@ test_description='tests for the peel_ref optimization of packed-refs'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create annotated tag in refs/tags' '
index f5bf16abcd8ce2aae5929129913cbb8927b06539..d3ac826283e1a16315bb158754224067d5b0afe4 100755 (executable)
@@ -9,6 +9,7 @@ This test tries pathnames with funny characters in the working
 tree, index, and tree objects.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 HT='   '
index 7de21f8bcff1c5cf713c69e3ed1c8ff9a41c10a8..b2a8db69afc69f20c33309e808711c0502467770 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='rm --pathspec-from-file'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_tick
index 4086e1ebbc97f1220669a2a3236b255b6c20ce95..283a66955d6dbb8415697a8a911effa6f291f8e9 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='Test of git add, including the -- option.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # Test the file mode "$1" of the file "$2" in the index.
index f528008c363c68f40da3b88a34ae8ec931d0c1ac..72a5a565e973ad634ecfa4418d8934d3c7b85ae1 100755 (executable)
@@ -5,6 +5,7 @@
 
 test_description='quoted output'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 FN='濱野'
index 6a9f010197c7ca60a859670abeb3a3df1950a09c..ea52e5b91b75e3df741cbc12b927cacd6ee08afb 100755 (executable)
@@ -6,6 +6,8 @@
 test_description='Test diff raw-output.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
index db07ff3eb1968d606176f44ac2428899de78c6eb..f4485a87c6317c98d82a6af4014520d9e55ee73d 100755 (executable)
@@ -11,7 +11,7 @@ test_description='More rename detection
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+    'COPYING_test_data >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+    'COPYING_test_data >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
index 86479061325b67038e31bc13a7948d9b0b0f9323..6f1b323f979a42d663959986e3949f216c1c7669 100755 (executable)
@@ -9,7 +9,7 @@ test_description='Same rename detection as t4003 but testing diff-raw.'
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success 'setup reference tree' '
-       cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+       COPYING_test_data >COPYING &&
        echo frotz >rezrov &&
        git update-index --add COPYING rezrov &&
        tree=$(git write-tree) &&
@@ -64,7 +64,7 @@ test_expect_success 'validate output from rename/copy detection (#2)' '
 # nows how to say Copy.
 
 test_expect_success 'validate output from rename/copy detection (#3)' '
-       cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+       COPYING_test_data >COPYING &&
        git update-index --add --remove COPYING COPYING.1 &&
 
        cat <<-EOF >expected &&
index cbb9c62f535e807d13d72008088b108101c6ec57..c634653b5be6874f1f9f26f6e2d6c3605d2330e1 100755 (executable)
@@ -11,13 +11,12 @@ test_description='Rename interaction with pathspec.
 
 test_expect_success 'prepare reference tree' '
        mkdir path0 path1 &&
-       cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING &&
+       COPYING_test_data >path0/COPYING &&
        git update-index --add path0/COPYING &&
        tree=$(git write-tree) &&
-       echo $tree
+       blob=$(git rev-parse :path0/COPYING)
 '
 
-blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING")
 test_expect_success 'prepare work tree' '
        cp path0/COPYING path1/COPYING &&
        git update-index --add --remove path0/COPYING path1/COPYING
index 2299f27511bf29fce131a890e0d8e9c6361d8a3c..562aaf3a2a295e9bb800e1ff363a0209c8f893ba 100755 (executable)
@@ -25,8 +25,8 @@ Further, with -B and -M together, these should turn into two renames.
 . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success setup '
-       cat "$TEST_DIRECTORY"/lib-diff/README >file0 &&
-       cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 &&
+       echo some dissimilar content >file0 &&
+       COPYING_test_data >file1 &&
        blob0_id=$(git hash-object file0) &&
        blob1_id=$(git hash-object file1) &&
        git update-index --add file0 file1 &&
index b1da807f1693147cf9a0d5fffeb1173921f08e57..59b7f44f0585796ddfd99951bdf23a4eab9fa259 100755 (executable)
@@ -11,7 +11,7 @@ test_description='Same rename detection as t4003 but testing diff-raw -z.
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+    'COPYING_test_data >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     orig=$(git hash-object COPYING) &&
@@ -81,7 +81,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
+    'COPYING_test_data >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
index 876271d6826cca4fea73e8cdb459e6983db5b209..5a8d8876831657d93837fe3ccfad141f15119ec9 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='Quoting paths in diff output.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 P0='pathname'
index c6135c75488ff01797a98e43163e0b0c5e6be169..c68729ac098401c16c2d7d232dec35d56e545846 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff whitespace error detection'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 6d1c3d949c78bc8fd5a82dbaee519163a8653e86..1c89050a974c7b101056865a4b48e7f18a90d29e 100755 (executable)
@@ -3,15 +3,17 @@
 test_description='rewrite diff'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
 
 test_expect_success setup '
 
-       cat "$TEST_DIRECTORY"/../COPYING >test &&
+       COPYING_test_data >test.data &&
+       cp test.data test &&
        git add test &&
        tr \
          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
          "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
-         <"$TEST_DIRECTORY"/../COPYING >test &&
+         <test.data >test &&
        echo "to be deleted" >test2 &&
        blob=$(git hash-object test2) &&
        blob=$(git rev-parse --short $blob) &&
index 8c9823765e66aca886a1ec32a8ad523c1c28e1bc..47d6f35dcc4d93bbbdd083d6cdd23e7a5c2dff36 100755 (executable)
@@ -3,25 +3,26 @@
 test_description='typechange rename detection'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success setup '
 
        rm -f foo bar &&
-       cat "$TEST_DIRECTORY"/../COPYING >foo &&
+       COPYING_test_data >foo &&
        test_ln_s_add linklink bar &&
        git add foo &&
        git commit -a -m Initial &&
        git tag one &&
 
        git rm -f foo bar &&
-       cat "$TEST_DIRECTORY"/../COPYING >bar &&
+       COPYING_test_data >bar &&
        test_ln_s_add linklink foo &&
        git add bar &&
        git commit -a -m Second &&
        git tag two &&
 
        git rm -f foo bar &&
-       cat "$TEST_DIRECTORY"/../COPYING >foo &&
+       COPYING_test_data >foo &&
        git add foo &&
        git commit -a -m Third &&
        git tag three &&
@@ -35,7 +36,7 @@ test_expect_success setup '
        # This is purely for sanity check
 
        git rm -f foo bar &&
-       cat "$TEST_DIRECTORY"/../COPYING >foo &&
+       COPYING_test_data >foo &&
        cat "$TEST_DIRECTORY"/../Makefile >bar &&
        git add foo bar &&
        git commit -a -m Fifth &&
@@ -43,7 +44,7 @@ test_expect_success setup '
 
        git rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../Makefile >foo &&
-       cat "$TEST_DIRECTORY"/../COPYING >bar &&
+       COPYING_test_data >bar &&
        git add foo bar &&
        git commit -a -m Sixth &&
        git tag six
index 35578f2bb91dab8a00f40b5fbb26bd0a31ca7d58..6356961de46c78f5168ab437b9aff50fc979afd0 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='diff hunk header truncation'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 N='日本語'
index c0b642c1ab03f27dabc58e3b99ba9f8f48bf35d8..cc73161b466b56615e969685f874130af951ddfd 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='Test diff/status color escape codes'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 ESC=$(printf '\033')
index 37d1ea258702684717db5658f4e0867444fed82b..dc500ae092b8f1eccbade351f6b8c1e72b0dcd69 100644 (file)
@@ -1,36 +1,35 @@
 <BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>index a1a09b7..f1b6f3c 100644<RESET>
 <BOLD>--- a/pre<RESET>
 <BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,19 +1,19 @@<RESET>
-Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+<CYAN>@@ -1,30 +1,30 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <RED>foo0<RESET><GREEN>bar<RESET>(x.<RED>find<RESET><GREEN>Find<RESET>); }
 cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
-<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
-[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
-!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>^<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>|<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>&&<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>||<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
-<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
-<RED>a<RESET><GREEN>y<RESET>
-<GREEN>x<RESET>,y
-<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
+<GREEN>(<RESET>1 <RED>-<RESET><GREEN>+<RESET>1e10 0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>2<RESET>'
+// long double<RESET>
+<RED>3.141592653e-10l<RESET><GREEN>3.141592654e+10l<RESET>
+// float<RESET>
+<RED>120E5f<RESET><GREEN>120E6f<RESET>
+// hex<RESET>
+<RED>0xdead<RESET><GREEN>0xdeaf<RESET>'1<RED>eaF<RESET><GREEN>eaf<RESET>+<RED>8ULL<RESET><GREEN>7ULL<RESET>
+// octal<RESET>
+<RED>01234567<RESET><GREEN>01234560<RESET>
+// binary<RESET>
+<RED>0b1000<RESET><GREEN>0b1100<RESET>+e1
+// expression<RESET>
+1.5-e+<RED>2<RESET><GREEN>3<RESET>+f
+// another one<RESET>
+str.e+<RED>65<RESET><GREEN>75<RESET>
+[a] b<RED>-><RESET><GREEN>->*<RESET>v d<RED>.<RESET><GREEN>.*<RESET>e
+<GREEN>~<RESET>!a <GREEN>!<RESET>~b c<RED>++<RESET><GREEN>+<RESET> d<RED>--<RESET><GREEN>-<RESET> e*<GREEN>*<RESET>f g<RED>&<RESET><GREEN>&&<RESET>h
+a<RED>*<RESET><GREEN>*=<RESET>b c<RED>/<RESET><GREEN>/=<RESET>d e<RED>%<RESET><GREEN>%=<RESET>f
+a<RED>+<RESET><GREEN>++<RESET>b c<RED>-<RESET><GREEN>--<RESET>d
+a<RED><<<RESET><GREEN><<=<RESET>b c<RED>>><RESET><GREEN>>>=<RESET>d
+a<RED><<RESET><GREEN><=<RESET>b c<RED><=<RESET><GREEN><<RESET>d e<RED>><RESET><GREEN>>=<RESET>f g<RED>>=<RESET><GREEN>><RESET>h i<RED><=<RESET><GREEN><=><RESET>j
+a<RED>==<RESET><GREEN>!=<RESET>b c<RED>!=<RESET><GREEN>=<RESET>d
+a<RED>^<RESET><GREEN>^=<RESET>b c<RED>|<RESET><GREEN>|=<RESET>d e<RED>&&<RESET><GREEN>&=<RESET>f
+a<RED>||<RESET><GREEN>|<RESET>b
+a?<GREEN>:<RESET>b
+a<RED>=<RESET><GREEN>==<RESET>b c<RED>+=<RESET><GREEN>+<RESET>d e<RED>-=<RESET><GREEN>-<RESET>f g<RED>*=<RESET><GREEN>*<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v
+a,b<RESET>
+a<RED>::<RESET><GREEN>:<RESET>b
index 7e8c026cefb0020370c3415e3b0c59489ccd78d6..f1b6f3c22854b0520c78ca2bc27457b04cdeb9a8 100644 (file)
@@ -1,19 +1,30 @@
-Foo() : x(0&42) { bar(x); }
+Foo() : x(0&42) { bar(x.Find); }
 cout<<"Hello World?\n"<<endl;
-(1) (-1e10) (0xabcdef) 'y'
-[x] x->y x.y
-!x ~x x++ x-- x*y x&y
-x*y x/y x%y
-x+y x-y
-x<<y x>>y
-x<y x<=y x>y x>=y
-x==y x!=y
-x&y
-x^y
-x|y
-x&&y
-x||y
-x?y:z
-x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
-x,y
-x::y
+(1 +1e10 0xabcdef) '2'
+// long double
+3.141592654e+10l
+// float
+120E6f
+// hex
+0xdeaf'1eaf+7ULL
+// octal
+01234560
+// binary
+0b1100+e1
+// expression
+1.5-e+3+f
+// another one
+str.e+75
+[a] b->*v d.*e
+~!a !~b c+ d- e**f g&&h
+a*=b c/=d e%=f
+a++b c--d
+a<<=b c>>=d
+a<=b c<d e>=f g>h i<=>j
+a!=b c=d
+a^=b c|=d e&=f
+a|b
+a?:b
+a==b c+d e-f g*h i/j k%l m<<n o>>p q&r s^t u|v
+a,b
+a:b
index 23d5c8adf5453c7a5100a83d413848d7e7bf8c18..a1a09b771250e360f80af7e66a3a58ac1014acd7 100644 (file)
@@ -1,19 +1,30 @@
-Foo():x(0&&1){}
+Foo():x(0&&1){ foo0( x.find); }
 cout<<"Hello World!\n"<<endl;
 1 -1e10 0xabcdef 'x'
-[a] a->b a.b
-!a ~a a++ a-- a*b a&b
-a*b a/b a%b
-a+b a-b
-a<<b a>>b
-a<b a<=b a>b a>=b
-a==b a!=b
-a&b
-a^b
-a|b
-a&&b
+// long double
+3.141592653e-10l
+// float
+120E5f
+// hex
+0xdead'1eaF+8ULL
+// octal
+01234567
+// binary
+0b1000+e1
+// expression
+1.5-e+2+f
+// another one
+str.e+65
+[a] b->v d.e
+!a ~b c++ d-- e*f g&h
+a*b c/d e%f
+a+b c-d
+a<<b c>>d
+a<b c<=d e>f g>=h i<=j
+a==b c!=d
+a^b c|d e&&f
 a||b
-a?b:z
-a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
-a,y
+a?b
+a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v
+a,b
 a::b
index 9dfead936b7b5be7e0c5a16c2deb90837d7c96d4..6a650dacd6e70b29a32919328a098eb885a3ec5d 100755 (executable)
@@ -1616,6 +1616,16 @@ test_expect_success GPGSM 'setup signed branch x509' '
        git commit -S -m signed_commit
 '
 
+test_expect_success GPGSSH 'setup sshkey signed branch' '
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       test_when_finished "git reset --hard && git checkout main" &&
+       git checkout -b signed-ssh main &&
+       echo foo >foo &&
+       git add foo &&
+       git commit -S -m signed_commit
+'
+
 test_expect_success GPGSM 'log x509 fingerprint' '
        echo "F8BF62E0693D0694816377099909C779FA23FD65 | " >expect &&
        git log -n1 --format="%GF | %GP" signed-x509 >actual &&
@@ -1628,6 +1638,13 @@ test_expect_success GPGSM 'log OpenPGP fingerprint' '
        test_cmp expect actual
 '
 
+test_expect_success GPGSSH 'log ssh key fingerprint' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       ssh-keygen -lf  "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2\" | \"}" >expect &&
+       git log -n1 --format="%GF | %GP" signed-ssh >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success GPG 'log --graph --show-signature' '
        git log --graph --show-signature -n1 signed >actual &&
        grep "^| gpg: Signature made" actual &&
@@ -1640,6 +1657,12 @@ test_expect_success GPGSM 'log --graph --show-signature x509' '
        grep "^| gpgsm: Good signature" actual
 '
 
+test_expect_success GPGSSH 'log --graph --show-signature ssh' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git log --graph --show-signature -n1 signed-ssh >actual &&
+       grep "${GOOD_SIGNATURE_TRUSTED}" actual
+'
+
 test_expect_success GPG 'log --graph --show-signature for merged tag' '
        test_when_finished "git reset --hard && git checkout main" &&
        git checkout -b plain main &&
index e59601e5fe9b75eda9f4daaa801c1ed6c40d6fc6..c52c8a21fae6d0593cc120485e8d6eaa8a95ccbc 100755 (executable)
@@ -4,6 +4,8 @@
 #
 
 test_description='git merge-tree'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index ed11569d8d7ec605198773351e75baeaa232cb1a..2dc75b80db80964a6bcdbc3977be98f815b40dd0 100755 (executable)
@@ -6,6 +6,9 @@ test_description='Recursive "git fetch" for submodules'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 
 pwd=$(pwd)
index d573ca496ab2234e1c5b7a228b9291320bf358c2..3f58b515cee5d8f8ec72d22e363512b5a5a40c1f 100755 (executable)
@@ -5,6 +5,9 @@ test_description='test push with submodules'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 
 test_expect_success setup '
index bba768f5ded1fc9b74a6920db74436335d254559..24d374adbae8846de060ae24db9f92b1e8e5e207 100755 (executable)
@@ -137,6 +137,53 @@ test_expect_success GPG 'signed push sends push certificate' '
        test_cmp expect dst/push-cert-status
 '
 
+test_expect_success GPGSSH 'ssh signed push sends push certificate' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git -C dst config receive.certnonceseed sekrit &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       # discard the update list
+       cat >/dev/null
+       # record the push certificate
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi &&
+
+       cat >../push-cert-status <<E_O_F
+       SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+       KEY=${GIT_PUSH_CERT_KEY-nokey}
+       STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+       NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+       NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+       E_O_F
+
+       EOF
+
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+       git push --signed dst noop ff +noff &&
+
+       (
+               cat <<-\EOF &&
+               SIGNER=principal with number 1
+               KEY=FINGERPRINT
+               STATUS=G
+               NONCE_STATUS=OK
+               EOF
+               sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+       ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
+
+       noop=$(git rev-parse noop) &&
+       ff=$(git rev-parse ff) &&
+       noff=$(git rev-parse noff) &&
+       grep "$noop $ff refs/heads/ff" dst/push-cert &&
+       grep "$noop $noff refs/heads/noff" dst/push-cert &&
+       test_cmp expect dst/push-cert-status
+'
+
 test_expect_success GPG 'inconsistent push options in signed push not allowed' '
        # First, invoke receive-pack with dummy input to obtain its preamble.
        prepare_dst &&
@@ -276,6 +323,60 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
        test_cmp expect dst/push-cert-status
 '
 
+test_expect_success GPGSSH 'fail without key and heed user.signingkey ssh' '
+       test_config gpg.format ssh &&
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git -C dst config receive.certnonceseed sekrit &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       # discard the update list
+       cat >/dev/null
+       # record the push certificate
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi &&
+
+       cat >../push-cert-status <<E_O_F
+       SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+       KEY=${GIT_PUSH_CERT_KEY-nokey}
+       STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+       NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+       NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+       E_O_F
+
+       EOF
+
+       test_config user.email hasnokey@nowhere.com &&
+       test_config gpg.format ssh &&
+       test_config user.signingkey "" &&
+       (
+               sane_unset GIT_COMMITTER_EMAIL &&
+               test_must_fail git push --signed dst noop ff +noff
+       ) &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+       git push --signed dst noop ff +noff &&
+
+       (
+               cat <<-\EOF &&
+               SIGNER=principal with number 1
+               KEY=FINGERPRINT
+               STATUS=G
+               NONCE_STATUS=OK
+               EOF
+               sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+       ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
+
+       noop=$(git rev-parse noop) &&
+       ff=$(git rev-parse ff) &&
+       noff=$(git rev-parse noff) &&
+       grep "$noop $ff refs/heads/ff" dst/push-cert &&
+       grep "$noop $noff refs/heads/noff" dst/push-cert &&
+       test_cmp expect dst/push-cert-status
+'
+
 test_expect_success GPG 'failed atomic push does not execute GPG' '
        prepare_dst &&
        git -C dst config receive.certnonceseed sekrit &&
index c024fa2818314d3d127e93da2e281dc259820733..8ca50f8b18c1ce68b553efffd1daaa57b730de07 100755 (executable)
@@ -509,4 +509,20 @@ test_expect_success 'colorize errors/hints' '
        test_i18ngrep ! "^hint: " decoded
 '
 
+test_expect_success 'report error server does not provide ref status' '
+       git init "$HTTPD_DOCUMENT_ROOT_PATH/no_report" &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" config http.receivepack true &&
+       test_must_fail git push --porcelain \
+               $HTTPD_URL_USER_PASS/smart/no_report \
+               HEAD:refs/tags/will-fail >actual &&
+       test_must_fail git -C "$HTTPD_DOCUMENT_ROOT_PATH/no_report" \
+               rev-parse --verify refs/tags/will-fail &&
+       cat >expect <<-EOF &&
+       To $HTTPD_URL/smart/no_report
+       !       HEAD:refs/tags/will-fail        [remote failure] (remote failed to report status)
+       Done
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 58c7add7eefad8dc8280590975b71a0b04296633..214228349ad3995e49815850412e9c24bfc58065 100755 (executable)
@@ -5,6 +5,9 @@ test_description='pushing to a repository using push options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 
 mk_repo_pair () {
index 4f92a116e1f0ec48bdf79f1963da32c8ecdcd8dd..fa6b4cca65c8c687bf83f9eaf0ecb5568c9373f6 100755 (executable)
@@ -2,6 +2,9 @@
 
 test_description='pull can handle submodules'
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-submodule-update.sh
 
index cd803ae8bf1abd47e691004e34149a059b8d611b..cd7604fff93a55ff9bb9e4bcae905c4386f0374a 100755 (executable)
@@ -4,6 +4,7 @@ test_description='various Windows-only path tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CYGWIN
index b4905b822c07048ddfbc43cfd39265bd2116f197..83513e46a3556baa014c76f613a4da5504b8b548 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='handling of alternates in environment variables'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_obj () {
index 44f55d93fefbc766feca7e264df9319f7591feb9..06c5fb56157f28359b47c04818e2a51f9098ee36 100755 (executable)
@@ -81,6 +81,16 @@ test_expect_success GPG 'set up a signed tag' '
        git tag -s -m signed-tag-msg signed-good-tag left
 '
 
+test_expect_success GPGSSH 'created ssh signed commit and tag' '
+       test_config gpg.format ssh &&
+       git checkout -b signed-ssh &&
+       touch file &&
+       git add file &&
+       git commit -m "ssh signed" -S"${GPGSSH_KEY_PRIMARY}" &&
+       git tag -s -u"${GPGSSH_KEY_PRIMARY}" -m signed-ssh-tag-msg signed-good-ssh-tag left &&
+       git tag -s -u"${GPGSSH_KEY_UNTRUSTED}" -m signed-ssh-tag-msg-untrusted signed-untrusted-ssh-tag left
+'
+
 test_expect_success 'message for merging local branch' '
        echo "Merge branch ${apos}left${apos}" >expected &&
 
@@ -109,6 +119,24 @@ test_expect_success GPG 'message for merging local tag signed by unknown key' '
        grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual
 '
 
+test_expect_success GPGSSH 'message for merging local tag signed by good ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . signed-good-ssh-tag &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh key' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout main &&
+       git fetch . signed-untrusted-ssh-tag &&
+       git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
+       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+       grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
 test_expect_success 'message for merging external branch' '
        echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
 
index e5e89c2045e714aca5e52d7612ba6bed67fa3c5e..178413c22f0dc1dd03152ec1d84843e070d1d843 100755 (executable)
@@ -5,6 +5,9 @@ test_description='merging with submodules'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
index 25bb9bbb89994a1cb850b349342cef322c864b65..963356ba5f9257c3691e67267ff1d0cfb86355ad 100755 (executable)
@@ -2,10 +2,11 @@
 
 test_description='git mv in subdirs'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
 
 test_expect_success 'prepare reference tree' '
        mkdir path0 path1 &&
-       cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+       COPYING_test_data >path0/COPYING &&
        git add path0/COPYING &&
        git commit -m add -a
 '
@@ -107,7 +108,7 @@ test_expect_success 'clean up' '
 '
 
 test_expect_success 'adding another file' '
-       cp "$TEST_DIRECTORY"/../README.md path0/README &&
+       COPYING_test_data | tr A-Za-z N-ZA-Mn-za-m >path0/README &&
        git add path0/README &&
        git commit -m add2 -a
 '
diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh
new file mode 100755 (executable)
index 0000000..06c9dd6
--- /dev/null
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='signed tag tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed tags ssh' '
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+       echo 1 >file && git add file &&
+       test_tick && git commit -m initial &&
+       git tag -s -m initial initial &&
+       git branch side &&
+
+       echo 2 >file && test_tick && git commit -a -m second &&
+       git tag -s -m second second &&
+
+       git checkout side &&
+       echo 3 >elif && git add elif &&
+       test_tick && git commit -m "third on side" &&
+
+       git checkout main &&
+       test_tick && git merge -S side &&
+       git tag -s -m merge merge &&
+
+       echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" &&
+       git tag -a -m fourth-unsigned fourth-unsigned &&
+
+       test_tick && git commit --amend -S -m "fourth signed" &&
+       git tag -s -m fourth fourth-signed &&
+
+       echo 5 >file && test_tick && git commit -a -m "fifth" &&
+       git tag fifth-unsigned &&
+
+       git config commit.gpgsign true &&
+       echo 6 >file && test_tick && git commit -a -m "sixth" &&
+       git tag -a -m sixth sixth-unsigned &&
+
+       test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ &&
+       git tag -m seventh -s seventh-signed &&
+
+       echo 8 >file && test_tick && git commit -a -m eighth &&
+       git tag -u"${GPGSSH_KEY_UNTRUSTED}" -m eighth eighth-signed-alt
+'
+
+test_expect_success GPGSSH 'verify and show ssh signatures' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       (
+               for tag in initial second merge fourth-signed sixth-signed seventh-signed
+               do
+                       git verify-tag $tag 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $tag OK || exit 1
+               done
+       ) &&
+       (
+               for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+               do
+                       test_must_fail git verify-tag $tag 2>actual &&
+                       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $tag OK || exit 1
+               done
+       ) &&
+       (
+               for tag in eighth-signed-alt
+               do
+                       test_must_fail git verify-tag $tag 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+                       echo $tag OK || exit 1
+               done
+       )
+'
+
+test_expect_success GPGSSH 'detect fudged ssh signature' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git cat-file tag seventh-signed >raw &&
+       sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
+       git hash-object -w -t tag forged1 >forged1.tag &&
+       test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 &&
+       grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'verify ssh signatures with --raw' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       (
+               for tag in initial second merge fourth-signed sixth-signed seventh-signed
+               do
+                       git verify-tag --raw $tag 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $tag OK || exit 1
+               done
+       ) &&
+       (
+               for tag in fourth-unsigned fifth-unsigned sixth-unsigned
+               do
+                       test_must_fail git verify-tag --raw $tag 2>actual &&
+                       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $tag OK || exit 1
+               done
+       ) &&
+       (
+               for tag in eighth-signed-alt
+               do
+                       test_must_fail git verify-tag --raw $tag 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $tag OK || exit 1
+               done
+       )
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw ssh' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git verify-tag --raw sixth-signed 2>actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+       echo sixth-signed OK
+'
+
+test_expect_success GPGSSH 'verify multiple tags ssh' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       tags="seventh-signed sixth-signed" &&
+       for i in $tags
+       do
+               git verify-tag -v --raw $i || return 1
+       done >expect.stdout 2>expect.stderr.1 &&
+       grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <expect.stderr.1 >expect.stderr &&
+       git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+       grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <actual.stderr.1 >actual.stderr &&
+       test_cmp expect.stdout actual.stdout &&
+       test_cmp expect.stderr actual.stderr
+'
+
+test_expect_success GPGSSH 'verifying tag with --format - ssh' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       cat >expect <<-\EOF &&
+       tagname : fourth-signed
+       EOF
+       git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'verifying a forged tag with --format should fail silently - ssh' '
+       test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
+       test_must_be_empty actual-forged
+'
+
+test_done
index bfce05ac5dea71559e09f6023df2e5b6e1abdcbd..5530651eea492cacf106a89a88c261bc101707d7 100755 (executable)
@@ -5,10 +5,11 @@
 
 test_description='git reset should cull empty subdirs'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-diff-data.sh
 
 test_expect_success 'creating initial files' '
      mkdir path0 &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+     COPYING_test_data >path0/COPYING &&
      git add path0/COPYING &&
      git commit -m add -a
 '
@@ -16,10 +17,10 @@ test_expect_success 'creating initial files' '
 test_expect_success 'creating second files' '
      mkdir path1 &&
      mkdir path1/path2 &&
-     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
+     COPYING_test_data >path1/path2/COPYING &&
+     COPYING_test_data >path1/COPYING &&
+     COPYING_test_data >COPYING &&
+     COPYING_test_data >path0/COPYING-TOO &&
      git add path1/path2/COPYING &&
      git add path1/COPYING &&
      git add COPYING &&
index 7948ec392b3599a3884d97ae21f5deece84e638c..cf9697eba9a6aeb9579abe5b7b97b400aca8c7d0 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='reset --hard unmerged'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
index 3f7f27188313fb97e9e69e5be8d6c0b005b59e59..f87e524d6d467fd723c083c105f092850051f566 100755 (executable)
@@ -12,6 +12,9 @@ The test setup uses a sparse checkout, however the same scenario can be set up
 also by committing .gitmodules and then just removing it from the filesystem.
 '
 
+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
+export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+
 . ./test-lib.sh
 
 test_expect_success 'sparse checkout setup which hides .gitmodules' '
index 8df5a74f1db4ecd846c9365c0474f6628cbbd443..d65a0171f29c85b437721d823737189a9d72df81 100755 (executable)
@@ -71,7 +71,25 @@ test_expect_success GPG 'create signed commits' '
        git tag eleventh-signed $(cat oid) &&
        echo 12 | git commit-tree --gpg-sign=B7227189 HEAD^{tree} >oid &&
        test_line_count = 1 oid &&
-       git tag twelfth-signed-alt $(cat oid)
+       git tag twelfth-signed-alt $(cat oid) &&
+
+       cat >keydetails <<-\EOF &&
+       Key-Type: RSA
+       Key-Length: 2048
+       Subkey-Type: RSA
+       Subkey-Length: 2048
+       Name-Real: Unknown User
+       Name-Email: unknown@git.com
+       Expire-Date: 0
+       %no-ask-passphrase
+       %no-protection
+       EOF
+       gpg --batch --gen-key keydetails &&
+       echo 13 >file && git commit -a -S"unknown@git.com" -m thirteenth &&
+       git tag thirteenth-signed &&
+       DELETE_FINGERPRINT=$(gpg -K --with-colons --fingerprint --batch unknown@git.com | grep "^fpr" | head -n 1 | awk -F ":" "{print \$10;}") &&
+       gpg --batch --yes --delete-secret-keys $DELETE_FINGERPRINT &&
+       gpg --batch --yes --delete-keys unknown@git.com
 '
 
 test_expect_success GPG 'verify and show signatures' '
@@ -110,6 +128,13 @@ test_expect_success GPG 'verify and show signatures' '
        )
 '
 
+test_expect_success GPG 'verify-commit exits failure on unknown signature' '
+       test_must_fail git verify-commit thirteenth-signed 2>actual &&
+       ! grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual &&
+       grep -q -F -e "No public key" -e "public key not found" actual
+'
+
 test_expect_success GPG 'verify-commit exits success on untrusted signature' '
        git verify-commit eighth-signed-alt 2>actual &&
        grep "Good signature from" actual &&
@@ -338,6 +363,8 @@ test_expect_success GPG 'show double signature with custom format' '
 '
 
 
+# NEEDSWORK: This test relies on the test_tick commit/author dates from the first
+# 'create signed commits' test even though it creates its own
 test_expect_success GPG 'verify-commit verifies multiply signed commits' '
        git init multiply-signed &&
        cd multiply-signed &&
index 905957bd0a1681265f6064a65b319c1c3fa95484..fffdb6ff2e751d2441e8f805c475a044bfdd0920 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='corner cases in ident strings'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # confirm that we do not segfault _and_ that we do not say "(null)", as
diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh
new file mode 100755 (executable)
index 0000000..badf3ed
--- /dev/null
@@ -0,0 +1,398 @@
+#!/bin/sh
+
+test_description='ssh signed commit tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPGSSH 'create signed commits' '
+       test_oid_cache <<-\EOF &&
+       header sha1:gpgsig
+       header sha256:gpgsig-sha256
+       EOF
+
+       test_when_finished "test_unconfig commit.gpgsign" &&
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+
+       echo 1 >file && git add file &&
+       test_tick && git commit -S -m initial &&
+       git tag initial &&
+       git branch side &&
+
+       echo 2 >file && test_tick && git commit -a -S -m second &&
+       git tag second &&
+
+       git checkout side &&
+       echo 3 >elif && git add elif &&
+       test_tick && git commit -m "third on side" &&
+
+       git checkout main &&
+       test_tick && git merge -S side &&
+       git tag merge &&
+
+       echo 4 >file && test_tick && git commit -a -m "fourth unsigned" &&
+       git tag fourth-unsigned &&
+
+       test_tick && git commit --amend -S -m "fourth signed" &&
+       git tag fourth-signed &&
+
+       git config commit.gpgsign true &&
+       echo 5 >file && test_tick && git commit -a -m "fifth signed" &&
+       git tag fifth-signed &&
+
+       git config commit.gpgsign false &&
+       echo 6 >file && test_tick && git commit -a -m "sixth" &&
+       git tag sixth-unsigned &&
+
+       git config commit.gpgsign true &&
+       echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign &&
+       git tag seventh-unsigned &&
+
+       test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ &&
+       git tag seventh-signed &&
+
+       echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" &&
+       git tag eighth-signed-alt &&
+
+       # commit.gpgsign is still on but this must not be signed
+       echo 9 | git commit-tree HEAD^{tree} >oid &&
+       test_line_count = 1 oid &&
+       git tag ninth-unsigned $(cat oid) &&
+       # explicit -S of course must sign.
+       echo 10 | git commit-tree -S HEAD^{tree} >oid &&
+       test_line_count = 1 oid &&
+       git tag tenth-signed $(cat oid) &&
+
+       # --gpg-sign[=<key-id>] must sign.
+       echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid &&
+       test_line_count = 1 oid &&
+       git tag eleventh-signed $(cat oid) &&
+       echo 12 | git commit-tree --gpg-sign="${GPGSSH_KEY_UNTRUSTED}" HEAD^{tree} >oid &&
+       test_line_count = 1 oid &&
+       git tag twelfth-signed-alt $(cat oid)
+'
+
+test_expect_success GPGSSH 'verify and show signatures' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_config gpg.mintrustlevel UNDEFINED &&
+       (
+               for commit in initial second merge fourth-signed \
+                       fifth-signed sixth-signed seventh-signed tenth-signed \
+                       eleventh-signed
+               do
+                       git verify-commit $commit &&
+                       git show --pretty=short --show-signature $commit >actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in merge^2 fourth-unsigned sixth-unsigned \
+                       seventh-unsigned ninth-unsigned
+               do
+                       test_must_fail git verify-commit $commit &&
+                       git show --pretty=short --show-signature $commit >actual &&
+                       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in eighth-signed-alt twelfth-signed-alt
+               do
+                       git show --pretty=short --show-signature $commit >actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       grep "${GPGSSH_KEY_NOT_TRUSTED}" actual &&
+                       echo $commit OK || exit 1
+               done
+       )
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git verify-commit eighth-signed-alt 2>actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+       grep "${GPGSSH_KEY_NOT_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_config gpg.minTrustLevel fully &&
+       git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits success with low minTrustLevel' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_config gpg.minTrustLevel marginal &&
+       git verify-commit sixth-signed
+'
+
+test_expect_success GPGSSH 'verify-commit exits failure with high minTrustLevel' '
+       test_config gpg.minTrustLevel ultimate &&
+       test_must_fail git verify-commit eighth-signed-alt
+'
+
+test_expect_success GPGSSH 'verify signatures with --raw' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       (
+               for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
+               do
+                       git verify-commit --raw $commit 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned
+               do
+                       test_must_fail git verify-commit --raw $commit 2>actual &&
+                       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $commit OK || exit 1
+               done
+       ) &&
+       (
+               for commit in eighth-signed-alt
+               do
+                       test_must_fail git verify-commit --raw $commit 2>actual &&
+                       grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual &&
+                       ! grep "${GPGSSH_BAD_SIGNATURE}" actual &&
+                       echo $commit OK || exit 1
+               done
+       )
+'
+
+test_expect_success GPGSSH 'proper header is used for hash algorithm' '
+       git cat-file commit fourth-signed >output &&
+       grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output
+'
+
+test_expect_success GPGSSH 'show signed commit with signature' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git show -s initial >commit &&
+       git show -s --show-signature initial >show &&
+       git verify-commit -v initial >verify.1 2>verify.2 &&
+       git cat-file commit initial >cat &&
+       grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit &&
+       grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg &&
+       grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
+       test_cmp show.commit commit &&
+       test_cmp show.gpg verify.2 &&
+       test_cmp cat.commit verify.1
+'
+
+test_expect_success GPGSSH 'detect fudged signature' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git cat-file commit seventh-signed >raw &&
+       sed -e "s/^seventh/7th forged/" raw >forged1 &&
+       git hash-object -w -t commit forged1 >forged1.commit &&
+       test_must_fail git verify-commit $(cat forged1.commit) &&
+       git show --pretty=short --show-signature $(cat forged1.commit) >actual1 &&
+       grep "${GPGSSH_BAD_SIGNATURE}" actual1 &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1
+'
+
+test_expect_success GPGSSH 'detect fudged signature with NUL' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git cat-file commit seventh-signed >raw &&
+       cat raw >forged2 &&
+       echo Qwik | tr "Q" "\000" >>forged2 &&
+       git hash-object -w -t commit forged2 >forged2.commit &&
+       test_must_fail git verify-commit $(cat forged2.commit) &&
+       git show --pretty=short --show-signature $(cat forged2.commit) >actual2 &&
+       grep "${GPGSSH_BAD_SIGNATURE}" actual2 &&
+       ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2
+'
+
+test_expect_success GPGSSH 'amending already signed commit' '
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       git checkout fourth-signed^0 &&
+       git commit --amend -S --no-edit &&
+       git verify-commit HEAD &&
+       git show -s --show-signature HEAD >actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual &&
+       ! grep "${GPGSSH_BAD_SIGNATURE}" actual
+'
+
+test_expect_success GPGSSH 'show good signature with custom format' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+       cat >expect.tmpl <<-\EOF &&
+       G
+       FINGERPRINT
+       principal with number 1
+       FINGERPRINT
+
+       EOF
+       sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show bad signature with custom format' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       cat >expect <<-\EOF &&
+       B
+
+
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with custom format' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       cat >expect.tmpl <<-\EOF &&
+       U
+       FINGERPRINT
+
+       FINGERPRINT
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+       sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with undefined trust level' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       cat >expect.tmpl <<-\EOF &&
+       undefined
+       FINGERPRINT
+
+       FINGERPRINT
+
+       EOF
+       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") &&
+       sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show untrusted signature with ultimate trust level' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       cat >expect.tmpl <<-\EOF &&
+       fully
+       FINGERPRINT
+       principal with number 1
+       FINGERPRINT
+
+       EOF
+       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+       FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
+       sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'show lack of signature with custom format' '
+       cat >expect <<-\EOF &&
+       N
+
+
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPGSSH 'log.showsignature behaves like --show-signature' '
+       test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_config log.showsignature true &&
+       git show initial >actual &&
+       grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual
+'
+
+test_expect_success GPGSSH 'check config gpg.format values' '
+       test_config gpg.format ssh &&
+       test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
+       test_config gpg.format ssh &&
+       git commit -S --amend -m "success" &&
+       test_config gpg.format OpEnPgP &&
+       test_must_fail git commit -S --amend -m "fail"
+'
+
+test_expect_failure GPGSSH 'detect fudged commit with double signature (TODO)' '
+       sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
+       sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
+               sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
+       gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
+       cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
+       sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
+               double-combined.asc > double-gpgsig &&
+       sed -e "/committer/r double-gpgsig" double-base >double-commit &&
+       git hash-object -w -t commit double-commit >double-commit.commit &&
+       test_must_fail git verify-commit $(cat double-commit.commit) &&
+       git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
+       grep "BAD signature from" double-actual &&
+       grep "Good signature from" double-actual
+'
+
+test_expect_failure GPGSSH 'show double signature with custom format (TODO)' '
+       cat >expect <<-\EOF &&
+       E
+
+
+
+
+       EOF
+       git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
+       test_cmp expect actual
+'
+
+
+test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' '
+       git init multiply-signed &&
+       cd multiply-signed &&
+       test_commit first &&
+       echo 1 >second &&
+       git add second &&
+       tree=$(git write-tree) &&
+       parent=$(git rev-parse HEAD^{commit}) &&
+       git commit --gpg-sign -m second &&
+       git cat-file commit HEAD &&
+       # Avoid trailing whitespace.
+       sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
+       Qtree $tree
+       Qparent $parent
+       Qauthor A U Thor <author@example.com> 1112912653 -0700
+       Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
+       Qgpgsig -----BEGIN PGP SIGNATURE-----
+       QZ
+       Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
+       Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
+       Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
+       Q =tQ0N
+       Q -----END PGP SIGNATURE-----
+       Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+       QZ
+       Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
+       Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
+       Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
+       Q =pIwP
+       Q -----END PGP SIGNATURE-----
+       Q
+       Qsecond
+       EOF
+       head=$(git hash-object -t commit -w commit) &&
+       git reset --hard $head &&
+       git verify-commit $head 2>actual &&
+       grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual
+'
+
+test_done
index cd4f9607dc13ce7acfebc62d67bb4dbf1ef46135..eca755510160be797ff1b08b67f4e18fa10dddb9 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing merge when using a custom message for the merge commit.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_merge_msgs() {
index a98785da7955b6cc41fe5aedc6f339311e3f989e..1dd07141a7df9f9666ac01a611c6c6a81ddc6424 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='git grep --open-files-in-pager
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pager.sh
 unset PAGER GIT_PAGER
@@ -114,8 +115,8 @@ test_expect_success 'modified file' '
        unrelated
        EOF
 
+       test_when_finished "git reset --hard" &&
        echo "enum grep_pat_token" >unrelated &&
-       test_when_finished "git checkout HEAD unrelated" &&
        GIT_PAGER=./less git grep -F -O "enum grep_pat_token" >out &&
        test_cmp expect actual &&
        test_must_be_empty out
index 701e08a8e5941d711ffbef52da972acb7ab9a41e..1227885737b859fd4937f1f78a0a9250f103703c 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='grep icase on non-English locales'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 test_expect_success GETTEXT_ISO_LOCALE 'setup' '
index 9d67a5fc4cfad3b8a8ac9c2d3e7689150dbbf2cc..fdb2355649e31a8153f3ff60fe3ffbb531a2a7aa 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git grep with a binary pattern files'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gettext.sh
 
 nul_match_internal () {
index 151da80c56132ba0c705499e88fb611ec1c5ddec..2679a7596a610bca1a884be085dead4229962270 100644 (file)
@@ -589,17 +589,33 @@ USER_TERM="$TERM"
 TERM=dumb
 export TERM USER_TERM
 
-error () {
-       say_color error "error: $*"
+_error_exit () {
        finalize_junit_xml
        GIT_EXIT_OK=t
        exit 1
 }
 
+error () {
+       say_color error "error: $*"
+       _error_exit
+}
+
 BUG () {
        error >&7 "bug in the test script: $*"
 }
 
+BAIL_OUT () {
+       test $# -ne 1 && BUG "1 param"
+
+       # Do not change "Bail out! " string. It's part of TAP syntax:
+       # https://testanything.org/tap-specification.html
+       local bail_out="Bail out! "
+       local message="$1"
+
+       say_color error $bail_out "$message"
+       _error_exit
+}
+
 say () {
        say_color info "$*"
 }
@@ -608,9 +624,7 @@ if test -n "$HARNESS_ACTIVE"
 then
        if test "$verbose" = t || test -n "$verbose_only"
        then
-               printf 'Bail out! %s\n' \
-                'verbose mode forbidden under TAP harness; try --verbose-log'
-               exit 1
+               BAIL_OUT 'verbose mode forbidden under TAP harness; try --verbose-log'
        fi
 fi
 
@@ -720,7 +734,7 @@ test_failure_ () {
        say_color error "not ok $test_count - $1"
        shift
        printf '%s\n' "$*" | sed -e 's/^/#      /'
-       test "$immediate" = "" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }
+       test "$immediate" = "" || _error_exit
 }
 
 test_known_broken_ok_ () {
@@ -1398,7 +1412,7 @@ then
        fi
 elif test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false
 then
-       error "GIT_TEST_PASSING_SANITIZE_LEAK=true has no effect except when compiled with SANITIZE=leak"
+       BAIL_OUT "GIT_TEST_PASSING_SANITIZE_LEAK=true has no effect except when compiled with SANITIZE=leak"
 fi
 
 # Last-minute variable setup
@@ -1407,8 +1421,20 @@ HOME="$TRASH_DIRECTORY"
 GNUPGHOME="$HOME/gnupg-home-not-used"
 export HOME GNUPGHOME USER_HOME
 
+# "rm -rf" existing trash directory, even if a previous run left it
+# with bad permissions.
+remove_trash_directory () {
+       dir="$1"
+       if ! rm -rf "$dir" 2>/dev/null
+       then
+               chmod -R u+rwx "$dir"
+               rm -rf "$dir"
+       fi
+       ! test -d "$dir"
+}
+
 # Test repository
-rm -fr "$TRASH_DIRECTORY" || {
+remove_trash_directory "$TRASH_DIRECTORY" || {
        GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
index e8dbdd115309c860d8c458379fef07ac0af37c3b..a0297b0986c62e7a6e8f8951071ed5ea9da2640b 100644 (file)
@@ -845,6 +845,10 @@ static int push_update_ref_status(struct strbuf *buf,
                        forced = 1;
                        FREE_AND_NULL(msg);
                }
+               else if (!strcmp(msg, "expecting report")) {
+                       status = REF_STATUS_EXPECTING_REPORT;
+                       FREE_AND_NULL(msg);
+               }
        }
 
        if (state->hint)
index a7e1712d2368a42ffb65e64778a322c2e5c18842..89ca95ce90b369bc521fc52fc9b071c467a74022 100644 (file)
@@ -2156,9 +2156,10 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
        if (o->dir)
                d.exclude_per_dir = o->dir->exclude_per_dir;
        i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
+       dir_clear(&d);
+       free(pathbuf);
        if (i)
                return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
-       free(pathbuf);
        return cnt;
 }
 
index af02b1878c7d0128a2b650d03c050168e9ac7a3e..8578cb0d12e59848ebd3d4bff754ada89ec99656 100644 (file)
@@ -64,9 +64,15 @@ PATTERNS("cpp",
         /* functions/methods, variables, and compounds at top level */
         "^((::[[:space:]]*)?[A-Za-z_].*)$",
         /* -- */
+        /* identifiers and keywords */
         "[a-zA-Z_][a-zA-Z0-9_]*"
-        "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
-        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+        /* decimal and octal integers as well as floatingpoint numbers */
+        "|[0-9][0-9.]*([Ee][-+]?[0-9]+)?[fFlLuU]*"
+        /* hexadecimal and binary integers */
+        "|0[xXbB][0-9a-fA-F]+[lLuU]*"
+        /* floatingpoint numbers that begin with a decimal point */
+        "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?"
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"),
 PATTERNS("csharp",
         /* Keywords */
         "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"