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
  ...
This commit is contained in:
Junio C Hamano 2021-01-25 14:19:16 -08:00
commit c7d6d419b0
8 changed files with 370 additions and 230 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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.
*/
static char const * const builtin_mktag_usage[] = {
N_("git mktag"),
NULL
};
static int option_strict = 1;
/*
* 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 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);
}
free(buffer);
}
return ret;
return fsck_config_internal(var, value, cb, &fsck_options);
}
static int verify_tag(char *buffer, unsigned long size)
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)
{
int typelen;
char type[20];
struct object_id oid;
const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
size_t len;
switch (msg_type) {
case FSCK_WARN:
if (!option_strict) {
fprintf_ln(stderr, _("warning: tag input does not pass fsck: %s"), message);
return 0;
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));
}
/* 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);
}
}
/* Verify the tagger line */
tagger_line = tag_line;
static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
{
int ret;
enum object_type type;
unsigned long size;
void *buffer;
const struct object_id *repl;
if (memcmp(tagger_line, "tagger ", 7))
return error("char%"PRIuMAX": could not find \"tagger \"",
(uintmax_t) (tagger_line - buffer));
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));
/*
* 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));
repl = lookup_replace_object(the_repository, tagged_oid);
ret = check_object_signature(the_repository, repl,
buffer, size, type_name(*tagged_type));
free(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"));
/* 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");
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"));
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;
}

58
fsck.c
View file

@ -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);
}

16
fsck.h
View file

@ -62,6 +62,15 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
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
@ -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

View file

@ -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

View file

@ -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,10 +155,32 @@ 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 (hash/type) check -- made-up type, valid object' \
'^error:.* badType:'
cat >tag.sig <<EOF
object $(test_oid deadbeef)
@ -135,8 +190,48 @@ 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, 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

View file

@ -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' '