From: "John Passaro via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: John Passaro <john.a.passaro@gmail.com>,
John Passaro <john.a.passaro@gmail.com>
Subject: [PATCH v2] builtin/tag.c: add --trailer arg
Date: Mon, 29 Apr 2024 16:53:57 +0000 [thread overview]
Message-ID: <pull.1723.v2.git.1714409638089.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1723.git.1714365076246.gitgitgadget@gmail.com>
From: John Passaro <john.a.passaro@gmail.com>
Teach git-tag to accept --trailer option to add trailers to annotated
tag messages, like git-commit. Move the code that git-commit uses for
trailers to the trailer.h API, so it can be re-used for git-tag.
Signed-off-by: John Passaro <john.a.passaro@gmail.com>
---
builtin/tag.c: add --trailer arg
cc: Patrick Steinhardt ps@pks.im cc: John Passaro
john.a.passaro@gmail.com
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1723%2Fjpassaro%2Fjp%2Ftag-trailer-arg-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1723/jpassaro/jp/tag-trailer-arg-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/1723
Range-diff vs v1:
1: 02d7a0f035e ! 1: d4beb7cd67e builtin/tag.c: add --trailer arg
@@ Commit message
builtin/tag.c: add --trailer arg
Teach git-tag to accept --trailer option to add trailers to annotated
- tag messages, like git-commit.
+ tag messages, like git-commit. Move the code that git-commit uses for
+ trailers to the trailer.h API, so it can be re-used for git-tag.
Signed-off-by: John Passaro <john.a.passaro@gmail.com>
@@ Documentation/git-tag.txt: This option is only applicable when listing tags with
--edit::
The message taken from file with `-F` and command line with
+ ## builtin/commit.c ##
+@@
+ #include "commit-reach.h"
+ #include "commit-graph.h"
+ #include "pretty.h"
++#include "trailer.h"
+
+ static const char * const builtin_commit_usage[] = {
+ N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+@@ builtin/commit.c: static int prepare_to_commit(const char *index_file, const char *prefix,
+ fclose(s->fp);
+
+ if (trailer_args.nr) {
+- struct child_process run_trailer = CHILD_PROCESS_INIT;
+-
+- strvec_pushl(&run_trailer.args, "interpret-trailers",
+- "--in-place", "--no-divider",
+- git_path_commit_editmsg(), NULL);
+- strvec_pushv(&run_trailer.args, trailer_args.v);
+- run_trailer.git_cmd = 1;
+- if (run_command(&run_trailer))
++ if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
+ die(_("unable to pass trailers to --trailers"));
+ strvec_clear(&trailer_args);
+ }
+
## builtin/tag.c ##
@@
#include "date.h"
#include "write-or-die.h"
#include "object-file-convert.h"
-+#include "run-command.h"
++#include "trailer.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
@@ builtin/tag.c: static const char message_advice_nested_tag[] =
const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- struct object_id *prev, struct object_id *result, char *path)
-+ struct object_id *prev, struct object_id *result, struct strvec *trailer_args, char *path)
++ struct object_id *prev, struct object_id *result,
++ struct strvec *trailer_args, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
@@ builtin/tag.c: static void create_tag(const struct object_id *object, const char
- if (opt->message_given) {
+ if (opt->message_given && buf->len) {
++ strbuf_complete(buf, '\n');
write_or_die(fd, buf->buf, buf->len);
-+ if (trailer_args->nr && buf->buf[buf->len-1] != '\n') {
-+ write_or_die(fd, "\n", 1);
-+ }
strbuf_reset(buf);
} else if (!is_null_oid(prev)) {
- write_tag_body(fd, prev);
@@ builtin/tag.c: static void create_tag(const struct object_id *object, const char *object_ref,
}
close(fd);
@@ builtin/tag.c: static void create_tag(const struct object_id *object, const char
- fprintf(stderr,
- _("Please supply the message using either -m or -F option.\n"));
- exit(1);
-+ if (trailer_args->nr) {
-+ struct child_process run_trailer = CHILD_PROCESS_INIT;
-+
-+ strvec_pushl(&run_trailer.args, "interpret-trailers",
-+ "--in-place", "--no-divider",
-+ path, NULL);
-+ strvec_pushv(&run_trailer.args, trailer_args->v);
-+ run_trailer.git_cmd = 1;
-+ if (run_command(&run_trailer))
-+ die(_("unable to pass trailers to --trailers"));
-+ }
++ if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
++ die(_("unable to pass trailers to --trailers"));
+
+ if (should_edit) {
+ if (launch_editor(path, buf, NULL)) {
+ fprintf(stderr,
-+ _("Please supply the message using either -m or -F option.\n"));
++ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
+ } else if (trailer_args->nr) {
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0) {
+ fprintf(stderr,
-+ _("Please supply the message using either -m or -F option.\n"));
++ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
}
@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
-+ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
++ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"),
++ PARSE_OPT_NONEG, opt_pass_trailer),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_CLEANUP(&cleanup_arg),
@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
set_signing_key(keyid);
}
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
-+ create_tag_object = (opt.sign || annotate || msg.given || msgfile || edit_flag || trailer_args.nr);
++ create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
++ edit_flag || trailer_args.nr);
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
- else if (!force)
- die(_("tag '%s' already exists"), tag);
-
-- opt.message_given = msg.given || msgfile;
-+ opt.message_given = msg.given || (msgfile != NULL);
- opt.use_editor = edit_flag;
-
- if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
-@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
- if (force_sign_annotate && !annotate)
opt.sign = 1;
path = git_pathdup("TAG_EDITMSG");
-- create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
-+ create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object, &trailer_args,
- path);
+ create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+- path);
++ &trailer_args, path);
}
+ transaction = ref_transaction_begin(&err);
@@ builtin/tag.c: int cmd_tag(int argc, const char **argv, const char *prefix)
strbuf_release(&reflog_msg);
strbuf_release(&msg.buf);
@@ t/t7004-tag.sh: test_expect_success \
+# trailers
+
-+get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect
-+cat >>expect <<EOF
-+create tag with trailers
-+
-+my-trailer: here
-+alt-trailer: there
-+EOF
+test_expect_success 'create tag with -m and --trailer' '
-+ git tag -m "create tag with trailers" --trailer my-trailer=here --trailer alt-trailer=there tag-with-inline-message-and-trailers &&
++ get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
++ cat >>expect <<-\EOF &&
++ create tag with trailers
++
++ my-trailer: here
++ alt-trailer: there
++ EOF
++ git tag -m "create tag with trailers" \
++ --trailer my-trailer=here \
++ --trailer alt-trailer=there \
++ tag-with-inline-message-and-trailers &&
+ get_tag_msg tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
@@ t/t7004-tag.sh: test_expect_success \
+ test_cmp expect actual
+'
+
-+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
-+get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect
-+cat >>expect <<EOF
-+create tag from message file using --trailer
-+
-+my-trailer: here
-+alt-trailer: there
-+EOF
+test_expect_success 'create tag with -F and --trailer' '
-+ git tag -F messagefilewithnotrailers --trailer my-trailer=here --trailer alt-trailer=there tag-with-file-message-and-trailers &&
++ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
++ get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
++ cat >>expect <<-\EOF &&
++ create tag from message file using --trailer
++
++ my-trailer: here
++ alt-trailer: there
++ EOF
++ git tag -F messagefilewithnotrailers \
++ --trailer my-trailer=here \
++ --trailer alt-trailer=there \
++ tag-with-file-message-and-trailers &&
+ get_tag_msg tag-with-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
-+test_expect_success 'set up editor' '
-+ write_script fakeeditor <<-\EOF
++test_expect_success 'create tag with -m and --trailer and --edit' '
++ write_script fakeeditor <<-\EOF &&
+ sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
-+'
-+
-+get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect
-+cat >>expect <<EOF
-+EDITED: create tag with trailers
++ get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
++ cat >>expect <<-\EOF &&
++ EDITED: create tag with trailers
+
-+my-trailer: here
-+alt-trailer: there
-+EOF
-+test_expect_success 'create tag with -m and --trailer and --edit' '
-+ GIT_EDITOR=./fakeeditor git tag --edit -m "create tag with trailers" --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-inline-message-and-trailers &&
++ my-trailer: here
++ alt-trailer: there
++ EOF
++ GIT_EDITOR=./fakeeditor git tag --edit \
++ -m "create tag with trailers" \
++ --trailer my-trailer=here \
++ --trailer alt-trailer=there \
++ tag-with-edited-inline-message-and-trailers &&
+ get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
-+echo 'create tag from message file using --trailer' >messagefilewithnotrailers
-+get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect
-+cat >>expect <<EOF
-+EDITED: create tag from message file using --trailer
-+
-+my-trailer: here
-+alt-trailer: there
-+EOF
+test_expect_success 'create tag with -F and --trailer and --edit' '
-+ GIT_EDITOR=./fakeeditor git tag --edit -F messagefilewithnotrailers --trailer my-trailer=here --trailer alt-trailer=there tag-with-edited-file-message-and-trailers &&
++ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
++ get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
++ cat >>expect <<-\EOF &&
++ EDITED: create tag from message file using --trailer
++
++ my-trailer: here
++ alt-trailer: there
++ EOF
++ GIT_EDITOR=./fakeeditor git tag --edit \
++ -F messagefilewithnotrailers \
++ --trailer my-trailer=here \
++ --trailer alt-trailer=there \
++ tag-with-edited-file-message-and-trailers &&
+ get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
-+test_expect_success 'set up editor' '
-+ write_script fakeeditor <<-\EOF
++test_expect_success 'create annotated tag and force editor when only --trailer is given' '
++ write_script fakeeditor <<-\EOF &&
+ echo "add a line" >"$1-"
-+ echo >>"$1-"
+ cat <"$1" >>"$1-"
+ mv "$1-" "$1"
+ EOF
-+'
-+
-+get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect
-+cat >>expect <<EOF
-+add a line
++ get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
++ cat >>expect <<-\EOF &&
++ add a line
+
-+my-trailer: here
-+alt-trailer: there
-+EOF
-+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
-+ GIT_EDITOR=./fakeeditor git tag --trailer my-trailer=here --trailer alt-trailer=there tag-with-trailers-and-no-message &&
++ my-trailer: here
++ alt-trailer: there
++ EOF
++ GIT_EDITOR=./fakeeditor git tag \
++ --trailer my-trailer=here \
++ --trailer alt-trailer=there \
++ tag-with-trailers-and-no-message &&
+ get_tag_msg tag-with-trailers-and-no-message >actual &&
+ test_cmp expect actual
+'
@@ t/t7004-tag.sh: test_expect_success 'git tag --format with ahead-behind' '
refs/tags/tag-zero-lines 0 1 !
EOF
git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+
+ ## trailer.c ##
+@@
+ #include "commit.h"
+ #include "trailer.h"
+ #include "list.h"
++#include "run-command.h"
+ /*
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ */
+@@ trailer.c: void trailer_iterator_release(struct trailer_iterator *iter)
+ strbuf_release(&iter->val);
+ strbuf_release(&iter->key);
+ }
++
++int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
++ struct child_process run_trailer = CHILD_PROCESS_INIT;
++
++ run_trailer.git_cmd = 1;
++ strvec_pushl(&run_trailer.args, "interpret-trailers",
++ "--in-place", "--no-divider",
++ path, NULL);
++ strvec_pushv(&run_trailer.args, trailer_args->v);
++ return run_command(&run_trailer);
++}
+
+ ## trailer.h ##
+@@
+
+ #include "list.h"
+ #include "strbuf.h"
++#include "strvec.h"
+
+ enum trailer_where {
+ WHERE_DEFAULT,
+@@ trailer.h: int trailer_iterator_advance(struct trailer_iterator *iter);
+ */
+ void trailer_iterator_release(struct trailer_iterator *iter);
+
++/*
++ * Augment a file to add trailers to it by running git-interpret-trailers.
++ * This calls run_command() and its return value is the same (i.e. 0 for
++ * success, various non-zero for other errors). See run-command.h.
++ */
++int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
++
+ #endif /* TRAILER_H */
Documentation/git-tag.txt | 18 +++++-
builtin/commit.c | 10 +---
builtin/tag.c | 49 +++++++++++++---
t/t7004-tag.sh | 114 ++++++++++++++++++++++++++++++++++++++
trailer.c | 12 ++++
trailer.h | 8 +++
6 files changed, 192 insertions(+), 19 deletions(-)
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c31ec..79b0a7e9644 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+ [(--trailer <token>[(=|:)<value>])...]
<tagname> [<commit> | <object>]
'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@ creates a 'tag' object, and requires a tag message. Unless
`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
Otherwise, a tag reference that points directly at the given object
(i.e., a lightweight tag) is created.
@@ -178,6 +179,19 @@ This option is only applicable when listing tags without annotation lines.
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+--trailer <token>[(=|:)<value>]::
+ Specify a (<token>, <value>) pair that should be applied as a
+ trailer. (e.g. `git tag --trailer "Signed-off-by:T A Ger \
+ <tagger@example.com>" --trailer "Helped-by:C O Mitter \
+ <committer@example.com>"` will add the "Signed-off-by" trailer
+ and the "Helped-by" trailer to the tag message.)
+ The `trailer.*` configuration variables
+ (linkgit:git-interpret-trailers[1]) can be used to define if
+ a duplicated trailer is omitted, where in the run of trailers
+ each trailer would appear, and other details.
+ The trailers can be seen in `git tag --list` using
+ `--format="%(trailers)"` placeholder.
+
-e::
--edit::
The message taken from file with `-F` and command line with
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e1484446b0..a1cbc128429 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -38,6 +38,7 @@
#include "commit-reach.h"
#include "commit-graph.h"
#include "pretty.h"
+#include "trailer.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
@@ -1038,14 +1039,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fclose(s->fp);
if (trailer_args.nr) {
- struct child_process run_trailer = CHILD_PROCESS_INIT;
-
- strvec_pushl(&run_trailer.args, "interpret-trailers",
- "--in-place", "--no-divider",
- git_path_commit_editmsg(), NULL);
- strvec_pushv(&run_trailer.args, trailer_args.v);
- run_trailer.git_cmd = 1;
- if (run_command(&run_trailer))
+ if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
die(_("unable to pass trailers to --trailers"));
strvec_clear(&trailer_args);
}
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb50b45..0a029fb8c30 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
#include "date.h"
#include "write-or-die.h"
#include "object-file-convert.h"
+#include "trailer.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+ " [(--trailer <token>[(=|:)<value>])...]\n"
" <tagname> [<commit> | <object>]"),
N_("git tag -d <tagname>..."),
N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -290,10 +292,12 @@ static const char message_advice_nested_tag[] =
static void create_tag(const struct object_id *object, const char *object_ref,
const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- struct object_id *prev, struct object_id *result, char *path)
+ struct object_id *prev, struct object_id *result,
+ struct strvec *trailer_args, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
+ int should_edit;
type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
@@ -313,13 +317,15 @@ static void create_tag(const struct object_id *object, const char *object_ref,
tag,
git_committer_info(IDENT_STRICT));
- if (!opt->message_given || opt->use_editor) {
+ should_edit = opt->use_editor || !opt->message_given;
+ if (should_edit || trailer_args->nr) {
int fd;
/* write the template message before editing: */
fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (opt->message_given) {
+ if (opt->message_given && buf->len) {
+ strbuf_complete(buf, '\n');
write_or_die(fd, buf->buf, buf->len);
strbuf_reset(buf);
} else if (!is_null_oid(prev)) {
@@ -338,10 +344,22 @@ static void create_tag(const struct object_id *object, const char *object_ref,
}
close(fd);
- if (launch_editor(path, buf, NULL)) {
- fprintf(stderr,
- _("Please supply the message using either -m or -F option.\n"));
- exit(1);
+ if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
+ die(_("unable to pass trailers to --trailers"));
+
+ if (should_edit) {
+ if (launch_editor(path, buf, NULL)) {
+ fprintf(stderr,
+ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
+ } else if (trailer_args->nr) {
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0) {
+ fprintf(stderr,
+ _("Please supply the message using either -m or -F option.\n"));
+ exit(1);
+ }
}
}
@@ -416,6 +434,14 @@ struct msg_arg {
struct strbuf buf;
};
+static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
+{
+ BUG_ON_OPT_NEG(unset);
+
+ strvec_pushl(opt->value, "--trailer", arg, NULL);
+ return 0;
+}
+
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
{
struct msg_arg *msg = opt->value;
@@ -463,6 +489,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
+ struct strvec trailer_args = STRVEC_INIT;
int icase = 0;
int edit_flag = 0;
struct option options[] = {
@@ -479,6 +506,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_CALLBACK_F('m', "message", &msg, N_("message"),
N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+ OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"),
+ PARSE_OPT_NONEG, opt_pass_trailer),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +577,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
set_signing_key(keyid);
}
- create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+ create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
+ edit_flag || trailer_args.nr);
if ((create_tag_object || force) && (cmdmode != 0))
usage_with_options(git_tag_usage, options);
@@ -654,7 +684,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
opt.sign = 1;
path = git_pathdup("TAG_EDITMSG");
create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
- path);
+ &trailer_args, path);
}
transaction = ref_transaction_begin(&err);
@@ -686,6 +716,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
strbuf_release(&reflog_msg);
strbuf_release(&msg.buf);
strbuf_release(&err);
+ strvec_clear(&trailer_args);
free(msgfile);
return ret;
}
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 696866d7794..fa6336edf98 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -668,6 +668,115 @@ test_expect_success \
test_cmp expect actual
'
+# trailers
+
+test_expect_success 'create tag with -m and --trailer' '
+ get_tag_header tag-with-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-inline-message-and-trailers &&
+ get_tag_msg tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'list tag extracting trailers' '
+ cat >expect <<-\EOF &&
+ my-trailer: here
+ alt-trailer: there
+
+ EOF
+ git tag --list --format="%(trailers)" tag-with-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ git tag -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-file-message-and-trailers &&
+ get_tag_msg tag-with-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -m and --trailer and --edit' '
+ write_script fakeeditor <<-\EOF &&
+ sed -e "1s/^/EDITED: /g" <"$1" >"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-edited-inline-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag with trailers
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -m "create tag with trailers" \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-inline-message-and-trailers &&
+ get_tag_msg tag-with-edited-inline-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create tag with -F and --trailer and --edit' '
+ echo "create tag from message file using --trailer" >messagefilewithnotrailers &&
+ get_tag_header tag-with-edited-file-message-and-trailers $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ EDITED: create tag from message file using --trailer
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag --edit \
+ -F messagefilewithnotrailers \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-edited-file-message-and-trailers &&
+ get_tag_msg tag-with-edited-file-message-and-trailers >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create annotated tag and force editor when only --trailer is given' '
+ write_script fakeeditor <<-\EOF &&
+ echo "add a line" >"$1-"
+ cat <"$1" >>"$1-"
+ mv "$1-" "$1"
+ EOF
+ get_tag_header tag-with-trailers-and-no-message $commit commit $time >expect &&
+ cat >>expect <<-\EOF &&
+ add a line
+
+ my-trailer: here
+ alt-trailer: there
+ EOF
+ GIT_EDITOR=./fakeeditor git tag \
+ --trailer my-trailer=here \
+ --trailer alt-trailer=there \
+ tag-with-trailers-and-no-message &&
+ get_tag_msg tag-with-trailers-and-no-message >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bad editor causes panic when only --trailer is given' '
+ test_must_fail env GIT_EDITOR=false git tag --trailer my-trailer=here tag-will-not-exist
+'
+
# listing messages for annotated non-signed tags:
test_expect_success \
@@ -810,6 +919,11 @@ test_expect_success 'git tag --format with ahead-behind' '
refs/tags/tag-lines 0 1 !
refs/tags/tag-one-line 0 1 !
refs/tags/tag-right 0 0 !
+ refs/tags/tag-with-edited-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-edited-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-file-message-and-trailers 0 1 !
+ refs/tags/tag-with-inline-message-and-trailers 0 1 !
+ refs/tags/tag-with-trailers-and-no-message 0 1 !
refs/tags/tag-zero-lines 0 1 !
EOF
git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
diff --git a/trailer.c b/trailer.c
index c72ae687099..843c378199e 100644
--- a/trailer.c
+++ b/trailer.c
@@ -7,6 +7,7 @@
#include "commit.h"
#include "trailer.h"
#include "list.h"
+#include "run-command.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
@@ -1170,3 +1171,14 @@ void trailer_iterator_release(struct trailer_iterator *iter)
strbuf_release(&iter->val);
strbuf_release(&iter->key);
}
+
+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args) {
+ struct child_process run_trailer = CHILD_PROCESS_INIT;
+
+ run_trailer.git_cmd = 1;
+ strvec_pushl(&run_trailer.args, "interpret-trailers",
+ "--in-place", "--no-divider",
+ path, NULL);
+ strvec_pushv(&run_trailer.args, trailer_args->v);
+ return run_command(&run_trailer);
+}
diff --git a/trailer.h b/trailer.h
index 9f42aa75994..55f85b008ee 100644
--- a/trailer.h
+++ b/trailer.h
@@ -3,6 +3,7 @@
#include "list.h"
#include "strbuf.h"
+#include "strvec.h"
enum trailer_where {
WHERE_DEFAULT,
@@ -158,4 +159,11 @@ int trailer_iterator_advance(struct trailer_iterator *iter);
*/
void trailer_iterator_release(struct trailer_iterator *iter);
+/*
+ * Augment a file to add trailers to it by running git-interpret-trailers.
+ * This calls run_command() and its return value is the same (i.e. 0 for
+ * success, various non-zero for other errors). See run-command.h.
+ */
+int amend_file_with_trailers(const char *path, struct strvec const* trailer_args);
+
#endif /* TRAILER_H */
base-commit: e326e520101dcf43a0499c3adc2df7eca30add2d
--
gitgitgadget
next prev parent reply other threads:[~2024-04-29 16:54 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-29 4:31 [PATCH] builtin/tag.c: add --trailer arg John Passaro via GitGitGadget
2024-04-29 6:50 ` Patrick Steinhardt
2024-04-29 14:50 ` John Passaro
2024-04-29 15:05 ` John Passaro
2024-04-29 17:07 ` Junio C Hamano
2024-04-29 15:29 ` Junio C Hamano
2024-04-29 16:38 ` John Passaro
2024-04-29 17:04 ` Junio C Hamano
2024-04-29 16:53 ` John Passaro via GitGitGadget [this message]
2024-04-29 18:54 ` [PATCH v3 0/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
2024-04-29 18:54 ` [PATCH v3 1/3] builtin/commit.c: refactor --trailer logic John Passaro via GitGitGadget
2024-04-30 5:54 ` Patrick Steinhardt
2024-04-30 16:38 ` Junio C Hamano
2024-04-29 18:54 ` [PATCH v3 2/3] builtin/tag.c: add --trailer arg John Passaro via GitGitGadget
2024-04-30 5:54 ` Patrick Steinhardt
2024-04-30 16:53 ` Junio C Hamano
2024-04-30 21:48 ` John Passaro
2024-04-30 22:23 ` Junio C Hamano
2024-05-05 18:59 ` John Passaro
2024-04-29 18:54 ` [PATCH v3 3/3] po: update git-tag translations John Passaro via GitGitGadget
2024-04-29 19:22 ` Junio C Hamano
2024-04-29 19:28 ` John Passaro
2024-04-30 14:41 ` [PATCH v4 0/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
2024-04-30 14:41 ` [PATCH v4 1/3] builtin/commit.c: remove bespoke option callback John Passaro via GitGitGadget
2024-05-02 6:27 ` Patrick Steinhardt
2024-04-30 14:41 ` [PATCH v4 2/3] builtin/commit.c: refactor --trailer logic John Passaro via GitGitGadget
2024-05-02 6:27 ` Patrick Steinhardt
2024-04-30 14:41 ` [PATCH v4 3/3] builtin/tag.c: add --trailer option John Passaro via GitGitGadget
2024-05-02 6:27 ` [PATCH v4 0/3] " Patrick Steinhardt
2024-05-05 18:49 ` [PATCH v5 " John Passaro via GitGitGadget
2024-05-05 18:49 ` [PATCH v5 1/3] builtin/commit: use ARGV macro to collect trailers John Passaro via GitGitGadget
2024-05-07 15:38 ` John Passaro
2024-05-07 17:06 ` Junio C Hamano
2024-05-05 18:49 ` [PATCH v5 2/3] builtin/commit: refactor --trailer logic John Passaro via GitGitGadget
2024-05-05 18:49 ` [PATCH v5 3/3] builtin/tag: add --trailer option John Passaro via GitGitGadget
2024-05-06 5:40 ` [PATCH v5 0/3] builtin/tag.c: " Patrick Steinhardt
2024-05-06 17:52 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.1723.v2.git.1714409638089.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=john.a.passaro@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.