diff options
192 files changed, 2788 insertions, 1027 deletions
diff --git a/Documentation/.gitignore b/Documentation/.gitignore index 9022d48355..1c3771e7d7 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -14,4 +14,5 @@ manpage-base-url.xsl SubmittingPatches.txt tmp-doc-diff/ GIT-ASCIIDOCFLAGS +/.build/ /GIT-EXCLUDED-PROGRAMS diff --git a/Documentation/Makefile b/Documentation/Makefile index 2021568cd5..911b6bf79c 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -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 + $(QUIET)mkdir $@ +.build/lint-docs/gitlink/config: | .build/lint-docs + $(QUIET)mkdir $@ +LINT_DOCS_GITLINK = $(patsubst %.txt,.build/lint-docs/gitlink/%.ok,$(HOWTO_TXT) $(DOC_DEP_TXT)) +$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink +$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/howto +$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/config +$(LINT_DOCS_GITLINK): lint-gitlink.perl +$(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt + $(QUIET_LINT_GITLINK)$(PERL_PATH) lint-gitlink.perl \ + $< \ $(HOWTO_TXT) $(DOC_DEP_TXT) \ --section=1 $(MAN1_TXT) \ --section=5 $(MAN5_TXT) \ - --section=7 $(MAN7_TXT); \ - $(PERL_PATH) lint-man-end-blurb.perl $(MAN_TXT); \ - $(PERL_PATH) lint-man-section-order.perl $(MAN_TXT); + --section=7 $(MAN7_TXT) >$@ +.PHONY: lint-docs-gitlink +lint-docs-gitlink: $(LINT_DOCS_GITLINK) + +## Lint: man-end-blurb +.build/lint-docs/man-end-blurb: | .build/lint-docs + $(QUIET)mkdir $@ +LINT_DOCS_MAN_END_BLURB = $(patsubst %.txt,.build/lint-docs/man-end-blurb/%.ok,$(MAN_TXT)) +$(LINT_DOCS_MAN_END_BLURB): | .build/lint-docs/man-end-blurb +$(LINT_DOCS_MAN_END_BLURB): lint-man-end-blurb.perl +$(LINT_DOCS_MAN_END_BLURB): .build/lint-docs/man-end-blurb/%.ok: %.txt + $(QUIET_LINT_MANEND)$(PERL_PATH) lint-man-end-blurb.perl $< >$@ +.PHONY: lint-docs-man-end-blurb +lint-docs-man-end-blurb: $(LINT_DOCS_MAN_END_BLURB) + +## Lint: man-section-order +.build/lint-docs/man-section-order: | .build/lint-docs + $(QUIET)mkdir $@ +LINT_DOCS_MAN_SECTION_ORDER = $(patsubst %.txt,.build/lint-docs/man-section-order/%.ok,$(MAN_TXT)) +$(LINT_DOCS_MAN_SECTION_ORDER): | .build/lint-docs/man-section-order +$(LINT_DOCS_MAN_SECTION_ORDER): lint-man-section-order.perl +$(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt + $(QUIET_LINT_MANSEC)$(PERL_PATH) lint-man-section-order.perl $< >$@ +.PHONY: lint-docs-man-section-order +lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER) + +## Lint: list of targets above +.PHONY: lint-docs +lint-docs: lint-docs-gitlink +lint-docs: lint-docs-man-end-blurb +lint-docs: lint-docs-man-section-order ifeq ($(wildcard po/Makefile),po/Makefile) doc-l10n install-l10n:: diff --git a/Documentation/RelNotes/2.34.0.txt b/Documentation/RelNotes/2.34.0.txt index c85385dc03..fdf7992193 100644 --- a/Documentation/RelNotes/2.34.0.txt +++ b/Documentation/RelNotes/2.34.0.txt @@ -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). diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt index dd2d2e0d84..6e817f6047 100644 --- a/Documentation/config/color.txt +++ b/Documentation/config/color.txt @@ -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 diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt index d94025cb36..4f30c7dbdd 100644 --- a/Documentation/config/gpg.txt +++ b/Documentation/config/gpg.txt @@ -11,13 +11,13 @@ gpg.program:: gpg.format:: Specifies which key format to use when signing with `--gpg-sign`. - Default is "openpgp" and another possible value is "x509". + Default is "openpgp". Other possible values are "x509", "ssh". gpg.<format>.program:: Use this to customize the program used for the signing format you chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still be used as a legacy synonym for `gpg.openpgp.program`. The default - value for `gpg.x509.program` is "gpgsm". + value for `gpg.x509.program` is "gpgsm" and `gpg.ssh.program` is "ssh-keygen". gpg.minTrustLevel:: Specifies a minimum trust level for signature verification. If @@ -33,3 +33,42 @@ gpg.minTrustLevel:: * `marginal` * `fully` * `ultimate` + +gpg.ssh.defaultKeyCommand: + This command that will be run when user.signingkey is not set and a ssh + signature is requested. On successful exit a valid ssh public key is + expected in the first line of its output. To automatically use the first + available key from your ssh-agent set this to "ssh-add -L". + +gpg.ssh.allowedSignersFile:: + A file containing ssh public keys which you are willing to trust. + The file consists of one or more lines of principals followed by an ssh + public key. + e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1... + See ssh-keygen(1) "ALLOWED SIGNERS" for details. + The principal is only used to identify the key and is available when + verifying a signature. ++ +SSH has no concept of trust levels like gpg does. To be able to differentiate +between valid signatures and trusted signatures the trust level of a signature +verification is set to `fully` when the public key is present in the allowedSignersFile. +Otherwise the trust level is `undefined` and git verify-commit/tag will fail. ++ +This file can be set to a location outside of the repository and every developer +maintains their own trust store. A central repository server could generate this +file automatically from ssh keys with push access to verify the code against. +In a corporate setting this file is probably generated at a global location +from automation that already handles developer ssh keys. ++ +A repository that only allows signed commits can store the file +in the repository itself using a path relative to the top-level of the working tree. +This way only committers with an already valid key can add or change keys in the keyring. ++ +Using a SSH CA key with the cert-authority option +(see ssh-keygen(1) "CERTIFICATES") is also valid. + +gpg.ssh.revocationFile:: + Either a SSH KRL or a list of revoked public keys (without the principal prefix). + See ssh-keygen(1) for details. + If a public key is found in this file then it will always be treated + as having trust level "never" and signatures will show as invalid. diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt index 59aec7c3ae..ad78dce9ec 100644 --- a/Documentation/config/user.txt +++ b/Documentation/config/user.txt @@ -36,3 +36,10 @@ user.signingKey:: commit, you can override the default selection with this variable. This option is passed unchanged to gpg's --local-user parameter, so you may specify a key using any method that gpg supports. + If gpg.format is set to "ssh" this can contain the literal ssh public + key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and + corresponds to the private key used for signing. The private key + needs to be available via ssh-agent. Alternatively it can be set to + a file containing a private key directly. If not set git will call + gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first + key available. diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl index b22a367844..1c61dd9512 100755 --- a/Documentation/lint-gitlink.perl +++ b/Documentation/lint-gitlink.perl @@ -5,11 +5,12 @@ use warnings; # Parse arguments, a simple state machine for input like: # -# howto/*.txt config/*.txt --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...] +# <file-to-check.txt> <valid-files-to-link-to> --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...] my %TXT; my %SECTION; my $section; my $lint_these = 0; +my $to_check = shift @ARGV; for my $arg (@ARGV) { if (my ($sec) = $arg =~ /^--section=(\d+)$/s) { $section = $sec; @@ -30,13 +31,14 @@ sub report { my ($pos, $line, $target, $msg) = @_; substr($line, $pos) = "' <-- HERE"; $line =~ s/^\s+//; - print "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n"; - print "$ARGV:$.:\t'$line\n"; + print STDERR "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n"; + print STDERR "$ARGV:$.:\t'$line\n"; $exit_code = 1; } @ARGV = sort values %TXT; -die "BUG: Nothing to process!" unless @ARGV; +die "BUG: No list of valid linkgit:* files given" unless @ARGV; +@ARGV = $to_check; while (<>) { my $line = $_; while ($line =~ m/linkgit:((.*?)\[(\d)\])/g) { diff --git a/Documentation/lint-man-end-blurb.perl b/Documentation/lint-man-end-blurb.perl index d69312e5db..6bdb13ad9f 100755 --- a/Documentation/lint-man-end-blurb.perl +++ b/Documentation/lint-man-end-blurb.perl @@ -6,7 +6,7 @@ use warnings; my $exit_code = 0; sub report { my ($target, $msg) = @_; - print "error: $target: $msg\n"; + print STDERR "error: $target: $msg\n"; $exit_code = 1; } diff --git a/Documentation/lint-man-section-order.perl b/Documentation/lint-man-section-order.perl index b05f9156dd..425377dfeb 100755 --- a/Documentation/lint-man-section-order.perl +++ b/Documentation/lint-man-section-order.perl @@ -46,7 +46,7 @@ my $SECTION_RX = do { my $exit_code = 0; sub report { my ($msg) = @_; - print "$ARGV:$.: $msg\n"; + print STDERR "$ARGV:$.: $msg\n"; $exit_code = 1; } diff --git a/Documentation/technical/signature-format.txt b/Documentation/technical/signature-format.txt index 2c9406a56a..166721be6f 100644 --- a/Documentation/technical/signature-format.txt +++ b/Documentation/technical/signature-format.txt @@ -13,6 +13,22 @@ Signatures always begin with `-----BEGIN PGP SIGNATURE-----` and end with `-----END PGP SIGNATURE-----`, unless gpg is told to produce RFC1991 signatures which use `MESSAGE` instead of `SIGNATURE`. +Signatures sometimes appear as a part of the normal payload +(e.g. a signed tag has the signature block appended after the payload +that the signature applies to), and sometimes appear in the value of +an object header (e.g. a merge commit that merged a signed tag would +have the entire tag contents on its "mergetag" header). In the case +of the latter, the usual multi-line formatting rule for object +headers applies. I.e. the second and subsequent lines are prefixed +with a SP to signal that the line is continued from the previous +line. + +This is even true for an originally empty line. In the following +examples, the end of line that ends with a whitespace letter is +highlighted with a `$` sign; if you are trying to recreate these +example by hand, do not cut and paste them---they are there +primarily to highlight extra whitespace at the end of some lines. + The signed payload and the way the signature is embedded depends on the type of the object resp. transaction. @@ -78,7 +94,7 @@ author A U Thor <author@example.com> 1465981137 +0000 committer C O Mitter <committer@example.com> 1465981137 +0000 gpgsig -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 - + $ iQEcBAABAgAGBQJXYRjRAAoJEGEJLoW3InGJ3IwIAIY4SA6GxY3BjL60YyvsJPh/ HRCJwH+w7wt3Yc/9/bW2F+gF72kdHOOs2jfv+OZhq0q4OAN6fvVSczISY/82LpS7 DVdMQj2/YcHDT4xrDNBnXnviDO9G7am/9OE77kEbXrp7QPxvhjkicHNwy2rEflAA @@ -128,13 +144,13 @@ mergetag object 04b871796dc0420f8e7561a895b52484b701d51a type commit tag signedtag tagger C O Mitter <committer@example.com> 1465981006 +0000 - + $ signed tag - + $ signed tag message body -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 - + $ iQEcBAABAgAGBQJXYRhOAAoJEGEJLoW3InGJklkIAIcnhL7RwEb/+QeX9enkXhxn rxfdqrvWd1K80sl2TOt8Bg/NYwrUBw/RWJ+sg/hhHp4WtvE1HDGHlkEz3y11Lkuh 8tSxS3qKTxXUGozyPGuE90sJfExhZlW4knIQ1wt/yWqM+33E9pN4hzPqLwyrdods @@ -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) $< && \ diff --git a/builtin/blame.c b/builtin/blame.c index 1c31a99640..f9ee3f8c68 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -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); diff --git a/builtin/branch.c b/builtin/branch.c index 0b7ed82654..7a1d1eeb07 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -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; diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c index fb9fd13b73..ede7dc32a4 100644 --- a/builtin/checkout--worker.c +++ b/builtin/checkout--worker.c @@ -82,8 +82,8 @@ static void worker_loop(struct checkout *state) size_t i, nr = 0, alloc = 0; while (1) { - int len = packet_read(0, NULL, NULL, packet_buffer, - sizeof(packet_buffer), 0); + int len = packet_read(0, packet_buffer, sizeof(packet_buffer), + 0); if (len < 0) BUG("packet_read() returned negative value"); diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 3c3de3a156..ab8e5cd59a 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -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; } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 95e8e89e81..8e2caf7281 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -312,7 +312,7 @@ static void export_blob(const struct object_id *oid) if (!buf) die("could not read blob %s", oid_to_hex(oid)); if (check_object_signature(the_repository, oid, buf, size, - type_name(type)) < 0) + type_name(type), NULL) < 0) die("oid mismatch in blob %s", oid_to_hex(oid)); object = parse_object_buffer(the_repository, oid, type, size, buf, &eaten); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 642b4b888f..16a2c7d57c 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -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; } diff --git a/builtin/fsck.c b/builtin/fsck.c index b42b6fe21f..30a516da29 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -593,18 +593,43 @@ static void get_default_heads(void) } } +struct for_each_loose_cb +{ + struct progress *progress; + struct strbuf obj_type; +}; + static int fsck_loose(const struct object_id *oid, const char *path, void *data) { + struct for_each_loose_cb *cb_data = data; struct object *obj; - enum object_type type; + enum object_type type = OBJ_NONE; unsigned long size; void *contents; int eaten; + struct object_info oi = OBJECT_INFO_INIT; + struct object_id real_oid = *null_oid(); + int err = 0; - if (read_loose_object(path, oid, &type, &size, &contents) < 0) { + strbuf_reset(&cb_data->obj_type); + oi.type_name = &cb_data->obj_type; + oi.sizep = &size; + oi.typep = &type; + + if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) { + if (contents && !oideq(&real_oid, oid)) + err = error(_("%s: hash-path mismatch, found at: %s"), + oid_to_hex(&real_oid), path); + else + err = error(_("%s: object corrupt or missing: %s"), + oid_to_hex(oid), path); + } + if (type != OBJ_NONE && type < 0) + err = error(_("%s: object is of unknown type '%s': %s"), + oid_to_hex(&real_oid), cb_data->obj_type.buf, + path); + if (err < 0) { errors_found |= ERROR_OBJECT; - error(_("%s: object corrupt or missing: %s"), - oid_to_hex(oid), path); return 0; /* keep checking other objects */ } @@ -640,8 +665,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data) return 0; } -static int fsck_subdir(unsigned int nr, const char *path, void *progress) +static int fsck_subdir(unsigned int nr, const char *path, void *data) { + struct for_each_loose_cb *cb_data = data; + struct progress *progress = cb_data->progress; display_progress(progress, nr + 1); return 0; } @@ -649,6 +676,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress) static void fsck_object_dir(const char *path) { struct progress *progress = NULL; + struct for_each_loose_cb cb_data = { + .obj_type = STRBUF_INIT, + .progress = progress, + }; if (verbose) fprintf_ln(stderr, _("Checking object directory")); @@ -657,9 +688,10 @@ static void fsck_object_dir(const char *path) progress = start_progress(_("Checking object directories"), 256); for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir, - progress); + &cb_data); display_progress(progress, 256); stop_progress(&progress); + strbuf_release(&cb_data.obj_type); } static int fsck_head_link(const char *head_ref_name, diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 7ce69c087e..15ae406e6b 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -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)); /* diff --git a/builtin/ls-files.c b/builtin/ls-files.c index a2000ed6bf..031fef1bca 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -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; } diff --git a/builtin/merge.c b/builtin/merge.c index cc4a910c69..ea3112e0c0 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -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; } diff --git a/builtin/mktag.c b/builtin/mktag.c index dddcccdd36..3b2dbbb37e 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) repl = lookup_replace_object(the_repository, tagged_oid); ret = check_object_signature(the_repository, repl, - buffer, size, type_name(*tagged_type)); + buffer, size, type_name(*tagged_type), + NULL); free(buffer); return ret; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 25cc0c907e..49b846d960 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -135,6 +135,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb) if (status) return status; + status = git_gpg_config(var, value, NULL); + if (status) + return status; + if (strcmp(var, "receive.denydeletes") == 0) { deny_deletes = git_config_bool(var, value); return 0; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 8932142312..69c432ef1a 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -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; } diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 3e7ab1ca82..e7f7af5de3 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -374,6 +374,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) for (;;) { switch (parse_options_step(&ctx, options, shortlog_usage)) { + case PARSE_OPT_NON_OPTION: + case PARSE_OPT_UNKNOWN: + break; case PARSE_OPT_HELP: case PARSE_OPT_ERROR: exit(129); diff --git a/builtin/tag.c b/builtin/tag.c index 6535ed27ee..6fe646710d 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -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; } diff --git a/cache-tree.c b/cache-tree.c index 90919f9e34..79d168192d 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -440,8 +440,9 @@ static int update_one(struct cache_tree *it, } else if (dryrun) { hash_object_file(the_hash_algo, buffer.buf, buffer.len, tree_type, &it->oid); - } else if (write_object_file(buffer.buf, buffer.len, tree_type, - &it->oid)) { + } else if (write_object_file_flags(buffer.buf, buffer.len, tree_type, + &it->oid, flags & WRITE_TREE_SILENT + ? HASH_SILENT : 0)) { strbuf_release(&buffer); return -1; } @@ -826,10 +827,17 @@ static void verify_one_sparse(struct repository *r, path->buf); } -static void verify_one(struct repository *r, - struct index_state *istate, - struct cache_tree *it, - struct strbuf *path) +/* + * Returns: + * 0 - Verification completed. + * 1 - Restart verification - a call to ensure_full_index() freed the cache + * tree that is being verified and verification needs to be restarted from + * the new toplevel cache tree. + */ +static int verify_one(struct repository *r, + struct index_state *istate, + struct cache_tree *it, + struct strbuf *path) { int i, pos, len = path->len; struct strbuf tree_buf = STRBUF_INIT; @@ -837,21 +845,30 @@ static void verify_one(struct repository *r, for (i = 0; i < it->subtree_nr; i++) { strbuf_addf(path, "%s/", it->down[i]->name); - verify_one(r, istate, it->down[i]->cache_tree, path); + if (verify_one(r, istate, it->down[i]->cache_tree, path)) + return 1; strbuf_setlen(path, len); } if (it->entry_count < 0 || /* no verification on tests (t7003) that replace trees */ lookup_replace_object(r, &it->oid) != &it->oid) - return; + return 0; if (path->len) { + /* + * If the index is sparse and the cache tree is not + * index_name_pos() may trigger ensure_full_index() which will + * free the tree that is being verified. + */ + int is_sparse = istate->sparse_index; pos = index_name_pos(istate, path->buf, path->len); + if (is_sparse && !istate->sparse_index) + return 1; if (pos >= 0) { verify_one_sparse(r, istate, it, path, pos); - return; + return 0; } pos = -pos - 1; @@ -899,6 +916,7 @@ static void verify_one(struct repository *r, oid_to_hex(&new_oid), oid_to_hex(&it->oid)); strbuf_setlen(path, len); strbuf_release(&tree_buf); + return 0; } void cache_tree_verify(struct repository *r, struct index_state *istate) @@ -907,6 +925,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate) if (!istate->cache_tree) return; - verify_one(r, istate, istate->cache_tree, &path); + if (verify_one(r, istate, istate->cache_tree, &path)) { + strbuf_reset(&path); + if (verify_one(r, istate, istate->cache_tree, &path)) + BUG("ensure_full_index() called twice while verifying cache tree"); + } strbuf_release(&path); } @@ -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); @@ -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); diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index b969dc6ebb..5216191488 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -9,6 +9,7 @@ #include "branch.h" #include "fmt-merge-msg.h" #include "commit-reach.h" +#include "gpg-interface.h" static int use_branch_desc; static int suppress_dest_pattern_seen; @@ -16,6 +17,8 @@ static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP; int fmt_merge_msg_config(const char *key, const char *value, void *cb) { + int status = 0; + if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) { int is_bool; merge_log_config = git_config_bool_or_int(key, value, &is_bool); @@ -34,6 +37,9 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb) string_list_append(&suppress_dest_patterns, value); suppress_dest_pattern_seen = 1; } else { + status = git_gpg_config(key, value, NULL); + if (status) + return status; return git_default_config(key, value, cb); } return 0; @@ -528,11 +534,11 @@ static void fmt_merge_msg_sigs(struct strbuf *out) buf = payload.buf; len = payload.len; if (check_signature(payload.buf, payload.len, sig.buf, - sig.len, &sigc) && - !sigc.gpg_output) + sig.len, &sigc) && + !sigc.output) strbuf_addstr(&sig, "gpg verification failed.\n"); else - strbuf_addstr(&sig, sigc.gpg_output); + strbuf_addstr(&sig, sigc.output); } signature_check_clear(&sigc); diff --git a/gpg-interface.c b/gpg-interface.c index 127aecfc2b..800d8caa67 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -3,11 +3,14 @@ #include "config.h" #include "run-command.h" #include "strbuf.h" +#include "dir.h" #include "gpg-interface.h" #include "sigchain.h" #include "tempfile.h" +#include "alias.h" static char *configured_signing_key; +static const char *ssh_default_key_command, *ssh_allowed_signers, *ssh_revocation_file; static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED; struct gpg_format { @@ -15,6 +18,14 @@ struct gpg_format { const char *program; const char **verify_args; const char **sigs; + int (*verify_signed_buffer)(struct signature_check *sigc, + struct gpg_format *fmt, const char *payload, + size_t payload_size, const char *signature, + size_t signature_size); + int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key); + const char *(*get_default_key)(void); + const char *(*get_key_id)(void); }; static const char *openpgp_verify_args[] = { @@ -35,14 +46,59 @@ static const char *x509_sigs[] = { NULL }; +static const char *ssh_verify_args[] = { NULL }; +static const char *ssh_sigs[] = { + "-----BEGIN SSH SIGNATURE-----", + NULL +}; + +static int verify_gpg_signed_buffer(struct signature_check *sigc, + struct gpg_format *fmt, const char *payload, + size_t payload_size, const char *signature, + size_t signature_size); +static int verify_ssh_signed_buffer(struct signature_check *sigc, + struct gpg_format *fmt, const char *payload, + size_t payload_size, const char *signature, + size_t signature_size); +static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key); +static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key); + +static const char *get_default_ssh_signing_key(void); + +static const char *get_ssh_key_id(void); + static struct gpg_format gpg_format[] = { - { .name = "openpgp", .program = "gpg", - .verify_args = openpgp_verify_args, - .sigs = openpgp_sigs + { + .name = "openpgp", + .program = "gpg", + .verify_args = openpgp_verify_args, + .sigs = openpgp_sigs, + .verify_signed_buffer = verify_gpg_signed_buffer, + .sign_buffer = sign_buffer_gpg, + .get_default_key = NULL, + .get_key_id = NULL, + }, + { + .name = "x509", + .program = "gpgsm", + .verify_args = x509_verify_args, + .sigs = x509_sigs, + .verify_signed_buffer = verify_gpg_signed_buffer, + .sign_buffer = sign_buffer_gpg, + .get_default_key = NULL, + .get_key_id = NULL, }, - { .name = "x509", .program = "gpgsm", - .verify_args = x509_verify_args, - .sigs = x509_sigs + { + .name = "ssh", + .program = "ssh-keygen", + .verify_args = ssh_verify_args, + .sigs = ssh_sigs, + .verify_signed_buffer = verify_ssh_signed_buffer, + .sign_buffer = sign_buffer_ssh, + .get_default_key = get_default_ssh_signing_key, + .get_key_id = get_ssh_key_id, }, }; @@ -72,7 +128,7 @@ static struct gpg_format *get_format_by_sig(const char *sig) void signature_check_clear(struct signature_check *sigc) { FREE_AND_NULL(sigc->payload); - FREE_AND_NULL(sigc->gpg_output); + FREE_AND_NULL(sigc->output); FREE_AND_NULL(sigc->gpg_status); FREE_AND_NULL(sigc->signer); FREE_AND_NULL(sigc->key); @@ -257,16 +313,16 @@ error: FREE_AND_NULL(sigc->key); } -static int verify_signed_buffer(const char *payload, size_t payload_size, - const char *signature, size_t signature_size, - struct strbuf *gpg_output, - struct strbuf *gpg_status) +static int verify_gpg_signed_buffer(struct signature_check *sigc, + struct gpg_format *fmt, const char *payload, + size_t payload_size, const char *signature, + size_t signature_size) { struct child_process gpg = CHILD_PROCESS_INIT; - struct gpg_format *fmt; struct tempfile *temp; int ret; - struct strbuf buf = STRBUF_INIT; + struct strbuf gpg_stdout = STRBUF_INIT; + struct strbuf gpg_stderr = STRBUF_INIT; temp = mks_tempfile_t(".git_vtag_tmpXXXXXX"); if (!temp) @@ -279,10 +335,6 @@ static int verify_signed_buffer(const char *payload, size_t payload_size, return -1; } - fmt = get_format_by_sig(signature); - if (!fmt) - BUG("bad signature '%s'", signature); - strvec_push(&gpg.args, fmt->program); strvec_pushv(&gpg.args, fmt->verify_args); strvec_pushl(&gpg.args, @@ -290,18 +342,220 @@ static int verify_signed_buffer(const char *payload, size_t payload_size, "--verify", temp->filename.buf, "-", NULL); - if (!gpg_status) - gpg_status = &buf; - sigchain_push(SIGPIPE, SIG_IGN); - ret = pipe_command(&gpg, payload, payload_size, - gpg_status, 0, gpg_output, 0); + ret = pipe_command(&gpg, payload, payload_size, &gpg_stdout, 0, + &gpg_stderr, 0); sigchain_pop(SIGPIPE); delete_tempfile(&temp); - ret |= !strstr(gpg_status->buf, "\n[GNUPG:] GOODSIG "); - strbuf_release(&buf); /* no matter it was used or not */ + ret |= !strstr(gpg_stdout.buf, "\n[GNUPG:] GOODSIG "); + sigc->payload = xmemdupz(payload, payload_size); + sigc->output = strbuf_detach(&gpg_stderr, NULL); + sigc->gpg_status = strbuf_detach(&gpg_stdout, NULL); + + parse_gpg_output(sigc); + + strbuf_release(&gpg_stdout); + strbuf_release(&gpg_stderr); + + return ret; +} + +static void parse_ssh_output(struct signature_check *sigc) +{ + const char *line, *principal, *search; + char *to_free; + char *key = NULL; + + /* + * ssh-keygen output should be: + * Good "git" signature for PRINCIPAL with RSA key SHA256:FINGERPRINT + * + * or for valid but unknown keys: + * Good "git" signature with RSA key SHA256:FINGERPRINT + * + * Note that "PRINCIPAL" can contain whitespace, "RSA" and + * "SHA256" part could be a different token that names of + * the algorithms used, and "FINGERPRINT" is a hexadecimal + * string. By finding the last occurence of " with ", we can + * reliably parse out the PRINCIPAL. + */ + sigc->result = 'B'; + sigc->trust_level = TRUST_NEVER; + + line = to_free = xmemdupz(sigc->output, strcspn(sigc->output, "\n")); + + if (skip_prefix(line, "Good \"git\" signature for ", &line)) { + /* Valid signature and known principal */ + sigc->result = 'G'; + sigc->trust_level = TRUST_FULLY; + + /* Search for the last "with" to get the full principal */ + principal = line; + do { + search = strstr(line, " with "); + if (search) + line = search + 1; + } while (search != NULL); + sigc->signer = xmemdupz(principal, line - principal - 1); + } else if (skip_prefix(line, "Good \"git\" signature with ", &line)) { + /* Valid signature, but key unknown */ + sigc->result = 'G'; + sigc->trust_level = TRUST_UNDEFINED; + } else { + goto cleanup; + } + + key = strstr(line, "key"); + if (key) { + sigc->fingerprint = xstrdup(strstr(line, "key") + 4); + sigc->key = xstrdup(sigc->fingerprint); + } else { + /* + * Output did not match what we expected + * Treat the signature as bad + */ + sigc->result = 'B'; + } + +cleanup: + free(to_free); +} + +static int verify_ssh_signed_buffer(struct signature_check *sigc, + struct gpg_format *fmt, const char *payload, + size_t payload_size, const char *signature, + size_t signature_size) +{ + struct child_process ssh_keygen = CHILD_PROCESS_INIT; + struct tempfile *buffer_file; + int ret = -1; + const char *line; + size_t trust_size; + char *principal; + struct strbuf ssh_principals_out = STRBUF_INIT; + struct strbuf ssh_principals_err = STRBUF_INIT; + struct strbuf ssh_keygen_out = STRBUF_INIT; + struct strbuf ssh_keygen_err = STRBUF_INIT; + + if (!ssh_allowed_signers) { + error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification")); + return -1; + } + + buffer_file = mks_tempfile_t(".git_vtag_tmpXXXXXX"); + if (!buffer_file) + return error_errno(_("could not create temporary file")); + if (write_in_full(buffer_file->fd, signature, signature_size) < 0 || + close_tempfile_gently(buffer_file) < 0) { + error_errno(_("failed writing detached signature to '%s'"), + buffer_file->filename.buf); + delete_tempfile(&buffer_file); + return -1; + } + + /* Find the principal from the signers */ + strvec_pushl(&ssh_keygen.args, fmt->program, + "-Y", "find-principals", + "-f", ssh_allowed_signers, + "-s", buffer_file->filename.buf, + NULL); + ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0, + &ssh_principals_err, 0); + if (ret && strstr(ssh_principals_err.buf, "usage:")) { + error(_("ssh-keygen -Y find-principals/verify is needed for ssh signature verification (available in openssh version 8.2p1+)")); + goto out; + } + if (ret || !ssh_principals_out.len) { + /* + * We did not find a matching principal in the allowedSigners + * Check without validation + */ + child_process_init(&ssh_keygen); + strvec_pushl(&ssh_keygen.args, fmt->program, + "-Y", "check-novalidate", + "-n", "git", + "-s", buffer_file->filename.buf, + NULL); + pipe_command(&ssh_keygen, payload, payload_size, + &ssh_keygen_out, 0, &ssh_keygen_err, 0); + + /* + * Fail on unknown keys + * we still call check-novalidate to display the signature info + */ + ret = -1; + } else { + /* Check every principal we found (one per line) */ + for (line = ssh_principals_out.buf; *line; + line = strchrnul(line + 1, '\n')) { + while (*line == '\n') + line++; + if (!*line) + break; + + trust_size = strcspn(line, "\n"); + principal = xmemdupz(line, trust_size); + + child_process_init(&ssh_keygen); + strbuf_release(&ssh_keygen_out); + strbuf_release(&ssh_keygen_err); + strvec_push(&ssh_keygen.args, fmt->program); + /* + * We found principals + * Try with each until we find a match + */ + strvec_pushl(&ssh_keygen.args, "-Y", "verify", + "-n", "git", + "-f", ssh_allowed_signers, + "-I", principal, + "-s", buffer_file->filename.buf, + NULL); + + if (ssh_revocation_file) { + if (file_exists(ssh_revocation_file)) { + strvec_pushl(&ssh_keygen.args, "-r", + ssh_revocation_file, NULL); + } else { + warning(_("ssh signing revocation file configured but not found: %s"), + ssh_revocation_file); + } + } + + sigchain_push(SIGPIPE, SIG_IGN); + ret = pipe_command(&ssh_keygen, payload, payload_size, + &ssh_keygen_out, 0, &ssh_keygen_err, 0); + sigchain_pop(SIGPIPE); + + FREE_AND_NULL(principal); + + if (!ret) + ret = !starts_with(ssh_keygen_out.buf, "Good"); + + if (!ret) + break; + } + } + + sigc->payload = xmemdupz(payload, payload_size); + strbuf_stripspace(&ssh_keygen_out, 0); + strbuf_stripspace(&ssh_keygen_err, 0); + /* Add stderr outputs to show the user actual ssh-keygen errors */ + strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len); + strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len); + sigc->output = strbuf_detach(&ssh_keygen_out, NULL); + sigc->gpg_status = xstrdup(sigc->output); + + parse_ssh_output(sigc); + +out: + if (buffer_file) + delete_tempfile(&buffer_file); + strbuf_release(&ssh_principals_out); + strbuf_release(&ssh_principals_err); + strbuf_release(&ssh_keygen_out); + strbuf_release(&ssh_keygen_err); return ret; } @@ -309,35 +563,32 @@ static int verify_signed_buffer(const char *payload, size_t payload_size, int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc) { - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; + struct gpg_format *fmt; int status; sigc->result = 'N'; sigc->trust_level = -1; - status = verify_signed_buffer(payload, plen, signature, slen, - &gpg_output, &gpg_status); - if (status && !gpg_output.len) - goto out; - sigc->payload = xmemdupz(payload, plen); - sigc->gpg_output = strbuf_detach(&gpg_output, NULL); - sigc->gpg_status = strbuf_detach(&gpg_status, NULL); - parse_gpg_output(sigc); + fmt = get_format_by_sig(signature); + if (!fmt) + die(_("bad/incompatible signature '%s'"), signature); + + status = fmt->verify_signed_buffer(sigc, fmt, payload, plen, signature, + slen); + + if (status && !sigc->output) + return !!status; + status |= sigc->result != 'G'; status |= sigc->trust_level < configured_min_trust_level; - out: - strbuf_release(&gpg_status); - strbuf_release(&gpg_output); - return !!status; } void print_signature_buffer(const struct signature_check *sigc, unsigned flags) { - const char *output = flags & GPG_VERIFY_RAW ? - sigc->gpg_status : sigc->gpg_output; + const char *output = flags & GPG_VERIFY_RAW ? sigc->gpg_status : + sigc->output; if (flags & GPG_VERIFY_VERBOSE && sigc->payload) fputs(sigc->payload, stdout); @@ -419,12 +670,33 @@ int git_gpg_config(const char *var, const char *value, void *cb) return 0; } + if (!strcmp(var, "gpg.ssh.defaultkeycommand")) { + if (!value) + return config_error_nonbool(var); + return git_config_string(&ssh_default_key_command, var, value); + } + + if (!strcmp(var, "gpg.ssh.allowedsignersfile")) { + if (!value) + return config_error_nonbool(var); + return git_config_pathname(&ssh_allowed_signers, var, value); + } + + if (!strcmp(var, "gpg.ssh.revocationfile")) { + if (!value) + return config_error_nonbool(var); + return git_config_pathname(&ssh_revocation_file, var, value); + } + if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program")) fmtname = "openpgp"; if (!strcmp(var, "gpg.x509.program")) fmtname = "x509"; + if (!strcmp(var, "gpg.ssh.program")) + fmtname = "ssh"; + if (fmtname) { fmt = get_format_by_name(fmtname); return git_config_string(&fmt->program, var, value); @@ -433,18 +705,148 @@ int git_gpg_config(const char *var, const char *value, void *cb) return 0; } +static char *get_ssh_key_fingerprint(const char *signing_key) +{ + struct child_process ssh_keygen = CHILD_PROCESS_INIT; + int ret = -1; + struct strbuf fingerprint_stdout = STRBUF_INIT; + struct strbuf **fingerprint; + char *fingerprint_ret; + + /* + * With SSH Signing this can contain a filename or a public key + * For textual representation we usually want a fingerprint + */ + if (starts_with(signing_key, "ssh-")) { + strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL); + ret = pipe_command(&ssh_keygen, signing_key, + strlen(signing_key), &fingerprint_stdout, 0, + NULL, 0); + } else { + strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", + configured_signing_key, NULL); + ret = pipe_command(&ssh_keygen, NULL, 0, &fingerprint_stdout, 0, + NULL, 0); + } + + if (!!ret) + die_errno(_("failed to get the ssh fingerprint for key '%s'"), + signing_key); + + fingerprint = strbuf_split_max(&fingerprint_stdout, ' ', 3); + if (!fingerprint[1]) + die_errno(_("failed to get the ssh fingerprint for key '%s'"), + signing_key); + + fingerprint_ret = strbuf_detach(fingerprint[1], NULL); + strbuf_list_free(fingerprint); + strbuf_release(&fingerprint_stdout); + return fingerprint_ret; +} + +/* Returns the first public key from an ssh-agent to use for signing */ +static const char *get_default_ssh_signing_key(void) +{ + struct child_process ssh_default_key = CHILD_PROCESS_INIT; + int ret = -1; + struct strbuf key_stdout = STRBUF_INIT, key_stderr = STRBUF_INIT; + struct strbuf **keys; + char *key_command = NULL; + const char **argv; + int n; + char *default_key = NULL; + + if (!ssh_default_key_command) + die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured")); + + key_command = xstrdup(ssh_default_key_command); + n = split_cmdline(key_command, &argv); + + if (n < 0) + die("malformed build-time gpg.ssh.defaultKeyCommand: %s", + split_cmdline_strerror(n)); + + strvec_pushv(&ssh_default_key.args, argv); + ret = pipe_command(&ssh_default_key, NULL, 0, &key_stdout, 0, + &key_stderr, 0); + + if (!ret) { + keys = strbuf_split_max(&key_stdout, '\n', 2); + if (keys[0] && starts_with(keys[0]->buf, "ssh-")) { + default_key = strbuf_detach(keys[0], NULL); + } else { + warning(_("gpg.ssh.defaultKeycommand succeeded but returned no keys: %s %s"), + key_stderr.buf, key_stdout.buf); + } + + strbuf_list_free(keys); + } else { + warning(_("gpg.ssh.defaultKeyCommand failed: %s %s"), + key_stderr.buf, key_stdout.buf); + } + + free(key_command); + free(argv); + strbuf_release(&key_stdout); + + return default_key; +} + +static const char *get_ssh_key_id(void) { + return get_ssh_key_fingerprint(get_signing_key()); +} + +/* Returns a textual but unique representation of the signing key */ +const char *get_signing_key_id(void) +{ + if (use_format->get_key_id) { + return use_format->get_key_id(); + } + + /* GPG/GPGSM only store a key id on this variable */ + return get_signing_key(); +} + const char *get_signing_key(void) { if (configured_signing_key) return configured_signing_key; - return git_committer_info(IDENT_STRICT|IDENT_NO_DATE); + if (use_format->get_default_key) { + return use_format->get_default_key(); + } + + return git_committer_info(IDENT_STRICT | IDENT_NO_DATE); } int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key) { + return use_format->sign_buffer(buffer, signature, signing_key); +} + +/* + * Strip CR from the line endings, in case we are on Windows. + * NEEDSWORK: make it trim only CRs before LFs and rename + */ +static void remove_cr_after(struct strbuf *buffer, size_t offset) +{ + size_t i, j; + + for (i = j = offset; i < buffer->len; i++) { + if (buffer->buf[i] != '\r') { + if (i != j) + buffer->buf[j] = buffer->buf[i]; + j++; + } + } + strbuf_setlen(buffer, j); +} + +static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key) +{ struct child_process gpg = CHILD_PROCESS_INIT; int ret; - size_t i, j, bottom; + size_t bottom; struct strbuf gpg_status = STRBUF_INIT; strvec_pushl(&gpg.args, @@ -470,13 +872,98 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig return error(_("gpg failed to sign the data")); /* Strip CR from the line endings, in case we are on Windows. */ - for (i = j = bottom; i < signature->len; i++) - if (signature->buf[i] != '\r') { - if (i != j) - signature->buf[j] = signature->buf[i]; - j++; - } - strbuf_setlen(signature, j); + remove_cr_after(signature, bottom); return 0; } + +static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key) +{ + struct child_process signer = CHILD_PROCESS_INIT; + int ret = -1; + size_t bottom, keylen; + struct strbuf signer_stderr = STRBUF_INIT; + struct tempfile *key_file = NULL, *buffer_file = NULL; + char *ssh_signing_key_file = NULL; + struct strbuf ssh_signature_filename = STRBUF_INIT; + + if (!signing_key || signing_key[0] == '\0') + return error( + _("user.signingkey needs to be set for ssh signing")); + + if (starts_with(signing_key, "ssh-")) { + /* A literal ssh key */ + key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX"); + if (!key_file) + return error_errno( + _("could not create temporary file")); + keylen = strlen(signing_key); + if (write_in_full(key_file->fd, signing_key, keylen) < 0 || + close_tempfile_gently(key_file) < 0) { + error_errno(_("failed writing ssh signing key to '%s'"), + key_file->filename.buf); + goto out; + } + ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL); + } else { + /* We assume a file */ + ssh_signing_key_file = expand_user_path(signing_key, 1); + } + + buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX"); + if (!buffer_file) { + error_errno(_("could not create temporary file")); + goto out; + } + + if (write_in_full(buffer_file->fd, buffer->buf, buffer->len) < 0 || + close_tempfile_gently(buffer_file) < 0) { + error_errno(_("failed writing ssh signing key buffer to '%s'"), + buffer_file->filename.buf); + goto out; + } + + strvec_pushl(&signer.args, use_format->program, + "-Y", "sign", + "-n", "git", + "-f", ssh_signing_key_file, + buffer_file->filename.buf, + NULL); + + sigchain_push(SIGPIPE, SIG_IGN); + ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0); + sigchain_pop(SIGPIPE); + + if (ret) { + if (strstr(signer_stderr.buf, "usage:")) + error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)")); + + error("%s", signer_stderr.buf); + goto out; + } + + bottom = signature->len; + + strbuf_addbuf(&ssh_signature_filename, &buffer_file->filename); + strbuf_addstr(&ssh_signature_filename, ".sig"); + if (strbuf_read_file(signature, ssh_signature_filename.buf, 0) < 0) { + error_errno( + _("failed reading ssh signing data buffer from '%s'"), + ssh_signature_filename.buf); + } + unlink_or_warn(ssh_signature_filename.buf); + + /* Strip CR from the line endings, in case we are on Windows. */ + remove_cr_after(signature, bottom); + +out: + if (key_file) + delete_tempfile(&key_file); + if (buffer_file) + delete_tempfile(&buffer_file); + strbuf_release(&signer_stderr); + strbuf_release(&ssh_signature_filename); + FREE_AND_NULL(ssh_signing_key_file); + return ret; +} diff --git a/gpg-interface.h b/gpg-interface.h index 80567e4894..beefacbb1e 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -17,7 +17,7 @@ enum signature_trust_level { struct signature_check { char *payload; - char *gpg_output; + char *output; char *gpg_status; /* @@ -64,6 +64,12 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, int git_gpg_config(const char *, const char *, void *); void set_signing_key(const char *); const char *get_signing_key(void); + +/* + * Returns a textual unique representation of the signing key in use + * Either a GPG KeyID or a SSH Key Fingerprint + */ +const char *get_signing_key_id(void); int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc); diff --git a/log-tree.c b/log-tree.c index 6dc4412268..644893fd8c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -515,10 +515,10 @@ static void show_signature(struct rev_info *opt, struct commit *commit) status = check_signature(payload.buf, payload.len, signature.buf, signature.len, &sigc); - if (status && !sigc.gpg_output) + if (status && !sigc.output) show_sig_lines(opt, status, "No signature\n"); else - show_sig_lines(opt, status, sigc.gpg_output); + show_sig_lines(opt, status, sigc.output); signature_check_clear(&sigc); out: @@ -585,8 +585,8 @@ static int show_one_mergetag(struct commit *commit, /* could have a good signature */ status = check_signature(payload.buf, payload.len, signature.buf, signature.len, &sigc); - if (sigc.gpg_output) - strbuf_addstr(&verify_message, sigc.gpg_output); + if (sigc.output) + strbuf_addstr(&verify_message, sigc.output); else strbuf_addstr(&verify_message, "No signature\n"); signature_check_clear(&sigc); diff --git a/merge-ort.c b/merge-ort.c index e5456f4722..0342f10483 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -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 " diff --git a/merge-recursive.c b/merge-recursive.c index c553751889..d9457797db 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -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); diff --git a/mergetools/xxdiff b/mergetools/xxdiff index ce5b8e9f29..d5ce467995 100644 --- a/mergetools/xxdiff +++ b/mergetools/xxdiff @@ -3,6 +3,13 @@ diff_cmd () { -R 'Accel.Search: "Ctrl+F"' \ -R 'Accel.SearchForward: "Ctrl+G"' \ "$LOCAL" "$REMOTE" + + # xxdiff can segfault on binary files which are often uninteresting. + # Do not allow segfaults to stop us from continuing on to the next file. + if test $? = 128 + then + return 1 + fi } merge_cmd () { @@ -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; } diff --git a/object-file.c b/object-file.c index 112d9b4bad..02b7970274 100644 --- a/object-file.c +++ b/object-file.c @@ -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; } diff --git a/object-store.h b/object-store.h index 1e647a5be3..952efb6a4b 100644 --- a/object-store.h +++ b/object-store.h @@ -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 @@ -279,7 +279,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) || (!obj && repo_has_object_file(r, oid) && oid_object_info(r, oid, NULL) == OBJ_BLOB)) { - if (check_object_signature(r, repl, NULL, 0, NULL) < 0) { + if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) { error(_("hash mismatch %s"), oid_to_hex(oid)); return NULL; } @@ -290,7 +290,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) buffer = repo_read_object_file(r, oid, &type, &size); if (buffer) { if (check_object_signature(r, repl, buffer, size, - type_name(type)) < 0) { + type_name(type), NULL) < 0) { free(buffer); error(_("hash mismatch %s"), oid_to_hex(repl)); return NULL; diff --git a/pack-check.c b/pack-check.c index c8e560d71a..3f418e3a6a 100644 --- a/pack-check.c +++ b/pack-check.c @@ -142,7 +142,8 @@ static int verify_packfile(struct repository *r, err = error("cannot unpack %s from %s at offset %"PRIuMAX"", oid_to_hex(&oid), p->pack_name, (uintmax_t)entries[i].offset); - else if (check_object_signature(r, &oid, data, size, type_name(type))) + else if (check_object_signature(r, &oid, data, size, + type_name(type), NULL)) err = error("packed %s from %s is corrupt", oid_to_hex(&oid), p->pack_name); else if (fn) { diff --git a/parallel-checkout.c b/parallel-checkout.c index ddc0ff3c06..ed9c999520 100644 --- a/parallel-checkout.c +++ b/parallel-checkout.c @@ -603,8 +603,7 @@ static void gather_results_from_workers(struct pc_worker *workers, continue; if (pfd->revents & POLLIN) { - int len = packet_read(pfd->fd, NULL, NULL, - packet_buffer, + int len = packet_read(pfd->fd, packet_buffer, sizeof(packet_buffer), 0); if (len < 0) { diff --git a/parse-options.c b/parse-options.c index 6e0535bdaa..9a0484c883 100644 --- a/parse-options.c +++ b/parse-options.c @@ -8,10 +8,13 @@ static int disallow_abbreviated_options; -#define OPT_SHORT 1 -#define OPT_UNSET 2 +enum opt_parsed { + OPT_LONG = 0, + OPT_SHORT = 1<<0, + OPT_UNSET = 1<<1, +}; -int optbug(const struct option *opt, const char *reason) +static int optbug(const struct option *opt, const char *reason) { if (opt->long_name) { if (opt->short_name) @@ -22,9 +25,26 @@ int optbug(const struct option *opt, const char *reason) return error("BUG: switch '%c' %s", opt->short_name, reason); } +static const char *optname(const struct option *opt, enum opt_parsed flags) +{ + static struct strbuf sb = STRBUF_INIT; + + strbuf_reset(&sb); + if (flags & OPT_SHORT) + strbuf_addf(&sb, "switch `%c'", opt->short_name); + else if (flags & OPT_UNSET) + strbuf_addf(&sb, "option `no-%s'", opt->long_name); + else if (flags == OPT_LONG) + strbuf_addf(&sb, "option `%s'", opt->long_name); + else + BUG("optname() got unknown flags %d", flags); + + return sb.buf; +} + static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p, const struct option *opt, - int flags, const char **arg) + enum opt_parsed flags, const char **arg) { if (p->opt) { *arg = p->opt; @@ -50,7 +70,7 @@ static void fix_filename(const char *prefix, const char **file) static enum parse_opt_result opt_command_mode_error( const struct option *opt, const struct option *all_opts, - int flags) + enum opt_parsed flags) { const struct option *that; struct strbuf that_name = STRBUF_INIT; @@ -82,7 +102,7 @@ static enum parse_opt_result opt_command_mode_error( static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, const struct option *opt, const struct option *all_opts, - int flags) + enum opt_parsed flags) { const char *s, *arg; const int unset = flags & OPT_UNSET; @@ -298,11 +318,11 @@ static enum parse_opt_result parse_long_opt( const struct option *all_opts = options; const char *arg_end = strchrnul(arg, '='); const struct option *abbrev_option = NULL, *ambiguous_option = NULL; - int abbrev_flags = 0, ambiguous_flags = 0; + enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG; for (; options->type != OPTION_END; options++) { const char *rest, *long_name = options->long_name; - int flags = 0, opt_flags = 0; + enum opt_parsed flags = OPT_LONG, opt_flags = OPT_LONG; if (!long_name) continue; @@ -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; -} diff --git a/parse-options.h b/parse-options.h index 13405472ee..bdea052c39 100644 --- a/parse-options.h +++ b/parse-options.h @@ -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); diff --git a/pkt-line.c b/pkt-line.c index de4a94b437..2dc8ac274b 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -289,22 +289,6 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...) va_end(args); } -void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len) -{ - size_t orig_len, n; - - orig_len = buf->len; - strbuf_addstr(buf, "0000"); - strbuf_add(buf, data, len); - n = buf->len - orig_len; - - if (n > LARGE_PACKET_MAX) - die(_("protocol error: impossibly long line")); - - set_packet_header(&buf->buf[orig_len], n); - packet_trace(data, len, 1); -} - int write_packetized_from_fd_no_flush(int fd_in, int fd_out) { char *buf = xmalloc(LARGE_PACKET_DATA_MAX); @@ -453,38 +437,28 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer, return PACKET_READ_NORMAL; } -int packet_read(int fd, char **src_buffer, size_t *src_len, - char *buffer, unsigned size, int options) +int packet_read(int fd, char *buffer, unsigned size, int options) { int pktlen = -1; - packet_read_with_status(fd, src_buffer, src_len, buffer, size, - &pktlen, options); + packet_read_with_status(fd, NULL, NULL, buffer, size, &pktlen, + options); return pktlen; } -static char *packet_read_line_generic(int fd, - char **src, size_t *src_len, - int *dst_len) +char *packet_read_line(int fd, int *dst_len) { - int len = packet_read(fd, src, src_len, - packet_buffer, sizeof(packet_buffer), + int len = packet_read(fd, packet_buffer, sizeof(packet_buffer), PACKET_READ_CHOMP_NEWLINE); if (dst_len) *dst_len = len; return (len > 0) ? packet_buffer : NULL; } -char *packet_read_line(int fd, int *len_p) -{ - return packet_read_line_generic(fd, NULL, NULL, len_p); -} - int packet_read_line_gently(int fd, int *dst_len, char **dst_line) { - int len = packet_read(fd, NULL, NULL, - packet_buffer, sizeof(packet_buffer), + int len = packet_read(fd, packet_buffer, sizeof(packet_buffer), PACKET_READ_CHOMP_NEWLINE|PACKET_READ_GENTLE_ON_EOF); if (dst_len) *dst_len = len; @@ -493,11 +467,6 @@ int packet_read_line_gently(int fd, int *dst_len, char **dst_line) return len; } -char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len) -{ - return packet_read_line_generic(-1, src, src_len, dst_len); -} - ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options) { int packet_len; @@ -507,7 +476,7 @@ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options) for (;;) { strbuf_grow(sb_out, LARGE_PACKET_DATA_MAX); - packet_len = packet_read(fd_in, NULL, NULL, + packet_len = packet_read(fd_in, /* strbuf_grow() above always allocates one extra byte to * store a '\0' at the end of the string. packet_read() * writes a '\0' extra byte at the end, too. Let it know diff --git a/pkt-line.h b/pkt-line.h index 82b95e4bdd..467ae01357 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -29,7 +29,6 @@ void packet_buf_delim(struct strbuf *buf); void set_packet_header(char *buf, int size); void packet_write(int fd_out, const char *buf, size_t size); void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3))); -void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len); int packet_flush_gently(int fd); int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int write_packetized_from_fd_no_flush(int fd_in, int fd_out); @@ -88,8 +87,7 @@ void packet_fflush(FILE *f); #define PACKET_READ_CHOMP_NEWLINE (1u<<1) #define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2) #define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3) -int packet_read(int fd, char **src_buffer, size_t *src_len, char - *buffer, unsigned size, int options); +int packet_read(int fd, char *buffer, unsigned size, int options); /* * Convert a four hex digit packet line length header into its numeric @@ -139,12 +137,6 @@ char *packet_read_line(int fd, int *size); int packet_read_line_gently(int fd, int *size, char **dst_line); /* - * Same as packet_read_line, but read from a buf rather than a descriptor; - * see packet_read for details on how src_* is used. - */ -char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size); - -/* * Reads a stream of variable sized packets until a flush packet is detected. */ ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out, int options); @@ -1436,8 +1436,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ check_commit_signature(c->commit, &(c->signature_check)); switch (placeholder[1]) { case 'G': - if (c->signature_check.gpg_output) - strbuf_addstr(sb, c->signature_check.gpg_output); + if (c->signature_check.output) + strbuf_addstr(sb, c->signature_check.output); break; case '?': switch (c->signature_check.result) { diff --git a/read-cache.c b/read-cache.c index 8a50ff66b3..f398659662 100644 --- a/read-cache.c +++ b/read-cache.c @@ -738,7 +738,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, int intent_only = flags & ADD_CACHE_INTENT; int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE| (intent_only ? ADD_CACHE_NEW_ONLY : 0)); - int hash_flags = HASH_WRITE_OBJECT; + unsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT; struct object_id oid; if (flags & ADD_CACHE_RENORMALIZE) diff --git a/ref-filter.c b/ref-filter.c index add429be79..282cdad103 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -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; diff --git a/ref-filter.h b/ref-filter.h index b636f4389d..6228458d30 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -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 */ @@ -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) diff --git a/refs/files-backend.c b/refs/files-backend.c index 6a6ead0b99..151b0056fe 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -79,13 +79,15 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) * Create a new submodule ref cache and add it to the internal * set of caches. */ -static struct ref_store *files_ref_store_create(const char *gitdir, +static struct ref_store *files_ref_store_create(struct repository *repo, + const char *gitdir, unsigned int flags) { struct files_ref_store *refs = xcalloc(1, sizeof(*refs)); struct ref_store *ref_store = (struct ref_store *)refs; struct strbuf sb = STRBUF_INIT; + ref_store->repo = repo; ref_store->gitdir = xstrdup(gitdir); base_ref_store_init(ref_store, &refs_be_files); refs->store_flags = flags; @@ -93,7 +95,7 @@ static struct ref_store *files_ref_store_create(const char *gitdir, get_common_dir_noenv(&sb, gitdir); refs->gitcommondir = strbuf_detach(&sb, NULL); strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir); - refs->packed_ref_store = packed_ref_store_create(sb.buf, flags); + refs->packed_ref_store = packed_ref_store_create(repo, sb.buf, flags); strbuf_release(&sb); chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); @@ -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 diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 47247a1491..1c5211b03e 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -193,13 +193,15 @@ static int release_snapshot(struct snapshot *snapshot) } } -struct ref_store *packed_ref_store_create(const char *path, +struct ref_store *packed_ref_store_create(struct repository *repo, + const char *path, unsigned int store_flags) { struct packed_ref_store *refs = xcalloc(1, sizeof(*refs)); struct ref_store *ref_store = (struct ref_store *)refs; base_ref_store_init(ref_store, &refs_be_packed); + ref_store->repo = repo; ref_store->gitdir = xstrdup(path); refs->store_flags = store_flags; @@ -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) diff --git a/refs/packed-backend.h b/refs/packed-backend.h index a01a0aff9c..f61a73ec25 100644 --- a/refs/packed-backend.h +++ b/refs/packed-backend.h @@ -1,6 +1,7 @@ #ifndef REFS_PACKED_BACKEND_H #define REFS_PACKED_BACKEND_H +struct repository; struct ref_transaction; /* @@ -12,7 +13,8 @@ struct ref_transaction; * even among packed refs. */ -struct ref_store *packed_ref_store_create(const char *path, +struct ref_store *packed_ref_store_create(struct repository *repo, + const char *path, unsigned int store_flags); /* diff --git a/refs/ref-cache.c b/refs/ref-cache.c index a5ad8a39fb..be4aa5e098 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -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; } diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 5c042ae718..850d9d3744 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -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 */ diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 72746407fc..12224742ed 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -66,6 +66,7 @@ int refname_is_safe(const char *refname); * referred-to object does not exist, emit a warning and return false. */ int ref_resolves_to_object(const char *refname, + struct repository *repo, const struct object_id *oid, unsigned int flags); @@ -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; }; diff --git a/remote-curl.c b/remote-curl.c index 5975103b96..d69156312b 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -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; diff --git a/send-pack.c b/send-pack.c index b3a495b7b1..bc0fcdbb00 100644 --- a/send-pack.c +++ b/send-pack.c @@ -341,13 +341,13 @@ static int generate_push_cert(struct strbuf *req_buf, { const struct ref *ref; struct string_list_item *item; - char *signing_key = xstrdup(get_signing_key()); + char *signing_key_id = xstrdup(get_signing_key_id()); const char *cp, *np; struct strbuf cert = STRBUF_INIT; int update_seen = 0; strbuf_addstr(&cert, "certificate version 0.1\n"); - strbuf_addf(&cert, "pusher %s ", signing_key); + strbuf_addf(&cert, "pusher %s ", signing_key_id); datestamp(&cert); strbuf_addch(&cert, '\n'); if (args->url && *args->url) { @@ -374,7 +374,7 @@ static int generate_push_cert(struct strbuf *req_buf, if (!update_seen) goto free_return; - if (sign_buffer(&cert, &cert, signing_key)) + if (sign_buffer(&cert, &cert, get_signing_key())) die(_("failed to sign the push certificate")); packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string); @@ -386,7 +386,7 @@ static int generate_push_cert(struct strbuf *req_buf, packet_buf_write(req_buf, "push-cert-end\n"); free_return: - free(signing_key); + free(signing_key_id); strbuf_release(&cert); return update_seen; } diff --git a/sequencer.c b/sequencer.c index 74ecb23d20..cd2aabf1f7 100644 --- a/sequencer.c +++ b/sequencer.c @@ -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; } @@ -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. * @@ -634,8 +634,10 @@ void strbuf_list_free(struct strbuf **list); * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to * the strbuf `sb`. */ -void strbuf_add_unique_abbrev(struct strbuf *sb, - const struct object_id *oid, +struct repository; +void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo, + const struct object_id *oid, int abbrev_len); +void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid, int abbrev_len); /** diff --git a/streaming.c b/streaming.c index 5f480ad50c..fe54665d86 100644 --- a/streaming.c +++ b/streaming.c @@ -223,19 +223,24 @@ static int open_istream_loose(struct git_istream *st, struct repository *r, const struct object_id *oid, enum object_type *type) { + struct object_info oi = OBJECT_INFO_INIT; + oi.sizep = &st->size; + oi.typep = type; + st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize); if (!st->u.loose.mapped) return -1; - if ((unpack_loose_header(&st->z, - st->u.loose.mapped, - st->u.loose.mapsize, - st->u.loose.hdr, - sizeof(st->u.loose.hdr)) < 0) || - (parse_loose_header(st->u.loose.hdr, &st->size) < 0)) { - git_inflate_end(&st->z); - munmap(st->u.loose.mapped, st->u.loose.mapsize); - return -1; + switch (unpack_loose_header(&st->z, st->u.loose.mapped, + st->u.loose.mapsize, st->u.loose.hdr, + sizeof(st->u.loose.hdr), NULL)) { + case ULHR_OK: + break; + case ULHR_BAD: + case ULHR_TOO_LONG: + goto error; } + if (parse_loose_header(st->u.loose.hdr, &oi) < 0 || *type < 0) + goto error; st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1; st->u.loose.hdr_avail = st->z.total_out; @@ -244,6 +249,10 @@ static int open_istream_loose(struct git_istream *st, struct repository *r, st->read = read_istream_loose; return 0; +error: + git_inflate_end(&st->z); + munmap(st->u.loose.mapped, st->u.loose.mapsize); + return -1; } diff --git a/submodule.c b/submodule.c index f3c99634a9..c689070524 100644 --- a/submodule.c +++ b/submodule.c @@ -201,6 +201,8 @@ int register_all_submodule_odb_as_alternates(void) add_to_alternates_memory(added_submodule_odb_paths.items[i].string); if (ret) { string_list_clear(&added_submodule_odb_paths, 0); + trace2_data_intmax("submodule", the_repository, + "register_all_submodule_odb_as_alternates/registered", ret); if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0)) BUG("register_all_submodule_odb_as_alternates() called"); } @@ -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, diff --git a/t/.gitattributes b/t/.gitattributes index dafa17c3e6..9930e28351 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -1,6 +1,5 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /chainlint/*.expect eol=lf -/lib-diff/* eol=lf /t0110/url-* binary /t3206/* eol=lf /t3900/*.txt eol=lf @@ -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 ------------ diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c index b16cd0b11b..d1324d086a 100644 --- a/t/helper/test-oid-array.c +++ b/t/helper/test-oid-array.c @@ -35,5 +35,9 @@ int cmd__oid_array(int argc, const char **argv) else die("unknown command: %s", line.buf); } + + strbuf_release(&line); + oid_array_clear(&array); + return 0; } diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c index 180ee28dd9..d48a409f4e 100644 --- a/t/helper/test-oidtree.c +++ b/t/helper/test-oidtree.c @@ -45,5 +45,8 @@ int cmd__oidtree(int argc, const char **argv) die("unknown command: %s", line.buf); } } + + strbuf_release(&line); + return 0; } diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index a282b6ff13..48d3cf6692 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -14,7 +14,6 @@ static int dry_run = 0, quiet = 0; static char *string = NULL; static char *file = NULL; static int ambiguous; -static struct string_list list = STRING_LIST_INIT_NODUP; static struct { int called; @@ -107,6 +106,8 @@ int cmd__parse_options(int argc, const char **argv) NULL }; struct string_list expect = STRING_LIST_INIT_NODUP; + struct string_list list = STRING_LIST_INIT_NODUP; + struct option options[] = { OPT_BOOL(0, "yes", &boolean, "get a boolean"), OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), @@ -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; } diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c index f4028442e3..133b5e6f4a 100644 --- a/t/helper/test-prio-queue.c +++ b/t/helper/test-prio-queue.c @@ -46,5 +46,7 @@ int cmd__prio_queue(int argc, const char **argv) } } + clear_prio_queue(&pq); + return 0; } diff --git a/t/lib-diff-data.sh b/t/lib-diff-data.sh new file mode 100644 index 0000000000..c64ec18324 --- /dev/null +++ b/t/lib-diff-data.sh @@ -0,0 +1,22 @@ +COPYING_test_data () { + cat <<\EOF + + Note that the only valid version of the GPL as far as this project + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + HOWEVER, in order to allow a migration to GPLv3 if that seems like + a good idea, I also ask that people involved with the project make + their preferences known. In particular, if you trust me to make that + decision, you might note so in your copyright message, ie something + like + + This file is licensed under the GPL v2, or a later version + at the discretion of Linus. + + might avoid issues. But we can also just decide to synchronize and + contact all copyright holders on record if/when the occasion arises. + + Linus Torvalds +EOF +} diff --git a/t/lib-diff.sh b/t/lib-diff.sh index 2de880f7a5..c4606bd4b7 100644 --- a/t/lib-diff.sh +++ b/t/lib-diff.sh @@ -1,3 +1,5 @@ +. "$TEST_DIRECTORY"/lib-diff-data.sh + : sanitize_diff_raw='/^:/s/ '"\($OID_REGEX\)"' '"\($OID_REGEX\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /' diff --git a/t/lib-diff/COPYING b/t/lib-diff/COPYING deleted file mode 100644 index 6ff87c4664..0000000000 --- a/t/lib-diff/COPYING +++ /dev/null @@ -1,361 +0,0 @@ - - Note that the only valid version of the GPL as far as this project - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. - - HOWEVER, in order to allow a migration to GPLv3 if that seems like - a good idea, I also ask that people involved with the project make - their preferences known. In particular, if you trust me to make that - decision, you might note so in your copyright message, ie something - like - - This file is licensed under the GPL v2, or a later version - at the discretion of Linus. - - might avoid issues. But we can also just decide to synchronize and - contact all copyright holders on record if/when the occasion arises. - - Linus Torvalds - ----------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/t/lib-diff/README b/t/lib-diff/README deleted file mode 100644 index 548142c327..0000000000 --- a/t/lib-diff/README +++ /dev/null @@ -1,46 +0,0 @@ -//////////////////////////////////////////////////////////////// - - GIT - the stupid content tracker - -//////////////////////////////////////////////////////////////// - -"git" can mean anything, depending on your mood. - - - random three-letter combination that is pronounceable, and not - actually used by any common UNIX command. The fact that it is a - mispronunciation of "get" may or may not be relevant. - - stupid. contemptible and despicable. simple. Take your pick from the - dictionary of slang. - - "global information tracker": you're in a good mood, and it actually - works for you. Angels sing, and a light suddenly fills the room. - - "goddamn idiotic truckload of sh*t": when it breaks - -Git is a fast, scalable, distributed revision control system with an -unusually rich command set that provides both high-level operations -and full access to internals. - -Git is an Open Source project covered by the GNU General Public License. -It was originally written by Linus Torvalds with help of a group of -hackers around the net. It is currently maintained by Junio C Hamano. - -Please read the file INSTALL for installation instructions. -See Documentation/tutorial.txt to get started, then see -Documentation/everyday.txt for a useful minimum set of commands, -and "man git-commandname" for documentation of each command. -CVS users may also want to read Documentation/cvs-migration.txt. - -Many Git online resources are accessible from http://git.or.cz/ -including full documentation and Git related tools. - -The user discussion and development of Git take place on the Git -mailing list -- everyone is welcome to post bug reports, feature -requests, comments and patches to git@vger.kernel.org. To subscribe -to the list, send an email with just "subscribe git" in the body to -majordomo@vger.kernel.org. The mailing list archives are available at -http://marc.theaimsgroup.com/?l=git and other archival sites. - -The messages titled "A note from the maintainer", "What's in -git.git (stable)" and "What's cooking in git.git (topics)" and -the discussion following them on the mailing list give a good -reference for project status, development direction and -remaining tasks. diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index 9fc5241228..f99ef3e859 100644 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -87,6 +87,34 @@ test_lazy_prereq RFC1991 ' echo | gpg --homedir "${GNUPGHOME}" -b --rfc1991 >/dev/null ' +GPGSSH_KEY_PRIMARY="${GNUPGHOME}/ed25519_ssh_signing_key" +GPGSSH_KEY_SECONDARY="${GNUPGHOME}/rsa_2048_ssh_signing_key" +GPGSSH_KEY_UNTRUSTED="${GNUPGHOME}/untrusted_ssh_signing_key" +GPGSSH_KEY_WITH_PASSPHRASE="${GNUPGHOME}/protected_ssh_signing_key" +GPGSSH_KEY_PASSPHRASE="super_secret" +GPGSSH_ALLOWED_SIGNERS="${GNUPGHOME}/ssh.all_valid.allowedSignersFile" + +GPGSSH_GOOD_SIGNATURE_TRUSTED='Good "git" signature for' +GPGSSH_GOOD_SIGNATURE_UNTRUSTED='Good "git" signature with' +GPGSSH_KEY_NOT_TRUSTED="No principal matched" +GPGSSH_BAD_SIGNATURE="Signature verification failed" + +test_lazy_prereq GPGSSH ' + ssh_version=$(ssh-keygen -Y find-principals -n "git" 2>&1) + test $? != 127 || exit 1 + echo $ssh_version | grep -q "find-principals:missing signature file" + test $? = 0 || exit 1; + mkdir -p "${GNUPGHOME}" && + chmod 0700 "${GNUPGHOME}" && + ssh-keygen -t ed25519 -N "" -C "git ed25519 key" -f "${GPGSSH_KEY_PRIMARY}" >/dev/null && + echo "\"principal with number 1\" $(cat "${GPGSSH_KEY_PRIMARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" && + ssh-keygen -t rsa -b 2048 -N "" -C "git rsa2048 key" -f "${GPGSSH_KEY_SECONDARY}" >/dev/null && + echo "\"principal with number 2\" $(cat "${GPGSSH_KEY_SECONDARY}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" && + ssh-keygen -t ed25519 -N "${GPGSSH_KEY_PASSPHRASE}" -C "git ed25519 encrypted key" -f "${GPGSSH_KEY_WITH_PASSPHRASE}" >/dev/null && + echo "\"principal with number 3\" $(cat "${GPGSSH_KEY_WITH_PASSPHRASE}.pub")" >> "${GPGSSH_ALLOWED_SIGNERS}" && + ssh-keygen -t ed25519 -N "" -f "${GPGSSH_KEY_UNTRUSTED}" >/dev/null +' + sanitize_pgp() { perl -ne ' /^-----END PGP/ and $in_pgp = 0; diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index d2edfa4c50..782891908d 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -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 diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 180a41fe96..497b9b9d92 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -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 index 0000000000..39ff75bbc4 --- /dev/null +++ b/t/lib-httpd/error-no-report.sh @@ -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' diff --git a/t/oid-info/oid b/t/oid-info/oid index a754970523..7547d2c790 100644 --- a/t/oid-info/oid +++ b/t/oid-info/oid @@ -27,3 +27,5 @@ numeric sha1:0123456789012345678901234567890123456789 numeric sha256:0123456789012345678901234567890123456789012345678901234567890123 deadbeef sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef deadbeef sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef +deadbeef_short sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbee +deadbeef_short sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee diff --git a/t/perf/config b/t/perf/config new file mode 100644 index 0000000000..b92768b039 --- /dev/null +++ b/t/perf/config @@ -0,0 +1,2 @@ +[gc] + auto = 0 diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index f5ed092ee5..780a7402d5 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -27,6 +27,10 @@ TEST_NO_MALLOC_CHECK=t . ../test-lib.sh +unset GIT_CONFIG_NOSYSTEM +GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config" +export GIT_CONFIG_SYSTEM + if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED" then error "Do not use GIT_TEST_INSTALLED with the perf tests. @@ -230,6 +234,7 @@ test_perf_ () { test_ok_ "$1" fi "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result + rm test_time.* } test_perf () { diff --git a/t/t0001-init.sh b/t/t0001-init.sh index df544bb321..7603ad2f82 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -2,6 +2,7 @@ test_description='git init' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_config () { diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index 8440e6add1..76052cb562 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh @@ -7,6 +7,7 @@ Verify that plumbing commands work when .git is a file GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh objpath() { diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 1e4c672b84..b9ed612af1 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -2,6 +2,7 @@ test_description=gitattributes +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh attr_check_basic () { diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh index 37d68ef03b..2e9d652d82 100755 --- a/t/t0004-unwritable.sh +++ b/t/t0004-unwritable.sh @@ -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 diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh index 4c214bd11c..a5ec6a0315 100755 --- a/t/t0005-signals.sh +++ b/t/t0005-signals.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='signals work as we expect' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >expect <<EOF diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh index 88b9ae8158..53af92d571 100755 --- a/t/t0007-git-var.sh +++ b/t/t0007-git-var.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='basic sanity checks for git var' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'get GIT_AUTHOR_IDENT' ' diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index a594b4aa7d..532637de88 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -2,6 +2,7 @@ test_description=check-ignore +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh init_vars () { diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh index 3941ad2528..eea99107a4 100755 --- a/t/t0009-prio-queue.sh +++ b/t/t0009-prio-queue.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='basic tests for priority queue implementation' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >expect <<'EOF' diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh index 5657c5a87b..837c8b7228 100755 --- a/t/t0010-racy-git.sh +++ b/t/t0010-racy-git.sh @@ -2,6 +2,7 @@ test_description='racy GIT' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # This test can give false success if your machine is sufficiently diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh index 419f31a8f7..9ad76080aa 100755 --- a/t/t0013-sha1dc.sh +++ b/t/t0013-sha1dc.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test sha1 collision detection' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh TEST_DATA="$TEST_DIRECTORY/t0013" diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh index 7af3fbcc7b..c1a331e9e9 100755 --- a/t/t0022-crlf-rename.sh +++ b/t/t0022-crlf-rename.sh @@ -2,6 +2,7 @@ test_description='ignore CR in CRLF sequence while computing similiarity' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh index 4e9fa3cd68..a34de56420 100755 --- a/t/t0024-crlf-archive.sh +++ b/t/t0024-crlf-archive.sh @@ -2,6 +2,7 @@ test_description='respect crlf in git archive' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t0025-crlf-renormalize.sh b/t/t0025-crlf-renormalize.sh index e13363ade5..81447978b7 100755 --- a/t/t0025-crlf-renormalize.sh +++ b/t/t0025-crlf-renormalize.sh @@ -2,6 +2,7 @@ test_description='CRLF renormalization' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh index c5203e232c..cdcafcdff7 100755 --- a/t/t0026-eol-config.sh +++ b/t/t0026-eol-config.sh @@ -2,6 +2,7 @@ test_description='CRLF conversion' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh has_cr() { diff --git a/t/t0029-core-unsetenvvars.sh b/t/t0029-core-unsetenvvars.sh index 24ce46a6ea..b138e1d9cb 100755 --- a/t/t0029-core-unsetenvvars.sh +++ b/t/t0029-core-unsetenvvars.sh @@ -2,6 +2,7 @@ test_description='test the Windows-only core.unsetenvvars setting' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh if ! test_have_prereq MINGW diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index da310ed29b..ed2fb620a9 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -5,6 +5,7 @@ test_description='our own option parser' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >expect <<\EOF @@ -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 diff --git a/t/t0052-simple-ipc.sh b/t/t0052-simple-ipc.sh index ff98be31a5..1a36a53574 100755 --- a/t/t0052-simple-ipc.sh +++ b/t/t0052-simple-ipc.sh @@ -2,6 +2,7 @@ test_description='simple command server' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test-tool simple-ipc SUPPORTS_SIMPLE_IPC || { diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh index 0c6ff567a1..6bada37022 100755 --- a/t/t0055-beyond-symlinks.sh +++ b/t/t0055-beyond-symlinks.sh @@ -2,6 +2,7 @@ test_description='update-index and add refuse to add beyond symlinks' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success SYMLINKS setup ' diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 7d599675e3..ee281909bc 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -5,6 +5,7 @@ test_description='Test run command' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >hello-script <<-EOF diff --git a/t/t0064-oid-array.sh b/t/t0064-oid-array.sh index 2e5438ccda..88c89e8f48 100755 --- a/t/t0064-oid-array.sh +++ b/t/t0064-oid-array.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='basic tests for the oid array implementation' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh echoid () { diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh index 91fa639c4a..94e34c83ed 100755 --- a/t/t0065-strcmp-offset.sh +++ b/t/t0065-strcmp-offset.sh @@ -2,6 +2,7 @@ test_description='Test strcmp_offset functionality' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh while read s1 s2 expect diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh index 92910e4e6c..63a1a45cd3 100755 --- a/t/t0066-dir-iterator.sh +++ b/t/t0066-dir-iterator.sh @@ -2,6 +2,7 @@ test_description='Test the dir-iterator functionality' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t0067-parse_pathspec_file.sh b/t/t0067-parse_pathspec_file.sh index 7bab49f361..0188d0423a 100755 --- a/t/t0067-parse_pathspec_file.sh +++ b/t/t0067-parse_pathspec_file.sh @@ -2,6 +2,7 @@ test_description='Test parse_pathspec_file()' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'one item from stdin' ' diff --git a/t/t0069-oidtree.sh b/t/t0069-oidtree.sh index bfb1397d7b..74cc59bf8a 100755 --- a/t/t0069-oidtree.sh +++ b/t/t0069-oidtree.sh @@ -1,6 +1,7 @@ #!/bin/sh test_description='basic tests for the oidtree implementation' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh maxhexsz=$(test_oid hexsz) diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh index 0cf3a63b75..37c359bd5a 100755 --- a/t/t0210-trace2-normal.sh +++ b/t/t0210-trace2-normal.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test trace2 facility (normal target)' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Turn off any inherited trace2 settings for this test. diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 6ee8ee3b67..22d0845544 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test trace2 facility (perf target)' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Turn off any inherited trace2 settings for this test. diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh index 1529155cf0..6d3374ff77 100755 --- a/t/t0212-trace2-event.sh +++ b/t/t0212-trace2-event.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test trace2 facility' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Turn off any inherited trace2 settings for this test. diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh index 013c5a7bc3..0e8c0dfbbe 100755 --- a/t/t1000-read-tree-m-3way.sh +++ b/t/t1000-read-tree-m-3way.sh @@ -71,6 +71,8 @@ In addition: DF: a special case, where A makes a directory and B makes a file. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree.sh . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index 1057a96b24..d1115528cb 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -20,6 +20,8 @@ In the test, these paths are used: rezrov - in H, deleted in M yomin - not in H or M ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree.sh diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh index b6111cd150..e0db2066f3 100755 --- a/t/t1003-read-tree-prefix.sh +++ b/t/t1003-read-tree-prefix.sh @@ -6,6 +6,7 @@ test_description='git read-tree --prefix test. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 4a753705ec..658628375c 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -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 diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh index 2935f68f8d..fc179ac5dd 100755 --- a/t/t1009-read-tree-new-index.sh +++ b/t/t1009-read-tree-new-index.sh @@ -5,6 +5,7 @@ test_description='test read-tree into a fresh index file' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index b946f87686..48bfad07ab 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -2,6 +2,7 @@ test_description='git mktree' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh index 57f0770df1..cde93d22cd 100755 --- a/t/t1012-read-tree-df.sh +++ b/t/t1012-read-tree-df.sh @@ -2,6 +2,7 @@ test_description='read-tree D/F conflict corner cases' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree.sh diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh index da3376b3bb..8ea8d36818 100755 --- a/t/t1014-read-tree-confusing.sh +++ b/t/t1014-read-tree-confusing.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='check that read-tree rejects confusing paths' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'create base tree' ' diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index ca91c6a67f..16fbd2c6db 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -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 && diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh index ae66ba5bab..0f37a43fd3 100755 --- a/t/t1100-commit-tree-options.sh +++ b/t/t1100-commit-tree-options.sh @@ -12,6 +12,7 @@ Also make sure that command line parser understands the normal "flags first and then non flag arguments" command line. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >expected <<EOF diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index fa3aeb80f2..4c77cf89a6 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -4,6 +4,7 @@ test_description='Test handling of ref names that check-ref-format rejects' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 5071ac63a5..6337236fd8 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -48,24 +48,70 @@ remove_object () { rm "$(sha1_file "$1")" } -test_expect_success 'object with bad sha1' ' - sha=$(echo blob | git hash-object -w --stdin) && - old=$(test_oid_to_path "$sha") && - new=$(dirname $old)/$(test_oid ff_2) && - sha="$(dirname $new)$(basename $new)" && - mv .git/objects/$old .git/objects/$new && - test_when_finished "remove_object $sha" && - git update-index --add --cacheinfo 100644 $sha foo && - test_when_finished "git read-tree -u --reset HEAD" && - tree=$(git write-tree) && - test_when_finished "remove_object $tree" && - cmt=$(echo bogus | git commit-tree $tree) && - test_when_finished "remove_object $cmt" && - git update-ref refs/heads/bogus $cmt && - test_when_finished "git update-ref -d refs/heads/bogus" && +test_expect_success 'object with hash mismatch' ' + git init --bare hash-mismatch && + ( + cd hash-mismatch && - test_must_fail git fsck 2>out && - test_i18ngrep "$sha.*corrupt" out + oid=$(echo blob | git hash-object -w --stdin) && + oldoid=$oid && + old=$(test_oid_to_path "$oid") && + new=$(dirname $old)/$(test_oid ff_2) && + oid="$(dirname $new)$(basename $new)" && + + mv objects/$old objects/$new && + git update-index --add --cacheinfo 100644 $oid foo && + tree=$(git write-tree) && + cmt=$(echo bogus | git commit-tree $tree) && + git update-ref refs/heads/bogus $cmt && + + test_must_fail git fsck 2>out && + grep "$oldoid: hash-path mismatch, found at: .*$new" out + ) +' + +test_expect_success 'object with hash and type mismatch' ' + git init --bare hash-type-mismatch && + ( + cd hash-type-mismatch && + + oid=$(echo blob | git hash-object -w --stdin -t garbage --literally) && + oldoid=$oid && + old=$(test_oid_to_path "$oid") && + new=$(dirname $old)/$(test_oid ff_2) && + oid="$(dirname $new)$(basename $new)" && + + mv objects/$old objects/$new && + git update-index --add --cacheinfo 100644 $oid foo && + tree=$(git write-tree) && + cmt=$(echo bogus | git commit-tree $tree) && + git update-ref refs/heads/bogus $cmt && + + + test_must_fail git fsck 2>out && + grep "^error: $oldoid: hash-path mismatch, found at: .*$new" out && + grep "^error: $oldoid: object is of unknown type '"'"'garbage'"'"'" out + ) +' + +test_expect_success POSIXPERM 'zlib corrupt loose object output ' ' + git init --bare corrupt-loose-output && + ( + cd corrupt-loose-output && + oid=$(git hash-object -w --stdin --literally </dev/null) && + oidf=objects/$(test_oid_to_path "$oid") && + chmod 755 $oidf && + echo extra garbage >>$oidf && + + cat >expect.error <<-EOF && + error: garbage at end of loose object '\''$oid'\'' + error: unable to unpack contents of ./$oidf + error: $oid: object corrupt or missing: ./$oidf + EOF + test_must_fail git fsck 2>actual && + grep ^error: actual >error && + test_cmp expect.error error + ) ' test_expect_success 'branch pointing to non-commit' ' @@ -865,4 +911,21 @@ test_expect_success 'detect corrupt index file in fsck' ' test_i18ngrep "bad index file" errors ' +test_expect_success 'fsck error and recovery on invalid object type' ' + git init --bare garbage-type && + ( + cd garbage-type && + + garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) && + + cat >err.expect <<-\EOF && + fatal: invalid object type + EOF + test_must_fail git fsck >out 2>err && + grep -e "^error" -e "^fatal" err >errors && + test_line_count = 1 errors && + grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err + ) +' + test_done diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh index 3d51615e42..0fafcf9dde 100755 --- a/t/t1504-ceiling-dirs.sh +++ b/t/t1504-ceiling-dirs.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test GIT_CEILING_DIRECTORIES' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_prefix() { diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh index bbfe05b8e4..591505a39c 100755 --- a/t/t1510-repo-setup.sh +++ b/t/t1510-repo-setup.sh @@ -43,6 +43,7 @@ A few rules for repo setup: # This test heavily relies on the standard error of nested function calls. test_untraceable=UnfortunatelyYes +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh here=$(pwd) diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh index 70361c806e..fc95cf9048 100755 --- a/t/t2002-checkout-cache-u.sh +++ b/t/t2002-checkout-cache-u.sh @@ -8,6 +8,7 @@ test_description='git checkout-index -u test. With -u flag, git checkout-index internally runs the equivalent of git update-index --refresh on the checked out entry.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success \ diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh index ff163cf675..f0fd441d81 100755 --- a/t/t2003-checkout-cache-mkdir.sh +++ b/t/t2003-checkout-cache-mkdir.sh @@ -10,6 +10,7 @@ also verifies that such leading path may contain symlinks, unlike the GIT controlled paths. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh index a9352b08a8..9bb503a975 100755 --- a/t/t2004-checkout-cache-temp.sh +++ b/t/t2004-checkout-cache-temp.sh @@ -8,6 +8,7 @@ test_description='git checkout-index --temp test. With --temp flag, git checkout-index writes to temporary merge files rather than the tracked path.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh index 9fa5610474..112682a45a 100755 --- a/t/t2005-checkout-index-symlinks.sh +++ b/t/t2005-checkout-index-symlinks.sh @@ -8,6 +8,7 @@ test_description='git checkout-index on filesystem w/o symlinks test. This tests that git checkout-index creates a symbolic link as a plain file if core.symlinks is false.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success \ diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh index 21f4659a9d..1f193cde96 100755 --- a/t/t2050-git-dir-relative.sh +++ b/t/t2050-git-dir-relative.sh @@ -12,6 +12,7 @@ into the subdir while keeping the worktree location, and tries commits from the top and the subdir, checking that the commit-hook still gets called.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh COMMIT_FILE="$(pwd)/output" diff --git a/t/t2081-parallel-checkout-collisions.sh b/t/t2081-parallel-checkout-collisions.sh index f6fcfc0c1e..6acdb89d12 100755 --- a/t/t2081-parallel-checkout-collisions.sh +++ b/t/t2081-parallel-checkout-collisions.sh @@ -11,6 +11,7 @@ The tests in this file exercise parallel checkout's collision detection code in both these mechanics. " +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 45ca35d60a..94c4cb0672 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -129,12 +129,15 @@ test_expect_success 'add -n -u should not add but just report' ' echo "remove '\''top'\''" ) >expect && before=$(git ls-files -s check top) && + git count-objects -v >objects_before && echo changed >>check && rm -f top && git add -n -u >actual && after=$(git ls-files -s check top) && + git count-objects -v >objects_after && test "$before" = "$after" && + test_cmp objects_before objects_after && test_cmp expect actual ' diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh index c8de6d8a19..b40eeb263f 100755 --- a/t/t2300-cd-to-toplevel.sh +++ b/t/t2300-cd-to-toplevel.sh @@ -2,6 +2,7 @@ test_description='cd_to_toplevel' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh EXEC_PATH="$(git --exec-path)" diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index 740ce56eab..11af4552f7 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -15,6 +15,8 @@ filesystem. path3/file3 - a file in a directory path4 - an empty directory ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup ' ' diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index 516c95ea0e..48cec4e5f8 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -8,6 +8,7 @@ test_description='git ls-files --others --exclude This test runs git ls-files --others and tests --exclude patterns. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh rm -fr one three @@ -102,7 +103,7 @@ test_expect_success \ >output && test_cmp expect output' -test_expect_success 'restore gitignore' ' +test_expect_success !SANITIZE_LEAK 'restore gitignore' ' git checkout --ignore-skip-worktree-bits $allignores && rm .git/index ' @@ -125,7 +126,7 @@ cat > expect << EOF # three/ EOF -test_expect_success 'git status honors core.excludesfile' \ +test_expect_success !SANITIZE_LEAK 'git status honors core.excludesfile' \ 'test_cmp expect output' test_expect_success 'trailing slash in exclude allows directory match(1)' ' diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh index 8704b04e1b..54d22a45df 100755 --- a/t/t3002-ls-files-dashpath.sh +++ b/t/t3002-ls-files-dashpath.sh @@ -12,6 +12,8 @@ filesystem. -foo - a file with a funny name. -- - another file with a funny name. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success \ diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh index c41c4f046a..7933dff9b3 100755 --- a/t/t3003-ls-files-exclude.sh +++ b/t/t3003-ls-files-exclude.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='ls-files --exclude does not affect index files' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'create repo with file' ' diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh index 9fd5a1f188..a16e25c79b 100755 --- a/t/t3004-ls-files-basic.sh +++ b/t/t3004-ls-files-basic.sh @@ -6,6 +6,7 @@ This test runs git ls-files with various unusual or malformed command-line arguments. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'ls-files in empty repository' ' diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh index 727e9ae1a4..6ba8b589cd 100755 --- a/t/t3005-ls-files-relative.sh +++ b/t/t3005-ls-files-relative.sh @@ -5,6 +5,7 @@ test_description='ls-files tests with relative paths This test runs git ls-files with various relative path arguments. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'prepare' ' diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh index e109c3fbfb..2aaf91ebc8 100755 --- a/t/t3006-ls-files-long.sh +++ b/t/t3006-ls-files-long.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='overly long paths' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh index 85f3704958..51d3dffaa6 100755 --- a/t/t3008-ls-files-lazy-init-name-hash.sh +++ b/t/t3008-ls-files-lazy-init-name-hash.sh @@ -2,6 +2,7 @@ test_description='Test the lazy init name hash with various folder structures' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh if test 1 -eq $(test-tool online-cpus) diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh index 124e73b8e6..2cbcbc0721 100755 --- a/t/t3020-ls-files-error-unmatch.sh +++ b/t/t3020-ls-files-error-unmatch.sh @@ -9,6 +9,8 @@ This test runs git ls-files --error-unmatch to ensure it correctly returns an error when a non-existent path is provided on the command line. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 56ea4bda13..72d5b014d8 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -2,6 +2,7 @@ test_description='wildmatch tests' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Disable expensive chain-lint tests; all of the tests in this script diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index 18baf49a49..436de44971 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -16,6 +16,8 @@ This test runs git ls-tree with the following in a tree. The new path restriction code should do the right thing for path2 and path2/baz. Also path0/ should snow nothing. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success \ diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 12bf31022a..05fde64225 100755 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -19,6 +19,8 @@ This test runs git ls-tree with the following in a tree. Test the handling of multiple directories which have matching file entries. Also test odd filename and missing entries handling. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh index 1e16c6b8ea..3942db2290 100755 --- a/t/t3102-ls-tree-wildcards.sh +++ b/t/t3102-ls-tree-wildcards.sh @@ -2,6 +2,7 @@ test_description='ls-tree with(out) globs' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh index 14520913af..d18ba1bd84 100755 --- a/t/t3103-ls-tree-misc.sh +++ b/t/t3103-ls-tree-misc.sh @@ -7,6 +7,7 @@ Miscellaneous tests for git ls-tree. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3205-branch-color.sh b/t/t3205-branch-color.sh index 08bd906173..6a521c1a3e 100755 --- a/t/t3205-branch-color.sh +++ b/t/t3205-branch-color.sh @@ -4,6 +4,7 @@ test_description='basic branch output coloring' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'set up some sample branches' ' diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh index 37b9d26f4b..9cbc34fc58 100755 --- a/t/t3211-peel-ref.sh +++ b/t/t3211-peel-ref.sh @@ -4,6 +4,7 @@ test_description='tests for the peel_ref optimization of packed-refs' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'create annotated tag in refs/tags' ' diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index f5bf16abcd..d3ac826283 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -9,6 +9,7 @@ This test tries pathnames with funny characters in the working tree, index, and tree objects. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh HT=' ' diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh index 7de21f8bcf..b2a8db69af 100755 --- a/t/t3601-rm-pathspec-file.sh +++ b/t/t3601-rm-pathspec-file.sh @@ -2,6 +2,7 @@ test_description='rm --pathspec-from-file' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_tick diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 4086e1ebbc..283a66955d 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -5,6 +5,7 @@ test_description='Test of git add, including the -- option.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Test the file mode "$1" of the file "$2" in the index. diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index f528008c36..72a5a565e9 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -5,6 +5,7 @@ test_description='quoted output' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh FN='濱野' diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index 6a9f010197..ea52e5b91b 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -6,6 +6,8 @@ test_description='Test diff raw-output. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh index db07ff3eb1..f4485a87c6 100755 --- a/t/t4003-diff-rename-1.sh +++ b/t/t4003-diff-rename-1.sh @@ -11,7 +11,7 @@ test_description='More rename detection test_expect_success \ 'prepare reference tree' \ - 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + 'COPYING_test_data >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && tree=$(git write-tree) && @@ -99,7 +99,7 @@ test_expect_success \ test_expect_success \ 'prepare work tree once again' \ - 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + 'COPYING_test_data >COPYING && git update-index --add --remove COPYING COPYING.1' # tree has COPYING and rezrov. work tree has COPYING and COPYING.1, diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh index 8647906132..6f1b323f97 100755 --- a/t/t4005-diff-rename-2.sh +++ b/t/t4005-diff-rename-2.sh @@ -9,7 +9,7 @@ test_description='Same rename detection as t4003 but testing diff-raw.' . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash test_expect_success 'setup reference tree' ' - cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + COPYING_test_data >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && tree=$(git write-tree) && @@ -64,7 +64,7 @@ test_expect_success 'validate output from rename/copy detection (#2)' ' # nows how to say Copy. test_expect_success 'validate output from rename/copy detection (#3)' ' - cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + COPYING_test_data >COPYING && git update-index --add --remove COPYING COPYING.1 && cat <<-EOF >expected && diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh index cbb9c62f53..c634653b5b 100755 --- a/t/t4007-rename-3.sh +++ b/t/t4007-rename-3.sh @@ -11,13 +11,12 @@ test_description='Rename interaction with pathspec. test_expect_success 'prepare reference tree' ' mkdir path0 path1 && - cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING && + COPYING_test_data >path0/COPYING && git update-index --add path0/COPYING && tree=$(git write-tree) && - echo $tree + blob=$(git rev-parse :path0/COPYING) ' -blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING") test_expect_success 'prepare work tree' ' cp path0/COPYING path1/COPYING && git update-index --add --remove path0/COPYING path1/COPYING diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 2299f27511..562aaf3a2a 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -25,8 +25,8 @@ Further, with -B and -M together, these should turn into two renames. . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash test_expect_success setup ' - cat "$TEST_DIRECTORY"/lib-diff/README >file0 && - cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 && + echo some dissimilar content >file0 && + COPYING_test_data >file1 && blob0_id=$(git hash-object file0) && blob1_id=$(git hash-object file1) && git update-index --add file0 file1 && diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh index b1da807f16..59b7f44f05 100755 --- a/t/t4009-diff-rename-4.sh +++ b/t/t4009-diff-rename-4.sh @@ -11,7 +11,7 @@ test_description='Same rename detection as t4003 but testing diff-raw -z. test_expect_success \ 'prepare reference tree' \ - 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + 'COPYING_test_data >COPYING && echo frotz >rezrov && git update-index --add COPYING rezrov && orig=$(git hash-object COPYING) && @@ -81,7 +81,7 @@ test_expect_success \ test_expect_success \ 'prepare work tree once again' \ - 'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING && + 'COPYING_test_data >COPYING && git update-index --add --remove COPYING COPYING.1' git diff-index -z -C --find-copies-harder $tree >current diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh index 876271d682..5a8d887683 100755 --- a/t/t4016-diff-quote.sh +++ b/t/t4016-diff-quote.sh @@ -6,6 +6,7 @@ test_description='Quoting paths in diff output. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh P0='pathname' diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh index c6135c7548..c68729ac09 100755 --- a/t/t4019-diff-wserror.sh +++ b/t/t4019-diff-wserror.sh @@ -2,6 +2,7 @@ test_description='diff whitespace error detection' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index 6d1c3d949c..1c89050a97 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -3,15 +3,17 @@ test_description='rewrite diff' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-diff-data.sh test_expect_success setup ' - cat "$TEST_DIRECTORY"/../COPYING >test && + COPYING_test_data >test.data && + cp test.data test && git add test && tr \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \ - <"$TEST_DIRECTORY"/../COPYING >test && + <test.data >test && echo "to be deleted" >test2 && blob=$(git hash-object test2) && blob=$(git rev-parse --short $blob) && diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 8c9823765e..47d6f35dcc 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -3,25 +3,26 @@ test_description='typechange rename detection' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-diff.sh test_expect_success setup ' rm -f foo bar && - cat "$TEST_DIRECTORY"/../COPYING >foo && + COPYING_test_data >foo && test_ln_s_add linklink bar && git add foo && git commit -a -m Initial && git tag one && git rm -f foo bar && - cat "$TEST_DIRECTORY"/../COPYING >bar && + COPYING_test_data >bar && test_ln_s_add linklink foo && git add bar && git commit -a -m Second && git tag two && git rm -f foo bar && - cat "$TEST_DIRECTORY"/../COPYING >foo && + COPYING_test_data >foo && git add foo && git commit -a -m Third && git tag three && @@ -35,7 +36,7 @@ test_expect_success setup ' # This is purely for sanity check git rm -f foo bar && - cat "$TEST_DIRECTORY"/../COPYING >foo && + COPYING_test_data >foo && cat "$TEST_DIRECTORY"/../Makefile >bar && git add foo bar && git commit -a -m Fifth && @@ -43,7 +44,7 @@ test_expect_success setup ' git rm -f foo bar && cat "$TEST_DIRECTORY"/../Makefile >foo && - cat "$TEST_DIRECTORY"/../COPYING >bar && + COPYING_test_data >bar && git add foo bar && git commit -a -m Sixth && git tag six diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh index 35578f2bb9..6356961de4 100755 --- a/t/t4025-hunk-header.sh +++ b/t/t4025-hunk-header.sh @@ -2,6 +2,7 @@ test_description='diff hunk header truncation' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh N='日本語' diff --git a/t/t4026-color.sh b/t/t4026-color.sh index c0b642c1ab..cc73161b46 100755 --- a/t/t4026-color.sh +++ b/t/t4026-color.sh @@ -4,6 +4,8 @@ # test_description='Test diff/status color escape codes' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh ESC=$(printf '\033') diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect index 37d1ea2587..dc500ae092 100644 --- a/t/t4034/cpp/expect +++ b/t/t4034/cpp/expect @@ -1,36 +1,35 @@ <BOLD>diff --git a/pre b/post<RESET> -<BOLD>index 23d5c8a..7e8c026 100644<RESET> +<BOLD>index a1a09b7..f1b6f3c 100644<RESET> <BOLD>--- a/pre<RESET> <BOLD>+++ b/post<RESET> -<CYAN>@@ -1,19 +1,19 @@<RESET> -Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> } +<CYAN>@@ -1,30 +1,30 @@<RESET> +Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <RED>foo0<RESET><GREEN>bar<RESET>(x.<RED>find<RESET><GREEN>Find<RESET>); } cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl; -<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>' -[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET> -!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>&<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>^<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>|<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>&&<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>||<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z -<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET> -<RED>a<RESET><GREEN>y<RESET> -<GREEN>x<RESET>,y -<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET> +<GREEN>(<RESET>1 <RED>-<RESET><GREEN>+<RESET>1e10 0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>2<RESET>' +// long double<RESET> +<RED>3.141592653e-10l<RESET><GREEN>3.141592654e+10l<RESET> +// float<RESET> +<RED>120E5f<RESET><GREEN>120E6f<RESET> +// hex<RESET> +<RED>0xdead<RESET><GREEN>0xdeaf<RESET>'1<RED>eaF<RESET><GREEN>eaf<RESET>+<RED>8ULL<RESET><GREEN>7ULL<RESET> +// octal<RESET> +<RED>01234567<RESET><GREEN>01234560<RESET> +// binary<RESET> +<RED>0b1000<RESET><GREEN>0b1100<RESET>+e1 +// expression<RESET> +1.5-e+<RED>2<RESET><GREEN>3<RESET>+f +// another one<RESET> +str.e+<RED>65<RESET><GREEN>75<RESET> +[a] b<RED>-><RESET><GREEN>->*<RESET>v d<RED>.<RESET><GREEN>.*<RESET>e +<GREEN>~<RESET>!a <GREEN>!<RESET>~b c<RED>++<RESET><GREEN>+<RESET> d<RED>--<RESET><GREEN>-<RESET> e*<GREEN>*<RESET>f g<RED>&<RESET><GREEN>&&<RESET>h +a<RED>*<RESET><GREEN>*=<RESET>b c<RED>/<RESET><GREEN>/=<RESET>d e<RED>%<RESET><GREEN>%=<RESET>f +a<RED>+<RESET><GREEN>++<RESET>b c<RED>-<RESET><GREEN>--<RESET>d +a<RED><<<RESET><GREEN><<=<RESET>b c<RED>>><RESET><GREEN>>>=<RESET>d +a<RED><<RESET><GREEN><=<RESET>b c<RED><=<RESET><GREEN><<RESET>d e<RED>><RESET><GREEN>>=<RESET>f g<RED>>=<RESET><GREEN>><RESET>h i<RED><=<RESET><GREEN><=><RESET>j +a<RED>==<RESET><GREEN>!=<RESET>b c<RED>!=<RESET><GREEN>=<RESET>d +a<RED>^<RESET><GREEN>^=<RESET>b c<RED>|<RESET><GREEN>|=<RESET>d e<RED>&&<RESET><GREEN>&=<RESET>f +a<RED>||<RESET><GREEN>|<RESET>b +a?<GREEN>:<RESET>b +a<RED>=<RESET><GREEN>==<RESET>b c<RED>+=<RESET><GREEN>+<RESET>d e<RED>-=<RESET><GREEN>-<RESET>f g<RED>*=<RESET><GREEN>*<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v +a,b<RESET> +a<RED>::<RESET><GREEN>:<RESET>b diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post index 7e8c026cef..f1b6f3c228 100644 --- a/t/t4034/cpp/post +++ b/t/t4034/cpp/post @@ -1,19 +1,30 @@ -Foo() : x(0&42) { bar(x); } +Foo() : x(0&42) { bar(x.Find); } cout<<"Hello World?\n"<<endl; -(1) (-1e10) (0xabcdef) 'y' -[x] x->y x.y -!x ~x x++ x-- x*y x&y -x*y x/y x%y -x+y x-y -x<<y x>>y -x<y x<=y x>y x>=y -x==y x!=y -x&y -x^y -x|y -x&&y -x||y -x?y:z -x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y -x,y -x::y +(1 +1e10 0xabcdef) '2' +// long double +3.141592654e+10l +// float +120E6f +// hex +0xdeaf'1eaf+7ULL +// octal +01234560 +// binary +0b1100+e1 +// expression +1.5-e+3+f +// another one +str.e+75 +[a] b->*v d.*e +~!a !~b c+ d- e**f g&&h +a*=b c/=d e%=f +a++b c--d +a<<=b c>>=d +a<=b c<d e>=f g>h i<=>j +a!=b c=d +a^=b c|=d e&=f +a|b +a?:b +a==b c+d e-f g*h i/j k%l m<<n o>>p q&r s^t u|v +a,b +a:b diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre index 23d5c8adf5..a1a09b7712 100644 --- a/t/t4034/cpp/pre +++ b/t/t4034/cpp/pre @@ -1,19 +1,30 @@ -Foo():x(0&&1){} +Foo():x(0&&1){ foo0( x.find); } cout<<"Hello World!\n"<<endl; 1 -1e10 0xabcdef 'x' -[a] a->b a.b -!a ~a a++ a-- a*b a&b -a*b a/b a%b -a+b a-b -a<<b a>>b -a<b a<=b a>b a>=b -a==b a!=b -a&b -a^b -a|b -a&&b +// long double +3.141592653e-10l +// float +120E5f +// hex +0xdead'1eaF+8ULL +// octal +01234567 +// binary +0b1000+e1 +// expression +1.5-e+2+f +// another one +str.e+65 +[a] b->v d.e +!a ~b c++ d-- e*f g&h +a*b c/d e%f +a+b c-d +a<<b c>>d +a<b c<=d e>f g>=h i<=j +a==b c!=d +a^b c|d e&&f a||b -a?b:z -a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b -a,y +a?b +a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v +a,b a::b diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 9dfead936b..6a650dacd6 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -1616,6 +1616,16 @@ test_expect_success GPGSM 'setup signed branch x509' ' git commit -S -m signed_commit ' +test_expect_success GPGSSH 'setup sshkey signed branch' ' + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + test_when_finished "git reset --hard && git checkout main" && + git checkout -b signed-ssh main && + echo foo >foo && + git add foo && + git commit -S -m signed_commit +' + test_expect_success GPGSM 'log x509 fingerprint' ' echo "F8BF62E0693D0694816377099909C779FA23FD65 | " >expect && git log -n1 --format="%GF | %GP" signed-x509 >actual && @@ -1628,6 +1638,13 @@ test_expect_success GPGSM 'log OpenPGP fingerprint' ' test_cmp expect actual ' +test_expect_success GPGSSH 'log ssh key fingerprint' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2\" | \"}" >expect && + git log -n1 --format="%GF | %GP" signed-ssh >actual && + test_cmp expect actual +' + test_expect_success GPG 'log --graph --show-signature' ' git log --graph --show-signature -n1 signed >actual && grep "^| gpg: Signature made" actual && @@ -1640,6 +1657,12 @@ test_expect_success GPGSM 'log --graph --show-signature x509' ' grep "^| gpgsm: Good signature" actual ' +test_expect_success GPGSSH 'log --graph --show-signature ssh' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git log --graph --show-signature -n1 signed-ssh >actual && + grep "${GOOD_SIGNATURE_TRUSTED}" actual +' + test_expect_success GPG 'log --graph --show-signature for merged tag' ' test_when_finished "git reset --hard && git checkout main" && git checkout -b plain main && diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh index e59601e5fe..c52c8a21fa 100755 --- a/t/t4300-merge-tree.sh +++ b/t/t4300-merge-tree.sh @@ -4,6 +4,8 @@ # test_description='git merge-tree' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index ed11569d8d..2dc75b80db 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -6,6 +6,9 @@ test_description='Recursive "git fetch" for submodules' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh pwd=$(pwd) diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index d573ca496a..3f58b515ce 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -5,6 +5,9 @@ test_description='test push with submodules' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh test_expect_success setup ' diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index bba768f5de..24d374adba 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh @@ -137,6 +137,53 @@ test_expect_success GPG 'signed push sends push certificate' ' test_cmp expect dst/push-cert-status ' +test_expect_success GPGSSH 'ssh signed push sends push certificate' ' + prepare_dst && + mkdir -p dst/.git/hooks && + git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git -C dst config receive.certnonceseed sekrit && + write_script dst/.git/hooks/post-receive <<-\EOF && + # discard the update list + cat >/dev/null + # record the push certificate + if test -n "${GIT_PUSH_CERT-}" + then + git cat-file blob $GIT_PUSH_CERT >../push-cert + fi && + + cat >../push-cert-status <<E_O_F + SIGNER=${GIT_PUSH_CERT_SIGNER-nobody} + KEY=${GIT_PUSH_CERT_KEY-nokey} + STATUS=${GIT_PUSH_CERT_STATUS-nostatus} + NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus} + NONCE=${GIT_PUSH_CERT_NONCE-nononce} + E_O_F + + EOF + + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && + git push --signed dst noop ff +noff && + + ( + cat <<-\EOF && + SIGNER=principal with number 1 + KEY=FINGERPRINT + STATUS=G + NONCE_STATUS=OK + EOF + sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert + ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect && + + noop=$(git rev-parse noop) && + ff=$(git rev-parse ff) && + noff=$(git rev-parse noff) && + grep "$noop $ff refs/heads/ff" dst/push-cert && + grep "$noop $noff refs/heads/noff" dst/push-cert && + test_cmp expect dst/push-cert-status +' + test_expect_success GPG 'inconsistent push options in signed push not allowed' ' # First, invoke receive-pack with dummy input to obtain its preamble. prepare_dst && @@ -276,6 +323,60 @@ test_expect_success GPGSM 'fail without key and heed user.signingkey x509' ' test_cmp expect dst/push-cert-status ' +test_expect_success GPGSSH 'fail without key and heed user.signingkey ssh' ' + test_config gpg.format ssh && + prepare_dst && + mkdir -p dst/.git/hooks && + git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git -C dst config receive.certnonceseed sekrit && + write_script dst/.git/hooks/post-receive <<-\EOF && + # discard the update list + cat >/dev/null + # record the push certificate + if test -n "${GIT_PUSH_CERT-}" + then + git cat-file blob $GIT_PUSH_CERT >../push-cert + fi && + + cat >../push-cert-status <<E_O_F + SIGNER=${GIT_PUSH_CERT_SIGNER-nobody} + KEY=${GIT_PUSH_CERT_KEY-nokey} + STATUS=${GIT_PUSH_CERT_STATUS-nostatus} + NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus} + NONCE=${GIT_PUSH_CERT_NONCE-nononce} + E_O_F + + EOF + + test_config user.email hasnokey@nowhere.com && + test_config gpg.format ssh && + test_config user.signingkey "" && + ( + sane_unset GIT_COMMITTER_EMAIL && + test_must_fail git push --signed dst noop ff +noff + ) && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && + git push --signed dst noop ff +noff && + + ( + cat <<-\EOF && + SIGNER=principal with number 1 + KEY=FINGERPRINT + STATUS=G + NONCE_STATUS=OK + EOF + sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert + ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect && + + noop=$(git rev-parse noop) && + ff=$(git rev-parse ff) && + noff=$(git rev-parse noff) && + grep "$noop $ff refs/heads/ff" dst/push-cert && + grep "$noop $noff refs/heads/noff" dst/push-cert && + test_cmp expect dst/push-cert-status +' + test_expect_success GPG 'failed atomic push does not execute GPG' ' prepare_dst && git -C dst config receive.certnonceseed sekrit && diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh index c024fa2818..8ca50f8b18 100755 --- a/t/t5541-http-push-smart.sh +++ b/t/t5541-http-push-smart.sh @@ -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 diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh index 58c7add7ee..214228349a 100755 --- a/t/t5545-push-options.sh +++ b/t/t5545-push-options.sh @@ -5,6 +5,9 @@ test_description='pushing to a repository using push options' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh mk_repo_pair () { diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh index 4f92a116e1..fa6b4cca65 100755 --- a/t/t5572-pull-submodule.sh +++ b/t/t5572-pull-submodule.sh @@ -2,6 +2,9 @@ test_description='pull can handle submodules' +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh index cd803ae8bf..cd7604fff9 100755 --- a/t/t5580-unc-paths.sh +++ b/t/t5580-unc-paths.sh @@ -4,6 +4,7 @@ test_description='various Windows-only path tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh if test_have_prereq CYGWIN diff --git a/t/t5615-alternate-env.sh b/t/t5615-alternate-env.sh index b4905b822c..83513e46a3 100755 --- a/t/t5615-alternate-env.sh +++ b/t/t5615-alternate-env.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='handling of alternates in environment variables' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_obj () { diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 44f55d93fe..06c5fb5615 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -81,6 +81,16 @@ test_expect_success GPG 'set up a signed tag' ' git tag -s -m signed-tag-msg signed-good-tag left ' +test_expect_success GPGSSH 'created ssh signed commit and tag' ' + test_config gpg.format ssh && + git checkout -b signed-ssh && + touch file && + git add file && + git commit -m "ssh signed" -S"${GPGSSH_KEY_PRIMARY}" && + git tag -s -u"${GPGSSH_KEY_PRIMARY}" -m signed-ssh-tag-msg signed-good-ssh-tag left && + git tag -s -u"${GPGSSH_KEY_UNTRUSTED}" -m signed-ssh-tag-msg-untrusted signed-untrusted-ssh-tag left +' + test_expect_success 'message for merging local branch' ' echo "Merge branch ${apos}left${apos}" >expected && @@ -109,6 +119,24 @@ test_expect_success GPG 'message for merging local tag signed by unknown key' ' grep -E "^# gpg: Can${apos}t check signature: (public key not found|No public key)" actual ' +test_expect_success GPGSSH 'message for merging local tag signed by good ssh key' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git checkout main && + git fetch . signed-good-ssh-tag && + git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual +' + +test_expect_success GPGSSH 'message for merging local tag signed by unknown ssh key' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git checkout main && + git fetch . signed-untrusted-ssh-tag && + git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + grep "${GPGSSH_KEY_NOT_TRUSTED}" actual +' test_expect_success 'message for merging external branch' ' echo "Merge branch ${apos}left${apos} of $(pwd)" >expected && diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh index e5e89c2045..178413c22f 100755 --- a/t/t6437-submodule-merge.sh +++ b/t/t6437-submodule-merge.sh @@ -5,6 +5,9 @@ test_description='merging with submodules' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh . "$TEST_DIRECTORY"/lib-merge.sh diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 25bb9bbb89..963356ba5f 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -2,10 +2,11 @@ test_description='git mv in subdirs' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-diff-data.sh test_expect_success 'prepare reference tree' ' mkdir path0 path1 && - cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && + COPYING_test_data >path0/COPYING && git add path0/COPYING && git commit -m add -a ' @@ -107,7 +108,7 @@ test_expect_success 'clean up' ' ' test_expect_success 'adding another file' ' - cp "$TEST_DIRECTORY"/../README.md path0/README && + COPYING_test_data | tr A-Za-z N-ZA-Mn-za-m >path0/README && git add path0/README && git commit -m add2 -a ' diff --git a/t/t7031-verify-tag-signed-ssh.sh b/t/t7031-verify-tag-signed-ssh.sh new file mode 100755 index 0000000000..06c9dd6c93 --- /dev/null +++ b/t/t7031-verify-tag-signed-ssh.sh @@ -0,0 +1,161 @@ +#!/bin/sh + +test_description='signed tag tests' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-gpg.sh" + +test_expect_success GPGSSH 'create signed tags ssh' ' + test_when_finished "test_unconfig commit.gpgsign" && + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + + echo 1 >file && git add file && + test_tick && git commit -m initial && + git tag -s -m initial initial && + git branch side && + + echo 2 >file && test_tick && git commit -a -m second && + git tag -s -m second second && + + git checkout side && + echo 3 >elif && git add elif && + test_tick && git commit -m "third on side" && + + git checkout main && + test_tick && git merge -S side && + git tag -s -m merge merge && + + echo 4 >file && test_tick && git commit -a -S -m "fourth unsigned" && + git tag -a -m fourth-unsigned fourth-unsigned && + + test_tick && git commit --amend -S -m "fourth signed" && + git tag -s -m fourth fourth-signed && + + echo 5 >file && test_tick && git commit -a -m "fifth" && + git tag fifth-unsigned && + + git config commit.gpgsign true && + echo 6 >file && test_tick && git commit -a -m "sixth" && + git tag -a -m sixth sixth-unsigned && + + test_tick && git rebase -f HEAD^^ && git tag -s -m 6th sixth-signed HEAD^ && + git tag -m seventh -s seventh-signed && + + echo 8 >file && test_tick && git commit -a -m eighth && + git tag -u"${GPGSSH_KEY_UNTRUSTED}" -m eighth eighth-signed-alt +' + +test_expect_success GPGSSH 'verify and show ssh signatures' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + ( + for tag in initial second merge fourth-signed sixth-signed seventh-signed + do + git verify-tag $tag 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in fourth-unsigned fifth-unsigned sixth-unsigned + do + test_must_fail git verify-tag $tag 2>actual && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in eighth-signed-alt + do + test_must_fail git verify-tag $tag 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && + echo $tag OK || exit 1 + done + ) +' + +test_expect_success GPGSSH 'detect fudged ssh signature' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git cat-file tag seventh-signed >raw && + sed -e "/^tag / s/seventh/7th forged/" raw >forged1 && + git hash-object -w -t tag forged1 >forged1.tag && + test_must_fail git verify-tag $(cat forged1.tag) 2>actual1 && + grep "${GPGSSH_BAD_SIGNATURE}" actual1 && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && + ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 +' + +test_expect_success GPGSSH 'verify ssh signatures with --raw' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + ( + for tag in initial second merge fourth-signed sixth-signed seventh-signed + do + git verify-tag --raw $tag 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in fourth-unsigned fifth-unsigned sixth-unsigned + do + test_must_fail git verify-tag --raw $tag 2>actual && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $tag OK || exit 1 + done + ) && + ( + for tag in eighth-signed-alt + do + test_must_fail git verify-tag --raw $tag 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $tag OK || exit 1 + done + ) +' + +test_expect_success GPGSSH 'verify signatures with --raw ssh' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git verify-tag --raw sixth-signed 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo sixth-signed OK +' + +test_expect_success GPGSSH 'verify multiple tags ssh' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + tags="seventh-signed sixth-signed" && + for i in $tags + do + git verify-tag -v --raw $i || return 1 + done >expect.stdout 2>expect.stderr.1 && + grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <expect.stderr.1 >expect.stderr && + git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 && + grep "^${GPGSSH_GOOD_SIGNATURE_TRUSTED}" <actual.stderr.1 >actual.stderr && + test_cmp expect.stdout actual.stdout && + test_cmp expect.stderr actual.stderr +' + +test_expect_success GPGSSH 'verifying tag with --format - ssh' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + cat >expect <<-\EOF && + tagname : fourth-signed + EOF + git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual && + test_cmp expect actual +' + +test_expect_success GPGSSH 'verifying a forged tag with --format should fail silently - ssh' ' + test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged && + test_must_be_empty actual-forged +' + +test_done diff --git a/t/t7101-reset-empty-subdirs.sh b/t/t7101-reset-empty-subdirs.sh index bfce05ac5d..5530651eea 100755 --- a/t/t7101-reset-empty-subdirs.sh +++ b/t/t7101-reset-empty-subdirs.sh @@ -5,10 +5,11 @@ test_description='git reset should cull empty subdirs' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-diff-data.sh test_expect_success 'creating initial files' ' mkdir path0 && - cp "$TEST_DIRECTORY"/../COPYING path0/COPYING && + COPYING_test_data >path0/COPYING && git add path0/COPYING && git commit -m add -a ' @@ -16,10 +17,10 @@ test_expect_success 'creating initial files' ' test_expect_success 'creating second files' ' mkdir path1 && mkdir path1/path2 && - cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING && - cp "$TEST_DIRECTORY"/../COPYING path1/COPYING && - cp "$TEST_DIRECTORY"/../COPYING COPYING && - cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO && + COPYING_test_data >path1/path2/COPYING && + COPYING_test_data >path1/COPYING && + COPYING_test_data >COPYING && + COPYING_test_data >path0/COPYING-TOO && git add path1/path2/COPYING && git add path1/COPYING && git add COPYING && diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh index 7948ec392b..cf9697eba9 100755 --- a/t/t7104-reset-hard.sh +++ b/t/t7104-reset-hard.sh @@ -2,6 +2,7 @@ test_description='reset --hard unmerged' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh index 3f7f271883..f87e524d6d 100755 --- a/t/t7418-submodule-sparse-gitmodules.sh +++ b/t/t7418-submodule-sparse-gitmodules.sh @@ -12,6 +12,9 @@ The test setup uses a sparse checkout, however the same scenario can be set up also by committing .gitmodules and then just removing it from the filesystem. ' +GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 +export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB + . ./test-lib.sh test_expect_success 'sparse checkout setup which hides .gitmodules' ' diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 8df5a74f1d..d65a0171f2 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -71,7 +71,25 @@ test_expect_success GPG 'create signed commits' ' git tag eleventh-signed $(cat oid) && echo 12 | git commit-tree --gpg-sign=B7227189 HEAD^{tree} >oid && test_line_count = 1 oid && - git tag twelfth-signed-alt $(cat oid) + git tag twelfth-signed-alt $(cat oid) && + + cat >keydetails <<-\EOF && + Key-Type: RSA + Key-Length: 2048 + Subkey-Type: RSA + Subkey-Length: 2048 + Name-Real: Unknown User + Name-Email: unknown@git.com + Expire-Date: 0 + %no-ask-passphrase + %no-protection + EOF + gpg --batch --gen-key keydetails && + echo 13 >file && git commit -a -S"unknown@git.com" -m thirteenth && + git tag thirteenth-signed && + DELETE_FINGERPRINT=$(gpg -K --with-colons --fingerprint --batch unknown@git.com | grep "^fpr" | head -n 1 | awk -F ":" "{print \$10;}") && + gpg --batch --yes --delete-secret-keys $DELETE_FINGERPRINT && + gpg --batch --yes --delete-keys unknown@git.com ' test_expect_success GPG 'verify and show signatures' ' @@ -110,6 +128,13 @@ test_expect_success GPG 'verify and show signatures' ' ) ' +test_expect_success GPG 'verify-commit exits failure on unknown signature' ' + test_must_fail git verify-commit thirteenth-signed 2>actual && + ! grep "Good signature from" actual && + ! grep "BAD signature from" actual && + grep -q -F -e "No public key" -e "public key not found" actual +' + test_expect_success GPG 'verify-commit exits success on untrusted signature' ' git verify-commit eighth-signed-alt 2>actual && grep "Good signature from" actual && @@ -338,6 +363,8 @@ test_expect_success GPG 'show double signature with custom format' ' ' +# NEEDSWORK: This test relies on the test_tick commit/author dates from the first +# 'create signed commits' test even though it creates its own test_expect_success GPG 'verify-commit verifies multiply signed commits' ' git init multiply-signed && cd multiply-signed && diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh index 905957bd0a..fffdb6ff2e 100755 --- a/t/t7518-ident-corner-cases.sh +++ b/t/t7518-ident-corner-cases.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='corner cases in ident strings' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # confirm that we do not segfault _and_ that we do not say "(null)", as diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh new file mode 100755 index 0000000000..badf3ed320 --- /dev/null +++ b/t/t7528-signed-commit-ssh.sh @@ -0,0 +1,398 @@ +#!/bin/sh + +test_description='ssh signed commit tests' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh +GNUPGHOME_NOT_USED=$GNUPGHOME +. "$TEST_DIRECTORY/lib-gpg.sh" + +test_expect_success GPGSSH 'create signed commits' ' + test_oid_cache <<-\EOF && + header sha1:gpgsig + header sha256:gpgsig-sha256 + EOF + + test_when_finished "test_unconfig commit.gpgsign" && + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + + echo 1 >file && git add file && + test_tick && git commit -S -m initial && + git tag initial && + git branch side && + + echo 2 >file && test_tick && git commit -a -S -m second && + git tag second && + + git checkout side && + echo 3 >elif && git add elif && + test_tick && git commit -m "third on side" && + + git checkout main && + test_tick && git merge -S side && + git tag merge && + + echo 4 >file && test_tick && git commit -a -m "fourth unsigned" && + git tag fourth-unsigned && + + test_tick && git commit --amend -S -m "fourth signed" && + git tag fourth-signed && + + git config commit.gpgsign true && + echo 5 >file && test_tick && git commit -a -m "fifth signed" && + git tag fifth-signed && + + git config commit.gpgsign false && + echo 6 >file && test_tick && git commit -a -m "sixth" && + git tag sixth-unsigned && + + git config commit.gpgsign true && + echo 7 >file && test_tick && git commit -a -m "seventh" --no-gpg-sign && + git tag seventh-unsigned && + + test_tick && git rebase -f HEAD^^ && git tag sixth-signed HEAD^ && + git tag seventh-signed && + + echo 8 >file && test_tick && git commit -a -m eighth -S"${GPGSSH_KEY_UNTRUSTED}" && + git tag eighth-signed-alt && + + # commit.gpgsign is still on but this must not be signed + echo 9 | git commit-tree HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag ninth-unsigned $(cat oid) && + # explicit -S of course must sign. + echo 10 | git commit-tree -S HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag tenth-signed $(cat oid) && + + # --gpg-sign[=<key-id>] must sign. + echo 11 | git commit-tree --gpg-sign HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag eleventh-signed $(cat oid) && + echo 12 | git commit-tree --gpg-sign="${GPGSSH_KEY_UNTRUSTED}" HEAD^{tree} >oid && + test_line_count = 1 oid && + git tag twelfth-signed-alt $(cat oid) +' + +test_expect_success GPGSSH 'verify and show signatures' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_config gpg.mintrustlevel UNDEFINED && + ( + for commit in initial second merge fourth-signed \ + fifth-signed sixth-signed seventh-signed tenth-signed \ + eleventh-signed + do + git verify-commit $commit && + git show --pretty=short --show-signature $commit >actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in merge^2 fourth-unsigned sixth-unsigned \ + seventh-unsigned ninth-unsigned + do + test_must_fail git verify-commit $commit && + git show --pretty=short --show-signature $commit >actual && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in eighth-signed-alt twelfth-signed-alt + do + git show --pretty=short --show-signature $commit >actual && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + grep "${GPGSSH_KEY_NOT_TRUSTED}" actual && + echo $commit OK || exit 1 + done + ) +' + +test_expect_success GPGSSH 'verify-commit exits failure on untrusted signature' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_must_fail git verify-commit eighth-signed-alt 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + grep "${GPGSSH_KEY_NOT_TRUSTED}" actual +' + +test_expect_success GPGSSH 'verify-commit exits success with matching minTrustLevel' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_config gpg.minTrustLevel fully && + git verify-commit sixth-signed +' + +test_expect_success GPGSSH 'verify-commit exits success with low minTrustLevel' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_config gpg.minTrustLevel marginal && + git verify-commit sixth-signed +' + +test_expect_success GPGSSH 'verify-commit exits failure with high minTrustLevel' ' + test_config gpg.minTrustLevel ultimate && + test_must_fail git verify-commit eighth-signed-alt +' + +test_expect_success GPGSSH 'verify signatures with --raw' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + ( + for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed + do + git verify-commit --raw $commit 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in merge^2 fourth-unsigned sixth-unsigned seventh-unsigned + do + test_must_fail git verify-commit --raw $commit 2>actual && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $commit OK || exit 1 + done + ) && + ( + for commit in eighth-signed-alt + do + test_must_fail git verify-commit --raw $commit 2>actual && + grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual && + echo $commit OK || exit 1 + done + ) +' + +test_expect_success GPGSSH 'proper header is used for hash algorithm' ' + git cat-file commit fourth-signed >output && + grep "^$(test_oid header) -----BEGIN SSH SIGNATURE-----" output +' + +test_expect_success GPGSSH 'show signed commit with signature' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git show -s initial >commit && + git show -s --show-signature initial >show && + git verify-commit -v initial >verify.1 2>verify.2 && + git cat-file commit initial >cat && + grep -v -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.commit && + grep -e "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" -e "Warning: " show >show.gpg && + grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit && + test_cmp show.commit commit && + test_cmp show.gpg verify.2 && + test_cmp cat.commit verify.1 +' + +test_expect_success GPGSSH 'detect fudged signature' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git cat-file commit seventh-signed >raw && + sed -e "s/^seventh/7th forged/" raw >forged1 && + git hash-object -w -t commit forged1 >forged1.commit && + test_must_fail git verify-commit $(cat forged1.commit) && + git show --pretty=short --show-signature $(cat forged1.commit) >actual1 && + grep "${GPGSSH_BAD_SIGNATURE}" actual1 && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual1 && + ! grep "${GPGSSH_GOOD_SIGNATURE_UNTRUSTED}" actual1 +' + +test_expect_success GPGSSH 'detect fudged signature with NUL' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git cat-file commit seventh-signed >raw && + cat raw >forged2 && + echo Qwik | tr "Q" "\000" >>forged2 && + git hash-object -w -t commit forged2 >forged2.commit && + test_must_fail git verify-commit $(cat forged2.commit) && + git show --pretty=short --show-signature $(cat forged2.commit) >actual2 && + grep "${GPGSSH_BAD_SIGNATURE}" actual2 && + ! grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual2 +' + +test_expect_success GPGSSH 'amending already signed commit' ' + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + git checkout fourth-signed^0 && + git commit --amend -S --no-edit && + git verify-commit HEAD && + git show -s --show-signature HEAD >actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual && + ! grep "${GPGSSH_BAD_SIGNATURE}" actual +' + +test_expect_success GPGSSH 'show good signature with custom format' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && + cat >expect.tmpl <<-\EOF && + G + FINGERPRINT + principal with number 1 + FINGERPRINT + + EOF + sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && + git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual && + test_cmp expect actual +' + +test_expect_success GPGSSH 'show bad signature with custom format' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + cat >expect <<-\EOF && + B + + + + + EOF + git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat forged1.commit) >actual && + test_cmp expect actual +' + +test_expect_success GPGSSH 'show untrusted signature with custom format' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + cat >expect.tmpl <<-\EOF && + U + FINGERPRINT + + FINGERPRINT + + EOF + git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") && + sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && + test_cmp expect actual +' + +test_expect_success GPGSSH 'show untrusted signature with undefined trust level' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + cat >expect.tmpl <<-\EOF && + undefined + FINGERPRINT + + FINGERPRINT + + EOF + git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_UNTRUSTED}" | awk "{print \$2;}") && + sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && + test_cmp expect actual +' + +test_expect_success GPGSSH 'show untrusted signature with ultimate trust level' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + cat >expect.tmpl <<-\EOF && + fully + FINGERPRINT + principal with number 1 + FINGERPRINT + + EOF + git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && + sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && + test_cmp expect actual +' + +test_expect_success GPGSSH 'show lack of signature with custom format' ' + cat >expect <<-\EOF && + N + + + + + EOF + git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" seventh-unsigned >actual && + test_cmp expect actual +' + +test_expect_success GPGSSH 'log.showsignature behaves like --show-signature' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_config log.showsignature true && + git show initial >actual && + grep "${GPGSSH_GOOD_SIGNATURE_TRUSTED}" actual +' + +test_expect_success GPGSSH 'check config gpg.format values' ' + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + test_config gpg.format ssh && + git commit -S --amend -m "success" && + test_config gpg.format OpEnPgP && + test_must_fail git commit -S --amend -m "fail" +' + +test_expect_failure GPGSSH 'detect fudged commit with double signature (TODO)' ' + sed -e "/gpgsig/,/END PGP/d" forged1 >double-base && + sed -n -e "/gpgsig/,/END PGP/p" forged1 | \ + sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig && + gpg -o double-sig2.sig -u 29472784 --detach-sign double-base && + cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc && + sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \ + double-combined.asc > double-gpgsig && + sed -e "/committer/r double-gpgsig" double-base >double-commit && + git hash-object -w -t commit double-commit >double-commit.commit && + test_must_fail git verify-commit $(cat double-commit.commit) && + git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual && + grep "BAD signature from" double-actual && + grep "Good signature from" double-actual +' + +test_expect_failure GPGSSH 'show double signature with custom format (TODO)' ' + cat >expect <<-\EOF && + E + + + + + EOF + git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual && + test_cmp expect actual +' + + +test_expect_failure GPGSSH 'verify-commit verifies multiply signed commits (TODO)' ' + git init multiply-signed && + cd multiply-signed && + test_commit first && + echo 1 >second && + git add second && + tree=$(git write-tree) && + parent=$(git rev-parse HEAD^{commit}) && + git commit --gpg-sign -m second && + git cat-file commit HEAD && + # Avoid trailing whitespace. + sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF && + Qtree $tree + Qparent $parent + Qauthor A U Thor <author@example.com> 1112912653 -0700 + Qcommitter C O Mitter <committer@example.com> 1112912653 -0700 + Qgpgsig -----BEGIN PGP SIGNATURE----- + QZ + Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy + Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC + Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ== + Q =tQ0N + Q -----END PGP SIGNATURE----- + Qgpgsig-sha256 -----BEGIN PGP SIGNATURE----- + QZ + Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy + Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO + Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw== + Q =pIwP + Q -----END PGP SIGNATURE----- + Q + Qsecond + EOF + head=$(git hash-object -t commit -w commit) && + git reset --hard $head && + git verify-commit $head 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual +' + +test_done diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh index cd4f9607dc..eca7555101 100755 --- a/t/t7604-merge-custom-message.sh +++ b/t/t7604-merge-custom-message.sh @@ -4,6 +4,7 @@ test_description='git merge Testing merge when using a custom message for the merge commit.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh create_merge_msgs() { diff --git a/t/t7813-grep-icase-iso.sh b/t/t7813-grep-icase-iso.sh index 701e08a8e5..1227885737 100755 --- a/t/t7813-grep-icase-iso.sh +++ b/t/t7813-grep-icase-iso.sh @@ -2,6 +2,7 @@ test_description='grep icase on non-English locales' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-gettext.sh test_expect_success GETTEXT_ISO_LOCALE 'setup' ' diff --git a/t/t7816-grep-binary-pattern.sh b/t/t7816-grep-binary-pattern.sh index 9d67a5fc4c..fdb2355649 100755 --- a/t/t7816-grep-binary-pattern.sh +++ b/t/t7816-grep-binary-pattern.sh @@ -2,6 +2,7 @@ test_description='git grep with a binary pattern files' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-gettext.sh nul_match_internal () { diff --git a/t/test-lib.sh b/t/test-lib.sh index 151da80c56..2679a7596a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -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 diff --git a/transport-helper.c b/transport-helper.c index e8dbdd1153..a0297b0986 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -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) diff --git a/unpack-trees.c b/unpack-trees.c index a7e1712d23..89ca95ce90 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -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; } diff --git a/userdiff.c b/userdiff.c index af02b1878c..8578cb0d12 100644 --- a/userdiff.c +++ b/userdiff.c @@ -64,9 +64,15 @@ PATTERNS("cpp", /* functions/methods, variables, and compounds at top level */ "^((::[[:space:]]*)?[A-Za-z_].*)$", /* -- */ + /* identifiers and keywords */ "[a-zA-Z_][a-zA-Z0-9_]*" - "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*" - "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"), + /* decimal and octal integers as well as floatingpoint numbers */ + "|[0-9][0-9.]*([Ee][-+]?[0-9]+)?[fFlLuU]*" + /* hexadecimal and binary integers */ + "|0[xXbB][0-9a-fA-F]+[lLuU]*" + /* floatingpoint numbers that begin with a decimal point */ + "|\\.[0-9][0-9]*([Ee][-+]?[0-9]+)?[fFlL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*|<=>"), PATTERNS("csharp", /* Keywords */ "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" |