{"thread":{"id":"46494","subject":"[PATCH v4 0/4] interpret-trailers: add --where, --if-exists, --if-missing","startedAt":"2017-08-01T09:03:43Z","lastAt":"2017-08-14T17:57:30Z","messageCount":7,"participants":["Paolo Bonzini","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":4},"messages":[{"id":"325366","messageId":"20170801090333.32642-1-bonzini@gnu.org","threadId":"46494","inReplyTo":null,"subject":"[PATCH v4 0/4] interpret-trailers: add --where, --if-exists, --if-missing","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-01T09:03:29Z","receivedAt":"2017-08-01T09:03:43Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"From: Paolo Bonzini <pbonzini@redhat.com>\n\nThese options are useful to experiment with \"git interpret-trailers\"\nwithout having to tinker with .gitconfig (Junio said git should ahve\ndone this first and only added configuration afterwards).  It can\nbe useful in the case where you want a different placement for the trailer,\nor for scripts/aliases that don't want to rely on specific .gitconfig\nsettings.\n\nCompared to v2, the main change is that option order on the command-line\nis respected.  That is,\n\n\t--trailer 'acked-by: foo' --where end --trailer 'signed-off-by: me'\n\nwill only apply where=end to the second trailer.  Likewise,\n\n\t--where end --trailer 'signed-off-by: me' --no-where \\\n\t--trailer 'acked-by: foo'\n\nwill only apply it to the first, reverting to trailer.*.where for the\n\"acked-by\" trailer.\n\nPaolo\n\nv1->v2: support --no-* options, minor code fixes\n\nv2->v3: largely rewritten to respect option order on the command-line;\n\tkeep trailer.h namespace clean (Christian)\n\nv3->v4: fix compilation warnings (Junio), added documentation fix\n\nPaolo Bonzini (4):\n  trailers: export action enums and corresponding lookup functions\n  trailers: introduce struct new_trailer_item\n  interpret-trailers: add options for actions\n  interpret-trailers: fix documentation typo\n\n Documentation/git-interpret-trailers.txt |  27 ++++++-\n builtin/interpret-trailers.c             |  73 +++++++++++++++++--\n t/t7513-interpret-trailers.sh            |  66 +++++++++++++++++\n trailer.c                                | 118 +++++++++++++++++++------------\n trailer.h                                |  43 ++++++++++-\n 5 files changed, 274 insertions(+), 53 deletions(-)\n\n-- \n2.13.3\n\n"},{"id":"325367","messageId":"20170801090333.32642-2-bonzini@gnu.org","threadId":"46494","inReplyTo":"20170801090333.32642-1-bonzini@gnu.org","subject":"[PATCH v4 1/4] trailers: export action enums and corresponding lookup functions","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-01T09:03:30Z","receivedAt":"2017-08-01T09:03:46Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"From: Paolo Bonzini <pbonzini@redhat.com>\n\nSeparate the mechanical changes out of the next patch.  The functions\nare changed to take a pointer to enum, because struct conf_info is not\ngoing to be public.\n\nSet the default values explicitly in default_conf_info, since they are\nnot anymore close to default_conf_info and it's not obvious which\nconstant has value 0.  With the next patches, in fact, the values will\nnot be zero anymore!\n\nSigned-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n trailer.c | 65 ++++++++++++++++++++++++++++++++-------------------------------\n trailer.h | 22 +++++++++++++++++++++\n 2 files changed, 55 insertions(+), 32 deletions(-)\n\ndiff --git a/trailer.c b/trailer.c\nindex 751b56c00..f02895373 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -10,18 +10,13 @@\n  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n  */\n \n-enum action_where { WHERE_END, WHERE_AFTER, WHERE_BEFORE, WHERE_START };\n-enum action_if_exists { EXISTS_ADD_IF_DIFFERENT_NEIGHBOR, EXISTS_ADD_IF_DIFFERENT,\n-\t\t\tEXISTS_ADD, EXISTS_REPLACE, EXISTS_DO_NOTHING };\n-enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };\n-\n struct conf_info {\n \tchar *name;\n \tchar *key;\n \tchar *command;\n-\tenum action_where where;\n-\tenum action_if_exists if_exists;\n-\tenum action_if_missing if_missing;\n+\tenum trailer_where where;\n+\tenum trailer_if_exists if_exists;\n+\tenum trailer_if_missing if_missing;\n };\n \n static struct conf_info default_conf_info;\n@@ -63,7 +58,7 @@ static const char *git_generated_prefixes[] = {\n \t\tpos != (head); \\\n \t\tpos = is_reverse ? pos->prev : pos->next)\n \n-static int after_or_end(enum action_where where)\n+static int after_or_end(enum trailer_where where)\n {\n \treturn (where == WHERE_AFTER) || (where == WHERE_END);\n }\n@@ -201,7 +196,7 @@ static int check_if_different(struct trailer_item *in_tok,\n \t\t\t      int check_all,\n \t\t\t      struct list_head *head)\n {\n-\tenum action_where where = arg_tok->conf.where;\n+\tenum trailer_where where = arg_tok->conf.where;\n \tstruct list_head *next_head;\n \tdo {\n \t\tif (same_trailer(in_tok, arg_tok))\n@@ -306,7 +301,7 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,\n static void apply_arg_if_missing(struct list_head *head,\n \t\t\t\t struct arg_item *arg_tok)\n {\n-\tenum action_where where;\n+\tenum trailer_where where;\n \tstruct trailer_item *to_add;\n \n \tswitch (arg_tok->conf.if_missing) {\n@@ -331,7 +326,7 @@ static int find_same_and_apply_arg(struct list_head *head,\n \tstruct trailer_item *in_tok;\n \tstruct trailer_item *on_tok;\n \n-\tenum action_where where = arg_tok->conf.where;\n+\tenum trailer_where where = arg_tok->conf.where;\n \tint middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);\n \tint backwards = after_or_end(where);\n \tstruct trailer_item *start_tok;\n@@ -373,44 +368,44 @@ static void process_trailers_lists(struct list_head *head,\n \t}\n }\n \n-static int set_where(struct conf_info *item, const char *value)\n+int trailer_set_where(enum trailer_where *item, const char *value)\n {\n \tif (!strcasecmp(\"after\", value))\n-\t\titem->where = WHERE_AFTER;\n+\t\t*item = WHERE_AFTER;\n \telse if (!strcasecmp(\"before\", value))\n-\t\titem->where = WHERE_BEFORE;\n+\t\t*item = WHERE_BEFORE;\n \telse if (!strcasecmp(\"end\", value))\n-\t\titem->where = WHERE_END;\n+\t\t*item = WHERE_END;\n \telse if (!strcasecmp(\"start\", value))\n-\t\titem->where = WHERE_START;\n+\t\t*item = WHERE_START;\n \telse\n \t\treturn -1;\n \treturn 0;\n }\n \n-static int set_if_exists(struct conf_info *item, const char *value)\n+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)\n {\n \tif (!strcasecmp(\"addIfDifferent\", value))\n-\t\titem->if_exists = EXISTS_ADD_IF_DIFFERENT;\n+\t\t*item = EXISTS_ADD_IF_DIFFERENT;\n \telse if (!strcasecmp(\"addIfDifferentNeighbor\", value))\n-\t\titem->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;\n+\t\t*item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;\n \telse if (!strcasecmp(\"add\", value))\n-\t\titem->if_exists = EXISTS_ADD;\n+\t\t*item = EXISTS_ADD;\n \telse if (!strcasecmp(\"replace\", value))\n-\t\titem->if_exists = EXISTS_REPLACE;\n+\t\t*item = EXISTS_REPLACE;\n \telse if (!strcasecmp(\"doNothing\", value))\n-\t\titem->if_exists = EXISTS_DO_NOTHING;\n+\t\t*item = EXISTS_DO_NOTHING;\n \telse\n \t\treturn -1;\n \treturn 0;\n }\n \n-static int set_if_missing(struct conf_info *item, const char *value)\n+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)\n {\n \tif (!strcasecmp(\"doNothing\", value))\n-\t\titem->if_missing = MISSING_DO_NOTHING;\n+\t\t*item = MISSING_DO_NOTHING;\n \telse if (!strcasecmp(\"add\", value))\n-\t\titem->if_missing = MISSING_ADD;\n+\t\t*item = MISSING_ADD;\n \telse\n \t\treturn -1;\n \treturn 0;\n@@ -470,15 +465,18 @@ static int git_trailer_default_config(const char *conf_key, const char *value, v\n \tvariable_name = strrchr(trailer_item, '.');\n \tif (!variable_name) {\n \t\tif (!strcmp(trailer_item, \"where\")) {\n-\t\t\tif (set_where(&default_conf_info, value) < 0)\n+\t\t\tif (trailer_set_where(&default_conf_info.where,\n+\t\t\t\t\t      value) < 0)\n \t\t\t\twarning(_(\"unknown value '%s' for key '%s'\"),\n \t\t\t\t\tvalue, conf_key);\n \t\t} else if (!strcmp(trailer_item, \"ifexists\")) {\n-\t\t\tif (set_if_exists(&default_conf_info, value) < 0)\n+\t\t\tif (trailer_set_if_exists(&default_conf_info.if_exists,\n+\t\t\t\t\t\t  value) < 0)\n \t\t\t\twarning(_(\"unknown value '%s' for key '%s'\"),\n \t\t\t\t\tvalue, conf_key);\n \t\t} else if (!strcmp(trailer_item, \"ifmissing\")) {\n-\t\t\tif (set_if_missing(&default_conf_info, value) < 0)\n+\t\t\tif (trailer_set_if_missing(&default_conf_info.if_missing,\n+\t\t\t\t\t\t   value) < 0)\n \t\t\t\twarning(_(\"unknown value '%s' for key '%s'\"),\n \t\t\t\t\tvalue, conf_key);\n \t\t} else if (!strcmp(trailer_item, \"separators\")) {\n@@ -532,15 +530,15 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\tconf->command = xstrdup(value);\n \t\tbreak;\n \tcase TRAILER_WHERE:\n-\t\tif (set_where(conf, value))\n+\t\tif (trailer_set_where(&conf->where, value))\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tcase TRAILER_IF_EXISTS:\n-\t\tif (set_if_exists(conf, value))\n+\t\tif (trailer_set_if_exists(&conf->if_exists, value))\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tcase TRAILER_IF_MISSING:\n-\t\tif (set_if_missing(conf, value))\n+\t\tif (trailer_set_if_missing(&conf->if_missing, value))\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n@@ -555,6 +553,9 @@ static void ensure_configured(void)\n \t\treturn;\n \n \t/* Default config must be setup first */\n+\tdefault_conf_info.where = WHERE_END;\n+\tdefault_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;\n+\tdefault_conf_info.if_missing = MISSING_ADD;\n \tgit_config(git_trailer_default_config, NULL);\n \tgit_config(git_trailer_config, NULL);\n \tconfigured = 1;\ndiff --git a/trailer.h b/trailer.h\nindex 65cc5d79c..2b39a1bee 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -1,6 +1,28 @@\n #ifndef TRAILER_H\n #define TRAILER_H\n \n+enum trailer_where {\n+\tWHERE_END,\n+\tWHERE_AFTER,\n+\tWHERE_BEFORE,\n+\tWHERE_START\n+};\n+enum trailer_if_exists {\n+\tEXISTS_ADD_IF_DIFFERENT_NEIGHBOR,\n+\tEXISTS_ADD_IF_DIFFERENT,\n+\tEXISTS_ADD,\n+\tEXISTS_REPLACE,\n+\tEXISTS_DO_NOTHING\n+};\n+enum trailer_if_missing {\n+\tMISSING_ADD,\n+\tMISSING_DO_NOTHING\n+};\n+\n+int trailer_set_where(enum trailer_where *item, const char *value);\n+int trailer_set_if_exists(enum trailer_if_exists *item, const char *value);\n+int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);\n+\n struct trailer_info {\n \t/*\n \t * True if there is a blank line before the location pointed to by\n-- \n2.13.3\n\n\n"},{"id":"325368","messageId":"20170801090333.32642-4-bonzini@gnu.org","threadId":"46494","inReplyTo":"20170801090333.32642-1-bonzini@gnu.org","subject":"[PATCH v4 3/4] interpret-trailers: add options for actions","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-01T09:03:32Z","receivedAt":"2017-08-01T09:03:49Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"From: Paolo Bonzini <pbonzini@redhat.com>\n\nAllow using non-default values for trailers without having to set\nthem up in .gitconfig first.  For example, if you have the following\nconfiguration\n\n     trailer.signed-off-by.where = end\n\nyou may use \"--where before\" when a patch author forgets his\nSigned-off-by and provides it in a separate email.  Likewise for\n--if-exists and --if-missing\n\nReverting to the behavior specified by .gitconfig is done with\n--no-where, --no-if-exists and --no-if-missing.\n\nSigned-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n Documentation/git-interpret-trailers.txt | 23 +++++++++++\n builtin/interpret-trailers.c             | 32 ++++++++++++++++\n t/t7513-interpret-trailers.sh            | 66 ++++++++++++++++++++++++++++++++\n trailer.c                                | 34 +++++++++++++---\n trailer.h                                |  7 ++++\n 5 files changed, 156 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt\nindex 31cdeaecd..76d5fdfaf 100644\n--- a/Documentation/git-interpret-trailers.txt\n+++ b/Documentation/git-interpret-trailers.txt\n@@ -80,6 +80,29 @@ OPTIONS\n \ttrailer to the input messages. See the description of this\n \tcommand.\n \n+--where <placement>::\n+--no-where::\n+\tSpecify where all new trailers will be added.  A setting\n+\tprovided with '--where' overrides all configuration variables\n+\tand applies to all '--trailer' options until the next occurrence of\n+\t'--where' or '--no-where'.\n+\n+--if-exists <action>::\n+--no-if-exists::\n+\tSpecify what action will be performed when there is already at\n+\tleast one trailer with the same <token> in the message.  A setting\n+\tprovided with '--if-exists' overrides all configuration variables\n+\tand applies to all '--trailer' options until the next occurrence of\n+\t'--if-exists' or '--no-if-exists'.\n+\n+--if-missing <action>::\n+--no-if-missing::\n+\tSpecify what action will be performed when there is no other\n+\ttrailer with the same <token> in the message.  A setting\n+\tprovided with '--if-missing' overrides all configuration variables\n+\tand applies to all '--trailer' options until the next occurrence of\n+\t'--if-missing' or '--no-if-missing'.\n+\n CONFIGURATION VARIABLES\n -----------------------\n \ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 8f38fa318..83249e3eb 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -16,6 +16,28 @@ static const char * const git_interpret_trailers_usage[] = {\n \tNULL\n };\n \n+static enum trailer_where where;\n+static enum trailer_if_exists if_exists;\n+static enum trailer_if_missing if_missing;\n+\n+static int option_parse_where(const struct option *opt,\n+\t\t\t      const char *arg, int unset)\n+{\n+\treturn trailer_set_where(&where, arg);\n+}\n+\n+static int option_parse_if_exists(const struct option *opt,\n+\t\t\t\t  const char *arg, int unset)\n+{\n+\treturn trailer_set_if_exists(&if_exists, arg);\n+}\n+\n+static int option_parse_if_missing(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\treturn trailer_set_if_missing(&if_missing, arg);\n+}\n+\n static void new_trailers_clear(struct list_head *trailers)\n {\n \tstruct list_head *pos, *tmp;\n@@ -44,6 +66,9 @@ static int option_parse_trailer(const struct option *opt,\n \n \titem = xmalloc(sizeof *item);\n \titem->text = arg;\n+\titem->where = where;\n+\titem->if_exists = if_exists;\n+\titem->if_missing = if_missing;\n \tlist_add_tail(&item->list, trailers);\n \treturn 0;\n }\n@@ -58,6 +83,13 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"in-place\", &in_place, N_(\"edit files in place\")),\n \t\tOPT_BOOL(0, \"trim-empty\", &trim_empty, N_(\"trim empty trailers\")),\n \n+\t\tOPT_CALLBACK(0, \"where\", NULL, N_(\"action\"),\n+\t\t\t     N_(\"where to place the new trailer\"), option_parse_where),\n+\t\tOPT_CALLBACK(0, \"if-exists\", NULL, N_(\"action\"),\n+\t\t\t     N_(\"action if trailer already exists\"), option_parse_if_exists),\n+\t\tOPT_CALLBACK(0, \"if-missing\", NULL, N_(\"action\"),\n+\t\t\t     N_(\"action if trailer is missing\"), option_parse_if_missing),\n+\n \t\tOPT_CALLBACK(0, \"trailer\", &trailers, N_(\"trailer\"),\n \t\t\t\tN_(\"trailer(s) to add\"), option_parse_trailer),\n \t\tOPT_END()\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 0c6f91c43..adbdf54f8 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -681,6 +681,36 @@ test_expect_success 'using \"where = before\"' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'overriding configuration with \"--where after\"' '\n+\tgit config trailer.ack.where \"before\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers --where after --trailer \"ack: Peff\" \\\n+\t\tcomplex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"where = before\" with \"--no-where\"' '\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tBug #42\n+\t\tFixes: Z\n+\t\tAcked-by= Peff\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers --where after --no-where --trailer \"ack: Peff\" \\\n+\t\t--trailer \"bug: 42\" complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'using \"where = after\"' '\n \tgit config trailer.ack.where \"after\" &&\n \tcat complex_message_body >expected &&\n@@ -947,6 +977,23 @@ test_expect_success 'using \"ifExists = add\" with \"where = after\"' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'overriding configuration with \"--if-exists replace\"' '\n+\tgit config trailer.fix.key \"Fixes: \" &&\n+\tgit config trailer.fix.ifExists \"add\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tBug #42\n+\t\tAcked-by= Z\n+\t\tReviewed-by:\n+\t\tSigned-off-by: Z\n+\t\tFixes: 22\n+\tEOF\n+\tgit interpret-trailers --if-exists replace --trailer \"review:\" \\\n+\t\t--trailer \"fix=53\" --trailer \"fix=22\" --trailer \"bug: 42\" \\\n+\t\t<complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'using \"ifExists = replace\"' '\n \tgit config trailer.fix.key \"Fixes: \" &&\n \tgit config trailer.fix.ifExists \"replace\" &&\n@@ -1026,6 +1073,25 @@ test_expect_success 'the default is \"ifMissing = add\"' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'overriding configuration with \"--if-missing doNothing\"' '\n+\tgit config trailer.ifmissing \"add\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by:\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers --if-missing doNothing \\\n+\t\t--trailer \"review:\" --trailer \"fix=53\" \\\n+\t\t--trailer \"cc=Linus\" --trailer \"ack: Junio\" \\\n+\t\t--trailer \"fix=22\" --trailer \"bug: 42\" --trailer \"ack: Peff\" \\\n+\t\t<complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'when default \"ifMissing\" is \"doNothing\"' '\n \tgit config trailer.ifmissing \"doNothing\" &&\n \tcat complex_message_body >expected &&\ndiff --git a/trailer.c b/trailer.c\nindex 6941da799..d441cd9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -295,6 +295,9 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,\n \t\telse\n \t\t\tfree_arg_item(arg_tok);\n \t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: trailer.c: unhandled value %d\",\n+\t\t    arg_tok->conf.if_exists);\n \t}\n }\n \n@@ -316,6 +319,10 @@ static void apply_arg_if_missing(struct list_head *head,\n \t\t\tlist_add_tail(&to_add->list, head);\n \t\telse\n \t\t\tlist_add(&to_add->list, head);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: trailer.c: unhandled value %d\",\n+\t\t    arg_tok->conf.if_missing);\n \t}\n }\n \n@@ -370,7 +377,9 @@ static void process_trailers_lists(struct list_head *head,\n \n int trailer_set_where(enum trailer_where *item, const char *value)\n {\n-\tif (!strcasecmp(\"after\", value))\n+\tif (!value)\n+\t\t*item = WHERE_DEFAULT;\n+\telse if (!strcasecmp(\"after\", value))\n \t\t*item = WHERE_AFTER;\n \telse if (!strcasecmp(\"before\", value))\n \t\t*item = WHERE_BEFORE;\n@@ -385,7 +394,9 @@ int trailer_set_where(enum trailer_where *item, const char *value)\n \n int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)\n {\n-\tif (!strcasecmp(\"addIfDifferent\", value))\n+\tif (!value)\n+\t\t*item = EXISTS_DEFAULT;\n+\telse if (!strcasecmp(\"addIfDifferent\", value))\n \t\t*item = EXISTS_ADD_IF_DIFFERENT;\n \telse if (!strcasecmp(\"addIfDifferentNeighbor\", value))\n \t\t*item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;\n@@ -402,7 +413,9 @@ int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)\n \n int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)\n {\n-\tif (!strcasecmp(\"doNothing\", value))\n+\tif (!value)\n+\t\t*item = MISSING_DEFAULT;\n+\telse if (!strcasecmp(\"doNothing\", value))\n \t\t*item = MISSING_DO_NOTHING;\n \telse if (!strcasecmp(\"add\", value))\n \t\t*item = MISSING_ADD;\n@@ -659,12 +672,21 @@ static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,\n }\n \n static void add_arg_item(struct list_head *arg_head, char *tok, char *val,\n-\t\t\t const struct conf_info *conf)\n+\t\t\t const struct conf_info *conf,\n+\t\t\t const struct new_trailer_item *new_trailer_item)\n {\n \tstruct arg_item *new = xcalloc(sizeof(*new), 1);\n \tnew->token = tok;\n \tnew->value = val;\n \tduplicate_conf(&new->conf, conf);\n+\tif (new_trailer_item) {\n+\t\tif (new_trailer_item->where != WHERE_DEFAULT)\n+\t\t\tnew->conf.where = new_trailer_item->where;\n+\t\tif (new_trailer_item->if_exists != EXISTS_DEFAULT)\n+\t\t\tnew->conf.if_exists = new_trailer_item->if_exists;\n+\t\tif (new_trailer_item->if_missing != MISSING_DEFAULT)\n+\t\t\tnew->conf.if_missing = new_trailer_item->if_missing;\n+\t}\n \tlist_add_tail(&new->list, arg_head);\n }\n \n@@ -690,7 +712,7 @@ static void process_command_line_args(struct list_head *arg_head,\n \t\t\tadd_arg_item(arg_head,\n \t\t\t\t     xstrdup(token_from_item(item, NULL)),\n \t\t\t\t     xstrdup(\"\"),\n-\t\t\t\t     &item->conf);\n+\t\t\t\t     &item->conf, NULL);\n \t}\n \n \t/* Add an arg item for each trailer on the command line */\n@@ -712,7 +734,7 @@ static void process_command_line_args(struct list_head *arg_head,\n \t\t\tadd_arg_item(arg_head,\n \t\t\t\t     strbuf_detach(&tok, NULL),\n \t\t\t\t     strbuf_detach(&val, NULL),\n-\t\t\t\t     conf);\n+\t\t\t\t     conf, tr);\n \t\t}\n \t}\n \ndiff --git a/trailer.h b/trailer.h\nindex b83b249b6..973b533a1 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -4,12 +4,14 @@\n #include \"list.h\"\n \n enum trailer_where {\n+\tWHERE_DEFAULT,\n \tWHERE_END,\n \tWHERE_AFTER,\n \tWHERE_BEFORE,\n \tWHERE_START\n };\n enum trailer_if_exists {\n+\tEXISTS_DEFAULT,\n \tEXISTS_ADD_IF_DIFFERENT_NEIGHBOR,\n \tEXISTS_ADD_IF_DIFFERENT,\n \tEXISTS_ADD,\n@@ -17,6 +19,7 @@ enum trailer_if_exists {\n \tEXISTS_DO_NOTHING\n };\n enum trailer_if_missing {\n+\tMISSING_DEFAULT,\n \tMISSING_ADD,\n \tMISSING_DO_NOTHING\n };\n@@ -54,6 +57,10 @@ struct new_trailer_item {\n \tstruct list_head list;\n \n \tconst char *text;\n+\n+\tenum trailer_where where;\n+\tenum trailer_if_exists if_exists;\n+\tenum trailer_if_missing if_missing;\n };\n \n void process_trailers(const char *file, int in_place, int trim_empty,\n-- \n2.13.3\n\n\n"},{"id":"325369","messageId":"20170801090333.32642-3-bonzini@gnu.org","threadId":"46494","inReplyTo":"20170801090333.32642-1-bonzini@gnu.org","subject":"[PATCH v4 2/4] trailers: introduce struct new_trailer_item","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-01T09:03:31Z","receivedAt":"2017-08-01T09:03:52Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"From: Paolo Bonzini <pbonzini@redhat.com>\n\nThis will provide a place to store the current state of the\n--where, --if-exists and --if-missing options.\n\nSigned-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n builtin/interpret-trailers.c | 41 +++++++++++++++++++++++++++++++++++++----\n trailer.c                    | 19 +++++++++++--------\n trailer.h                    | 14 +++++++++++++-\n 3 files changed, 61 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nindex 175f14797..8f38fa318 100644\n--- a/builtin/interpret-trailers.c\n+++ b/builtin/interpret-trailers.c\n@@ -16,17 +16,50 @@ static const char * const git_interpret_trailers_usage[] = {\n \tNULL\n };\n \n+static void new_trailers_clear(struct list_head *trailers)\n+{\n+\tstruct list_head *pos, *tmp;\n+\tstruct new_trailer_item *item;\n+\n+\tlist_for_each_safe(pos, tmp, trailers) {\n+\t\titem = list_entry(pos, struct new_trailer_item, list);\n+\t\tlist_del(pos);\n+\t\tfree(item);\n+\t}\n+}\n+\n+static int option_parse_trailer(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\tstruct list_head *trailers = opt->value;\n+\tstruct new_trailer_item *item;\n+\n+\tif (unset) {\n+\t\tnew_trailers_clear(trailers);\n+\t\treturn 0;\n+\t}\n+\n+\tif (!arg)\n+\t\treturn -1;\n+\n+\titem = xmalloc(sizeof *item);\n+\titem->text = arg;\n+\tlist_add_tail(&item->list, trailers);\n+\treturn 0;\n+}\n+\n int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)\n {\n \tint in_place = 0;\n \tint trim_empty = 0;\n-\tstruct string_list trailers = STRING_LIST_INIT_NODUP;\n+\tLIST_HEAD(trailers);\n \n \tstruct option options[] = {\n \t\tOPT_BOOL(0, \"in-place\", &in_place, N_(\"edit files in place\")),\n \t\tOPT_BOOL(0, \"trim-empty\", &trim_empty, N_(\"trim empty trailers\")),\n-\t\tOPT_STRING_LIST(0, \"trailer\", &trailers, N_(\"trailer\"),\n-\t\t\t\tN_(\"trailer(s) to add\")),\n+\n+\t\tOPT_CALLBACK(0, \"trailer\", &trailers, N_(\"trailer\"),\n+\t\t\t\tN_(\"trailer(s) to add\"), option_parse_trailer),\n \t\tOPT_END()\n \t};\n \n@@ -43,7 +76,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)\n \t\tprocess_trailers(NULL, in_place, trim_empty, &trailers);\n \t}\n \n-\tstring_list_clear(&trailers, 0);\n+\tnew_trailers_clear(&trailers);\n \n \treturn 0;\n }\ndiff --git a/trailer.c b/trailer.c\nindex f02895373..6941da799 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -669,9 +669,8 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,\n }\n \n static void process_command_line_args(struct list_head *arg_head,\n-\t\t\t\t      struct string_list *trailers)\n+\t\t\t\t      struct list_head *new_trailer_head)\n {\n-\tstruct string_list_item *tr;\n \tstruct arg_item *item;\n \tstruct strbuf tok = STRBUF_INIT;\n \tstruct strbuf val = STRBUF_INIT;\n@@ -695,17 +694,20 @@ static void process_command_line_args(struct list_head *arg_head,\n \t}\n \n \t/* Add an arg item for each trailer on the command line */\n-\tfor_each_string_list_item(tr, trailers) {\n-\t\tint separator_pos = find_separator(tr->string, cl_separators);\n+\tlist_for_each(pos, new_trailer_head) {\n+\t\tstruct new_trailer_item *tr =\n+\t\t\tlist_entry(pos, struct new_trailer_item, list);\n+\t\tint separator_pos = find_separator(tr->text, cl_separators);\n+\n \t\tif (separator_pos == 0) {\n \t\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\t\tstrbuf_addstr(&sb, tr->string);\n+\t\t\tstrbuf_addstr(&sb, tr->text);\n \t\t\tstrbuf_trim(&sb);\n \t\t\terror(_(\"empty trailer token in trailer '%.*s'\"),\n \t\t\t      (int) sb.len, sb.buf);\n \t\t\tstrbuf_release(&sb);\n \t\t} else {\n-\t\t\tparse_trailer(&tok, &val, &conf, tr->string,\n+\t\t\tparse_trailer(&tok, &val, &conf, tr->text,\n \t\t\t\t      separator_pos);\n \t\t\tadd_arg_item(arg_head,\n \t\t\t\t     strbuf_detach(&tok, NULL),\n@@ -969,7 +971,8 @@ static FILE *create_in_place_tempfile(const char *file)\n \treturn outfile;\n }\n \n-void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)\n+void process_trailers(const char *file, int in_place, int trim_empty,\n+\t\t      struct list_head *new_trailer_head)\n {\n \tLIST_HEAD(head);\n \tLIST_HEAD(arg_head);\n@@ -987,7 +990,7 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str\n \t/* Print the lines before the trailers */\n \ttrailer_end = process_input_file(outfile, sb.buf, &head);\n \n-\tprocess_command_line_args(&arg_head, trailers);\n+\tprocess_command_line_args(&arg_head, new_trailer_head);\n \n \tprocess_trailers_lists(&head, &arg_head);\n \ndiff --git a/trailer.h b/trailer.h\nindex 2b39a1bee..b83b249b6 100644\n--- a/trailer.h\n+++ b/trailer.h\n@@ -1,6 +1,8 @@\n #ifndef TRAILER_H\n #define TRAILER_H\n \n+#include \"list.h\"\n+\n enum trailer_where {\n \tWHERE_END,\n \tWHERE_AFTER,\n@@ -44,8 +46,18 @@ struct trailer_info {\n \tsize_t trailer_nr;\n };\n \n+/*\n+ * A list that represents newly-added trailers, such as those provided\n+ * with the --trailer command line option of git-interpret-trailers.\n+ */\n+struct new_trailer_item {\n+\tstruct list_head list;\n+\n+\tconst char *text;\n+};\n+\n void process_trailers(const char *file, int in_place, int trim_empty,\n-\t\t      struct string_list *trailers);\n+\t\t      struct list_head *new_trailer_head);\n \n void trailer_info_get(struct trailer_info *info, const char *str);\n \n-- \n2.13.3\n\n\n"},{"id":"325370","messageId":"20170801090333.32642-5-bonzini@gnu.org","threadId":"46494","inReplyTo":"20170801090333.32642-1-bonzini@gnu.org","subject":"[PATCH v4 4/4] interpret-trailers: fix documentation typo","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-01T09:03:33Z","receivedAt":"2017-08-01T09:03:55Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"From: Paolo Bonzini <pbonzini@redhat.com>\n\nSelf-explanatory... trailer.ifexists is documented with the\nright name, but after a while it switches to ifexist.\n\nSigned-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n Documentation/git-interpret-trailers.txt | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt\nindex 76d5fdfaf..0ef93204f 100644\n--- a/Documentation/git-interpret-trailers.txt\n+++ b/Documentation/git-interpret-trailers.txt\n@@ -193,8 +193,8 @@ trailer.<token>.where::\n \tconfiguration variable and it overrides what is specified by\n \tthat option for trailers with the specified <token>.\n \n-trailer.<token>.ifexist::\n-\tThis option takes the same values as the 'trailer.ifexist'\n+trailer.<token>.ifexists::\n+\tThis option takes the same values as the 'trailer.ifexists'\n \tconfiguration variable and it overrides what is specified by\n \tthat option for trailers with the specified <token>.\n \n-- \n2.13.3\n\n"},{"id":"326267","messageId":"0ac82014-4da9-9ef2-5da0-07996c8aa8fd@gnu.org","threadId":"46494","inReplyTo":"20170801090333.32642-1-bonzini@gnu.org","subject":"Re: [PATCH v4 0/4] interpret-trailers: add --where, --if-exists, --if-missing","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2017-08-14T09:26:29Z","receivedAt":"2017-08-14T09:26:38Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"On 01/08/2017 11:03, Paolo Bonzini wrote:\n> From: Paolo Bonzini <pbonzini@redhat.com>\n> \n> These options are useful to experiment with \"git interpret-trailers\"\n> without having to tinker with .gitconfig (Junio said git should ahve\n> done this first and only added configuration afterwards).  It can\n> be useful in the case where you want a different placement for the trailer,\n> or for scripts/aliases that don't want to rely on specific .gitconfig\n> settings.\n> \n> Compared to v2, the main change is that option order on the command-line\n> is respected.  That is,\n> \n> \t--trailer 'acked-by: foo' --where end --trailer 'signed-off-by: me'\n> \n> will only apply where=end to the second trailer.  Likewise,\n> \n> \t--where end --trailer 'signed-off-by: me' --no-where \\\n> \t--trailer 'acked-by: foo'\n> \n> will only apply it to the first, reverting to trailer.*.where for the\n> \"acked-by\" trailer.\n\nJunio, I see you haven't yet applied this v4 to origin/pu, did you miss it?\n\nThanks,\n\nPaolo\n\n> Paolo\n> \n> v1->v2: support --no-* options, minor code fixes\n> \n> v2->v3: largely rewritten to respect option order on the command-line;\n> \tkeep trailer.h namespace clean (Christian)\n> \n> v3->v4: fix compilation warnings (Junio), added documentation fix\n> \n> Paolo Bonzini (4):\n>   trailers: export action enums and corresponding lookup functions\n>   trailers: introduce struct new_trailer_item\n>   interpret-trailers: add options for actions\n>   interpret-trailers: fix documentation typo\n> \n>  Documentation/git-interpret-trailers.txt |  27 ++++++-\n>  builtin/interpret-trailers.c             |  73 +++++++++++++++++--\n>  t/t7513-interpret-trailers.sh            |  66 +++++++++++++++++\n>  trailer.c                                | 118 +++++++++++++++++++------------\n>  trailer.h                                |  43 ++++++++++-\n>  5 files changed, 274 insertions(+), 53 deletions(-)\n> \n\n"},{"id":"326290","messageId":"xmqq1soevxq4.fsf@gitster.mtv.corp.google.com","threadId":"46494","inReplyTo":"0ac82014-4da9-9ef2-5da0-07996c8aa8fd@gnu.org","subject":"Re: [PATCH v4 0/4] interpret-trailers: add --where, --if-exists, --if-missing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-08-14T17:57:23Z","receivedAt":"2017-08-14T17:57:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n> On 01/08/2017 11:03, Paolo Bonzini wrote:\n>> From: Paolo Bonzini <pbonzini@redhat.com>\n>> \n>> These options are useful to experiment with \"git interpret-trailers\"\n>> without having to tinker with .gitconfig (Junio said git should ahve\n>> done this first and only added configuration afterwards).  It can\n>> be useful in the case where you want a different placement for the trailer,\n>> or for scripts/aliases that don't want to rely on specific .gitconfig\n>> settings.\n>> \n>> Compared to v2, the main change is that option order on the command-line\n>> is respected.  That is,\n>> \n>> \t--trailer 'acked-by: foo' --where end --trailer 'signed-off-by: me'\n>> \n>> will only apply where=end to the second trailer.  Likewise,\n>> \n>> \t--where end --trailer 'signed-off-by: me' --no-where \\\n>> \t--trailer 'acked-by: foo'\n>> \n>> will only apply it to the first, reverting to trailer.*.where for the\n>> \"acked-by\" trailer.\n>\n> Junio, I see you haven't yet applied this v4 to origin/pu, did you miss it?\n\nThanks for pinging.  Either it was not noticed by mistake or was\ndeliberately ignored during the pre-release freeze, I do not\nremember.\n"}]}