Merge branch 'cr/tag-options'
* cr/tag-options: git-tag: test that -s implies an annotated tag "git-tag -s" should create a signed annotated tag builtin-tag: accept and process multiple -m just like git-commit Make builtin-tag.c use parse_options.
This commit is contained in:
commit
c4d48ab5af
@ -65,7 +65,9 @@ OPTIONS
|
||||
Typing "git tag" without arguments, also lists all tags.
|
||||
|
||||
-m <msg>::
|
||||
Use the given tag message (instead of prompting)
|
||||
Use the given tag message (instead of prompting).
|
||||
If multiple `-m` options are given, there values are
|
||||
concatenated as separate paragraphs.
|
||||
|
||||
-F <file>::
|
||||
Take the tag message from the given file. Use '-' to
|
||||
|
167
builtin-tag.c
167
builtin-tag.c
@ -11,9 +11,15 @@
|
||||
#include "refs.h"
|
||||
#include "tag.h"
|
||||
#include "run-command.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char builtin_tag_usage[] =
|
||||
"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
|
||||
static const char * const git_tag_usage[] = {
|
||||
"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
|
||||
"git-tag -d <tagname>...",
|
||||
"git-tag [-n [<num>]] -l [<pattern>]",
|
||||
"git-tag -v <tagname>...",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char signingkey[1000];
|
||||
|
||||
@ -276,7 +282,7 @@ static void write_tag_body(int fd, const unsigned char *sha1)
|
||||
|
||||
static void create_tag(const unsigned char *object, const char *tag,
|
||||
struct strbuf *buf, int message, int sign,
|
||||
unsigned char *prev, unsigned char *result)
|
||||
unsigned char *prev, unsigned char *result)
|
||||
{
|
||||
enum object_type type;
|
||||
char header_buf[1024];
|
||||
@ -335,105 +341,101 @@ static void create_tag(const unsigned char *object, const char *tag,
|
||||
die("unable to write tag file");
|
||||
}
|
||||
|
||||
struct msg_arg {
|
||||
int given;
|
||||
struct strbuf buf;
|
||||
};
|
||||
|
||||
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct msg_arg *msg = opt->value;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (msg->buf.len)
|
||||
strbuf_addstr(&(msg->buf), "\n\n");
|
||||
strbuf_addstr(&(msg->buf), arg);
|
||||
msg->given = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct strbuf buf;
|
||||
unsigned char object[20], prev[20];
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0, message = 0;
|
||||
char ref[PATH_MAX];
|
||||
const char *object_ref, *tag;
|
||||
int i;
|
||||
struct ref_lock *lock;
|
||||
|
||||
int annotate = 0, sign = 0, force = 0, lines = 0,
|
||||
delete = 0, verify = 0;
|
||||
char *list = NULL, *msgfile = NULL, *keyid = NULL;
|
||||
const char *no_pattern = "NO_PATTERN";
|
||||
struct msg_arg msg = { 0, STRBUF_INIT };
|
||||
struct option options[] = {
|
||||
{ OPTION_STRING, 'l', NULL, &list, "pattern", "list tag names",
|
||||
PARSE_OPT_OPTARG, NULL, (intptr_t) no_pattern },
|
||||
{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
|
||||
"print n lines of each tag message",
|
||||
PARSE_OPT_OPTARG, NULL, 1 },
|
||||
OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
|
||||
OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
|
||||
|
||||
OPT_GROUP("Tag creation options"),
|
||||
OPT_BOOLEAN('a', NULL, &annotate,
|
||||
"annotated tag, needs a message"),
|
||||
OPT_CALLBACK('m', NULL, &msg, "msg",
|
||||
"message for the tag", parse_msg_arg),
|
||||
OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
|
||||
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
|
||||
OPT_STRING('u', NULL, &keyid, "key-id",
|
||||
"use another key to sign the tag"),
|
||||
OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
git_config(git_tag_config);
|
||||
|
||||
argc = parse_options(argc, argv, options, git_tag_usage, 0);
|
||||
|
||||
if (sign)
|
||||
annotate = 1;
|
||||
|
||||
if (list)
|
||||
return list_tags(list == no_pattern ? NULL : list, lines);
|
||||
if (delete)
|
||||
return for_each_tag_name(argv, delete_tag);
|
||||
if (verify)
|
||||
return for_each_tag_name(argv, verify_tag);
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (arg[0] != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "-a")) {
|
||||
annotate = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-s")) {
|
||||
annotate = 1;
|
||||
sign = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-f")) {
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
if (i + 1 == argc || *argv[i + 1] == '-')
|
||||
/* no argument */
|
||||
lines = 1;
|
||||
else
|
||||
lines = isdigit(*argv[++i]) ?
|
||||
atoi(argv[i]) : 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-m")) {
|
||||
annotate = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -m needs an argument.");
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
strbuf_addstr(&buf, argv[i]);
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-F")) {
|
||||
annotate = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -F needs an argument.");
|
||||
if (message)
|
||||
die("only one -F or -m option is allowed.");
|
||||
|
||||
if (!strcmp(argv[i], "-")) {
|
||||
if (msg.given || msgfile) {
|
||||
if (msg.given && msgfile)
|
||||
die("only one -F or -m option is allowed.");
|
||||
annotate = 1;
|
||||
if (msg.given)
|
||||
strbuf_addbuf(&buf, &(msg.buf));
|
||||
else {
|
||||
if (!strcmp(msgfile, "-")) {
|
||||
if (strbuf_read(&buf, 0, 1024) < 0)
|
||||
die("cannot read %s", argv[i]);
|
||||
die("cannot read %s", msgfile);
|
||||
} else {
|
||||
if (strbuf_read_file(&buf, argv[i], 1024) < 0)
|
||||
if (strbuf_read_file(&buf, msgfile, 1024) < 0)
|
||||
die("could not open or read '%s': %s",
|
||||
argv[i], strerror(errno));
|
||||
msgfile, strerror(errno));
|
||||
}
|
||||
message = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-u")) {
|
||||
annotate = 1;
|
||||
sign = 1;
|
||||
i++;
|
||||
if (i == argc)
|
||||
die("option -u needs an argument.");
|
||||
if (strlcpy(signingkey, argv[i], sizeof(signingkey))
|
||||
>= sizeof(signingkey))
|
||||
die("argument to option -u too long");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-l"))
|
||||
return list_tags(argv[i + 1], lines);
|
||||
if (!strcmp(arg, "-d"))
|
||||
return for_each_tag_name(argv + i + 1, delete_tag);
|
||||
if (!strcmp(arg, "-v"))
|
||||
return for_each_tag_name(argv + i + 1, verify_tag);
|
||||
usage(builtin_tag_usage);
|
||||
}
|
||||
|
||||
if (i == argc) {
|
||||
if (argc == 0) {
|
||||
if (annotate)
|
||||
usage(builtin_tag_usage);
|
||||
usage_with_options(git_tag_usage, options);
|
||||
return list_tags(NULL, lines);
|
||||
}
|
||||
tag = argv[i++];
|
||||
tag = argv[0];
|
||||
|
||||
object_ref = i < argc ? argv[i] : "HEAD";
|
||||
if (i + 1 < argc)
|
||||
object_ref = argc == 2 ? argv[1] : "HEAD";
|
||||
if (argc > 2)
|
||||
die("too many params");
|
||||
|
||||
if (get_sha1(object_ref, object))
|
||||
@ -450,7 +452,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
die("tag '%s' already exists", tag);
|
||||
|
||||
if (annotate)
|
||||
create_tag(object, tag, &buf, message, sign, prev, object);
|
||||
create_tag(object, tag, &buf, msg.given || msgfile,
|
||||
sign, prev, object);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, prev, 0);
|
||||
if (!lock)
|
||||
|
@ -339,20 +339,14 @@ test_expect_success \
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'trying to create tags giving many -m or -F options should fail' '
|
||||
'trying to create tags giving both -m or -F options should fail' '
|
||||
echo "message file 1" >msgfile1 &&
|
||||
echo "message file 2" >msgfile2 &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -m "message 2" msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -F msgfile2 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -F msgfile1 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -m "message 1" msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
|
||||
! tag_exists msgtag &&
|
||||
! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
|
||||
! tag_exists msgtag
|
||||
'
|
||||
@ -673,6 +667,22 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
|
||||
git diff expect actual
|
||||
'
|
||||
|
||||
cat >fakeeditor <<'EOF'
|
||||
#!/bin/sh
|
||||
test -n "$1" && exec >"$1"
|
||||
echo A signed tag message
|
||||
echo from a fake editor.
|
||||
EOF
|
||||
chmod +x fakeeditor
|
||||
get_tag_header implied-annotate $commit commit $time >expect
|
||||
./fakeeditor >>expect
|
||||
echo '-----BEGIN PGP SIGNATURE-----' >>expect
|
||||
test_expect_success '-s implies annotated tag' '
|
||||
GIT_EDITOR=./fakeeditor git-tag -s implied-annotate &&
|
||||
get_tag_msg implied-annotate >actual &&
|
||||
git diff expect actual
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'trying to create a signed tag with non-existing -F file should fail' '
|
||||
! test -f nonexistingfile &&
|
||||
|
Loading…
Reference in New Issue
Block a user