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:
commit
c7d6d419b0
@ -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.
|
||||
|
@ -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)
|
||||
|
227
builtin/mktag.c
227
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.
|
||||
*/
|
||||
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
58
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);
|
||||
}
|
||||
|
16
fsck.h
16
fsck.h
@ -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
|
||||
|
@ -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
|
||||
|
236
t/t3800-mktag.sh
236
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,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
|
||||
|
@ -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' '
|
||||
|
Loading…
Reference in New Issue
Block a user