interpret-trailers: add options for actions

Allow using non-default values for trailers without having to set them up in .gitconfig first. For example, if you have the following configuration trailer.signed-off-by.where = end you may use "--where before" when a patch author forgets his Signed-off-by and provides it in a separate email. Likewise for --if-exists and --if-missing Reverting to the behavior specified by .gitconfig is done with --no-where, --no-if-exists and --no-if-missing. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Paolo Bonzini committed Aug 1, 2017 at 11:03 UTC 0ea5292e6be2f37b647b744177cfe6cc5e6f605d
5 files changed +156 -6
Documentation/git-interpret-trailers.txt
+23
@@ -80,6 +80,29 @@ OPTIONS
80 trailer to the input messages. See the description of this
81 command.
82
83 +--where <placement>::
84 +--no-where::
85 + Specify where all new trailers will be added. A setting
86 + provided with '--where' overrides all configuration variables
87 + and applies to all '--trailer' options until the next occurrence of
88 + '--where' or '--no-where'.
89 +
90 +--if-exists <action>::
91 +--no-if-exists::
92 + Specify what action will be performed when there is already at
93 + least one trailer with the same <token> in the message. A setting
94 + provided with '--if-exists' overrides all configuration variables
95 + and applies to all '--trailer' options until the next occurrence of
96 + '--if-exists' or '--no-if-exists'.
97 +
98 +--if-missing <action>::
99 +--no-if-missing::
100 + Specify what action will be performed when there is no other
101 + trailer with the same <token> in the message. A setting
102 + provided with '--if-missing' overrides all configuration variables
103 + and applies to all '--trailer' options until the next occurrence of
104 + '--if-missing' or '--no-if-missing'.
105 +
106 CONFIGURATION VARIABLES
107 -----------------------
108
builtin/interpret-trailers.c
+32
@@ -16,6 +16,28 @@ static const char * const git_interpret_trailers_usage[] = {
16 NULL
17 };
18
19 +static enum trailer_where where;
20 +static enum trailer_if_exists if_exists;
21 +static enum trailer_if_missing if_missing;
22 +
23 +static int option_parse_where(const struct option *opt,
24 + const char *arg, int unset)
25 +{
26 + return trailer_set_where(&where, arg);
27 +}
28 +
29 +static int option_parse_if_exists(const struct option *opt,
30 + const char *arg, int unset)
31 +{
32 + return trailer_set_if_exists(&if_exists, arg);
33 +}
34 +
35 +static int option_parse_if_missing(const struct option *opt,
36 + const char *arg, int unset)
37 +{
38 + return trailer_set_if_missing(&if_missing, arg);
39 +}
40 +
41 static void new_trailers_clear(struct list_head *trailers)
42 {
43 struct list_head *pos, *tmp;
@@ -44,6 +66,9 @@ static int option_parse_trailer(const struct option *opt,
66
67 item = xmalloc(sizeof(*item));
68 item->text = arg;
69 + item->where = where;
70 + item->if_exists = if_exists;
71 + item->if_missing = if_missing;
72 list_add_tail(&item->list, trailers);
73 return 0;
74 }
@@ -58,6 +83,13 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
83 OPT_BOOL(0, "in-place", &in_place, N_("edit files in place")),
84 OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
85
86 + OPT_CALLBACK(0, "where", NULL, N_("action"),
87 + N_("where to place the new trailer"), option_parse_where),
88 + OPT_CALLBACK(0, "if-exists", NULL, N_("action"),
89 + N_("action if trailer already exists"), option_parse_if_exists),
90 + OPT_CALLBACK(0, "if-missing", NULL, N_("action"),
91 + N_("action if trailer is missing"), option_parse_if_missing),
92 +
93 OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
94 N_("trailer(s) to add"), option_parse_trailer),
95 OPT_END()
t/t7513-interpret-trailers.sh
+66
@@ -681,6 +681,36 @@ test_expect_success 'using "where = before"' '
681 test_cmp expected actual
682 '
683
684 +test_expect_success 'overriding configuration with "--where after"' '
685 + git config trailer.ack.where "before" &&
686 + cat complex_message_body >expected &&
687 + sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
688 + Fixes: Z
689 + Acked-by= Z
690 + Acked-by= Peff
691 + Reviewed-by: Z
692 + Signed-off-by: Z
693 + EOF
694 + git interpret-trailers --where after --trailer "ack: Peff" \
695 + complex_message >actual &&
696 + test_cmp expected actual
697 +'
698 +
699 +test_expect_success 'using "where = before" with "--no-where"' '
700 + cat complex_message_body >expected &&
701 + sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
702 + Bug #42
703 + Fixes: Z
704 + Acked-by= Peff
705 + Acked-by= Z
706 + Reviewed-by: Z
707 + Signed-off-by: Z
708 + EOF
709 + git interpret-trailers --where after --no-where --trailer "ack: Peff" \
710 + --trailer "bug: 42" complex_message >actual &&
711 + test_cmp expected actual
712 +'
713 +
714 test_expect_success 'using "where = after"' '
715 git config trailer.ack.where "after" &&
716 cat complex_message_body >expected &&
@@ -947,6 +977,23 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
977 test_cmp expected actual
978 '
979
980 +test_expect_success 'overriding configuration with "--if-exists replace"' '
981 + git config trailer.fix.key "Fixes: " &&
982 + git config trailer.fix.ifExists "add" &&
983 + cat complex_message_body >expected &&
984 + sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
985 + Bug #42
986 + Acked-by= Z
987 + Reviewed-by:
988 + Signed-off-by: Z
989 + Fixes: 22
990 + EOF
991 + git interpret-trailers --if-exists replace --trailer "review:" \
992 + --trailer "fix=53" --trailer "fix=22" --trailer "bug: 42" \
993 + <complex_message >actual &&
994 + test_cmp expected actual
995 +'
996 +
997 test_expect_success 'using "ifExists = replace"' '
998 git config trailer.fix.key "Fixes: " &&
999 git config trailer.fix.ifExists "replace" &&
@@ -1026,6 +1073,25 @@ test_expect_success 'the default is "ifMissing = add"' '
1073 test_cmp expected actual
1074 '
1075
1076 +test_expect_success 'overriding configuration with "--if-missing doNothing"' '
1077 + git config trailer.ifmissing "add" &&
1078 + cat complex_message_body >expected &&
1079 + sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
1080 + Fixes: Z
1081 + Acked-by= Z
1082 + Acked-by= Junio
1083 + Acked-by= Peff
1084 + Reviewed-by:
1085 + Signed-off-by: Z
1086 + EOF
1087 + git interpret-trailers --if-missing doNothing \
1088 + --trailer "review:" --trailer "fix=53" \
1089 + --trailer "cc=Linus" --trailer "ack: Junio" \
1090 + --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
1091 + <complex_message >actual &&
1092 + test_cmp expected actual
1093 +'
1094 +
1095 test_expect_success 'when default "ifMissing" is "doNothing"' '
1096 git config trailer.ifmissing "doNothing" &&
1097 cat complex_message_body >expected &&
trailer.c
+28 -6
@@ -295,6 +295,9 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,
295 else
296 free_arg_item(arg_tok);
297 break;
298 + default:
299 + die("BUG: trailer.c: unhandled value %d",
300 + arg_tok->conf.if_exists);
301 }
302 }
303
@@ -316,6 +319,10 @@ static void apply_arg_if_missing(struct list_head *head,
319 list_add_tail(&to_add->list, head);
320 else
321 list_add(&to_add->list, head);
322 + break;
323 + default:
324 + die("BUG: trailer.c: unhandled value %d",
325 + arg_tok->conf.if_missing);
326 }
327 }
328
@@ -370,7 +377,9 @@ static void process_trailers_lists(struct list_head *head,
377
378 int trailer_set_where(enum trailer_where *item, const char *value)
379 {
373 - if (!strcasecmp("after", value))
380 + if (!value)
381 + *item = WHERE_DEFAULT;
382 + else if (!strcasecmp("after", value))
383 *item = WHERE_AFTER;
384 else if (!strcasecmp("before", value))
385 *item = WHERE_BEFORE;
@@ -385,7 +394,9 @@ int trailer_set_where(enum trailer_where *item, const char *value)
394
395 int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
396 {
388 - if (!strcasecmp("addIfDifferent", value))
397 + if (!value)
398 + *item = EXISTS_DEFAULT;
399 + else if (!strcasecmp("addIfDifferent", value))
400 *item = EXISTS_ADD_IF_DIFFERENT;
401 else if (!strcasecmp("addIfDifferentNeighbor", value))
402 *item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
@@ -402,7 +413,9 @@ int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
413
414 int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
415 {
405 - if (!strcasecmp("doNothing", value))
416 + if (!value)
417 + *item = MISSING_DEFAULT;
418 + else if (!strcasecmp("doNothing", value))
419 *item = MISSING_DO_NOTHING;
420 else if (!strcasecmp("add", value))
421 *item = MISSING_ADD;
@@ -659,12 +672,21 @@ static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
672 }
673
674 static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
662 - const struct conf_info *conf)
675 + const struct conf_info *conf,
676 + const struct new_trailer_item *new_trailer_item)
677 {
678 struct arg_item *new = xcalloc(sizeof(*new), 1);
679 new->token = tok;
680 new->value = val;
681 duplicate_conf(&new->conf, conf);
682 + if (new_trailer_item) {
683 + if (new_trailer_item->where != WHERE_DEFAULT)
684 + new->conf.where = new_trailer_item->where;
685 + if (new_trailer_item->if_exists != EXISTS_DEFAULT)
686 + new->conf.if_exists = new_trailer_item->if_exists;
687 + if (new_trailer_item->if_missing != MISSING_DEFAULT)
688 + new->conf.if_missing = new_trailer_item->if_missing;
689 + }
690 list_add_tail(&new->list, arg_head);
691 }
692
@@ -690,7 +712,7 @@ static void process_command_line_args(struct list_head *arg_head,
712 add_arg_item(arg_head,
713 xstrdup(token_from_item(item, NULL)),
714 xstrdup(""),
693 - &item->conf);
715 + &item->conf, NULL);
716 }
717
718 /* Add an arg item for each trailer on the command line */
@@ -712,7 +734,7 @@ static void process_command_line_args(struct list_head *arg_head,
734 add_arg_item(arg_head,
735 strbuf_detach(&tok, NULL),
736 strbuf_detach(&val, NULL),
715 - conf);
737 + conf, tr);
738 }
739 }
740
trailer.h
+7
@@ -4,12 +4,14 @@
4 #include "list.h"
5
6 enum trailer_where {
7 + WHERE_DEFAULT,
8 WHERE_END,
9 WHERE_AFTER,
10 WHERE_BEFORE,
11 WHERE_START
12 };
13 enum trailer_if_exists {
14 + EXISTS_DEFAULT,
15 EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
16 EXISTS_ADD_IF_DIFFERENT,
17 EXISTS_ADD,
@@ -17,6 +19,7 @@ enum trailer_if_exists {
19 EXISTS_DO_NOTHING
20 };
21 enum trailer_if_missing {
22 + MISSING_DEFAULT,
23 MISSING_ADD,
24 MISSING_DO_NOTHING
25 };
@@ -54,6 +57,10 @@ struct new_trailer_item {
57 struct list_head list;
58
59 const char *text;
60 +
61 + enum trailer_where where;
62 + enum trailer_if_exists if_exists;
63 + enum trailer_if_missing if_missing;
64 };
65
66 void process_trailers(const char *file, int in_place, int trim_empty,