aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2021-01-25 14:19:16 -0800
committerJunio C Hamano <gitster@pobox.com>2021-01-25 14:19:17 -0800
commitc7d6d419b0ddbc895aaf5de81a79a10773842d89 (patch)
tree71494cf43cdfb9e37f380f6a57c007c0940ac57e
parentThe third batch (diff)
parentmktag: add a --[no-]strict option (diff)
downloadgit-c7d6d419b0ddbc895aaf5de81a79a10773842d89.tar.gz
git-c7d6d419b0ddbc895aaf5de81a79a10773842d89.zip
Merge branch 'ab/mktag'
"git mktag" validates its input using its own rules before writing a tag object---it has been updated to share the logic with "git fsck". * ab/mktag: (23 commits) mktag: add a --[no-]strict option mktag: mark strings for translation mktag: convert to parse-options mktag: allow omitting the header/body \n separator mktag: allow turning off fsck.extraHeaderEntry fsck: make fsck_config() re-usable mktag: use fsck instead of custom verify_tag() mktag: use puts(str) instead of printf("%s\n", str) mktag: remove redundant braces in one-line body "if" mktag: use default strbuf_read() hint mktag tests: test verify_object() with replaced objects mktag tests: improve verify_object() test coverage mktag tests: test "hash-object" compatibility mktag tests: stress test whitespace handling mktag tests: run "fsck" after creating "mytag" mktag tests: don't create "mytag" twice mktag tests: don't redirect stderr to a file needlessly mktag tests: remove needless SHA-1 hardcoding mktag tests: use "test_commit" helper mktag tests: don't needlessly use a subshell ...
Diffstat (limited to '')
-rw-r--r--Documentation/git-mktag.txt39
-rw-r--r--builtin/fsck.c20
-rw-r--r--builtin/mktag.c235
-rw-r--r--fsck.c58
-rw-r--r--fsck.h16
-rwxr-xr-xt/t1006-cat-file.sh2
-rwxr-xr-xt/t3800-mktag.sh236
-rwxr-xr-xt/t6050-replace.sh2
8 files changed, 374 insertions, 234 deletions
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index fa6a756123..17a2603a60 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -3,7 +3,7 @@ git-mktag(1)
NAME
----
-git-mktag - Creates a tag object
+git-mktag - Creates a tag object with extra validation
SYNOPSIS
@@ -11,25 +11,52 @@ SYNOPSIS
[verse]
'git mktag'
+OPTIONS
+-------
+
+--strict::
+ By default mktag turns on the equivalent of
+ linkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to
+ disable it.
+
DESCRIPTION
-----------
-Reads a tag contents on standard input and creates a tag object
-that can also be used to sign other objects.
-The output is the new tag's <object> identifier.
+Reads a tag contents on standard input and creates a tag object. The
+output is the new tag's <object> identifier.
+
+This command is mostly equivalent to linkgit:git-hash-object[1]
+invoked with `-t tag -w --stdin`. I.e. both of these will create and
+write a tag found in `my-tag`:
+
+ git mktag <my-tag
+ git hash-object -t tag -w --stdin <my-tag
+
+The difference is that mktag will die before writing the tag if the
+tag doesn't pass a linkgit:git-fsck[1] check.
+
+The "fsck" check done mktag is stricter than what linkgit:git-fsck[1]
+would run by default in that all `fsck.<msg-id>` messages are promoted
+from warnings to errors (so e.g. a missing "tagger" line is an error).
+
+Extra headers in the object are also an error under mktag, but ignored
+by linkgit:git-fsck[1]. This extra check can be turned off by setting
+the appropriate `fsck.<msg-id>` varible:
+
+ git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers
Tag Format
----------
A tag signature file, to be fed to this command's standard input,
has a very simple fixed format: four lines of
- object <sha1>
+ object <hash>
type <typename>
tag <tagname>
tagger <tagger>
followed by some 'optional' free-form message (some tags created
-by older Git may not have `tagger` line). The message, when
+by older Git may not have `tagger` line). The message, when it
exists, is separated by a blank line from the header. The
message part may contain a signature that Git itself doesn't
care about, but that can be verified with gpg.
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fbf26cafcf..821e7798c7 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -73,25 +73,7 @@ static const char *printable_type(const struct object_id *oid,
static int fsck_config(const char *var, const char *value, void *cb)
{
- if (strcmp(var, "fsck.skiplist") == 0) {
- const char *path;
- struct strbuf sb = STRBUF_INIT;
-
- if (git_config_pathname(&path, var, value))
- return 1;
- strbuf_addf(&sb, "skiplist=%s", path);
- free((char *)path);
- fsck_set_msg_types(&fsck_obj_options, sb.buf);
- strbuf_release(&sb);
- return 0;
- }
-
- if (skip_prefix(var, "fsck.", &var)) {
- fsck_set_msg_type(&fsck_obj_options, var, value);
- return 0;
- }
-
- return git_default_config(var, value, cb);
+ return fsck_config_internal(var, value, cb, &fsck_obj_options);
}
static int objerror(struct object *obj, const char *err)
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 4982d3a93e..41a399a69e 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,179 +1,110 @@
#include "builtin.h"
+#include "parse-options.h"
#include "tag.h"
#include "replace-object.h"
#include "object-store.h"
+#include "fsck.h"
+#include "config.h"
-/*
- * A signature file has a very simple fixed format: four lines
- * of "object <sha1>" + "type <typename>" + "tag <tagname>" +
- * "tagger <committer>", followed by a blank line, a free-form tag
- * message and a signature block that git itself doesn't care about,
- * but that can be verified with gpg or similar.
- *
- * The first four lines are guaranteed to be at least 83 bytes:
- * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
- * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
- * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
- * the shortest possible tagger-line.
- */
-
-/*
- * We refuse to tag something we can't verify. Just because.
- */
-static int verify_object(const struct object_id *oid, const char *expected_type)
+static char const * const builtin_mktag_usage[] = {
+ N_("git mktag"),
+ NULL
+};
+static int option_strict = 1;
+
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+
+static int mktag_config(const char *var, const char *value, void *cb)
{
- int ret = -1;
- enum object_type type;
- unsigned long size;
- void *buffer = read_object_file(oid, &type, &size);
- const struct object_id *repl = lookup_replace_object(the_repository, oid);
-
- if (buffer) {
- if (type == type_from_string(expected_type)) {
- ret = check_object_signature(the_repository, repl,
- buffer, size,
- expected_type);
+ return fsck_config_internal(var, value, cb, &fsck_options);
+}
+
+static int mktag_fsck_error_func(struct fsck_options *o,
+ const struct object_id *oid,
+ enum object_type object_type,
+ int msg_type, const char *message)
+{
+ switch (msg_type) {
+ case FSCK_WARN:
+ if (!option_strict) {
+ fprintf_ln(stderr, _("warning: tag input does not pass fsck: %s"), message);
+ return 0;
+
}
- free(buffer);
+ /* fallthrough */
+ case FSCK_ERROR:
+ /*
+ * We treat both warnings and errors as errors, things
+ * like missing "tagger" lines are "only" warnings
+ * under fsck, we've always considered them an error.
+ */
+ fprintf_ln(stderr, _("error: tag input does not pass fsck: %s"), message);
+ return 1;
+ default:
+ BUG(_("%d (FSCK_IGNORE?) should never trigger this callback"),
+ msg_type);
}
- return ret;
}
-static int verify_tag(char *buffer, unsigned long size)
+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
{
- int typelen;
- char type[20];
- struct object_id oid;
- const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
- size_t len;
-
- if (size < 84)
- return error("wanna fool me ? you obviously got the size wrong !");
-
- buffer[size] = 0;
-
- /* Verify object line */
- object = buffer;
- if (memcmp(object, "object ", 7))
- return error("char%d: does not start with \"object \"", 0);
-
- if (parse_oid_hex(object + 7, &oid, &p))
- return error("char%d: could not get SHA1 hash", 7);
-
- /* Verify type line */
- type_line = p + 1;
- if (memcmp(type_line - 1, "\ntype ", 6))
- return error("char%d: could not find \"\\ntype \"", 47);
-
- /* Verify tag-line */
- tag_line = strchr(type_line, '\n');
- if (!tag_line)
- return error("char%"PRIuMAX": could not find next \"\\n\"",
- (uintmax_t) (type_line - buffer));
- tag_line++;
- if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
- return error("char%"PRIuMAX": no \"tag \" found",
- (uintmax_t) (tag_line - buffer));
-
- /* Get the actual type */
- typelen = tag_line - type_line - strlen("type \n");
- if (typelen >= sizeof(type))
- return error("char%"PRIuMAX": type too long",
- (uintmax_t) (type_line+5 - buffer));
-
- memcpy(type, type_line+5, typelen);
- type[typelen] = 0;
-
- /* Verify that the object matches */
- if (verify_object(&oid, type))
- return error("char%d: could not verify object %s", 7, oid_to_hex(&oid));
-
- /* Verify the tag-name: we don't allow control characters or spaces in it */
- tag_line += 4;
- for (;;) {
- unsigned char c = *tag_line++;
- if (c == '\n')
- break;
- if (c > ' ')
- continue;
- return error("char%"PRIuMAX": could not verify tag name",
- (uintmax_t) (tag_line - buffer));
- }
+ int ret;
+ enum object_type type;
+ unsigned long size;
+ void *buffer;
+ const struct object_id *repl;
+
+ buffer = read_object_file(tagged_oid, &type, &size);
+ if (!buffer)
+ die(_("could not read tagged object '%s'"),
+ oid_to_hex(tagged_oid));
+ if (type != *tagged_type)
+ die(_("object '%s' tagged as '%s', but is a '%s' type"),
+ oid_to_hex(tagged_oid),
+ type_name(*tagged_type), type_name(type));
+
+ repl = lookup_replace_object(the_repository, tagged_oid);
+ ret = check_object_signature(the_repository, repl,
+ buffer, size, type_name(*tagged_type));
+ free(buffer);
- /* Verify the tagger line */
- tagger_line = tag_line;
-
- if (memcmp(tagger_line, "tagger ", 7))
- return error("char%"PRIuMAX": could not find \"tagger \"",
- (uintmax_t) (tagger_line - buffer));
-
- /*
- * Check for correct form for name and email
- * i.e. " <" followed by "> " on _this_ line
- * No angle brackets within the name or email address fields.
- * No spaces within the email address field.
- */
- tagger_line += 7;
- if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
- strpbrk(tagger_line, "<>\n") != lb+1 ||
- strpbrk(lb+2, "><\n ") != rb)
- return error("char%"PRIuMAX": malformed tagger field",
- (uintmax_t) (tagger_line - buffer));
-
- /* Check for author name, at least one character, space is acceptable */
- if (lb == tagger_line)
- return error("char%"PRIuMAX": missing tagger name",
- (uintmax_t) (tagger_line - buffer));
-
- /* timestamp, 1 or more digits followed by space */
- tagger_line = rb + 2;
- if (!(len = strspn(tagger_line, "0123456789")))
- return error("char%"PRIuMAX": missing tag timestamp",
- (uintmax_t) (tagger_line - buffer));
- tagger_line += len;
- if (*tagger_line != ' ')
- return error("char%"PRIuMAX": malformed tag timestamp",
- (uintmax_t) (tagger_line - buffer));
- tagger_line++;
-
- /* timezone, 5 digits [+-]hhmm, max. 1400 */
- if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
- strspn(tagger_line+1, "0123456789") == 4 &&
- tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
- return error("char%"PRIuMAX": malformed tag timezone",
- (uintmax_t) (tagger_line - buffer));
- tagger_line += 6;
-
- /* Verify the blank line separating the header from the body */
- if (*tagger_line != '\n')
- return error("char%"PRIuMAX": trailing garbage in tag header",
- (uintmax_t) (tagger_line - buffer));
-
- /* The actual stuff afterwards we don't care about.. */
- return 0;
+ return ret;
}
int cmd_mktag(int argc, const char **argv, const char *prefix)
{
+ static struct option builtin_mktag_options[] = {
+ OPT_BOOL(0, "strict", &option_strict,
+ N_("enable more strict checking")),
+ OPT_END(),
+ };
struct strbuf buf = STRBUF_INIT;
+ struct object_id tagged_oid;
+ int tagged_type;
struct object_id result;
- if (argc != 1)
- usage("git mktag");
+ argc = parse_options(argc, argv, NULL,
+ builtin_mktag_options,
+ builtin_mktag_usage, 0);
- if (strbuf_read(&buf, 0, 4096) < 0) {
- die_errno("could not read from stdin");
- }
+ if (strbuf_read(&buf, 0, 0) < 0)
+ die_errno(_("could not read from stdin"));
+
+ fsck_options.error_func = mktag_fsck_error_func;
+ fsck_set_msg_type(&fsck_options, "extraheaderentry", "warn");
+ /* config might set fsck.extraHeaderEntry=* again */
+ git_config(mktag_config, NULL);
+ if (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,
+ &tagged_oid, &tagged_type))
+ die(_("tag on stdin did not pass our strict fsck check"));
- /* Verify it for some basic sanity: it needs to start with
- "object <sha1>\ntype\ntagger " */
- if (verify_tag(buf.buf, buf.len) < 0)
- die("invalid tag signature file");
+ if (verify_object_in_tag(&tagged_oid, &tagged_type))
+ die(_("tag on stdin did not refer to a valid object"));
if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
- die("unable to write tag file");
+ die(_("unable to write tag file"));
strbuf_release(&buf);
- printf("%s\n", oid_to_hex(&result));
+ puts(oid_to_hex(&result));
return 0;
}
diff --git a/fsck.c b/fsck.c
index f82e2fe9e3..69d0049e4d 100644
--- a/fsck.c
+++ b/fsck.c
@@ -80,7 +80,9 @@ static struct oidset gitmodules_done = OIDSET_INIT;
/* infos (reported as warnings, but ignored by default) */ \
FUNC(GITMODULES_PARSE, INFO) \
FUNC(BAD_TAG_NAME, INFO) \
- FUNC(MISSING_TAGGER_ENTRY, INFO)
+ FUNC(MISSING_TAGGER_ENTRY, INFO) \
+ /* ignored (elevated when requested) */ \
+ FUNC(EXTRA_HEADER_ENTRY, IGNORE)
#define MSG_ID(id, msg_type) FSCK_MSG_##id,
enum fsck_msg_id {
@@ -911,6 +913,16 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,
unsigned long size, struct fsck_options *options)
{
struct object_id tagged_oid;
+ int tagged_type;
+ return fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,
+ &tagged_type);
+}
+
+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
+ unsigned long size, struct fsck_options *options,
+ struct object_id *tagged_oid,
+ int *tagged_type)
+{
int ret = 0;
char *eol;
struct strbuf sb = STRBUF_INIT;
@@ -924,7 +936,7 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
goto done;
}
- if (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\n') {
+ if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
if (ret)
goto done;
@@ -940,7 +952,8 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
goto done;
}
- if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
+ *tagged_type = type_from_string_gently(buffer, eol - buffer, 1);
+ if (*tagged_type < 0)
ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
if (ret)
goto done;
@@ -974,6 +987,21 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,
}
else
ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
+ if (!*buffer)
+ goto done;
+
+ if (!starts_with(buffer, "\n")) {
+ /*
+ * The verify_headers() check will allow
+ * e.g. "[...]tagger <tagger>\nsome
+ * garbage\n\nmessage" to pass, thinking "some
+ * garbage" could be a custom header. E.g. "mktag"
+ * doesn't want any unknown headers.
+ */
+ ret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, "invalid format - extra header(s) after 'tagger'");
+ if (ret)
+ goto done;
+ }
done:
strbuf_release(&sb);
@@ -1284,3 +1312,27 @@ int fsck_finish(struct fsck_options *options)
oidset_clear(&gitmodules_done);
return ret;
}
+
+int fsck_config_internal(const char *var, const char *value, void *cb,
+ struct fsck_options *options)
+{
+ if (strcmp(var, "fsck.skiplist") == 0) {
+ const char *path;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (git_config_pathname(&path, var, value))
+ return 1;
+ strbuf_addf(&sb, "skiplist=%s", path);
+ free((char *)path);
+ fsck_set_msg_types(options, sb.buf);
+ strbuf_release(&sb);
+ return 0;
+ }
+
+ if (skip_prefix(var, "fsck.", &var)) {
+ fsck_set_msg_type(options, var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value, cb);
+}
diff --git a/fsck.h b/fsck.h
index 69cf715e79..423c467feb 100644
--- a/fsck.h
+++ b/fsck.h
@@ -63,6 +63,15 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
struct fsck_options *options);
/*
+ * fsck a tag, and pass info about it back to the caller. This is
+ * exposed fsck_object() internals for git-mktag(1).
+ */
+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
+ unsigned long size, struct fsck_options *options,
+ struct object_id *tagged_oid,
+ int *tag_type);
+
+/*
* Some fsck checks are context-dependent, and may end up queued; run this
* after completing all fsck_object() calls in order to resolve any remaining
* checks.
@@ -94,4 +103,11 @@ void fsck_put_object_name(struct fsck_options *options,
const char *fsck_describe_object(struct fsck_options *options,
const struct object_id *oid);
+/*
+ * git_config() callback for use by fsck-y tools that want to support
+ * fsck.<msg> fsck.skipList etc.
+ */
+int fsck_config_internal(const char *var, const char *value, void *cb,
+ struct fsck_options *options);
+
#endif
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 2f501d2dc9..5d2dc99b74 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -166,7 +166,7 @@ tag_content="$tag_header_without_timestamp 0000000000 +0000
$tag_description"
-tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
+tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
tag_size=$(strlen "$tag_content")
run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index d696aa4e52..86bfeb271e 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -12,10 +12,29 @@ test_description='git mktag: tag object verify test'
# given in the expect.pat file.
check_verify_failure () {
- expect="$2"
+ test_expect_success "$1" "
+ test_must_fail env GIT_TEST_GETTEXT_POISON=false \
+ git mktag <tag.sig 2>message &&
+ grep '$2' message &&
+ if test '$3' != '--no-strict'
+ then
+ test_must_fail env GIT_TEST_GETTEXT_POISON=false \
+ git mktag --no-strict <tag.sig 2>message.no-strict &&
+ grep '$2' message.no-strict
+ fi
+ "
+}
+
+test_expect_mktag_success() {
test_expect_success "$1" '
- ( test_must_fail git mktag <tag.sig 2>message ) &&
- grep "$expect" message
+ git hash-object -t tag -w --stdin <tag.sig >expected &&
+ git fsck --strict &&
+
+ git mktag <tag.sig >hash &&
+ test_cmp expected hash &&
+ test_when_finished "git update-ref -d refs/tags/mytag $(cat hash)" &&
+ git update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&
+ git fsck --strict
'
}
@@ -23,10 +42,24 @@ check_verify_failure () {
# first create a commit, so we have a valid object/type
# for the tag.
test_expect_success 'setup' '
- echo Hello >A &&
- git update-index --add A &&
- git commit -m "Initial commit" &&
- head=$(git rev-parse --verify HEAD)
+ test_commit A &&
+ test_commit B &&
+ head=$(git rev-parse --verify HEAD) &&
+ head_parent=$(git rev-parse --verify HEAD~) &&
+ tree=$(git rev-parse HEAD^{tree}) &&
+ blob=$(git rev-parse --verify HEAD:B.t)
+'
+
+test_expect_success 'basic usage' '
+ cat >tag.sig <<-EOF &&
+ object $head
+ type commit
+ tag mytag
+ tagger T A Gger <tagger@example.com> 1206478233 -0500
+ EOF
+ git mktag <tag.sig &&
+ git mktag --end-of-options <tag.sig &&
+ test_expect_code 129 git mktag --unknown-option
'
############################################################
@@ -37,33 +70,33 @@ too short for a tag
EOF
check_verify_failure 'Tag object length check' \
- '^error: .*size wrong.*$'
+ '^error:.* missingObject:' 'strict'
############################################################
# 2. object line label check
cat >tag.sig <<EOF
-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
+xxxxxx $head
type tag
tag mytag
tagger . <> 0 +0000
EOF
-check_verify_failure '"object" line label check' '^error: char0: .*"object "$'
+check_verify_failure '"object" line label check' '^error:.* missingObject:'
############################################################
-# 3. object line SHA1 check
+# 3. object line hash check
cat >tag.sig <<EOF
-object zz9e9b33986b1c2670fff52c5067603117b3e895
+object $(echo $head | tr 0-9a-f z)
type tag
tag mytag
tagger . <> 0 +0000
EOF
-check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
+check_verify_failure '"object" line check' '^error:.* badObjectSha1:'
############################################################
# 4. type line label check
@@ -76,7 +109,7 @@ tagger . <> 0 +0000
EOF
-check_verify_failure '"type" line label check' '^error: char.*: .*"\\ntype "$'
+check_verify_failure '"type" line label check' '^error:.* missingTypeEntry:'
############################################################
# 5. type line eol check
@@ -84,7 +117,7 @@ check_verify_failure '"type" line label check' '^error: char.*: .*"\\ntype "$'
echo "object $head" >tag.sig
printf "type tagsssssssssssssssssssssssssssssss" >>tag.sig
-check_verify_failure '"type" line eol check' '^error: char.*: .*"\\n"$'
+check_verify_failure '"type" line eol check' '^error:.* unterminatedHeader:'
############################################################
# 6. tag line label check #1
@@ -98,7 +131,7 @@ tagger . <> 0 +0000
EOF
check_verify_failure '"tag" line label check #1' \
- '^error: char.*: no "tag " found$'
+ '^error:.* missingTagEntry:'
############################################################
# 7. tag line label check #2
@@ -110,7 +143,7 @@ tag
EOF
check_verify_failure '"tag" line label check #2' \
- '^error: char.*: no "tag " found$'
+ '^error:.* badType:'
############################################################
# 8. type line type-name length check
@@ -122,21 +155,83 @@ tag mytag
EOF
check_verify_failure '"type" line type-name length check' \
- '^error: char.*: type too long$'
+ '^error:.* badType:'
############################################################
-# 9. verify object (SHA1/type) check
+# 9. verify object (hash/type) check
cat >tag.sig <<EOF
object $(test_oid deadbeef)
+type tag
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \
+ '^fatal: could not read tagged object'
+
+cat >tag.sig <<EOF
+object $head
type tagggg
tag mytag
tagger . <> 0 +0000
EOF
-check_verify_failure 'verify object (SHA1/type) check' \
- '^error: char7: could not verify object.*$'
+check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \
+ '^error:.* badType:'
+
+cat >tag.sig <<EOF
+object $(test_oid deadbeef)
+type tagggg
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \
+ '^error:.* badType:'
+
+cat >tag.sig <<EOF
+object $head
+type tree
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \
+ '^fatal: object.*tagged as.*tree.*but is.*commit'
+
+############################################################
+# 9.5. verify object (hash/type) check -- replacement
+
+test_expect_success 'setup replacement of commit -> commit and tree -> blob' '
+ git replace $head_parent $head &&
+ git replace -f $tree $blob
+'
+
+cat >tag.sig <<EOF
+object $head_parent
+type commit
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+test_expect_mktag_success 'tag to a commit replaced by another commit'
+
+cat >tag.sig <<EOF
+object $tree
+type tree
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \
+ '^fatal: object.*tagged as.*tree.*but is.*blob'
############################################################
# 10. verify tag-name check
@@ -150,7 +245,7 @@ tagger . <> 0 +0000
EOF
check_verify_failure 'verify tag-name check' \
- '^error: char.*: could not verify tag name$'
+ '^error:.* badTagName:' '--no-strict'
############################################################
# 11. tagger line label check #1
@@ -164,7 +259,7 @@ This is filler
EOF
check_verify_failure '"tagger" line label check #1' \
- '^error: char.*: could not find "tagger "$'
+ '^error:.* missingTaggerEntry:' '--no-strict'
############################################################
# 12. tagger line label check #2
@@ -179,10 +274,10 @@ This is filler
EOF
check_verify_failure '"tagger" line label check #2' \
- '^error: char.*: could not find "tagger "$'
+ '^error:.* missingTaggerEntry:' '--no-strict'
############################################################
-# 13. disallow missing tag author name
+# 13. allow missing tag author name like fsck
cat >tag.sig <<EOF
object $head
@@ -193,8 +288,7 @@ tagger <> 0 +0000
This is filler
EOF
-check_verify_failure 'disallow missing tag author name' \
- '^error: char.*: missing tagger name$'
+test_expect_mktag_success 'allow missing tag author name'
############################################################
# 14. disallow missing tag author name
@@ -209,7 +303,7 @@ tagger T A Gger <
EOF
check_verify_failure 'disallow malformed tagger' \
- '^error: char.*: malformed tagger field$'
+ '^error:.* badEmail:' '--no-strict'
############################################################
# 15. allow empty tag email
@@ -222,12 +316,10 @@ tagger T A Gger <> 0 +0000
EOF
-test_expect_success \
- 'allow empty tag email' \
- 'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+test_expect_mktag_success 'allow empty tag email'
############################################################
-# 16. disallow spaces in tag email
+# 16. allow spaces in tag email like fsck
cat >tag.sig <<EOF
object $head
@@ -237,8 +329,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000
EOF
-check_verify_failure 'disallow spaces in tag email' \
- '^error: char.*: malformed tagger field$'
+test_expect_mktag_success 'allow spaces in tag email like fsck'
############################################################
# 17. disallow missing tag timestamp
@@ -252,7 +343,7 @@ tagger T A Gger <tagger@example.com>__
EOF
check_verify_failure 'disallow missing tag timestamp' \
- '^error: char.*: missing tag timestamp$'
+ '^error:.* badDate:'
############################################################
# 18. detect invalid tag timestamp1
@@ -266,7 +357,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008
EOF
check_verify_failure 'detect invalid tag timestamp1' \
- '^error: char.*: missing tag timestamp$'
+ '^error:.* badDate:'
############################################################
# 19. detect invalid tag timestamp2
@@ -280,7 +371,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500
EOF
check_verify_failure 'detect invalid tag timestamp2' \
- '^error: char.*: malformed tag timestamp$'
+ '^error:.* badDate:'
############################################################
# 20. detect invalid tag timezone1
@@ -294,7 +385,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT
EOF
check_verify_failure 'detect invalid tag timezone1' \
- '^error: char.*: malformed tag timezone$'
+ '^error:.* badTimezone:'
############################################################
# 21. detect invalid tag timezone2
@@ -308,10 +399,10 @@ tagger T A Gger <tagger@example.com> 1206478233 + 30
EOF
check_verify_failure 'detect invalid tag timezone2' \
- '^error: char.*: malformed tag timezone$'
+ '^error:.* badTimezone:'
############################################################
-# 22. detect invalid tag timezone3
+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)
cat >tag.sig <<EOF
object $head
@@ -321,8 +412,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430
EOF
-check_verify_failure 'detect invalid tag timezone3' \
- '^error: char.*: malformed tag timezone$'
+test_expect_mktag_success 'allow invalid tag timezone'
############################################################
# 23. detect invalid header entry
@@ -337,10 +427,41 @@ this line should not be here
EOF
check_verify_failure 'detect invalid header entry' \
- '^error: char.*: trailing garbage in tag header$'
+ '^error:.* extraHeaderEntry:' '--no-strict'
-############################################################
-# 24. create valid tag
+test_expect_success 'invalid header entry config & fsck' '
+ test_must_fail git mktag <tag.sig &&
+ git mktag --no-strict <tag.sig &&
+
+ test_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&
+ test_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&
+
+ test_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&
+ git -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&
+
+ git -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&
+ git -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&
+
+ git fsck &&
+ env GIT_TEST_GETTEXT_POISON=false \
+ git -c fsck.extraHeaderEntry=warn fsck 2>err &&
+ grep "warning .*extraHeaderEntry:" err &&
+ test_must_fail env GIT_TEST_GETTEXT_POISON=false \
+ git -c fsck.extraHeaderEntry=error 2>err fsck &&
+ grep "error .* extraHeaderEntry:" err
+'
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+
+
+this line comes after an extra newline
+EOF
+
+test_expect_mktag_success 'allow extra newlines at start of body'
cat >tag.sig <<EOF
object $head
@@ -350,16 +471,27 @@ tagger T A Gger <tagger@example.com> 1206478233 -0500
EOF
-test_expect_success \
- 'create valid tag' \
- 'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+test_expect_mktag_success 'allow a blank line before an empty body (1)'
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+EOF
+
+test_expect_mktag_success 'allow no blank line before an empty body (2)'
############################################################
-# 25. check mytag
+# 24. create valid tag
-test_expect_success \
- 'check mytag' \
- 'git tag -l | grep mytag'
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+EOF
+test_expect_mktag_success 'create valid tag object'
test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index c80dc10b8f..0dbe086118 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -129,7 +129,7 @@ tagger T A Gger <> 0 +0000
EOF
test_expect_success 'tag replaced commit' '
- git mktag <tag.sig >.git/refs/tags/mytag 2>message
+ git mktag <tag.sig >.git/refs/tags/mytag
'
test_expect_success '"git fsck" works' '