{"thread":{"id":"36360","subject":"[PATCH v10 03/12] trailer: read and process config information","startedAt":"2014-04-06T17:01:51Z","lastAt":"2014-05-27T19:18:20Z","messageCount":33,"participants":["Christian Couder","Junio C Hamano","Michael Haggerty","Jeremy Morton","Johan Herland"],"isPatch":true,"patchVersion":10,"patchTotal":12},"messages":[{"id":"238481","messageId":"20140406163214.15116.91484.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":null,"subject":"[PATCH v10 00/12] Add interpret-trailers builtin","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:51Z","receivedAt":"2014-04-06T17:01:51Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch series implements a new command:\n\n        git interpret-trailers\n\nand an infrastructure to process trailers that can be reused,\nfor example in \"commit.c\".\n\n1) Rationale:\n\nThis command should help with RFC 822 style headers, called\n\"trailers\", that are found at the end of commit messages.\n\n(Note that these headers do not follow and are not intended to\nfollow many rules that are in RFC 822. For example they do not\nfollow the line breaking rules, the encoding rules and probably\nmany other rules.)\n\nFor a long time, these trailers have become a de facto standard\nway to add helpful information into commit messages.\n\nUntil now git commit has only supported the well known\n\"Signed-off-by: \" trailer, that is used by many projects like\nthe Linux kernel and Git.\n\nIt is better to keep builtin/commit.c uncontaminated by any more\nhard-wired logic, like what we have for the signed-off-by line.  Any\nnew things can and should be doable in hooks, and this filter would\nhelp writing these hooks.\n\nAnd that is why the design goal of the filter is to make it at least\nas powerful as the built-in logic we have for signed-off-by lines;\nthat would allow us to later eject the hard-wired logic for\nsigned-off-by line from the main codepath, if/when we wanted to.\n\nAlternatively, we could build a library-ish API around this filter\ncode and replace the hard-wired logic for signed-off-by line with a\ncall into that API, if/when we wanted to, but that requires (in\naddition to the \"at least as powerful as the built-in logic\") that\nthe implementation of this stand-alone filter can be cleanly made\ninto a reusable library, so that is a bit higher bar to cross than\n\"everything can be doable with hooks\" alternative.\n\n2) Current state:\n\nCurrently the usage string of this command is:\n\ngit interpret-trailers [--trim-empty] [(<token>[(=|:)<value>])...]\n\nThe following features are implemented:\n\n        - the result is printed on stdout\n        - the [<token>[=<value>]>] arguments are interpreted\n        - a commit message read from stdin is interpreted\n        - the \"trailer.<token>.key\" options in the config are interpreted\n        - the \"trailer.<token>.where\" options are interpreted\n        - the \"trailer.<token>.ifExist\" options are interpreted\n        - the \"trailer.<token>.ifMissing\" options are interpreted\n        - the \"trailer.<token>.command\" config works\n        - $ARG can be used in commands\n        - there are 31 tests (4 more than in version 9)\n        - there is some documentation\n\nThe following features are planned but not yet implemented:\n        - add examples in documentation\n\nPossible improvements:\n        - integration with \"git commit\"\n        - support GIT_COMMIT_PROTO env variable in commands\n\n3) Changes since version 9, thanks to Jonathan and Junio:\n\n* added 1 test with empty trailers in patch 10/12\n* fixed bugs when there was no 'key' in the config in patch\n  4/12 and added 2 related tests in patch 10/12\n* fixed bug when command failed in patch 9/12 and added 1\n  related test in patch 10/12\n* added patch 12/12 which add one blank line before the\n  trailers if there is not one already\n\nThis means code changes only in patches 4/12, 9/12, 10/12\nand 12/12.\n\n\nChristian Couder (12):\n  trailer: add data structures and basic functions\n  trailer: process trailers from stdin and arguments\n  trailer: read and process config information\n  trailer: process command line trailer arguments\n  trailer: parse trailers from stdin\n  trailer: put all the processing together and print\n  trailer: add interpret-trailers command\n  trailer: add tests for \"git interpret-trailers\"\n  trailer: execute command from 'trailer.<name>.command'\n  trailer: add tests for commands in config file\n  Documentation: add documentation for 'git interpret-trailers'\n  trailer: add blank line before the trailers if needed\n\n .gitignore                               |   1 +\n Documentation/git-interpret-trailers.txt | 123 ++++++\n Makefile                                 |   2 +\n builtin.h                                |   1 +\n builtin/interpret-trailers.c             |  33 ++\n git.c                                    |   1 +\n t/t7513-interpret-trailers.sh            | 477 +++++++++++++++++++++\n trailer.c                                | 709 +++++++++++++++++++++++++++++++\n trailer.h                                |   6 +\n 9 files changed, 1353 insertions(+)\n create mode 100644 Documentation/git-interpret-trailers.txt\n create mode 100644 builtin/interpret-trailers.c\n create mode 100755 t/t7513-interpret-trailers.sh\n create mode 100644 trailer.c\n create mode 100644 trailer.h\n\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238491","messageId":"20140406170204.15116.49111.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 01/12] trailer: add data structures and basic functions","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:52Z","receivedAt":"2014-04-06T17:01:52Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"We will use a doubly linked list to store all information\nabout trailers and their configuration.\n\nThis way we can easily remove or add trailers to or from\ntrailer lists while traversing the lists in either direction.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile  |  1 +\n trailer.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 50 insertions(+)\n create mode 100644 trailer.c\n\ndiff --git a/Makefile b/Makefile\nindex c5316a3..179be0a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -879,6 +879,7 @@ LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += trace.o\n+LIB_OBJS += trailer.o\n LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n LIB_OBJS += tree-diff.o\ndiff --git a/trailer.c b/trailer.c\nnew file mode 100644\nindex 0000000..db93a63\n--- /dev/null\n+++ b/trailer.c\n@@ -0,0 +1,49 @@\n+#include \"cache.h\"\n+/*\n+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n+ */\n+\n+enum action_where { WHERE_AFTER, WHERE_BEFORE };\n+enum action_if_exists { EXISTS_ADD_IF_DIFFERENT, EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,\n+\t\t\tEXISTS_ADD, EXISTS_OVERWRITE, 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+};\n+\n+struct trailer_item {\n+\tstruct trailer_item *previous;\n+\tstruct trailer_item *next;\n+\tconst char *token;\n+\tconst char *value;\n+\tstruct conf_info conf;\n+};\n+\n+static int same_token(struct trailer_item *a, struct trailer_item *b, int alnum_len)\n+{\n+\treturn !strncasecmp(a->token, b->token, alnum_len);\n+}\n+\n+static int same_value(struct trailer_item *a, struct trailer_item *b)\n+{\n+\treturn !strcasecmp(a->value, b->value);\n+}\n+\n+static int same_trailer(struct trailer_item *a, struct trailer_item *b, int alnum_len)\n+{\n+\treturn same_token(a, b, alnum_len) && same_value(a, b);\n+}\n+\n+/* Get the length of buf from its beginning until its last alphanumeric character */\n+static size_t alnum_len(const char *buf, size_t len)\n+{\n+\twhile (len > 0 && !isalnum(buf[len - 1]))\n+\t\tlen--;\n+\treturn len;\n+}\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238492","messageId":"20140406170204.15116.43642.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 02/12] trailer: process trailers from stdin and arguments","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:53Z","receivedAt":"2014-04-06T17:01:53Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Implement the logic to process trailers from stdin and arguments.\n\nAt the beginning trailers from stdin are in their own in_tok\ndoubly linked list, and trailers from arguments are in their own\narg_tok doubly linked list.\n\nThe lists are traversed and when an arg_tok should be \"applied\",\nit is removed from its list and inserted into the in_tok list.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 198 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex db93a63..52108c2 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -47,3 +47,201 @@ static size_t alnum_len(const char *buf, size_t len)\n \t\tlen--;\n \treturn len;\n }\n+\n+static void free_trailer_item(struct trailer_item *item)\n+{\n+\tfree(item->conf.name);\n+\tfree(item->conf.key);\n+\tfree(item->conf.command);\n+\tfree((char *)item->token);\n+\tfree((char *)item->value);\n+\tfree(item);\n+}\n+\n+static void add_arg_to_input_list(struct trailer_item *in_tok,\n+\t\t\t\t  struct trailer_item *arg_tok)\n+{\n+\tif (arg_tok->conf.where == WHERE_AFTER) {\n+\t\targ_tok->next = in_tok->next;\n+\t\tin_tok->next = arg_tok;\n+\t\targ_tok->previous = in_tok;\n+\t\tif (arg_tok->next)\n+\t\t\targ_tok->next->previous = arg_tok;\n+\t} else {\n+\t\targ_tok->previous = in_tok->previous;\n+\t\tin_tok->previous = arg_tok;\n+\t\targ_tok->next = in_tok;\n+\t\tif (arg_tok->previous)\n+\t\t\targ_tok->previous->next = arg_tok;\n+\t}\n+}\n+\n+static int check_if_different(struct trailer_item *in_tok,\n+\t\t\t      struct trailer_item *arg_tok,\n+\t\t\t      int alnum_len, int check_all)\n+{\n+\tenum action_where where = arg_tok->conf.where;\n+\tdo {\n+\t\tif (!in_tok)\n+\t\t\treturn 1;\n+\t\tif (same_trailer(in_tok, arg_tok, alnum_len))\n+\t\t\treturn 0;\n+\t\t/*\n+\t\t * if we want to add a trailer after another one,\n+\t\t * we have to check those before this one\n+\t\t */\n+\t\tin_tok = (where == WHERE_AFTER) ? in_tok->previous : in_tok->next;\n+\t} while (check_all);\n+\treturn 1;\n+}\n+\n+static void apply_arg_if_exists(struct trailer_item *in_tok,\n+\t\t\t\tstruct trailer_item *arg_tok,\n+\t\t\t\tint alnum_len)\n+{\n+\tswitch (arg_tok->conf.if_exists) {\n+\tcase EXISTS_DO_NOTHING:\n+\t\tfree_trailer_item(arg_tok);\n+\t\tbreak;\n+\tcase EXISTS_OVERWRITE:\n+\t\tfree((char *)in_tok->value);\n+\t\tin_tok->value = xstrdup(arg_tok->value);\n+\t\tfree_trailer_item(arg_tok);\n+\t\tbreak;\n+\tcase EXISTS_ADD:\n+\t\tadd_arg_to_input_list(in_tok, arg_tok);\n+\t\tbreak;\n+\tcase EXISTS_ADD_IF_DIFFERENT:\n+\t\tif (check_if_different(in_tok, arg_tok, alnum_len, 1))\n+\t\t\tadd_arg_to_input_list(in_tok, arg_tok);\n+\t\telse\n+\t\t\tfree_trailer_item(arg_tok);\n+\t\tbreak;\n+\tcase EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:\n+\t\tif (check_if_different(in_tok, arg_tok, alnum_len, 0))\n+\t\t\tadd_arg_to_input_list(in_tok, arg_tok);\n+\t\telse\n+\t\t\tfree_trailer_item(arg_tok);\n+\t\tbreak;\n+\t}\n+}\n+\n+static void remove_from_list(struct trailer_item *item,\n+\t\t\t     struct trailer_item **first)\n+{\n+\tif (item->next)\n+\t\titem->next->previous = item->previous;\n+\tif (item->previous)\n+\t\titem->previous->next = item->next;\n+\telse\n+\t\t*first = item->next;\n+}\n+\n+static struct trailer_item *remove_first(struct trailer_item **first)\n+{\n+\tstruct trailer_item *item = *first;\n+\t*first = item->next;\n+\tif (item->next) {\n+\t\titem->next->previous = NULL;\n+\t\titem->next = NULL;\n+\t}\n+\treturn item;\n+}\n+\n+static void process_input_token(struct trailer_item *in_tok,\n+\t\t\t\tstruct trailer_item **arg_tok_first,\n+\t\t\t\tenum action_where where)\n+{\n+\tstruct trailer_item *arg_tok;\n+\tstruct trailer_item *next_arg;\n+\n+\tint after = where == WHERE_AFTER;\n+\tint tok_alnum_len = alnum_len(in_tok->token, strlen(in_tok->token));\n+\n+\tfor (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {\n+\t\tnext_arg = arg_tok->next;\n+\t\tif (!same_token(in_tok, arg_tok, tok_alnum_len))\n+\t\t\tcontinue;\n+\t\tif (arg_tok->conf.where != where)\n+\t\t\tcontinue;\n+\t\tremove_from_list(arg_tok, arg_tok_first);\n+\t\tapply_arg_if_exists(in_tok, arg_tok, tok_alnum_len);\n+\t\t/*\n+\t\t * If arg has been added to input,\n+\t\t * then we need to process it too now.\n+\t\t */\n+\t\tif ((after ? in_tok->next : in_tok->previous) == arg_tok)\n+\t\t\tin_tok = arg_tok;\n+\t}\n+}\n+\n+static void update_last(struct trailer_item **last)\n+{\n+\tif (*last)\n+\t\twhile ((*last)->next != NULL)\n+\t\t\t*last = (*last)->next;\n+}\n+\n+static void update_first(struct trailer_item **first)\n+{\n+\tif (*first)\n+\t\twhile ((*first)->previous != NULL)\n+\t\t\t*first = (*first)->previous;\n+}\n+\n+static void apply_arg_if_missing(struct trailer_item **in_tok_first,\n+\t\t\t\t struct trailer_item **in_tok_last,\n+\t\t\t\t struct trailer_item *arg_tok)\n+{\n+\tstruct trailer_item **in_tok;\n+\tenum action_where where;\n+\n+\tswitch (arg_tok->conf.if_missing) {\n+\tcase MISSING_DO_NOTHING:\n+\t\tfree_trailer_item(arg_tok);\n+\t\tbreak;\n+\tcase MISSING_ADD:\n+\t\twhere = arg_tok->conf.where;\n+\t\tin_tok = (where == WHERE_AFTER) ? in_tok_last : in_tok_first;\n+\t\tif (*in_tok) {\n+\t\t\tadd_arg_to_input_list(*in_tok, arg_tok);\n+\t\t\t*in_tok = arg_tok;\n+\t\t} else {\n+\t\t\t*in_tok_first = arg_tok;\n+\t\t\t*in_tok_last = arg_tok;\n+\t\t}\n+\t\tbreak;\n+\t}\n+}\n+\n+static void process_trailers_lists(struct trailer_item **in_tok_first,\n+\t\t\t\t   struct trailer_item **in_tok_last,\n+\t\t\t\t   struct trailer_item **arg_tok_first)\n+{\n+\tstruct trailer_item *in_tok;\n+\tstruct trailer_item *arg_tok;\n+\n+\tif (!*arg_tok_first)\n+\t\treturn;\n+\n+\t/* Process input from end to start */\n+\tfor (in_tok = *in_tok_last; in_tok; in_tok = in_tok->previous)\n+\t\tprocess_input_token(in_tok, arg_tok_first, WHERE_AFTER);\n+\n+\tupdate_last(in_tok_last);\n+\n+\tif (!*arg_tok_first)\n+\t\treturn;\n+\n+\t/* Process input from start to end */\n+\tfor (in_tok = *in_tok_first; in_tok; in_tok = in_tok->next)\n+\t\tprocess_input_token(in_tok, arg_tok_first, WHERE_BEFORE);\n+\n+\tupdate_first(in_tok_first);\n+\n+\t/* Process args left */\n+\twhile (*arg_tok_first) {\n+\t\targ_tok = remove_first(arg_tok_first);\n+\t\tapply_arg_if_missing(in_tok_first, in_tok_last, arg_tok);\n+\t}\n+}\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238480","messageId":"20140406170204.15116.78730.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 03/12] trailer: read and process config information","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:54Z","receivedAt":"2014-04-06T17:01:54Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Read the configuration to get trailer information, and then process\nit and storing it in a doubly linked list.\n\nThe config information is stored in the list whose first item is\npointed to by:\n\nstatic struct trailer_item *first_conf_item;\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 146 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex 52108c2..c7c0f54 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -25,6 +25,8 @@ struct trailer_item {\n \tstruct conf_info conf;\n };\n \n+static struct trailer_item *first_conf_item;\n+\n static int same_token(struct trailer_item *a, struct trailer_item *b, int alnum_len)\n {\n \treturn !strncasecmp(a->token, b->token, alnum_len);\n@@ -245,3 +247,147 @@ static void process_trailers_lists(struct trailer_item **in_tok_first,\n \t\tapply_arg_if_missing(in_tok_first, in_tok_last, arg_tok);\n \t}\n }\n+\n+static int set_where(struct conf_info *item, const char *value)\n+{\n+\tif (!strcmp(\"after\", value))\n+\t\titem->where = WHERE_AFTER;\n+\telse if (!strcmp(\"before\", value))\n+\t\titem->where = WHERE_BEFORE;\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+{\n+\tif (!strcmp(\"addIfDifferent\", value))\n+\t\titem->if_exists = EXISTS_ADD_IF_DIFFERENT;\n+\telse if (!strcmp(\"addIfDifferentNeighbor\", value))\n+\t\titem->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;\n+\telse if (!strcmp(\"add\", value))\n+\t\titem->if_exists = EXISTS_ADD;\n+\telse if (!strcmp(\"overwrite\", value))\n+\t\titem->if_exists = EXISTS_OVERWRITE;\n+\telse if (!strcmp(\"doNothing\", value))\n+\t\titem->if_exists = 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+{\n+\tif (!strcmp(\"doNothing\", value))\n+\t\titem->if_missing = MISSING_DO_NOTHING;\n+\telse if (!strcmp(\"add\", value))\n+\t\titem->if_missing = MISSING_ADD;\n+\telse\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static struct trailer_item *get_conf_item(const char *name)\n+{\n+\tstruct trailer_item *item;\n+\tstruct trailer_item *previous;\n+\n+\t/* Look up item with same name */\n+\tfor (previous = NULL, item = first_conf_item;\n+\t     item;\n+\t     previous = item, item = item->next) {\n+\t\tif (!strcasecmp(item->conf.name, name))\n+\t\t\treturn item;\n+\t}\n+\n+\t/* Item does not already exists, create it */\n+\titem = xcalloc(sizeof(struct trailer_item), 1);\n+\titem->conf.name = xstrdup(name);\n+\n+\tif (!previous)\n+\t\tfirst_conf_item = item;\n+\telse {\n+\t\tprevious->next = item;\n+\t\titem->previous = previous;\n+\t}\n+\n+\treturn item;\n+}\n+\n+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,\n+\t\t\t TRAILER_IF_EXISTS, TRAILER_IF_MISSING };\n+\n+static struct {\n+\tconst char *name;\n+\tenum trailer_info_type type;\n+} trailer_config_items[] = {\n+\t{ \"key\", TRAILER_KEY },\n+\t{ \"command\", TRAILER_COMMAND },\n+\t{ \"where\", TRAILER_WHERE },\n+\t{ \"ifexists\", TRAILER_IF_EXISTS },\n+\t{ \"ifmissing\", TRAILER_IF_MISSING }\n+};\n+\n+static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n+{\n+\tconst char *trailer_item, *variable_name;\n+\tstruct trailer_item *item;\n+\tstruct conf_info *conf;\n+\tchar *name = NULL;\n+\tenum trailer_info_type type;\n+\tint i;\n+\n+\ttrailer_item = skip_prefix(conf_key, \"trailer.\");\n+\tif (!trailer_item)\n+\t\treturn 0;\n+\n+\tvariable_name = strrchr(trailer_item, '.');\n+\tif (!variable_name) {\n+\t\twarning(_(\"two level trailer config variable %s\"), conf_key);\n+\t\treturn 0;\n+\t}\n+\n+\tvariable_name++;\n+\tfor (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {\n+\t\tif (strcmp(trailer_config_items[i].name, variable_name))\n+\t\t\tcontinue;\n+\t\tname = xstrndup(trailer_item,  variable_name - trailer_item - 1);\n+\t\ttype = trailer_config_items[i].type;\n+\t\tbreak;\n+\t}\n+\n+\tif (!name)\n+\t\treturn 0;\n+\n+\titem = get_conf_item(name);\n+\tconf = &item->conf;\n+\tfree(name);\n+\n+\tswitch (type) {\n+\tcase TRAILER_KEY:\n+\t\tif (conf->key)\n+\t\t\twarning(_(\"more than one %s\"), conf_key);\n+\t\tconf->key = xstrdup(value);\n+\t\tbreak;\n+\tcase TRAILER_COMMAND:\n+\t\tif (conf->command)\n+\t\t\twarning(_(\"more than one %s\"), conf_key);\n+\t\tconf->command = xstrdup(value);\n+\t\tbreak;\n+\tcase TRAILER_WHERE:\n+\t\tif (set_where(conf, 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\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\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"internal bug in trailer.c\");\n+\t}\n+\treturn 0;\n+}\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238489","messageId":"20140406170204.15116.3903.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 04/12] trailer: process command line trailer arguments","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:55Z","receivedAt":"2014-04-06T17:01:55Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Parse the trailer command line arguments and put\nthe result into an arg_tok doubly linked list.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 117 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex c7c0f54..89ebff1 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -391,3 +391,120 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t}\n \treturn 0;\n }\n+\n+static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)\n+{\n+\tsize_t len = strcspn(trailer, \"=:\");\n+\tif (len == 0)\n+\t\treturn error(_(\"empty trailer token in trailer '%s'\"), trailer);\n+\tif (len < strlen(trailer)) {\n+\t\tstrbuf_add(tok, trailer, len);\n+\t\tstrbuf_trim(tok);\n+\t\tstrbuf_addstr(val, trailer + len + 1);\n+\t\tstrbuf_trim(val);\n+\t} else {\n+\t\tstrbuf_addstr(tok, trailer);\n+\t\tstrbuf_trim(tok);\n+\t}\n+\treturn 0;\n+}\n+\n+\n+static void duplicate_conf(struct conf_info *dst, struct conf_info *src)\n+{\n+\t*dst = *src;\n+\tif (src->name)\n+\t\tdst->name = xstrdup(src->name);\n+\tif (src->key)\n+\t\tdst->key = xstrdup(src->key);\n+\tif (src->command)\n+\t\tdst->command = xstrdup(src->command);\n+}\n+\n+static const char *token_from_item(struct trailer_item *item)\n+{\n+\tif (item->conf.key)\n+\t\treturn item->conf.key;\n+\n+\treturn item->conf.name;\n+}\n+\n+static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,\n+\t\t\t\t\t     char *tok, char *val)\n+{\n+\tstruct trailer_item *new = xcalloc(sizeof(*new), 1);\n+\tnew->value = val;\n+\n+\tif (conf_item) {\n+\t\tduplicate_conf(&new->conf, &conf_item->conf);\n+\t\tnew->token = xstrdup(token_from_item(conf_item));\n+\t\tfree(tok);\n+\t} else\n+\t\tnew->token = tok;\n+\n+\treturn new;\n+}\n+\n+static int token_matches_item(const char *tok, struct trailer_item *item, int alnum_len)\n+{\n+\tif (!strncasecmp(tok, item->conf.name, alnum_len))\n+\t\treturn 1;\n+\treturn item->conf.key ? !strncasecmp(tok, item->conf.key, alnum_len) : 0;\n+}\n+\n+static struct trailer_item *create_trailer_item(const char *string)\n+{\n+\tstruct strbuf tok = STRBUF_INIT;\n+\tstruct strbuf val = STRBUF_INIT;\n+\tstruct trailer_item *item;\n+\tint tok_alnum_len;\n+\n+\tif (parse_trailer(&tok, &val, string))\n+\t\treturn NULL;\n+\n+\ttok_alnum_len = alnum_len(tok.buf, tok.len);\n+\n+\t/* Lookup if the token matches something in the config */\n+\tfor (item = first_conf_item; item; item = item->next) {\n+\t\tif (token_matches_item(tok.buf, item, tok_alnum_len)) {\n+\t\t\tstrbuf_release(&tok);\n+\t\t\treturn new_trailer_item(item,\n+\t\t\t\t\t\tNULL,\n+\t\t\t\t\t\tstrbuf_detach(&val, NULL));\n+\t\t}\n+\t}\n+\n+\treturn new_trailer_item(NULL,\n+\t\t\t\tstrbuf_detach(&tok, NULL),\n+\t\t\t\tstrbuf_detach(&val, NULL));\n+}\n+\n+static void add_trailer_item(struct trailer_item **first,\n+\t\t\t     struct trailer_item **last,\n+\t\t\t     struct trailer_item *new)\n+{\n+\tif (!new)\n+\t\treturn;\n+\tif (!*last) {\n+\t\t*first = new;\n+\t\t*last = new;\n+\t} else {\n+\t\t(*last)->next = new;\n+\t\tnew->previous = *last;\n+\t\t*last = new;\n+\t}\n+}\n+\n+static struct trailer_item *process_command_line_args(int argc, const char **argv)\n+{\n+\tint i;\n+\tstruct trailer_item *arg_tok_first = NULL;\n+\tstruct trailer_item *arg_tok_last = NULL;\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tstruct trailer_item *new = create_trailer_item(argv[i]);\n+\t\tadd_trailer_item(&arg_tok_first, &arg_tok_last, new);\n+\t}\n+\n+\treturn arg_tok_first;\n+}\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238485","messageId":"20140406170204.15116.49476.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 05/12] trailer: parse trailers from stdin","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:56Z","receivedAt":"2014-04-06T17:01:56Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Read trailers from stdin, parse them and put the result into a doubly linked\nlist.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 76 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex 89ebff1..6d2da32 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -50,6 +50,14 @@ static size_t alnum_len(const char *buf, size_t len)\n \treturn len;\n }\n \n+static inline int contains_only_spaces(const char *str)\n+{\n+\tconst char *s = str;\n+\twhile (*s && isspace(*s))\n+\t\ts++;\n+\treturn !*s;\n+}\n+\n static void free_trailer_item(struct trailer_item *item)\n {\n \tfree(item->conf.name);\n@@ -508,3 +516,71 @@ static struct trailer_item *process_command_line_args(int argc, const char **arg\n \n \treturn arg_tok_first;\n }\n+\n+static struct strbuf **read_stdin(void)\n+{\n+\tstruct strbuf **lines;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (strbuf_read(&sb, fileno(stdin), 0) < 0)\n+\t\tdie_errno(_(\"could not read from stdin\"));\n+\n+\tlines = strbuf_split(&sb, '\\n');\n+\n+\tstrbuf_release(&sb);\n+\n+\treturn lines;\n+}\n+\n+/*\n+ * Return the the (0 based) index of the first trailer line\n+ * or the line count if there are no trailers.\n+ */\n+static int find_trailer_start(struct strbuf **lines)\n+{\n+\tint start, empty = 1, count = 0;\n+\n+\t/* Get the line count */\n+\twhile (lines[count])\n+\t\tcount++;\n+\n+\t/*\n+\t * Get the start of the trailers by looking starting from the end\n+\t * for a line with only spaces before lines with one ':'.\n+\t */\n+\tfor (start = count - 1; start >= 0; start--) {\n+\t\tif (contains_only_spaces(lines[start]->buf)) {\n+\t\t\tif (empty)\n+\t\t\t\tcontinue;\n+\t\t\treturn start + 1;\n+\t\t}\n+\t\tif (strchr(lines[start]->buf, ':')) {\n+\t\t\tif (empty)\n+\t\t\t\tempty = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\treturn count;\n+\t}\n+\n+\treturn empty ? count : start + 1;\n+}\n+\n+static void process_stdin(struct trailer_item **in_tok_first,\n+\t\t\t  struct trailer_item **in_tok_last)\n+{\n+\tstruct strbuf **lines = read_stdin();\n+\tint start = find_trailer_start(lines);\n+\tint i;\n+\n+\t/* Print non trailer lines as is */\n+\tfor (i = 0; lines[i] && i < start; i++)\n+\t\tprintf(\"%s\", lines[i]->buf);\n+\n+\t/* Parse trailer lines */\n+\tfor (i = start; lines[i]; i++) {\n+\t\tstruct trailer_item *new = create_trailer_item(lines[i]->buf);\n+\t\tadd_trailer_item(in_tok_first, in_tok_last, new);\n+\t}\n+\n+\tstrbuf_list_free(lines);\n+}\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238482","messageId":"20140406170204.15116.73542.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 06/12] trailer: put all the processing together and print","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:57Z","receivedAt":"2014-04-06T17:01:57Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch adds the process_trailers() function that\ncalls all the previously added processing functions\nand then prints the results on the standard output.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++\n trailer.h |  6 ++++++\n 2 files changed, 55 insertions(+)\n create mode 100644 trailer.h\n\ndiff --git a/trailer.c b/trailer.c\nindex 6d2da32..16465e5 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"trailer.h\"\n /*\n  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n  */\n@@ -68,6 +69,26 @@ static void free_trailer_item(struct trailer_item *item)\n \tfree(item);\n }\n \n+static void print_tok_val(const char *tok, const char *val)\n+{\n+\tchar c = tok[strlen(tok) - 1];\n+\tif (isalnum(c))\n+\t\tprintf(\"%s: %s\\n\", tok, val);\n+\telse if (isspace(c) || c == '#')\n+\t\tprintf(\"%s%s\\n\", tok, val);\n+\telse\n+\t\tprintf(\"%s %s\\n\", tok, val);\n+}\n+\n+static void print_all(struct trailer_item *first, int trim_empty)\n+{\n+\tstruct trailer_item *item;\n+\tfor (item = first; item; item = item->next) {\n+\t\tif (!trim_empty || strlen(item->value) > 0)\n+\t\t\tprint_tok_val(item->token, item->value);\n+\t}\n+}\n+\n static void add_arg_to_input_list(struct trailer_item *in_tok,\n \t\t\t\t  struct trailer_item *arg_tok)\n {\n@@ -584,3 +605,31 @@ static void process_stdin(struct trailer_item **in_tok_first,\n \n \tstrbuf_list_free(lines);\n }\n+\n+static void free_all(struct trailer_item **first)\n+{\n+\twhile (*first) {\n+\t\tstruct trailer_item *item = remove_first(first);\n+\t\tfree_trailer_item(item);\n+\t}\n+}\n+\n+void process_trailers(int trim_empty, int argc, const char **argv)\n+{\n+\tstruct trailer_item *in_tok_first = NULL;\n+\tstruct trailer_item *in_tok_last = NULL;\n+\tstruct trailer_item *arg_tok_first;\n+\n+\tgit_config(git_trailer_config, NULL);\n+\n+\t/* Print the non trailer part of stdin */\n+\tprocess_stdin(&in_tok_first, &in_tok_last);\n+\n+\targ_tok_first = process_command_line_args(argc, argv);\n+\n+\tprocess_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);\n+\n+\tprint_all(in_tok_first, trim_empty);\n+\n+\tfree_all(&in_tok_first);\n+}\ndiff --git a/trailer.h b/trailer.h\nnew file mode 100644\nindex 0000000..9323b1e\n--- /dev/null\n+++ b/trailer.h\n@@ -0,0 +1,6 @@\n+#ifndef TRAILER_H\n+#define TRAILER_H\n+\n+void process_trailers(int trim_empty, int argc, const char **argv);\n+\n+#endif /* TRAILER_H */\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238490","messageId":"20140406170204.15116.73833.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 07/12] trailer: add interpret-trailers command","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:58Z","receivedAt":"2014-04-06T17:01:58Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch adds the \"git interpret-trailers\" command.\nThis command uses the previously added process_trailers()\nfunction in trailer.c.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore                   |  1 +\n Makefile                     |  1 +\n builtin.h                    |  1 +\n builtin/interpret-trailers.c | 33 +++++++++++++++++++++++++++++++++\n git.c                        |  1 +\n 5 files changed, 37 insertions(+)\n create mode 100644 builtin/interpret-trailers.c\n\ndiff --git a/.gitignore b/.gitignore\nindex dc600f9..c2a0b19 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -74,6 +74,7 @@\n /git-index-pack\n /git-init\n /git-init-db\n+/git-interpret-trailers\n /git-instaweb\n /git-log\n /git-ls-files\ndiff --git a/Makefile b/Makefile\nindex 179be0a..499ca30 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -944,6 +944,7 @@ BUILTIN_OBJS += builtin/hash-object.o\n BUILTIN_OBJS += builtin/help.o\n BUILTIN_OBJS += builtin/index-pack.o\n BUILTIN_OBJS += builtin/init-db.o\n+BUILTIN_OBJS += builtin/interpret-trailers.o\n BUILTIN_OBJS += builtin/log.o\n BUILTIN_OBJS += builtin/ls-files.o\n BUILTIN_OBJS += builtin/ls-remote.o\ndiff --git a/builtin.h b/builtin.h\nindex c47c110..8ca0065 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -73,6 +73,7 @@ extern int cmd_hash_object(int argc, const char **argv, const char *prefix);\n extern int cmd_help(int argc, const char **argv, const char *prefix);\n extern int cmd_index_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_init_db(int argc, const char **argv, const char *prefix);\n+extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);\n extern int cmd_log(int argc, const char **argv, const char *prefix);\n extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_files(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c\nnew file mode 100644\nindex 0000000..0c8ca72\n--- /dev/null\n+++ b/builtin/interpret-trailers.c\n@@ -0,0 +1,33 @@\n+/*\n+ * Builtin \"git interpret-trailers\"\n+ *\n+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n+ *\n+ */\n+\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+#include \"trailer.h\"\n+\n+static const char * const git_interpret_trailers_usage[] = {\n+\tN_(\"git interpret-trailers [--trim-empty] [(<token>[(=|:)<value>])...]\"),\n+\tNULL\n+};\n+\n+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)\n+{\n+\tint trim_empty = 0;\n+\n+\tstruct option options[] = {\n+\t\tOPT_BOOL(0, \"trim-empty\", &trim_empty, N_(\"trim empty trailers\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options,\n+\t\t\t     git_interpret_trailers_usage, 0);\n+\n+\tprocess_trailers(trim_empty, argc, argv);\n+\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 9efd1a3..d432f11 100644\n--- a/git.c\n+++ b/git.c\n@@ -380,6 +380,7 @@ static struct cmd_struct commands[] = {\n \t{ \"index-pack\", cmd_index_pack, RUN_SETUP_GENTLY },\n \t{ \"init\", cmd_init_db },\n \t{ \"init-db\", cmd_init_db },\n+\t{ \"interpret-trailers\", cmd_interpret_trailers, RUN_SETUP },\n \t{ \"log\", cmd_log, RUN_SETUP },\n \t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n \t{ \"ls-remote\", cmd_ls_remote, RUN_SETUP_GENTLY },\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238488","messageId":"20140406170204.15116.45798.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 08/12] trailer: add tests for \"git interpret-trailers\"","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:01:59Z","receivedAt":"2014-04-06T17:01:59Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t7513-interpret-trailers.sh | 351 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 351 insertions(+)\n create mode 100755 t/t7513-interpret-trailers.sh\n\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nnew file mode 100755\nindex 0000000..0e5d57f\n--- /dev/null\n+++ b/t/t7513-interpret-trailers.sh\n@@ -0,0 +1,351 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2013 Christian Couder\n+#\n+\n+test_description='git interpret-trailers'\n+\n+. ./test-lib.sh\n+\n+# When we want one trailing space at the end of each line, let's use sed\n+# to make sure that these spaces are not removed by any automatic tool.\n+\n+test_expect_success 'setup' '\n+\tcat >basic_message <<-\\EOF &&\n+\t\tsubject\n+\n+\t\tbody\n+\tEOF\n+\tcat >complex_message_body <<-\\EOF &&\n+\t\tmy subject\n+\n+\t\tmy body which is long\n+\t\tand contains some special\n+\t\tchars like : = ? !\n+\n+\tEOF\n+\tsed -e \"s/ Z\\$/ /\" >complex_message_trailers <<-\\EOF\n+\t\tFixes: Z\n+\t\tAcked-by: Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+'\n+\n+test_expect_success 'without config' '\n+\tsed -e \"s/ Z\\$/ /\" >expected <<-\\EOF &&\n+\t\tack: Peff\n+\t\tReviewed-by: Z\n+\t\tAcked-by: Johan\n+\tEOF\n+\tgit interpret-trailers \"ack = Peff\" \"Reviewed-by\" \"Acked-by: Johan\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '--trim-empty without config' '\n+\tcat >expected <<-\\EOF &&\n+\t\tack: Peff\n+\t\tAcked-by: Johan\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack = Peff\" \\\n+\t\t\"Reviewed-by\" \"Acked-by: Johan\" \"sob:\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with config setup' '\n+\tgit config trailer.ack.key \"Acked-by: \" &&\n+\tcat >expected <<-\\EOF &&\n+\t\tAcked-by: Peff\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack = Peff\" >actual &&\n+\ttest_cmp expected actual &&\n+\tgit interpret-trailers --trim-empty \"Acked-by = Peff\" >actual &&\n+\ttest_cmp expected actual &&\n+\tgit interpret-trailers --trim-empty \"Acked-by :Peff\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with config setup and = sign' '\n+\tgit config trailer.ack.key \"Acked-by= \" &&\n+\tcat >expected <<-\\EOF &&\n+\t\tAcked-by= Peff\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack = Peff\" >actual &&\n+\ttest_cmp expected actual &&\n+\tgit interpret-trailers --trim-empty \"Acked-by= Peff\" >actual &&\n+\ttest_cmp expected actual &&\n+\tgit interpret-trailers --trim-empty \"Acked-by : Peff\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with config setup and # sign' '\n+\tgit config trailer.bug.key \"Bug #\" &&\n+\tcat >expected <<-\\EOF &&\n+\t\tBug #42\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"bug = 42\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with commit basic message' '\n+\tgit interpret-trailers <basic_message >actual &&\n+\ttest_cmp basic_message actual\n+'\n+\n+test_expect_success 'with commit complex message' '\n+\tcat complex_message_body complex_message_trailers >complex_message &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with commit complex message and args' '\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+\t\tBug #42\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"bug: 42\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with commit complex message, args and --trim-empty' '\n+\tcat complex_message_body >expected &&\n+\tcat >>expected <<-\\EOF &&\n+\t\tAcked-by= Peff\n+\t\tBug #42\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack: Peff\" \"bug: 42\" \\\n+\t\t<complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"where = before\"' '\n+\tgit config trailer.bug.where \"before\" &&\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= Z\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"bug: 42\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"where = before\" for a token in the middle of the message' '\n+\tgit config trailer.review.key \"Reviewed-by:\" &&\n+\tgit config trailer.review.where \"before\" &&\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= Z\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Johan\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"bug: 42\" \"review: Johan\" \\\n+\t\t<complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"where = before\" and --trim-empty' '\n+\tcat complex_message_body >expected &&\n+\tcat >>expected <<-\\EOF &&\n+\t\tBug #46\n+\t\tBug #42\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Johan\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack: Peff\" \"bug: 42\" \\\n+\t\t\"review: Johan\" \"Bug: 46\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'the default is \"ifExists = addIfDifferent\"' '\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= Z\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"review:\" \"bug: 42\" \\\n+\t\t\"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = addIfDifferent\"' '\n+\tgit config trailer.review.ifExists \"addIfDifferent\" &&\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= Z\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"review:\" \"bug: 42\" \\\n+\t\t\"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = addIfDifferentNeighbor\"' '\n+\tgit config trailer.ack.ifExists \"addIfDifferentNeighbor\" &&\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= Z\n+\t\tAcked-by= Peff\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"review:\" \"ack: Junio\" \"bug: 42\" \\\n+\t\t\"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = addIfDifferentNeighbor\" and --trim-empty' '\n+\tgit config trailer.ack.ifExists \"addIfDifferentNeighbor\" &&\n+\tcat complex_message_body >expected &&\n+\tcat >>expected <<-\\EOF &&\n+\t\tBug #42\n+\t\tAcked-by= Peff\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\tEOF\n+\tgit interpret-trailers --trim-empty \"ack: Peff\" \"Acked-by= Peff\" \\\n+\t\t\"review:\" \"ack: Junio\" \"bug: 42\" \"ack: Peff\" \\\n+\t\t<complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = add\"' '\n+\tgit config trailer.ack.ifExists \"add\" &&\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= Z\n+\t\tAcked-by= Peff\n+\t\tAcked-by= Peff\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"ack: Peff\" \"Acked-by= Peff\" \"review:\" \\\n+\t\t\"ack: Junio\" \"bug: 42\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = overwrite\"' '\n+\tgit config trailer.fix.key \"Fixes:\" &&\n+\tgit config trailer.fix.ifExists \"overwrite\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tBug #42\n+\t\tFixes: 22\n+\t\tAcked-by= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=53\" \"ack: Junio\" \"fix=22\" \\\n+\t\t\"bug: 42\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifExists = doNothing\"' '\n+\tgit config trailer.fix.ifExists \"doNothing\" &&\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= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=53\" \"ack: Junio\" \"fix=22\" \\\n+\t\t\"bug: 42\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'the default is \"ifMissing = add\"' '\n+\tgit config trailer.cc.key \"Cc: \" &&\n+\tgit config trailer.cc.where \"before\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tBug #42\n+\t\tCc: Linus\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=53\" \"cc=Linus\" \"ack: Junio\" \\\n+\t\t\"fix=22\" \"bug: 42\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifMissing = add\"' '\n+\tgit config trailer.cc.ifMissing \"add\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tCc: Linus\n+\t\tBug #42\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=53\" \"ack: Junio\" \"fix=22\" \\\n+\t\t\"bug: 42\" \"cc=Linus\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'using \"ifMissing = doNothing\"' '\n+\tgit config trailer.cc.ifMissing \"doNothing\" &&\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= Z\n+\t\tAcked-by= Junio\n+\t\tAcked-by= Peff\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=53\" \"cc=Linus\" \"ack: Junio\" \\\n+\t\t\"fix=22\" \"bug: 42\" \"ack: Peff\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238486","messageId":"20140406170204.15116.67057.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 09/12] trailer: execute command from 'trailer.<name>.command'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:02:00Z","receivedAt":"2014-04-06T17:02:00Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Let the user specify a command that will give on its standard output\nthe value to use for the specified trailer.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n trailer.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 64 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex 16465e5..09db2c2 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"run-command.h\"\n #include \"trailer.h\"\n /*\n  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n@@ -13,11 +14,14 @@ struct conf_info {\n \tchar *name;\n \tchar *key;\n \tchar *command;\n+\tunsigned command_uses_arg : 1;\n \tenum action_where where;\n \tenum action_if_exists if_exists;\n \tenum action_if_missing if_missing;\n };\n \n+#define TRAILER_ARG_STRING \"$ARG\"\n+\n struct trailer_item {\n \tstruct trailer_item *previous;\n \tstruct trailer_item *next;\n@@ -59,6 +63,13 @@ static inline int contains_only_spaces(const char *str)\n \treturn !*s;\n }\n \n+static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)\n+{\n+\tconst char *ptr = strstr(sb->buf, a);\n+\tif (ptr)\n+\t\tstrbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));\n+}\n+\n static void free_trailer_item(struct trailer_item *item)\n {\n \tfree(item->conf.name);\n@@ -402,6 +413,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\tif (conf->command)\n \t\t\twarning(_(\"more than one %s\"), conf_key);\n \t\tconf->command = xstrdup(value);\n+\t\tconf->command_uses_arg = !!strstr(conf->command, TRAILER_ARG_STRING);\n \t\tbreak;\n \tcase TRAILER_WHERE:\n \t\tif (set_where(conf, value))\n@@ -438,6 +450,45 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *tra\n \treturn 0;\n }\n \n+static int read_from_command(struct child_process *cp, struct strbuf *buf)\n+{\n+\tif (run_command(cp))\n+\t\treturn error(\"running trailer command '%s' failed\", cp->argv[0]);\n+\tif (strbuf_read(buf, cp->out, 1024) < 1)\n+\t\treturn error(\"reading from trailer command '%s' failed\", cp->argv[0]);\n+\tstrbuf_trim(buf);\n+\treturn 0;\n+}\n+\n+static const char *apply_command(const char *command, const char *arg)\n+{\n+\tstruct strbuf cmd = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct child_process cp;\n+\tconst char *argv[] = {NULL, NULL};\n+\tconst char *result;\n+\n+\tstrbuf_addstr(&cmd, command);\n+\tif (arg)\n+\t\tstrbuf_replace(&cmd, TRAILER_ARG_STRING, arg);\n+\n+\targv[0] = cmd.buf;\n+\tmemset(&cp, 0, sizeof(cp));\n+\tcp.argv = argv;\n+\tcp.env = local_repo_env;\n+\tcp.no_stdin = 1;\n+\tcp.out = -1;\n+\tcp.use_shell = 1;\n+\n+\tif (read_from_command(&cp, &buf)) {\n+\t\tstrbuf_release(&buf);\n+\t\tresult = xstrdup(\"\");\n+\t} else\n+\t\tresult = strbuf_detach(&buf, NULL);\n+\n+\tstrbuf_release(&cmd);\n+\treturn result;\n+}\n \n static void duplicate_conf(struct conf_info *dst, struct conf_info *src)\n {\n@@ -468,6 +519,10 @@ static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,\n \t\tduplicate_conf(&new->conf, &conf_item->conf);\n \t\tnew->token = xstrdup(token_from_item(conf_item));\n \t\tfree(tok);\n+\t\tif (conf_item->conf.command_uses_arg || !val) {\n+\t\t\tnew->value = apply_command(conf_item->conf.command, val);\n+\t\t\tfree(val);\n+\t\t}\n \t} else\n \t\tnew->token = tok;\n \n@@ -529,12 +584,21 @@ static struct trailer_item *process_command_line_args(int argc, const char **arg\n \tint i;\n \tstruct trailer_item *arg_tok_first = NULL;\n \tstruct trailer_item *arg_tok_last = NULL;\n+\tstruct trailer_item *item;\n \n \tfor (i = 0; i < argc; i++) {\n \t\tstruct trailer_item *new = create_trailer_item(argv[i]);\n \t\tadd_trailer_item(&arg_tok_first, &arg_tok_last, new);\n \t}\n \n+\t/* Add conf commands that don't use $ARG */\n+\tfor (item = first_conf_item; item; item = item->next) {\n+\t\tif (item->conf.command && !item->conf.command_uses_arg) {\n+\t\t\tstruct trailer_item *new = new_trailer_item(item, NULL, NULL);\n+\t\t\tadd_trailer_item(&arg_tok_first, &arg_tok_last, new);\n+\t\t}\n+\t}\n+\n \treturn arg_tok_first;\n }\n \n-- \n1.9.0.163.g8ca203c\n"},{"id":"238487","messageId":"20140406170204.15116.54727.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 10/12] trailer: add tests for commands in config file","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:02:01Z","receivedAt":"2014-04-06T17:02:01Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"And add a few other tests for some special cases.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t7513-interpret-trailers.sh | 116 ++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 116 insertions(+)\n\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 0e5d57f..262f7bf 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -348,4 +348,120 @@ test_expect_success 'using \"ifMissing = doNothing\"' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'with simple command' '\n+\tgit config trailer.sign.key \"Signed-off-by: \" &&\n+\tgit config trailer.sign.where \"after\" &&\n+\tgit config trailer.sign.ifExists \"addIfDifferentNeighbor\" &&\n+\tgit config trailer.sign.command \"echo \\\"A U Thor <author@example.com>\\\"\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\t\tSigned-off-by: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=22\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with command using commiter information' '\n+\tgit config trailer.sign.ifExists \"addIfDifferent\" &&\n+\tgit config trailer.sign.command \"echo \\\"\\$GIT_COMMITTER_NAME <\\$GIT_COMMITTER_EMAIL>\\\"\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\t\tSigned-off-by: C O Mitter <committer@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=22\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with command using author information' '\n+\tgit config trailer.sign.key \"Signed-off-by: \" &&\n+\tgit config trailer.sign.where \"after\" &&\n+\tgit config trailer.sign.ifExists \"addIfDifferentNeighbor\" &&\n+\tgit config trailer.sign.command \"echo \\\"\\$GIT_AUTHOR_NAME <\\$GIT_AUTHOR_EMAIL>\\\"\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-\\EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\t\tSigned-off-by: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=22\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'setup a commit' '\n+\techo \"Content of the first commit.\" > a.txt &&\n+\tgit add a.txt &&\n+\tgit commit -m \"Add file a.txt\"\n+'\n+\n+test_expect_success 'with command using $ARG' '\n+\tgit config trailer.fix.ifExists \"overwrite\" &&\n+\tgit config trailer.fix.command \"git log -1 --oneline --format=\\\"%h (%s)\\\" --abbrev-commit --abbrev=14 \\$ARG\" &&\n+\tFIXED=$(git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 HEAD) &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-EOF &&\n+\t\tFixes: $FIXED\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\t\tSigned-off-by: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=HEAD\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with failing command using $ARG' '\n+\tgit config trailer.fix.ifExists \"overwrite\" &&\n+\tgit config trailer.fix.command \"false \\$ARG\" &&\n+\tcat complex_message_body >expected &&\n+\tsed -e \"s/ Z\\$/ /\" >>expected <<-EOF &&\n+\t\tFixes: Z\n+\t\tAcked-by= Z\n+\t\tReviewed-by: Z\n+\t\tSigned-off-by: Z\n+\t\tSigned-off-by: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:\" \"fix=HEAD\" <complex_message >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with empty tokens' '\n+\tcat >expected <<-EOF &&\n+\t\tSigned-off-by: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \":\" \":test\" >actual <<-EOF &&\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with command but no key' '\n+\tgit config --unset trailer.sign.key &&\n+\tcat >expected <<-EOF &&\n+\t\tsign: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers >actual <<-EOF &&\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'with no command and no key' '\n+\tgit config --unset trailer.review.key &&\n+\tcat >expected <<-EOF &&\n+\t\treview: Junio\n+\t\tsign: A U Thor <author@example.com>\n+\tEOF\n+\tgit interpret-trailers \"review:Junio\" >actual <<-EOF &&\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238484","messageId":"20140406170204.15116.15559.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:02:02Z","receivedAt":"2014-04-06T17:02:02Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-interpret-trailers.txt | 123 +++++++++++++++++++++++++++++++\n 1 file changed, 123 insertions(+)\n create mode 100644 Documentation/git-interpret-trailers.txt\n\ndiff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt\nnew file mode 100644\nindex 0000000..75ae386\n--- /dev/null\n+++ b/Documentation/git-interpret-trailers.txt\n@@ -0,0 +1,123 @@\n+git-interpret-trailers(1)\n+=========================\n+\n+NAME\n+----\n+git-interpret-trailers - help add stuctured information into commit messages\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git interpret-trailers' [--trim-empty] [(<token>[(=|:)<value>])...]\n+\n+DESCRIPTION\n+-----------\n+Help add RFC 822-like headers, called 'trailers', at the end of the\n+otherwise free-form part of a commit message.\n+\n+This command is a filter. It reads the standard input for a commit\n+message and applies the `token` arguments, if any, to this\n+message. The resulting message is emited on the standard output.\n+\n+Some configuration variables control the way the `token` arguments are\n+applied to the message and the way any existing trailer in the message\n+is changed. They also make it possible to automatically add some\n+trailers.\n+\n+By default, a 'token=value' or 'token:value' argument will be added\n+only if no trailer with the same (token, value) pair is already in the\n+message. The 'token' and 'value' parts will be trimmed to remove\n+starting and trailing whitespace, and the resulting trimmed 'token'\n+and 'value' will appear in the message like this:\n+\n+------------------------------------------------\n+token: value\n+------------------------------------------------\n+\n+By default, if there are already trailers with the same 'token', the\n+new trailer will appear just after the last trailer with the same\n+'token'. Otherwise it will appear at the end of the message.\n+\n+Note that 'trailers' do not follow and are not intended to follow many\n+rules that are in RFC 822. For example they do not follow the line\n+breaking rules, the encoding rules and probably many other rules.\n+\n+OPTIONS\n+-------\n+--trim-empty::\n+\tIf the 'value' part of any trailer contains only whitespace,\n+\tthe whole trailer will be removed from the resulting message.\n+\n+CONFIGURATION VARIABLES\n+-----------------------\n+\n+trailer.<token>.key::\n+\tThis 'key' will be used instead of 'token' in the\n+\ttrailer. After some alphanumeric characters, it can contain\n+\tsome non alphanumeric characters like ':', '=' or '#' that will\n+\tbe used instead of ':' to separate the token from the value in\n+\tthe trailer, though the default ':' is more standard.\n+\n+trailer.<token>.where::\n+\tThis can be either `after`, which is the default, or\n+\t`before`. If it is `before`, then a trailer with the specified\n+\ttoken, will appear before, instead of after, other trailers\n+\twith the same token, or otherwise at the beginning, instead of\n+\tat the end, of all the trailers.\n+\n+trailer.<token>.ifexist::\n+\tThis option makes it possible to choose what action will be\n+\tperformed when there is already at least one trailer with the\n+\tsame token in the message.\n++\n+The valid values for this option are: `addIfDifferent` (this is the\n+default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.\n++\n+With `addIfDifferent`, a new trailer will be added only if no trailer\n+with the same (token, value) pair is already in the message.\n++\n+With `addIfDifferentNeighbor`, a new trailer will be added only if no\n+trailer with the same (token, value) pair is above or below the line\n+where the new trailer will be added.\n++\n+With `add`, a new trailer will be added, even if some trailers with\n+the same (token, value) pair are already in the message.\n++\n+With `overwrite`, the new trailer will overwrite an existing trailer\n+with the same token.\n++\n+With `doNothing`, nothing will be done, that is no new trailer will be\n+added if there is already one with the same token in the message.\n+\n+trailer.<token>.ifmissing::\n+\tThis option makes it possible to choose what action will be\n+\tperformed when there is not yet any trailer with the same\n+\ttoken in the message.\n++\n+The valid values for this option are: `add` (this is the default) and\n+`doNothing`.\n++\n+With `add`, a new trailer will be added.\n++\n+With `doNothing`, nothing will be done.\n+\n+trailer.<token>.command::\n+\tThis option can be used to specify a shell command that will\n+\tbe used to automatically add or modify a trailer with the\n+\tspecified 'token'.\n++\n+When this option is specified, it is like if a special 'token=value'\n+argument is added at the end of the command line, where 'value' will\n+be given by the standard output of the specified command.\n++\n+If the command contains the `$ARG` string, this string will be\n+replaced with the 'value' part of an existing trailer with the same\n+token, if any, before the command is launched.\n+\n+SEE ALSO\n+--------\n+linkgit:git-commit[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238483","messageId":"20140406170204.15116.15100.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"20140406163214.15116.91484.chriscool@tuxfamily.org","subject":"[PATCH v10 12/12] trailer: add blank line before the trailers if needed","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-06T17:02:03Z","receivedAt":"2014-04-06T17:02:03Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t7513-interpret-trailers.sh | 12 +++++++++++-\n trailer.c                     | 26 ++++++++++++++++++--------\n 2 files changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh\nindex 262f7bf..44a7131 100755\n--- a/t/t7513-interpret-trailers.sh\n+++ b/t/t7513-interpret-trailers.sh\n@@ -34,6 +34,7 @@ test_expect_success 'setup' '\n \n test_expect_success 'without config' '\n \tsed -e \"s/ Z\\$/ /\" >expected <<-\\EOF &&\n+\n \t\tack: Peff\n \t\tReviewed-by: Z\n \t\tAcked-by: Johan\n@@ -44,6 +45,7 @@ test_expect_success 'without config' '\n \n test_expect_success '--trim-empty without config' '\n \tcat >expected <<-\\EOF &&\n+\n \t\tack: Peff\n \t\tAcked-by: Johan\n \tEOF\n@@ -55,6 +57,7 @@ test_expect_success '--trim-empty without config' '\n test_expect_success 'with config setup' '\n \tgit config trailer.ack.key \"Acked-by: \" &&\n \tcat >expected <<-\\EOF &&\n+\n \t\tAcked-by: Peff\n \tEOF\n \tgit interpret-trailers --trim-empty \"ack = Peff\" >actual &&\n@@ -68,6 +71,7 @@ test_expect_success 'with config setup' '\n test_expect_success 'with config setup and = sign' '\n \tgit config trailer.ack.key \"Acked-by= \" &&\n \tcat >expected <<-\\EOF &&\n+\n \t\tAcked-by= Peff\n \tEOF\n \tgit interpret-trailers --trim-empty \"ack = Peff\" >actual &&\n@@ -81,6 +85,7 @@ test_expect_success 'with config setup and = sign' '\n test_expect_success 'with config setup and # sign' '\n \tgit config trailer.bug.key \"Bug #\" &&\n \tcat >expected <<-\\EOF &&\n+\n \t\tBug #42\n \tEOF\n \tgit interpret-trailers --trim-empty \"bug = 42\" >actual &&\n@@ -88,8 +93,10 @@ test_expect_success 'with config setup and # sign' '\n '\n \n test_expect_success 'with commit basic message' '\n+\tcat basic_message >expected &&\n+\techo >>expected &&\n \tgit interpret-trailers <basic_message >actual &&\n-\ttest_cmp basic_message actual\n+\ttest_cmp expected actual\n '\n \n test_expect_success 'with commit complex message' '\n@@ -436,6 +443,7 @@ test_expect_success 'with failing command using $ARG' '\n \n test_expect_success 'with empty tokens' '\n \tcat >expected <<-EOF &&\n+\n \t\tSigned-off-by: A U Thor <author@example.com>\n \tEOF\n \tgit interpret-trailers \":\" \":test\" >actual <<-EOF &&\n@@ -446,6 +454,7 @@ test_expect_success 'with empty tokens' '\n test_expect_success 'with command but no key' '\n \tgit config --unset trailer.sign.key &&\n \tcat >expected <<-EOF &&\n+\n \t\tsign: A U Thor <author@example.com>\n \tEOF\n \tgit interpret-trailers >actual <<-EOF &&\n@@ -456,6 +465,7 @@ test_expect_success 'with command but no key' '\n test_expect_success 'with no command and no key' '\n \tgit config --unset trailer.review.key &&\n \tcat >expected <<-EOF &&\n+\n \t\treview: Junio\n \t\tsign: A U Thor <author@example.com>\n \tEOF\ndiff --git a/trailer.c b/trailer.c\nindex 09db2c2..639f657 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -618,12 +618,14 @@ static struct strbuf **read_stdin(void)\n }\n \n /*\n- * Return the the (0 based) index of the first trailer line\n+ * Return the (0 based) index of the first trailer line\n  * or the line count if there are no trailers.\n+ * The has_blank_line parameter tells if there is a blank\n+ * line before the trailers.\n  */\n-static int find_trailer_start(struct strbuf **lines)\n+static int find_trailer_start(struct strbuf **lines, int *has_blank_line)\n {\n-\tint start, empty = 1, count = 0;\n+\tint start, only_spaces = 1, count = 0;\n \n \t/* Get the line count */\n \twhile (lines[count])\n@@ -635,32 +637,40 @@ static int find_trailer_start(struct strbuf **lines)\n \t */\n \tfor (start = count - 1; start >= 0; start--) {\n \t\tif (contains_only_spaces(lines[start]->buf)) {\n-\t\t\tif (empty)\n+\t\t\tif (only_spaces)\n \t\t\t\tcontinue;\n+\t\t\t*has_blank_line = 1;\n \t\t\treturn start + 1;\n \t\t}\n \t\tif (strchr(lines[start]->buf, ':')) {\n-\t\t\tif (empty)\n-\t\t\t\tempty = 0;\n+\t\t\tif (only_spaces)\n+\t\t\t\tonly_spaces = 0;\n \t\t\tcontinue;\n \t\t}\n+\t\t*has_blank_line = start == count - 1 ?\n+\t\t  0 : contains_only_spaces(lines[start + 1]->buf);\n \t\treturn count;\n \t}\n \n-\treturn empty ? count : start + 1;\n+\t*has_blank_line = only_spaces ? count > 0 : 0;\n+\treturn only_spaces ? count : start + 1;\n }\n \n static void process_stdin(struct trailer_item **in_tok_first,\n \t\t\t  struct trailer_item **in_tok_last)\n {\n \tstruct strbuf **lines = read_stdin();\n-\tint start = find_trailer_start(lines);\n+\tint has_blank_line;\n+\tint start = find_trailer_start(lines, &has_blank_line);\n \tint i;\n \n \t/* Print non trailer lines as is */\n \tfor (i = 0; lines[i] && i < start; i++)\n \t\tprintf(\"%s\", lines[i]->buf);\n \n+\tif (!has_blank_line)\n+\t\tprintf(\"\\n\");\n+\n \t/* Parse trailer lines */\n \tfor (i = start; lines[i]; i++) {\n \t\tstruct trailer_item *new = create_trailer_item(lines[i]->buf);\n-- \n1.9.0.163.g8ca203c\n"},{"id":"238513","messageId":"xmqqvbuklt0q.fsf@gitster.dls.corp.google.com","threadId":"36360","inReplyTo":"20140406170204.15116.15100.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 12/12] trailer: add blank line before the trailers if needed","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-07T21:38:29Z","receivedAt":"2014-04-07T21:38:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\n> ---\n\nHmph, this is more fixing a mistake made earlier in the series at\nthe end than adding a new feature or something.  Can you start from\na version that does not have the mistake from the beginning?\n"},{"id":"238524","messageId":"5343A589.10503@alum.mit.edu","threadId":"36360","inReplyTo":"20140406170204.15116.15559.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-04-08T07:30:17Z","receivedAt":"2014-04-08T07:30:17Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"Sorry for reappearing in this thread after such a long absence.  I\nwanted to see what is coming up (I think this interpret-trailers command\nwill be handy!) so I read this documentation patch carefully, and added\nsome questions and suggestions below.\n\nOn 04/06/2014 07:02 PM, Christian Couder wrote:\n> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/git-interpret-trailers.txt | 123 +++++++++++++++++++++++++++++++\n>  1 file changed, 123 insertions(+)\n>  create mode 100644 Documentation/git-interpret-trailers.txt\n> \n> diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt\n> new file mode 100644\n> index 0000000..75ae386\n> --- /dev/null\n> +++ b/Documentation/git-interpret-trailers.txt\n> @@ -0,0 +1,123 @@\n> +git-interpret-trailers(1)\n> +=========================\n> +\n> +NAME\n> +----\n> +git-interpret-trailers - help add stuctured information into commit messages\n> +\n> +SYNOPSIS\n> +--------\n> +[verse]\n> +'git interpret-trailers' [--trim-empty] [(<token>[(=|:)<value>])...]\n> +\n> +DESCRIPTION\n> +-----------\n> +Help add RFC 822-like headers, called 'trailers', at the end of the\n> +otherwise free-form part of a commit message.\n> +\n> +This command is a filter. It reads the standard input for a commit\n> +message and applies the `token` arguments, if any, to this\n> +message. The resulting message is emited on the standard output.\n\ns/emited/emitted/\n\n> +\n> +Some configuration variables control the way the `token` arguments are\n> +applied to the message and the way any existing trailer in the message\n> +is changed. They also make it possible to automatically add some\n> +trailers.\n> +\n> +By default, a 'token=value' or 'token:value' argument will be added\n> +only if no trailer with the same (token, value) pair is already in the\n> +message. The 'token' and 'value' parts will be trimmed to remove\n> +starting and trailing whitespace, and the resulting trimmed 'token'\n> +and 'value' will appear in the message like this:\n> +\n> +------------------------------------------------\n> +token: value\n> +------------------------------------------------\n> +\n> +By default, if there are already trailers with the same 'token', the\n> +new trailer will appear just after the last trailer with the same\n> +'token'. Otherwise it will appear at the end of the message.\n\nHow are existing trailers recognized in the input commit message?  Do\ntrailers have to be configured to be recognized?  Or are all lines\nmatching a specific pattern considered trailers?  If so, it might be\nhelpful to include a regexp here that describes the trailer \"syntax\".\n\nWhat about blank lines?  I see that you try to add a blank line before\nnew trailers.  But what about on input?  Do the trailer lines have to be\nseparated from the free-form comment by a blank line to be recognized?\nWhat if there are blank lines between trailer lines, or after them?  Is\nit allowed to have non-trailer lines between or after trailer lines?\n\n> +\n> +Note that 'trailers' do not follow and are not intended to follow many\n> +rules that are in RFC 822. For example they do not follow the line\n> +breaking rules, the encoding rules and probably many other rules.\n> +\n> +OPTIONS\n> +-------\n> +--trim-empty::\n> +\tIf the 'value' part of any trailer contains only whitespace,\n> +\tthe whole trailer will be removed from the resulting message.\n\nDoes this apply to existing trailers, new trailers, or both?  If it\napplies to existing trailers, then it seems a bit dangerous, in the\nsense that the command might end up changing trailers that are unrelated\nto the one that the command is trying to add.\n\n> +\n> +CONFIGURATION VARIABLES\n> +-----------------------\n> +\n> +trailer.<token>.key::\n> +\tThis 'key' will be used instead of 'token' in the\n> +\ttrailer. After some alphanumeric characters, it can contain\n\nTrailer keys can also contain '-', right?\n\n> +\tsome non alphanumeric characters like ':', '=' or '#' that will\n> +\tbe used instead of ':' to separate the token from the value in\n> +\tthe trailer, though the default ':' is more standard.\n\nAbove it looks like the default separator is not ':' but rather ': '\n(with a space).  Is the space always added regardless of the value of\nthis configuration variable, or should the configuration value include\nthe trailing space if it is desired?  Is there any way to get a trailer\nthat doesn't include a space, like\n\n    foo=bar\n\n?  (Changing this to \"foo= bar\" would look pretty ugly.)\n\nIf a commit message containing trailer lines with separators other than\n':' is input to the program, will it recognize them as trailer lines?\nDo such trailer lines have to have the same separator as the one listed\nin this configuration setting to be recognized?\n\nI suppose that there is some compelling reason to allow non-colon\nseparators here.  If not, it seems like it adds a lot of complexity and\nshould maybe be omitted, or limited to only a few specific separators.\n\n> +\n> +trailer.<token>.where::\n> +\tThis can be either `after`, which is the default, or\n> +\t`before`. If it is `before`, then a trailer with the specified\n> +\ttoken, will appear before, instead of after, other trailers\n> +\twith the same token, or otherwise at the beginning, instead of\n> +\tat the end, of all the trailers.\n\nBrainstorming: some other options that might make sense here someday:\n\n`end`: add new trailer after all existing trailers (even those with\ndifferent keys).  This would allow trailers to be kept in chronological\norder.\n\n`beginning`: add new trailer before the first existing trailer (allows\nreverse chronological order).\n\n`sorted`: add new trailer among the existing trailers with the same key\nso as to keep their values in lexicographic order.\n\n> +\n> +trailer.<token>.ifexist::\n> +\tThis option makes it possible to choose what action will be\n> +\tperformed when there is already at least one trailer with the\n> +\tsame token in the message.\n> ++\n> +The valid values for this option are: `addIfDifferent` (this is the\n> +default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.\n\nAre these option values case sensitive?  If so, it might be a little bit\nconfusing because the same camel-case is often used in documentation for\nconfiguration *keys*, which are not case sensitive [1], and users might\nhave gotten used to thinking of strings that look like this to be\nnon-case-sensitive.\n\n> ++\n> +With `addIfDifferent`, a new trailer will be added only if no trailer\n> +with the same (token, value) pair is already in the message.\n> ++\n> +With `addIfDifferentNeighbor`, a new trailer will be added only if no\n> +trailer with the same (token, value) pair is above or below the line\n> +where the new trailer will be added.\n> ++\n> +With `add`, a new trailer will be added, even if some trailers with\n> +the same (token, value) pair are already in the message.\n> ++\n> +With `overwrite`, the new trailer will overwrite an existing trailer\n> +with the same token.\n\nWhat if there are multiple existing trailers with the same token?  Are\nthey all overwritten?\n\n> ++\n> +With `doNothing`, nothing will be done, that is no new trailer will be\n> +added if there is already one with the same token in the message.\n> +\n> +trailer.<token>.ifmissing::\n> +\tThis option makes it possible to choose what action will be\n> +\tperformed when there is not yet any trailer with the same\n> +\ttoken in the message.\n> ++\n> +The valid values for this option are: `add` (this is the default) and\n> +`doNothing`.\n> ++\n> +With `add`, a new trailer will be added.\n> ++\n> +With `doNothing`, nothing will be done.\n> +\n> +trailer.<token>.command::\n> +\tThis option can be used to specify a shell command that will\n> +\tbe used to automatically add or modify a trailer with the\n> +\tspecified 'token'.\n> ++\n> +When this option is specified, it is like if a special 'token=value'\n> +argument is added at the end of the command line, where 'value' will\n> +be given by the standard output of the specified command.\n\nMaybe reword to\n\n    When this option is specified, the behavior is as if a special\n    'token=value' argument were added at the end of the command line,\n    where 'value' is taken to be the standard output of the specified\n    command.\n\nAnd if it is the case, maybe add \"with leading and trailing whitespace\ntrimmed off\" at the end of the sentence.\n\n> ++\n> +If the command contains the `$ARG` string, this string will be\n> +replaced with the 'value' part of an existing trailer with the same\n> +token, if any, before the command is launched.\n\nWhat if the key appears multiple times in existing trailers?\n\n> +\n> +SEE ALSO\n> +--------\n> +linkgit:git-commit[1]\n> +\n> +GIT\n> +---\n> +Part of the linkgit:git[1] suite\n> \n\nDoesn't this command have to be added to command-list.txt?\n\nMichael\n\n[1] Anti-nitpick declaration: yes, I know that the middle part of\nconfiguration keys is case-sensitive.\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"238529","messageId":"CAP8UFD0RftewWj-oivojUrXCDqXUq6xX7ndQdixA2i=1BzZEFg@mail.gmail.com","threadId":"36360","inReplyTo":"5343A589.10503@alum.mit.edu","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2014-04-08T11:35:48Z","receivedAt":"2014-04-08T11:35:48Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Tue, Apr 8, 2014 at 9:30 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>\n>> +This command is a filter. It reads the standard input for a commit\n>> +message and applies the `token` arguments, if any, to this\n>> +message. The resulting message is emited on the standard output.\n>\n> s/emited/emitted/\n\nOk.\n\n>> +Some configuration variables control the way the `token` arguments are\n>> +applied to the message and the way any existing trailer in the message\n>> +is changed. They also make it possible to automatically add some\n>> +trailers.\n>> +\n>> +By default, a 'token=value' or 'token:value' argument will be added\n>> +only if no trailer with the same (token, value) pair is already in the\n>> +message. The 'token' and 'value' parts will be trimmed to remove\n>> +starting and trailing whitespace, and the resulting trimmed 'token'\n>> +and 'value' will appear in the message like this:\n>> +\n>> +------------------------------------------------\n>> +token: value\n>> +------------------------------------------------\n>> +\n>> +By default, if there are already trailers with the same 'token', the\n>> +new trailer will appear just after the last trailer with the same\n>> +'token'. Otherwise it will appear at the end of the message.\n>\n> How are existing trailers recognized in the input commit message?  Do\n> trailers have to be configured to be recognized?  Or are all lines\n> matching a specific pattern considered trailers?  If so, it might be\n> helpful to include a regexp here that describes the trailer \"syntax\".\n\nThe trailers are recognized in the input commit message using the\nfollowing rules:\n - only lines that contains a ':' are considered trailers,\n - the trailer lines must all be next to each other,\n - after them it's only possible to have some lines that contain only spaces,\n - before them there must be at least one line with only spaces\n\n> What about blank lines?  I see that you try to add a blank line before\n> new trailers.  But what about on input?\n\nOne line with only spaces has to be before the trailers. Some can be\nafter the trailers.\n\n> Do the trailer lines have to be\n> separated from the free-form comment by a blank line to be recognized?\n\nYes.\n\n> What if there are blank lines between trailer lines, or after them?\n\nAfter them is ok. Between is not ok (only the trailers after the blank\nlines will be recognized).\n\n> Is it allowed to have non-trailer lines between or after trailer lines?\n\nNo except lines with spaces after the trailers lines.\n\n>> +Note that 'trailers' do not follow and are not intended to follow many\n>> +rules that are in RFC 822. For example they do not follow the line\n>> +breaking rules, the encoding rules and probably many other rules.\n>> +\n>> +OPTIONS\n>> +-------\n>> +--trim-empty::\n>> +     If the 'value' part of any trailer contains only whitespace,\n>> +     the whole trailer will be removed from the resulting message.\n>\n> Does this apply to existing trailers, new trailers, or both?\n\nBoth.\n\n> If it applies to existing trailers, then it seems a bit dangerous, in the\n> sense that the command might end up changing trailers that are unrelated\n> to the one that the command is trying to add.\n\nThe command is not just for adding trailers.\nBut there could be an option to just trim trailers that are added.\n\n>> +CONFIGURATION VARIABLES\n>> +-----------------------\n>> +\n>> +trailer.<token>.key::\n>> +     This 'key' will be used instead of 'token' in the\n>> +     trailer. After some alphanumeric characters, it can contain\n>\n> Trailer keys can also contain '-', right?\n\nYes.\nI should have written \"after the last alphanumeric character\".\nI will fix that.\n\n>> +     some non alphanumeric characters like ':', '=' or '#' that will\n>> +     be used instead of ':' to separate the token from the value in\n>> +     the trailer, though the default ':' is more standard.\n>\n> Above it looks like the default separator is not ':' but rather ': '\n> (with a space).  Is the space always added regardless of the value of\n> this configuration variable, or should the configuration value include\n> the trailing space if it is desired?  Is there any way to get a trailer\n> that doesn't include a space, like\n>\n>     foo=bar\n>\n> ?  (Changing this to \"foo= bar\" would look pretty ugly.)\n\nI will have a look, but I think that:\n\n- a space is always added after ':' or '=',\n- a space is never added after '#',\n- it doesn't matter if there is a space or not in the configured key.\n\n> If a commit message containing trailer lines with separators other than\n> ':' is input to the program, will it recognize them as trailer lines?\n\nNo, '=' and '#' are not supported in the input message, only in the output.\n\n> Do such trailer lines have to have the same separator as the one listed\n> in this configuration setting to be recognized?\n\nNo they need to have ':' as a separator.\n\nThe reason why only ':' is supported is because it is the cannonical\ntrailer separator and it could create problems with many input\nmessages if other separators where supported.\n\nMaybe we could detect a special line like the following:\n\n# TRAILERS START\n\nin the input message and consider everyhting after that line as trailers.\nIn this case it would be ok to accept other separators.\n\n> I suppose that there is some compelling reason to allow non-colon\n> separators here.  If not, it seems like it adds a lot of complexity and\n> should maybe be omitted, or limited to only a few specific separators.\n\nYeah, but in the early threads concerning this subject, someone said\nthat GitHub for example uses \"bug #XXX\".\nI will have a look again.\n\n>> +trailer.<token>.where::\n>> +     This can be either `after`, which is the default, or\n>> +     `before`. If it is `before`, then a trailer with the specified\n>> +     token, will appear before, instead of after, other trailers\n>> +     with the same token, or otherwise at the beginning, instead of\n>> +     at the end, of all the trailers.\n>\n> Brainstorming: some other options that might make sense here someday:\n>\n> `end`: add new trailer after all existing trailers (even those with\n> different keys).  This would allow trailers to be kept in chronological\n> order.\n>\n> `beginning`: add new trailer before the first existing trailer (allows\n> reverse chronological order).\n>\n> `sorted`: add new trailer among the existing trailers with the same key\n> so as to keep their values in lexicographic order.\n\nYeah, I thought about these, but I don't think there is a need for\nthem right now.\n\n>> +trailer.<token>.ifexist::\n>> +     This option makes it possible to choose what action will be\n>> +     performed when there is already at least one trailer with the\n>> +     same token in the message.\n>> ++\n>> +The valid values for this option are: `addIfDifferent` (this is the\n>> +default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.\n>\n> Are these option values case sensitive?  If so, it might be a little bit\n> confusing because the same camel-case is often used in documentation for\n> configuration *keys*, which are not case sensitive [1], and users might\n> have gotten used to thinking of strings that look like this to be\n> non-case-sensitive.\n\nThere were some discussions a few versions of this series ago with\nPeff, Junio and perhaps others about this.\nI thought that being case insensitive was better and Peff kind of\nagreed with that, but as Junio disagreed it is now case sensitive.\n\n>> +With `addIfDifferent`, a new trailer will be added only if no trailer\n>> +with the same (token, value) pair is already in the message.\n>> ++\n>> +With `addIfDifferentNeighbor`, a new trailer will be added only if no\n>> +trailer with the same (token, value) pair is above or below the line\n>> +where the new trailer will be added.\n>> ++\n>> +With `add`, a new trailer will be added, even if some trailers with\n>> +the same (token, value) pair are already in the message.\n>> ++\n>> +With `overwrite`, the new trailer will overwrite an existing trailer\n>> +with the same token.\n>\n> What if there are multiple existing trailers with the same token?  Are\n> they all overwritten?\n\nNo, if where == after, only the last one is overwritten, and if where\n== before, only the first one is overwritten.\n\nI could add an \"overwriteAll\" option. It could be interesting to use\nwhen a command using \"$ARG\" is configured, as this way the command\nwould apply to all the trailers with the given token instead of just\nthe last or first one.\n\n>> +With `doNothing`, nothing will be done, that is no new trailer will be\n>> +added if there is already one with the same token in the message.\n>> +\n>> +trailer.<token>.ifmissing::\n>> +     This option makes it possible to choose what action will be\n>> +     performed when there is not yet any trailer with the same\n>> +     token in the message.\n>> ++\n>> +The valid values for this option are: `add` (this is the default) and\n>> +`doNothing`.\n>> ++\n>> +With `add`, a new trailer will be added.\n>> ++\n>> +With `doNothing`, nothing will be done.\n>> +\n>> +trailer.<token>.command::\n>> +     This option can be used to specify a shell command that will\n>> +     be used to automatically add or modify a trailer with the\n>> +     specified 'token'.\n>> ++\n>> +When this option is specified, it is like if a special 'token=value'\n>> +argument is added at the end of the command line, where 'value' will\n>> +be given by the standard output of the specified command.\n>\n> Maybe reword to\n>\n>     When this option is specified, the behavior is as if a special\n>     'token=value' argument were added at the end of the command line,\n>     where 'value' is taken to be the standard output of the specified\n>     command.\n>\n> And if it is the case, maybe add \"with leading and trailing whitespace\n> trimmed off\" at the end of the sentence.\n\nOk.\n\n>> +If the command contains the `$ARG` string, this string will be\n>> +replaced with the 'value' part of an existing trailer with the same\n>> +token, if any, before the command is launched.\n>\n> What if the key appears multiple times in existing trailers?\n\nIt will be done only once for the last or first trailer with the key\ndepending on \"where\".\n\n>> +\n>> +SEE ALSO\n>> +--------\n>> +linkgit:git-commit[1]\n>> +\n>> +GIT\n>> +---\n>> +Part of the linkgit:git[1] suite\n>>\n>\n> Doesn't this command have to be added to command-list.txt?\n\nMaybe, I will have a look.\n\nThanks,\nChristian.\n"},{"id":"238530","messageId":"CAP8UFD3sgUQk3dtpRaqkut0biQuV8AMPNQXCybg7p4cgrW-D0A@mail.gmail.com","threadId":"36360","inReplyTo":"xmqqvbuklt0q.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v10 12/12] trailer: add blank line before the trailers if needed","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2014-04-08T12:48:02Z","receivedAt":"2014-04-08T12:48:02Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Mon, Apr 7, 2014 at 11:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Hmph, this is more fixing a mistake made earlier in the series at\n> the end than adding a new feature or something.  Can you start from\n> a version that does not have the mistake from the beginning?\n\nOk, I will squash this patch in other previous patches.\n\nThanks,\nChristian.\n"},{"id":"238535","messageId":"534414FB.6040604@alum.mit.edu","threadId":"36360","inReplyTo":"CAP8UFD0RftewWj-oivojUrXCDqXUq6xX7ndQdixA2i=1BzZEFg@mail.gmail.com","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-04-08T15:25:47Z","receivedAt":"2014-04-08T15:25:47Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 04/08/2014 01:35 PM, Christian Couder wrote:\n> On Tue, Apr 8, 2014 at 9:30 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> How are existing trailers recognized in the input commit message?  Do\n>> trailers have to be configured to be recognized?  Or are all lines\n>> matching a specific pattern considered trailers?  If so, it might be\n>> helpful to include a regexp here that describes the trailer \"syntax\".\n> \n> The trailers are recognized in the input commit message using the\n> following rules:\n>  - only lines that contains a ':' are considered trailers,\n>  - the trailer lines must all be next to each other,\n>  - after them it's only possible to have some lines that contain only spaces,\n>  - before them there must be at least one line with only spaces\n\nThanks for all the explanation.  I think that most/all of this\ninformation should be included in the documentation.\n\n>>> +OPTIONS\n>>> +-------\n>>> +--trim-empty::\n>>> +     If the 'value' part of any trailer contains only whitespace,\n>>> +     the whole trailer will be removed from the resulting message.\n>>\n>> Does this apply to existing trailers, new trailers, or both?\n> \n> Both.\n> \n>> If it applies to existing trailers, then it seems a bit dangerous, in the\n>> sense that the command might end up changing trailers that are unrelated\n>> to the one that the command is trying to add.\n> \n> The command is not just for adding trailers.\n> But there could be an option to just trim trailers that are added.\n\nMaybe that should be the *only* behavior of this option.\n\nMaybe there should be a trailer.<token>.trimEmpty config option.\n\n>>> +CONFIGURATION VARIABLES\n>>> +-----------------------\n>>> +\n>>> +trailer.<token>.key::\n>>> +     This 'key' will be used instead of 'token' in the\n>>> +     trailer. After some alphanumeric characters, it can contain\n>>\n>> Trailer keys can also contain '-', right?\n> \n> Yes.\n> I should have written \"after the last alphanumeric character\".\n> I will fix that.\n> \n>>> +     some non alphanumeric characters like ':', '=' or '#' that will\n>>> +     be used instead of ':' to separate the token from the value in\n>>> +     the trailer, though the default ':' is more standard.\n>>\n>> Above it looks like the default separator is not ':' but rather ': '\n>> (with a space).  Is the space always added regardless of the value of\n>> this configuration variable, or should the configuration value include\n>> the trailing space if it is desired?  Is there any way to get a trailer\n>> that doesn't include a space, like\n>>\n>>     foo=bar\n>>\n>> ?  (Changing this to \"foo= bar\" would look pretty ugly.)\n> \n> I will have a look, but I think that:\n> \n> - a space is always added after ':' or '=',\n> - a space is never added after '#',\n> - it doesn't matter if there is a space or not in the configured key.\n> \n>> If a commit message containing trailer lines with separators other than\n>> ':' is input to the program, will it recognize them as trailer lines?\n> \n> No, '=' and '#' are not supported in the input message, only in the output.\n> \n>> Do such trailer lines have to have the same separator as the one listed\n>> in this configuration setting to be recognized?\n> \n> No they need to have ':' as a separator.\n> \n> The reason why only ':' is supported is because it is the cannonical\n> trailer separator and it could create problems with many input\n> messages if other separators where supported.\n> \n> Maybe we could detect a special line like the following:\n> \n> # TRAILERS START\n> \n> in the input message and consider everyhting after that line as trailers.\n> In this case it would be ok to accept other separators.\n\nIt would be ugly to have to use such a line.  I think it would be\npreferable to be more restrictive about trailer separators than to\nrequire something like this.\n\n>From what you've said above, it sounds like your code might get confused\nwith the following input commit message:\n\n    This is the human-readable comment\n\n    Foo: bar\n    Fixes #123\n    Plugh: xyzzy\n\nIt seems to me that none of these lines would be accepted as trailers,\nbecause they include a non-trailer \"Fixes\" line (non-trailer in the\nsense that it doesn't use a colon separator).\n\n>> I suppose that there is some compelling reason to allow non-colon\n>> separators here.  If not, it seems like it adds a lot of complexity and\n>> should maybe be omitted, or limited to only a few specific separators.\n> \n> Yeah, but in the early threads concerning this subject, someone said\n> that GitHub for example uses \"bug #XXX\".\n> I will have a look again.\n\nYes, that's true: GitHub recognizes strings like \"fixes #33\" but not if\nthere is an intervening colon like in \"fixes: #33\".  OTOH GitHub\nrecognizes such strings wherever they appear in the commit message (they\ndon't have to be in \"trailer\" lines).  So I'm not sure that the added\ncomplication is worth it if GitHub is the only use case.  (And maybe we\ncould convince GitHub to recognize \"Fixes: #33\" if such syntax becomes\nthe de-facto Git standard for trailers.)\n\n>>> +trailer.<token>.where::\n>>> +     This can be either `after`, which is the default, or\n>>> +     `before`. If it is `before`, then a trailer with the specified\n>>> +     token, will appear before, instead of after, other trailers\n>>> +     with the same token, or otherwise at the beginning, instead of\n>>> +     at the end, of all the trailers.\n>>\n>> Brainstorming: some other options that might make sense here someday:\n>>\n>> `end`: add new trailer after all existing trailers (even those with\n>> different keys).  This would allow trailers to be kept in chronological\n>> order.\n>>\n>> `beginning`: add new trailer before the first existing trailer (allows\n>> reverse chronological order).\n>>\n>> `sorted`: add new trailer among the existing trailers with the same key\n>> so as to keep their values in lexicographic order.\n> \n> Yeah, I thought about these, but I don't think there is a need for\n> them right now.\n\nYes, I didn't mean to imply that any of these options have to be in the\nfirst version.\n\n>>> +trailer.<token>.ifexist::\n>>> +     This option makes it possible to choose what action will be\n>>> +     performed when there is already at least one trailer with the\n>>> +     same token in the message.\n>>> ++\n>>> +The valid values for this option are: `addIfDifferent` (this is the\n>>> +default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.\n>>\n>> Are these option values case sensitive?  If so, it might be a little bit\n>> confusing because the same camel-case is often used in documentation for\n>> configuration *keys*, which are not case sensitive [1], and users might\n>> have gotten used to thinking of strings that look like this to be\n>> non-case-sensitive.\n> \n> There were some discussions a few versions of this series ago with\n> Peff, Junio and perhaps others about this.\n> I thought that being case insensitive was better and Peff kind of\n> agreed with that, but as Junio disagreed it is now case sensitive.\n\nOK, it's my fault for not having followed along with the history of this\npatch series.\n\n>>> +With `addIfDifferent`, a new trailer will be added only if no trailer\n>>> +with the same (token, value) pair is already in the message.\n>>> ++\n>>> +With `addIfDifferentNeighbor`, a new trailer will be added only if no\n>>> +trailer with the same (token, value) pair is above or below the line\n>>> +where the new trailer will be added.\n>>> ++\n>>> +With `add`, a new trailer will be added, even if some trailers with\n>>> +the same (token, value) pair are already in the message.\n>>> ++\n>>> +With `overwrite`, the new trailer will overwrite an existing trailer\n>>> +with the same token.\n>>\n>> What if there are multiple existing trailers with the same token?  Are\n>> they all overwritten?\n> \n> No, if where == after, only the last one is overwritten, and if where\n> == before, only the first one is overwritten.\n> \n> I could add an \"overwriteAll\" option. It could be interesting to use\n> when a command using \"$ARG\" is configured, as this way the command\n> would apply to all the trailers with the given token instead of just\n> the last or first one.\n\nIt seems to me that the current behavior (rewriting exactly one existing\nline) is not that useful.  Why not make \"overwrite\" overwrite *all*\nexisting matching lines?\n\n>>> +With `doNothing`, nothing will be done, that is no new trailer will be\n>>> +added if there is already one with the same token in the message.\n\nI just noticed a punctuation problem (comma -> semicolon and add comma)\nin the sentence above:\n\n    With `doNothing`, nothing will be done; that is, no new trailer\n    will be added if there is already one with the same token in the\n    message.\n\n>>> +\n>>> +trailer.<token>.ifmissing::\n>>> +     This option makes it possible to choose what action will be\n>>> +     performed when there is not yet any trailer with the same\n>>> +     token in the message.\n>>> ++\n>>> +The valid values for this option are: `add` (this is the default) and\n>>> +`doNothing`.\n>>> ++\n>>> +With `add`, a new trailer will be added.\n>>> ++\n>>> +With `doNothing`, nothing will be done.\n>>> +\n>>> +trailer.<token>.command::\n>>> +     This option can be used to specify a shell command that will\n>>> +     be used to automatically add or modify a trailer with the\n>>> +     specified 'token'.\n>>> ++\n>>> +When this option is specified, it is like if a special 'token=value'\n>>> +argument is added at the end of the command line, where 'value' will\n>>> +be given by the standard output of the specified command.\n>>\n>> Maybe reword to\n>>\n>>     When this option is specified, the behavior is as if a special\n>>     'token=value' argument were added at the end of the command line,\n>>     where 'value' is taken to be the standard output of the specified\n>>     command.\n>>\n>> And if it is the case, maybe add \"with leading and trailing whitespace\n>> trimmed off\" at the end of the sentence.\n> \n> Ok.\n> \n>>> +If the command contains the `$ARG` string, this string will be\n>>> +replaced with the 'value' part of an existing trailer with the same\n>>> +token, if any, before the command is launched.\n>>\n>> What if the key appears multiple times in existing trailers?\n> \n> It will be done only once for the last or first trailer with the key\n> depending on \"where\".\n\nIt seems like the UI for \"git interpret-trailers\" is optimized for\ntrailers that appear only once.  That's a bit limiting.  Maybe it would\nbe better to pipe the existing values to the command's standard input,\none per line?  For example, suppose we run\n\n    git interpret-trailers \\\n        Signed-off-by='Christian Couder <christian.couder@gmail.com>'\n\nwith the following input:\n\n    Human-readable subject\n\n    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    Signed-off-by: Christian Couder <christian.couder@gmail.com>\n\nThen the following three lines could be piped to the command (i.e., one\nvalue per line, without the key):\n\n    Junio C Hamano <gitster@pobox.com>\n    Christian Couder <christian.couder@gmail.com>\n    Christian Couder <christian.couder@gmail.com>\n\nThen, supposing the command were \"sort --unique\", the command's output\nwould be\n\n    Christian Couder <christian.couder@gmail.com>\n    Junio C Hamano <gitster@pobox.com>\n\nwhich would be converted back into trailer lines by prepending\n\"Signed-off-by: \", resulting in the modified commit message\n\n    Human-readable subject\n\n    Signed-off-by: Christian Couder <christian.couder@gmail.com>\n    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\n(Not that we would want to do with \"Signed-off-by\" trailers, but you get\nthe idea.)\n\n\nI'm really sorry for coming so late to the show.  Feel free to ignore\nany of my comments with the justification \"too late\".\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"238538","messageId":"xmqq7g6zlq5a.fsf@gitster.dls.corp.google.com","threadId":"36360","inReplyTo":"5343A589.10503@alum.mit.edu","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-08T16:52:49Z","receivedAt":"2014-04-08T16:52:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> Sorry for reappearing in this thread after such a long absence.  I\n> wanted to see what is coming up (I think this interpret-trailers command\n> will be handy!) so I read this documentation patch carefully, and added\n> some questions and suggestions below.\n\nThanks for reading the patch carefully.  It helps to have fresh set\nof eyes that are not contaminated by the preconception formed by\nprevious discussions, especially when reviewing the documentation\nwhose primary target audiences are those who do not care about these\nprevious back-and-forth.\n\n>> +trailer.<token>.where::\n>> +\tThis can be either `after`, which is the default, or\n>> +\t`before`. If it is `before`, then a trailer with the specified\n>> +\ttoken, will appear before, instead of after, other trailers\n>> +\twith the same token, or otherwise at the beginning, instead of\n>> +\tat the end, of all the trailers.\n>\n> Brainstorming: some other options that might make sense here someday:\n> ...\n>> +trailer.<token>.ifexist::\n>> +\tThis option makes it possible to choose what action will be\n>> +\tperformed when there is already at least one trailer with the\n>> +\tsame token in the message.\n>> ++\n>> +The valid values for this option are: `addIfDifferent` (this is the\n>> +default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.\n>\n> Are these option values case sensitive?\n\nIt is interesting and somewhat sad that it all has to come back\ntogether inter-twined.  From the very beginning, I was opposed to\nhaving logical complexity that requires multi-words in both variable\nnames (e.g. \"if-exist\") and values (e.g. \"add-if-different\"), and\nafter $gmane/241929 where I let the devil's advocate \"how about\nmaking the variable simpler without logical operation and put all\nthe conditional on the value side?\" suggestion shot down, I somehow\nwas hoping that the value part got a lot simpler not to require\nmulti-words, which would have meant that we would not have to worry\nabout \"Is it addIfDifferent? add-if-different? or Add_If_Different?\"\nat all.  Sadly that is not what we have ended up with.\n\nSo, with that realization...\n\n> If so, it might be a little bit\n> confusing because the same camel-case is often used in documentation for\n> configuration *keys*, which are not case sensitive [1], and users might\n> have gotten used to thinking of strings that look like this to be\n> non-case-sensitive.\n\n... very true.  Having to have these enum values as so complex to\nrequire multi-words is probably the root cause of the confusion, and\nwe might probably be better off if we did not have to, but it would\nbe helpful to allow various different spellings (i.e. make them case\ninsensitive to allow random camel spellings, and also accept things\nlike \"add-if-different\" as well) if we absolutely have to have these\ncomplex values.\n\nBut you had a lot of good questions and suggestions for possible\nfuture enhancements that we would need to take into account while\ndesigning the overall scheme to later allow them to fit into.  Maybe\na value that is a single-token that consists of just a few words\n(e.g. \"addIfDifferent\") may not be the best way to go after all.\n\nI dunno.\n\n> What if there are multiple existing trailers with the same token?  Are\n> they all overwritten?\n> ...\n> What if the key appears multiple times in existing trailers?\n\nAll good questions, I would think.\n"},{"id":"238567","messageId":"xmqqmwfv3433.fsf@gitster.dls.corp.google.com","threadId":"36360","inReplyTo":"20140406170204.15116.15559.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-08T21:26:40Z","receivedAt":"2014-04-08T21:26:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> +Help add RFC 822-like headers, called 'trailers', at the end of the\n> +otherwise free-form part of a commit message.\n\nI think it is somewhat misleading to use the word \"headers\" like\nthat.  'trailers' look similar to RFC-822-headers but they come at\nthe end.  The sentence however reads as if they are \"headers\" that\nlook like RFC 822.  Perhaps shuffling words like so:\n\n\tHelp adding 'trailers' lines, that look similar to RFC 822\n\te-mail headers, at the end of the ...\n\nwould make it less confusing.\n\n> +Some configuration variables control the way the `token` arguments are\n> +applied to the message and the way any existing trailer in the message\n> +is changed. They also make it possible to automatically add some\n> +trailers.\n> +\n> +By default, a 'token=value' or 'token:value' argument will be added\n> +only if no trailer with the same (token, value) pair is already in the\n> +message. The 'token' and 'value' parts will be trimmed to remove\n> +starting and trailing whitespace, and the resulting trimmed 'token'\n> +and 'value' will appear in the message like this:\n> +\n> +------------------------------------------------\n> +token: value\n> +------------------------------------------------\n\nMental note: this does assume that the final output for the 'token'\nis to have a line <label> that is followed by a colon \":\", SP and\nthe value.\n\nAnd the natural way to express that on the command line would be to\nsay \"token: value\", I would think, but let's just read on.\n\n> +Note that 'trailers' do not follow and are not intended to follow many\n> +rules that are in RFC 822. For example they do not follow the line\n> +breaking rules, the encoding rules and probably many other rules.\n\ns/that are in RFC 822/for RFC 822 headers/.\ns/line breaking/line folding/. (see RFC 822, 3.1.1)\n\n> +OPTIONS\n> +-------\n> +--trim-empty::\n> +\tIf the 'value' part of any trailer contains only whitespace,\n> +\tthe whole trailer will be removed from the resulting message.\n> +\n> +CONFIGURATION VARIABLES\n> +-----------------------\n> +\n> +trailer.<token>.key::\n> +\tThis 'key' will be used instead of 'token' in the\n\nAs `key` is something that is typed literally, it should be typeset\nas `key` in the descriptive text.  I think other manpages spell the\nplaceholder as `<token>` (or '<token>', I am not sure which...).\n\n> +\ttrailer. After some alphanumeric characters, it can contain\n> +\tsome non alphanumeric characters like ':', '=' or '#' that will\n> +\tbe used instead of ':' to separate the token from the value in\n> +\tthe trailer, though the default ':' is more standard.\n\nI assume that this is for things like\n\n\tbug #538\n\nand the configuration would say something like:\n\n\t[trailer \"bug\"]\n        \tkey = \"bug #\"\n\nFor completeness (of this example), the bog-standard s-o-b would\nlook like\n\n\tSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n\nand the configuration for it that spell the redundant \"key\" would\nbe:\n\n\t[trailer \"Signed-off-by\"]\n        \tkey = \"Signed-off-by: \"\n\nAm I reading the intention correctly?\n\nThat is, when trailer.<token>.key is not defined, the value defaults\nto \"<token>: \" (with one SP after the label and colon), and when it\nis defined, the value can come directly after it.\n"},{"id":"239707","messageId":"20140425.215619.2296838250398594645.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"xmqqmwfv3433.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-25T19:56:19Z","receivedAt":"2014-04-25T19:56:19Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"From: Junio C Hamano <gitster@pobox.com>\n>\n> Christian Couder <chriscool@tuxfamily.org> writes:\n> \n>> +Help add RFC 822-like headers, called 'trailers', at the end of the\n>> +otherwise free-form part of a commit message.\n> \n> I think it is somewhat misleading to use the word \"headers\" like\n> that.  'trailers' look similar to RFC-822-headers but they come at\n> the end.  The sentence however reads as if they are \"headers\" that\n> look like RFC 822.  Perhaps shuffling words like so:\n> \n> \tHelp adding 'trailers' lines, that look similar to RFC 822\n> \te-mail headers, at the end of the ...\n> \n> would make it less confusing.\n\nOk, I made this change in v11.\n\n>> +Some configuration variables control the way the `token` arguments are\n>> +applied to the message and the way any existing trailer in the message\n>> +is changed. They also make it possible to automatically add some\n>> +trailers.\n>> +\n>> +By default, a 'token=value' or 'token:value' argument will be added\n>> +only if no trailer with the same (token, value) pair is already in the\n>> +message. The 'token' and 'value' parts will be trimmed to remove\n>> +starting and trailing whitespace, and the resulting trimmed 'token'\n>> +and 'value' will appear in the message like this:\n>> +\n>> +------------------------------------------------\n>> +token: value\n>> +------------------------------------------------\n> \n> Mental note: this does assume that the final output for the 'token'\n> is to have a line <label> that is followed by a colon \":\", SP and\n> the value.\n> \n> And the natural way to express that on the command line would be to\n> say \"token: value\", I would think, but let's just read on.\n> \n>> +Note that 'trailers' do not follow and are not intended to follow many\n>> +rules that are in RFC 822. For example they do not follow the line\n>> +breaking rules, the encoding rules and probably many other rules.\n> \n> s/that are in RFC 822/for RFC 822 headers/.\n> s/line breaking/line folding/. (see RFC 822, 3.1.1)\n\nOk, it's in v11 too.\n\n>> +OPTIONS\n>> +-------\n>> +--trim-empty::\n>> +\tIf the 'value' part of any trailer contains only whitespace,\n>> +\tthe whole trailer will be removed from the resulting message.\n>> +\n>> +CONFIGURATION VARIABLES\n>> +-----------------------\n>> +\n>> +trailer.<token>.key::\n>> +\tThis 'key' will be used instead of 'token' in the\n> \n> As `key` is something that is typed literally, it should be typeset\n> as `key` in the descriptive text.\n\nOk, I used `key` in v11.\n\n> I think other manpages spell the\n> placeholder as `<token>` (or '<token>', I am not sure which...).\n\nI found mostly <token>, so I used that in v11.\n\n>> +\ttrailer. After some alphanumeric characters, it can contain\n>> +\tsome non alphanumeric characters like ':', '=' or '#' that will\n>> +\tbe used instead of ':' to separate the token from the value in\n>> +\tthe trailer, though the default ':' is more standard.\n> \n> I assume that this is for things like\n> \n> \tbug #538\n> \n> and the configuration would say something like:\n> \n> \t[trailer \"bug\"]\n>         \tkey = \"bug #\"\n> \n> For completeness (of this example), the bog-standard s-o-b would\n> look like\n> \n> \tSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n> \n> and the configuration for it that spell the redundant \"key\" would\n> be:\n> \n> \t[trailer \"Signed-off-by\"]\n>         \tkey = \"Signed-off-by: \"\n\nYeah, but you can use the following instead:\n\n \t[trailer \"s-o-b\"]\n         \tkey = \"Signed-off-by: \"\n\nThe <token> and the key can be different.\n\n> Am I reading the intention correctly?\n\nYeah, I think so.\n\n> That is, when trailer.<token>.key is not defined, the value defaults\n> to \"<token>: \" (with one SP after the label and colon),\n\nYes.\n\n> and when it\n> is defined, the value can come directly after it.\n\nThe value can come directly after the key, only if the key ends with '#'.\n\nIf it ends with something else, except spaces, one SP will be added\nbetween the key and the value.\n\nYeah, I made '#' special in the hope that it would be more compatible\nwith GitHub and other services that might also use '#'.\n\nThanks,\nChristian.\n"},{"id":"239710","messageId":"20140425.230710.1024850359228182788.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"534414FB.6040604@alum.mit.edu","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-04-25T21:07:10Z","receivedAt":"2014-04-25T21:07:10Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n>\n> On 04/08/2014 01:35 PM, Christian Couder wrote:\n>> \n>> The trailers are recognized in the input commit message using the\n>> following rules:\n>>  - only lines that contains a ':' are considered trailers,\n>>  - the trailer lines must all be next to each other,\n>>  - after them it's only possible to have some lines that contain only spaces,\n>>  - before them there must be at least one line with only spaces\n> \n> Thanks for all the explanation.  I think that most/all of this\n> information should be included in the documentation.\n\nOk, I included the above rules in v11, but maybe not other pieces of\ninformation that you might have wanted.\n\n>>>> +OPTIONS\n>>>> +-------\n>>>> +--trim-empty::\n>>>> +     If the 'value' part of any trailer contains only whitespace,\n>>>> +     the whole trailer will be removed from the resulting message.\n>>>\n>>> Does this apply to existing trailers, new trailers, or both?\n>> \n>> Both.\n>> \n>>> If it applies to existing trailers, then it seems a bit dangerous, in the\n>>> sense that the command might end up changing trailers that are unrelated\n>>> to the one that the command is trying to add.\n>> \n>> The command is not just for adding trailers.\n>> But there could be an option to just trim trailers that are added.\n> \n> Maybe that should be the *only* behavior of this option.\n> \n> Maybe there should be a trailer.<token>.trimEmpty config option.\n\nOne possible usage of the \"git interpret-trailers\" command that was\ndiscussed in the early threads was the following:\n\n1) You have a commit message template like the following:\n\n-----------------\n***SUBJECT***\n\n***MESSAGE***\n\nFixes: \nCc: \nCc: \nReviewed-by: \nReviewed-by: \nSigned-off-by: \n-----------------\n\n2) The user add some information when committing:\n\n$ git commit --trailer \"Fixes:534\" --trailer \"Signed-off-by: Michael <mhagger@alum.mit.edu>\"\n\n3) \"git interpret-trailers\" is used automatically by \"git commit\"\nwithout --trim-empty, and it is passed the --trailer arguments and the\ncommit message template, so the user is shown the result which is for\nexample the following:\n\n-----------------\n***SUBJECT***\n\n***MESSAGE***\n\nFixes: 534\nCc: \nCc: \nReviewed-by: \nReviewed-by: \nSigned-off-by: Michael <mhagger@alum.mit.edu>\n-----------------\n\n4) The user adds some information and the resulting message is for\nexample:\n\n-----------------\nDoing foo and bar\n\nAnd also baz.\n\nFixes: 534\nCc: \nCc: Peff <peff@peff.net>\nReviewed-by: Junio <gitster@pobox.com>\nReviewed-by: \nSigned-off-by: Michael <mhagger@alum.mit.edu>\n-----------------\n\n5) Then a post commit hook automatically uses \"git interpret-trailers\n--trim-empty\" on the result, so the commit message is eventually the\nfollowing:\n\n-----------------\nDoing foo and bar\n\nAnd also baz.\n\nFixes: 534\nCc: Peff <peff@peff.net>\nReviewed-by: Junio <gitster@pobox.com>\nSigned-off-by: Michael <mhagger@alum.mit.edu>\n-----------------\n\nSo I think it could be very useful to have --trim-empty work on all\nthe trailers, not just those passed as arguments.\n\n>>> If a commit message containing trailer lines with separators other than\n>>> ':' is input to the program, will it recognize them as trailer lines?\n>> \n>> No, '=' and '#' are not supported in the input message, only in the output.\n>> \n>>> Do such trailer lines have to have the same separator as the one listed\n>>> in this configuration setting to be recognized?\n>> \n>> No they need to have ':' as a separator.\n>> \n>> The reason why only ':' is supported is because it is the cannonical\n>> trailer separator and it could create problems with many input\n>> messages if other separators where supported.\n>> \n>> Maybe we could detect a special line like the following:\n>> \n>> # TRAILERS START\n>> \n>> in the input message and consider everyhting after that line as trailers.\n>> In this case it would be ok to accept other separators.\n> \n> It would be ugly to have to use such a line.  I think it would be\n> preferable to be more restrictive about trailer separators than to\n> require something like this.\n\nThe code is already very restrictive about trailer separators.\n\n> From what you've said above, it sounds like your code might get confused\n> with the following input commit message:\n> \n>     This is the human-readable comment\n> \n>     Foo: bar\n>     Fixes #123\n>     Plugh: xyzzy\n> \n> It seems to me that none of these lines would be accepted as trailers,\n> because they include a non-trailer \"Fixes\" line (non-trailer in the\n> sense that it doesn't use a colon separator).\n\nYeah, they would not be accepted because the code is very restrictive.\n\nThe following would be accepted:\n\n     Foo: bar\n     Fixes: 123\n     Plugh: xyzzy\n\n>>> I suppose that there is some compelling reason to allow non-colon\n>>> separators here.  If not, it seems like it adds a lot of complexity and\n>>> should maybe be omitted, or limited to only a few specific separators.\n>> \n>> Yeah, but in the early threads concerning this subject, someone said\n>> that GitHub for example uses \"bug #XXX\".\n>> I will have a look again.\n> \n> Yes, that's true: GitHub recognizes strings like \"fixes #33\" but not if\n> there is an intervening colon like in \"fixes: #33\".  OTOH GitHub\n> recognizes such strings wherever they appear in the commit message (they\n> don't have to be in \"trailer\" lines).  So I'm not sure that the added\n> complication is worth it if GitHub is the only use case.  (And maybe we\n> could convince GitHub to recognize \"Fixes: #33\" if such syntax becomes\n> the de-facto Git standard for trailers.)\n\nI don't think there is a lot of complexity.\nBut maybe I need to explain how it works better.\nFeel free to suggest me sentences I could add.\n\n>>>> +With `addIfDifferent`, a new trailer will be added only if no trailer\n>>>> +with the same (token, value) pair is already in the message.\n>>>> ++\n>>>> +With `addIfDifferentNeighbor`, a new trailer will be added only if no\n>>>> +trailer with the same (token, value) pair is above or below the line\n>>>> +where the new trailer will be added.\n>>>> ++\n>>>> +With `add`, a new trailer will be added, even if some trailers with\n>>>> +the same (token, value) pair are already in the message.\n>>>> ++\n>>>> +With `overwrite`, the new trailer will overwrite an existing trailer\n>>>> +with the same token.\n>>>\n>>> What if there are multiple existing trailers with the same token?  Are\n>>> they all overwritten?\n>> \n>> No, if where == after, only the last one is overwritten, and if where\n>> == before, only the first one is overwritten.\n>> \n>> I could add an \"overwriteAll\" option. It could be interesting to use\n>> when a command using \"$ARG\" is configured, as this way the command\n>> would apply to all the trailers with the given token instead of just\n>> the last or first one.\n> \n> It seems to me that the current behavior (rewriting exactly one existing\n> line) is not that useful.  Why not make \"overwrite\" overwrite *all*\n> existing matching lines?\n\nI was thinking that people could use the following template message:\n\n---------------\nSigned-off-by: \nSigned-off-by: YOU-WILL-BE-AUTOMATICALLY-ADDED-HERE\n---------------\n\nand the following config:\n\n---------------\n[trailer \"s-o-b\"]\n\t key = \"Signed-off-by: \"\n\t ifexist = overwrite\n\t command = echo \\\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\\\"\n---------------\n\nThis way the user can add other people's s-o-b before the last one\nwhich will always contain his own s-o-b.\n\n>>>> +If the command contains the `$ARG` string, this string will be\n>>>> +replaced with the 'value' part of an existing trailer with the same\n>>>> +token, if any, before the command is launched.\n>>>\n>>> What if the key appears multiple times in existing trailers?\n>> \n>> It will be done only once for the last or first trailer with the key\n>> depending on \"where\".\n> \n> It seems like the UI for \"git interpret-trailers\" is optimized for\n> trailers that appear only once.  That's a bit limiting.\n\nAs I said, it is possible to add an overwriteAll option.\nI think that would fix the current limitations.\n\n> Maybe it would\n> be better to pipe the existing values to the command's standard input,\n> one per line?  For example, suppose we run\n> \n>     git interpret-trailers \\\n>         Signed-off-by='Christian Couder <christian.couder@gmail.com>'\n> \n> with the following input:\n> \n>     Human-readable subject\n> \n>     Signed-off-by: Junio C Hamano <gitster@pobox.com>\n>     Signed-off-by: Christian Couder <christian.couder@gmail.com>\n> \n> Then the following three lines could be piped to the command (i.e., one\n> value per line, without the key):\n> \n>     Junio C Hamano <gitster@pobox.com>\n>     Christian Couder <christian.couder@gmail.com>\n>     Christian Couder <christian.couder@gmail.com>\n> \n> Then, supposing the command were \"sort --unique\", the command's output\n> would be\n> \n>     Christian Couder <christian.couder@gmail.com>\n>     Junio C Hamano <gitster@pobox.com>\n> \n> which would be converted back into trailer lines by prepending\n> \"Signed-off-by: \", resulting in the modified commit message\n> \n>     Human-readable subject\n> \n>     Signed-off-by: Christian Couder <christian.couder@gmail.com>\n>     Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> \n> (Not that we would want to do with \"Signed-off-by\" trailers, but you get\n> the idea.)\n\nYeah, but I think the existing options like addIfDifferent and\naddIfDifferentNeighbor are simpler to do these kind of things.\n\nAnd it is also possible to add a \"where = sorted\" option.\n\nAnd if later we realize that people are still not happy, we can still\nadd a special \"trailer.<token>.filter\" that could do what you suggest.\n\nThanks,\nChristian.\n"},{"id":"239863","messageId":"535E2A69.30600@alum.mit.edu","threadId":"36360","inReplyTo":"20140425.230710.1024850359228182788.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-04-28T10:16:09Z","receivedAt":"2014-04-28T10:16:09Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 04/25/2014 11:07 PM, Christian Couder wrote:\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n>>>>> +OPTIONS\n>>>>> +-------\n>>>>> +--trim-empty::\n>>>>> +     If the 'value' part of any trailer contains only whitespace,\n>>>>> +     the whole trailer will be removed from the resulting message.\n>>>>\n>>>> Does this apply to existing trailers, new trailers, or both?\n>>>\n>>> Both.\n>>>\n>>>> If it applies to existing trailers, then it seems a bit dangerous, in the\n>>>> sense that the command might end up changing trailers that are unrelated\n>>>> to the one that the command is trying to add.\n>>>\n>>> The command is not just for adding trailers.\n>>> But there could be an option to just trim trailers that are added.\n>>\n>> Maybe that should be the *only* behavior of this option.\n>>\n>> Maybe there should be a trailer.<token>.trimEmpty config option.\n> \n> One possible usage of the \"git interpret-trailers\" command that was\n> discussed in the early threads was the following:\n> \n> 1) You have a commit message template like the following:\n> \n> -----------------\n> ***SUBJECT***\n> \n> ***MESSAGE***\n> \n> Fixes: \n> Cc: \n> Cc: \n> Reviewed-by: \n> Reviewed-by: \n> Signed-off-by: \n> -----------------\n> [...etc...]\n\nThanks for the explanation.  Now the --trim-empty option makes a lot\nmore sense.\n\n>>>> If a commit message containing trailer lines with separators other than\n>>>> ':' is input to the program, will it recognize them as trailer lines?\n>>>\n>>> No, '=' and '#' are not supported in the input message, only in the output.\n>>>\n>>>> Do such trailer lines have to have the same separator as the one listed\n>>>> in this configuration setting to be recognized?\n>>>\n>>> No they need to have ':' as a separator.\n>>>\n>>> The reason why only ':' is supported is because it is the cannonical\n>>> trailer separator and it could create problems with many input\n>>> messages if other separators where supported.\n>>>\n>>> Maybe we could detect a special line like the following:\n>>>\n>>> # TRAILERS START\n>>>\n>>> in the input message and consider everyhting after that line as trailers.\n>>> In this case it would be ok to accept other separators.\n>>\n>> It would be ugly to have to use such a line.  I think it would be\n>> preferable to be more restrictive about trailer separators than to\n>> require something like this.\n> \n> The code is already very restrictive about trailer separators.\n> \n>> From what you've said above, it sounds like your code might get confused\n>> with the following input commit message:\n>>\n>>     This is the human-readable comment\n>>\n>>     Foo: bar\n>>     Fixes #123\n>>     Plugh: xyzzy\n>>\n>> It seems to me that none of these lines would be accepted as trailers,\n>> because they include a non-trailer \"Fixes\" line (non-trailer in the\n>> sense that it doesn't use a colon separator).\n> \n> Yeah, they would not be accepted because the code is very restrictive.\n> \n> The following would be accepted:\n> \n>      Foo: bar\n>      Fixes: 123\n>      Plugh: xyzzy\n> \n>>>> I suppose that there is some compelling reason to allow non-colon\n>>>> separators here.  If not, it seems like it adds a lot of complexity and\n>>>> should maybe be omitted, or limited to only a few specific separators.\n>>>\n>>> Yeah, but in the early threads concerning this subject, someone said\n>>> that GitHub for example uses \"bug #XXX\".\n>>> I will have a look again.\n>>\n>> Yes, that's true: GitHub recognizes strings like \"fixes #33\" but not if\n>> there is an intervening colon like in \"fixes: #33\".  OTOH GitHub\n>> recognizes such strings wherever they appear in the commit message (they\n>> don't have to be in \"trailer\" lines).  So I'm not sure that the added\n>> complication is worth it if GitHub is the only use case.  (And maybe we\n>> could convince GitHub to recognize \"Fixes: #33\" if such syntax becomes\n>> the de-facto Git standard for trailers.)\n> \n> I don't think there is a lot of complexity.\n> But maybe I need to explain how it works better.\n> Feel free to suggest me sentences I could add.\n\nI am really excited about having better support for trailers in Git, and\nI want to thank you for your work.  For me the promise of trailers is\n\n* A way for users to add information to commits for whatever purpose\n  they want, without having to convince upstream to built support in.\n\n* A standard format for that information, so that all tools can agree\n  how to read/write trailers without being confused by or breaking\n  trailers that they didn't know about in advance.\n\n* A format that is straightforward enough that it can be machine-\n  readable with minimum ambiguity.\n\n* Some command-line tools to make it easy for scripts to work with\n  trailers, and that serve as a reference implementation that other\n  Git implementations can imitate.  For example, I totally expect that\n  we will soon want a command-line tool for inquiring about the\n  presence and contents of trailers, for use in scripting.  Eventually\n  we will want to be able to do stuff like\n\n      git trailers --get-all s-o-b origin/master..origin/next\n      git rev-list --trailer=s-o-b:gitster@pobox.com master\n      git trailers --pipe --draft \\\n          --add-first fixes \\\n          --append '# You can delete the following line:' \\\n          --append s-o-b:\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\" \\\n          --unset private\n      git trailers --pipe --verify --tidy-up\n\nI think it is really important to nail down the format of trailers\ntightly enough that everybody who reads/writes a commit message agrees\nabout exactly what trailers are there.  For example the specification\nmight look something like:\n\n    A commit message can optionally end with a block of trailers.\n    The trailers, if present, must be separated from the rest of the\n    commit message by one or more blank lines (lines that contain only\n    whitespace).  There must be no blank lines within the list of\n    trailers.  It is allowed to have blank lines after the trailers.\n\n    Each trailer line must match the following Perl regular\n    expression:\n\n        ^([A-Za-z0-9_-]+)\\s*:\\s*(.*[^\\s])\\s*$\n\n    The string matching the first group is called the key and the string\n    matching the second is called the value.  Keys are considered to be\n    case-insensitive [or should they be case-sensitive?].  The\n    interpretation of values is left entirely up to the application.\n    Values must not be empty.\n\n    However, in --draft and --cleanup modes, empty values *are*\n    allowed, as are comments (lines starting with `core.commentchar`)\n    within the trailer block.  In --draft mode such lines are passed\n    through unchanged, and in --cleanup mode such lines are removed.\n\nI'm not saying this is the exact definition that we want; I'm just\nproviding an example of the level of precision that I think is needed.\n\nWith regard to the separator character, my concern is not about how to\ndocument the rules for this one tool.  It's more about having really\nwell-defined rules that are consistent between reading and writing.  For\nme it seems silly to let \"git interpret-trailers\" output trailers that\nit doesn't know how to read back in, and pretty much be a show-stopper\nif the presence of such trailers makes the tool unable to read other\ntrailers in the same commit message.\n\nSo my preference would be to make the format of trailers really strict;\nfor example, only allowing colon separators as in the regexp above.\nPeople who want to work with trailers using Git tools will just have to\nconform to this format.\n\nBut if we must support flexibility in the separator characters, then I\nthink it is important that we be able to read whatever we can write.\nFor me this means:\n\n* Enumerating a list of allowed separators (e.g., [:=#])\n\n* Specifying how it is decided what separator to use when generating\n  new trailers\n\n* Specifying what appends when a trailer is read and then written again:\n  is its separator preserved, or is the trailer converted to use the\n  separator configured for that particular key in the config.  And if\n  the latter, what happens if a key's separator is not configured?\n\n* Specifying whether whitespace around a separator is adjusted when\n  reading then writing a trailer.  For example, is\n\n      Foo SP SP : HT bar SP\n\n  canonicalized to\n\n      Foo: SP bar\n\n  (SP=space, HT=tab)?  What about\n\n      Fixes SP #33\n\n  ?  What if the separator for the \"fixes\" key is not configured?\n\nThe reason that I prefer supporting only colons is that more flexibility\ninevitably raises lots of questions like this, makes the documentation\nand implementation more complicated, and makes it harder for other\nimplementations to be sure they agree 100% with the reference\nimplementation.\n\n>>>>> +With `addIfDifferent`, a new trailer will be added only if no trailer\n>>>>> +with the same (token, value) pair is already in the message.\n>>>>> ++\n>>>>> +With `addIfDifferentNeighbor`, a new trailer will be added only if no\n>>>>> +trailer with the same (token, value) pair is above or below the line\n>>>>> +where the new trailer will be added.\n>>>>> ++\n>>>>> +With `add`, a new trailer will be added, even if some trailers with\n>>>>> +the same (token, value) pair are already in the message.\n>>>>> ++\n>>>>> +With `overwrite`, the new trailer will overwrite an existing trailer\n>>>>> +with the same token.\n>>>>\n>>>> What if there are multiple existing trailers with the same token?  Are\n>>>> they all overwritten?\n>>>\n>>> No, if where == after, only the last one is overwritten, and if where\n>>> == before, only the first one is overwritten.\n>>>\n>>> I could add an \"overwriteAll\" option. It could be interesting to use\n>>> when a command using \"$ARG\" is configured, as this way the command\n>>> would apply to all the trailers with the given token instead of just\n>>> the last or first one.\n>>\n>> It seems to me that the current behavior (rewriting exactly one existing\n>> line) is not that useful.  Why not make \"overwrite\" overwrite *all*\n>> existing matching lines?\n> \n> I was thinking that people could use the following template message:\n> \n> ---------------\n> Signed-off-by: \n> Signed-off-by: YOU-WILL-BE-AUTOMATICALLY-ADDED-HERE\n> ---------------\n> \n> and the following config:\n> \n> ---------------\n> [trailer \"s-o-b\"]\n> \t key = \"Signed-off-by: \"\n> \t ifexist = overwrite\n> \t command = echo \\\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\\\"\n> ---------------\n> \n> This way the user can add other people's s-o-b before the last one\n> which will always contain his own s-o-b.\n\nThat seems fragile.  For example, if the user changes the lines to\n\n    Signed-off-by: Somebody Else <...>\n\n(deleting the \"YOU-WILL-BE\" line, maybe because they don't want to sign\noff the commit) then not only will their wish be contradicted, but also\nSomebody Else would be deleted.\n\nWhat about allowing a --draft option, which allows blank trailer values\nplus comments interspersed in the trailer lines?  (I.e., the equivalent\nof --trim-empty could be the default and --draft would turn it off plus\nallow interspersed comments.)  Then the template could be\n\n    # You can add one or more Signed-off-by lines for other people here.\n    # A Signed-off-by line for you will be appended automatically when\n    # you commit.\n    Signed-off-by:\n\nOr, even better:\n\n    # You can add one or more Signed-off-by lines here:\n    Signed-off-by:\n    Signed-off-by:\n    # You can delete the following line if you don't want it:\n    Signed-off-by: Me <me@example.com>\n\n; i.e., the Signed-off-by line for the author could be filled in\n*before* the user is asked to edit the commit message.  There could also\nbe a --cleanup mode that allows blank values and comments on input but\nremoves them from the output.\n\n> [...]\n\nGiven Git's requirements for backwards compatibility, a specification\nthat we release now will have to be supported forever (because it will\nbe baked into commits and can *never* be changed), and any\ntrailer-handling tools that we release now will have to be supported for\nyears (until at least Git 3.0).\n\nAll in all, I think that there has been a lot of discussion about the\ninterface of this one command, \"git interpret-trailers\", including its\nquite complicated configuration and a command-line behavior.  And yet it\nseems to me that not many Git developers have been very engaged in the\nconversation, and Junio (who has) still doesn't seem satisfied with it.\n I (though among the too-little engaged) have the feeling that it is\nstill a ways from maturity.\n\nOn the contrary, the data format and semantics of the finished trailers\nseem to have gotten too little attention, even though they are simpler\nto define and even more important than the interface of the command used\nto manipulate trailers.\n\nI think it would be really helpful to have a careful specification of\nthe data format, and make sure that everybody agrees on what we want.\nFor example, I think it is crucial that the trailers can be read and\nwritten unambiguously.\n\nOnce that's clear, it will be a lot easier to be sure that the tool(s)\nfor working with trailers conform to the specification.\n\nEven then, I think it might be prudent to mark \"git interpret-trailers\"\nas \"experimental\" and/or put it under \"contrib\" rather than among the\nmain Git commands for a couple of releases.  Luckily, it is very loosely\ncoupled to the rest of Git, so I don't see any urgency to having it in\ncore [1].  After people have had time to experiment with it, then it\ncould be moved to core.\n\nMichael\n\n[1] Having the script in contrib would also make it possible to\nimplement it use a scripting language to make it easier to iterate on\nthe design.  When the details are agreed it could have been\nreimplemented in C.  But I guess that ship has already sailed.\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"239974","messageId":"xmqq8uqptno9.fsf@gitster.dls.corp.google.com","threadId":"36360","inReplyTo":"20140425.215619.2296838250398594645.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-28T16:37:58Z","receivedAt":"2014-04-28T16:37:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> From: Junio C Hamano <gitster@pobox.com>\n>>\n>> Christian Couder <chriscool@tuxfamily.org> writes:\n>> ...\n>\n>>> +\ttrailer. After some alphanumeric characters, it can contain\n>>> +\tsome non alphanumeric characters like ':', '=' or '#' that will\n>>> +\tbe used instead of ':' to separate the token from the value in\n>>> +\tthe trailer, though the default ':' is more standard.\n>> \n>> I assume that this is for things like\n>> \n>> \tbug #538\n>> \n>> and the configuration would say something like:\n>> \n>> \t[trailer \"bug\"]\n>>         \tkey = \"bug #\"\n>> \n>> For completeness (of this example), the bog-standard s-o-b would\n>> look like\n>> \n>> \tSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n>> \n>> and the configuration for it that spell the redundant \"key\" would\n>> be:\n>> \n>> \t[trailer \"Signed-off-by\"]\n>>         \tkey = \"Signed-off-by: \"\n>\n> Yeah, but you can use the following instead:\n>\n>  \t[trailer \"s-o-b\"]\n>          \tkey = \"Signed-off-by: \"\n\nSure, but note that both of these have a SP at the end in the value\npart (which I think is a sensible thing to do).\n\n> The <token> and the key can be different.\n>\n>> Am I reading the intention correctly?\n>\n> Yeah, I think so.\n>\n>> That is, when trailer.<token>.key is not defined, the value defaults\n>> to \"<token>: \" (with one SP after the label and colon),\n>\n> Yes.\n>\n>> and when it\n>> is defined, the value can come directly after it.\n>\n> The value can come directly after the key, only if the key ends with '#'.\n>\n> If it ends with something else, except spaces, one SP will be added\n> between the key and the value.\n\nAnd I do not think we want (or even need) this \"only when it ends\nwith #\" special casing in the code at all.  When the project's\nconvention is to say \"frotz# value-of-frotz\", the users will specify\nthat with 'key = \"frotz# \"' (with a trailing SP in the value part),\nand in a project that wants 'nitfol %value-of-nitfol', your parser\nwill find 'key = \"nitfol %\"'.  The users will obtain the result they\nwant for either case, and a hard-coded special casing in the code\nthat only has incomplete knowledge on the project convention will\nactively harm them.  I'd suggest dropping that special case.\n"},{"id":"240160","messageId":"535F8785.10302@game-point.net","threadId":"36360","inReplyTo":"xmqq8uqptno9.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Jeremy Morton","fromEmail":"admin@game-point.net","sentAt":"2014-04-29T11:05:41Z","receivedAt":"2014-04-29T11:05:41Z","isPatch":true,"sender":{"key":"admin@game-point.net","avatar":null},"body":"On 28/04/2014 17:37, Junio C Hamano wrote:\n> Christian Couder<chriscool@tuxfamily.org>  writes:\n>\n>> From: Junio C Hamano<gitster@pobox.com>\n>>>\n>>> Christian Couder<chriscool@tuxfamily.org>  writes:\n>>> ...\n>>\n>>>> +\ttrailer. After some alphanumeric characters, it can contain\n>>>> +\tsome non alphanumeric characters like ':', '=' or '#' that will\n>>>> +\tbe used instead of ':' to separate the token from the value in\n>>>> +\tthe trailer, though the default ':' is more standard.\n>>>\n>>> I assume that this is for things like\n>>>\n>>> \tbug #538\n>>>\n>>> and the configuration would say something like:\n>>>\n>>> \t[trailer \"bug\"]\n>>>          \tkey = \"bug #\"\n>>>\n>>> For completeness (of this example), the bog-standard s-o-b would\n>>> look like\n>>>\n>>> \tSigned-off-by: Christian Couder<chriscool@tuxfamily.org>\n>>>\n>>> and the configuration for it that spell the redundant \"key\" would\n>>> be:\n>>>\n>>> \t[trailer \"Signed-off-by\"]\n>>>          \tkey = \"Signed-off-by: \"\n>>\n>> Yeah, but you can use the following instead:\n>>\n>>   \t[trailer \"s-o-b\"]\n>>           \tkey = \"Signed-off-by: \"\n\nOne thing I'm not quite understanding is where the \"Christian \nCouder<chriscool@tuxfamily.org>\" bit comes from.  So you've defined the \ntrailer token and key, but interpret-trailers then needs to get the \nvalue it will give for the key from somewhere.  Does it have to just be \nhardcoded in?  We probably want some way to get various variables like \ncurrent branch name, current git version, etc.  So in the case of always \nadding a trailer for the branch that the commit was checked in to at the \ntime (Developed-on, Made-on-branch, Author-branch, etc. [I think my \nfavourite is Made-on-branch]), you'd want something like:\n\n\t[trailer \"m-o-b\"]\n\t\tkey = \"Made-on-branch: \"\n\t\tvalue = \"$currentBranch\"\n\n... resulting in the trailer (for example):\n\tMade-on-branch: pacman-minigame\n\nAlso, if there were no current branch name because you're committing in \na detached head state, it would be nice if you could have some logic to \ndetermine that, and instead write the trailer as:\n\tMade-on-branch: (detached HEAD: AB12CD34)\n\n... or whatever.  And also how about some logic to be able to say that \nif you're committing to the \"master\" branch, the trailer doesn't get \ninserted at all?\n\n-- \nBest regards,\nJeremy Morton (Jez)\n"},{"id":"240168","messageId":"CAP8UFD2oXpW9QEkSh+vpNGRAxRFp0zJF39ZZ8sUZLTcKB9mHWQ@mail.gmail.com","threadId":"36360","inReplyTo":"535F8785.10302@game-point.net","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2014-04-29T11:47:51Z","receivedAt":"2014-04-29T11:47:51Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Tue, Apr 29, 2014 at 1:05 PM, Jeremy Morton <admin@game-point.net> wrote:\n> On 28/04/2014 17:37, Junio C Hamano wrote:\n>>\n>> Christian Couder<chriscool@tuxfamily.org>  writes:\n>>\n>>> From: Junio C Hamano<gitster@pobox.com>\n>>>>\n>>>>\n>>>> Christian Couder<chriscool@tuxfamily.org>  writes:\n>>>> ...\n>>>\n>>>\n>>>>> +       trailer. After some alphanumeric characters, it can contain\n>>>>> +       some non alphanumeric characters like ':', '=' or '#' that will\n>>>>> +       be used instead of ':' to separate the token from the value in\n>>>>> +       the trailer, though the default ':' is more standard.\n>>>>\n>>>>\n>>>> I assume that this is for things like\n>>>>\n>>>>         bug #538\n>>>>\n>>>> and the configuration would say something like:\n>>>>\n>>>>         [trailer \"bug\"]\n>>>>                 key = \"bug #\"\n>>>>\n>>>> For completeness (of this example), the bog-standard s-o-b would\n>>>> look like\n>>>>\n>>>>         Signed-off-by: Christian Couder<chriscool@tuxfamily.org>\n>>>>\n>>>> and the configuration for it that spell the redundant \"key\" would\n>>>> be:\n>>>>\n>>>>         [trailer \"Signed-off-by\"]\n>>>>                 key = \"Signed-off-by: \"\n>>>\n>>>\n>>> Yeah, but you can use the following instead:\n>>>\n>>>         [trailer \"s-o-b\"]\n>>>                 key = \"Signed-off-by: \"\n>\n>\n> One thing I'm not quite understanding is where the \"Christian\n> Couder<chriscool@tuxfamily.org>\" bit comes from.  So you've defined the\n> trailer token and key, but interpret-trailers then needs to get the value it\n> will give for the key from somewhere.  Does it have to just be hardcoded in?\n> We probably want some way to get various variables like current branch name,\n> current git version, etc.  So in the case of always adding a trailer for the\n> branch that the commit was checked in to at the time (Developed-on,\n> Made-on-branch, Author-branch, etc. [I think my favourite is\n> Made-on-branch]), you'd want something like:\n>\n>         [trailer \"m-o-b\"]\n>                 key = \"Made-on-branch: \"\n>                 value = \"$currentBranch\"\n>\n> ... resulting in the trailer (for example):\n>         Made-on-branch: pacman-minigame\n\nIn the documentation patch, there is:\n\ntrailer.<token>.command::\n       This option can be used to specify a shell command that will\n       be used to automatically add or modify a trailer with the\n       specified 'token'.\n\n       When this option is specified, it is like if a special 'token=value'\n       argument is added at the end of the command line, where 'value' will\n       be given by the standard output of the specified command.\n\n       If the command contains the `$ARG` string, this string will be\n       replaced with the 'value' part of an existing trailer with the same\n       token, if any, before the command is launched.\n\nThat's why Something like the following should work if \"git commit\"\nautomitically runs \"git interpret-trailers\":\n\n         [trailer \"m-o-b\"]\n                 key = \"Made-on-branch: \"\n                 command = \"git name-rev --name-only HEAD\"\n\n\n> Also, if there were no current branch name because you're committing in a\n> detached head state, it would be nice if you could have some logic to\n> determine that, and instead write the trailer as:\n>         Made-on-branch: (detached HEAD: AB12CD34)\n\nYou may need to write a small script for that.\nThen you just need the \"trailer.m-o-b.command\" config value to point\nto your script.\n\n> ... or whatever.  And also how about some logic to be able to say that if\n> you're committing to the \"master\" branch, the trailer doesn't get inserted\n> at all?\n\nYou can script that too.\n\nBest,\nChristian.\n"},{"id":"240176","messageId":"535FA83B.3010008@game-point.net","threadId":"36360","inReplyTo":"CAP8UFD2oXpW9QEkSh+vpNGRAxRFp0zJF39ZZ8sUZLTcKB9mHWQ@mail.gmail.com","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Jeremy Morton","fromEmail":"admin@game-point.net","sentAt":"2014-04-29T13:25:15Z","receivedAt":"2014-04-29T13:25:15Z","isPatch":true,"sender":{"key":"admin@game-point.net","avatar":null},"body":"On 29/04/2014 12:47, Christian Couder wrote:\n>> Also, if there were no current branch name because you're committing in a\n>> detached head state, it would be nice if you could have some logic to\n>> determine that, and instead write the trailer as:\n>>          Made-on-branch: (detached HEAD: AB12CD34)\n>\n> You may need to write a small script for that.\n> Then you just need the \"trailer.m-o-b.command\" config value to point\n> to your script.\n>\n>> ... or whatever.  And also how about some logic to be able to say that if\n>> you're committing to the \"master\" branch, the trailer doesn't get inserted\n>> at all?\n>\n> You can script that too.\n\nBut it would be nicer if the logic were built-in, then you wouldn't have \nto share some script with your work colleagues. :-)\n\n-- \nBest regards,\nJeremy Morton (Jez)\n"},{"id":"240175","messageId":"535FA843.1000706@game-point.net","threadId":"36360","inReplyTo":"CAP8UFD2oXpW9QEkSh+vpNGRAxRFp0zJF39ZZ8sUZLTcKB9mHWQ@mail.gmail.com","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Jeremy Morton","fromEmail":"admin@game-point.net","sentAt":"2014-04-29T13:25:23Z","receivedAt":"2014-04-29T13:25:23Z","isPatch":true,"sender":{"key":"admin@game-point.net","avatar":null},"body":"On 29/04/2014 12:47, Christian Couder wrote:\n>> Also, if there were no current branch name because you're committing in a\n>> detached head state, it would be nice if you could have some logic to\n>> determine that, and instead write the trailer as:\n>>          Made-on-branch: (detached HEAD: AB12CD34)\n>\n> You may need to write a small script for that.\n> Then you just need the \"trailer.m-o-b.command\" config value to point\n> to your script.\n>\n>> ... or whatever.  And also how about some logic to be able to say that if\n>> you're committing to the \"master\" branch, the trailer doesn't get inserted\n>> at all?\n>\n> You can script that too.\n\nBut it would be nicer if the logic were built-in, then you wouldn't have \nto share some script with your work colleagues. :-)\n\n-- \nBest regards,\nJeremy Morton (Jez)\n"},{"id":"240416","messageId":"20140501.205438.1173327839304856205.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"535FA83B.3010008@game-point.net","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-05-01T18:54:38Z","receivedAt":"2014-05-01T18:54:38Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"From: Jeremy Morton <admin@game-point.net>\n\n> On 29/04/2014 12:47, Christian Couder wrote:\n>>> Also, if there were no current branch name because you're committing\n>>> in a\n>>> detached head state, it would be nice if you could have some logic to\n>>> determine that, and instead write the trailer as:\n>>>          Made-on-branch: (detached HEAD: AB12CD34)\n>>\n>> You may need to write a small script for that.\n>> Then you just need the \"trailer.m-o-b.command\" config value to point\n>> to your script.\n>>\n>>> ... or whatever.  And also how about some logic to be able to say that\n>>> if\n>>> you're committing to the \"master\" branch, the trailer doesn't get\n>>> inserted\n>>> at all?\n>>\n>> You can script that too.\n> \n> But it would be nicer if the logic were built-in, then you wouldn't\n> have to share some script with your work colleagues. :-)\n\nThe above logic is very specific to your workflow. For example some\npeople might a \"Made-on-branch: \" trailer only when they are on real\nbranches except \"dev\" and \"master\".\n\nBest,\nChristian.\n"},{"id":"242662","messageId":"20140525.103721.1806399553055631284.chriscool@tuxfamily.org","threadId":"36360","inReplyTo":"535E2A69.30600@alum.mit.edu","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-05-25T08:37:21Z","receivedAt":"2014-05-25T08:37:21Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n> On 04/25/2014 11:07 PM, Christian Couder wrote:\n>> \n>> I don't think there is a lot of complexity.\n>> But maybe I need to explain how it works better.\n>> Feel free to suggest me sentences I could add.\n> \n> I am really excited about having better support for trailers in Git, and\n> I want to thank you for your work.  For me the promise of trailers is\n> \n> * A way for users to add information to commits for whatever purpose\n>   they want, without having to convince upstream to built support in.\n\nYeah, I agree this is the main purpose of trailers.\n\n> * A standard format for that information, so that all tools can agree\n>   how to read/write trailers without being confused by or breaking\n>   trailers that they didn't know about in advance.\n\nYeah, but don't you think this goal can sometimes go against the\nprevious goal?\n\nI mean, if some users for their project think that it's better, for\nexample, if they use trailers like \"Fix #42\" instead of \"Fix: 42\",\nbecause their bug tracking system supports \"Fix #42\" better, we should\nlet them do what suits them better, even if Git supports them not as\nwell as if they used \"Fix: 42\".\n\n> * A format that is straightforward enough that it can be machine-\n>   readable with minimum ambiguity.\n\nYeah, but again this could go against the main purpose of trailers\nabove.\n\n> * Some command-line tools to make it easy for scripts to work with\n>   trailers, and that serve as a reference implementation that other\n>   Git implementations can imitate.\n\nYeah, ok, as long as we keep in mind the main purpose.\n\n> For example, I totally expect that\n>   we will soon want a command-line tool for inquiring about the\n>   presence and contents of trailers, for use in scripting.  Eventually\n>   we will want to be able to do stuff like\n> \n>       git trailers --get-all s-o-b origin/master..origin/next\n>       git rev-list --trailer=s-o-b:gitster@pobox.com master\n>       git trailers --pipe --draft \\\n>           --add-first fixes \\\n>           --append '# You can delete the following line:' \\\n>           --append s-o-b:\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\" \\\n>           --unset private\n>       git trailers --pipe --verify --tidy-up\n\nYeah, feel free to help make this kind of things possible :-)\n\n> I think it is really important to nail down the format of trailers\n> tightly enough that everybody who reads/writes a commit message agrees\n> about exactly what trailers are there.\n\nI think we should have a default format for trailers that is clear,\nbut we should not force users to use this format. Because forcing it\nwould go against the main goal of trailers that you listed first\nabove.\n\n> For example the specification\n> might look something like:\n> \n>     A commit message can optionally end with a block of trailers.\n>     The trailers, if present, must be separated from the rest of the\n>     commit message by one or more blank lines (lines that contain only\n>     whitespace).  There must be no blank lines within the list of\n>     trailers.  It is allowed to have blank lines after the trailers.\n> \n>     Each trailer line must match the following Perl regular\n>     expression:\n> \n>         ^([A-Za-z0-9_-]+)\\s*:\\s*(.*[^\\s])\\s*$\n> \n>     The string matching the first group is called the key and the string\n>     matching the second is called the value.  Keys are considered to be\n>     case-insensitive [or should they be case-sensitive?].  The\n>     interpretation of values is left entirely up to the application.\n>     Values must not be empty.\n\nI tried to be clearer in the v12 I just posted, and I think it should\nbe enough to be very clear. We might want to tweak a little the\nspecifications later, so being too strict might be counter productive.\n\nAnd as other tools might already use trailers in a case-sensitive way\nand yet other tools in a case-insensitive way, I am not sure we would\ngain anything by specifying if keys or values should be interpreted in\na case-sensitive or case-insensitive way. On the contrary we might\nupset people already using some of these tools for no good reason.\n\n>     However, in --draft and --cleanup modes, empty values *are*\n>     allowed, as are comments (lines starting with `core.commentchar`)\n>     within the trailer block.  In --draft mode such lines are passed\n>     through unchanged, and in --cleanup mode such lines are removed.\n\nI am not sure we should use modes. I think options like\n\"--trim-empty\", \"--allow-comments\", \"--allow-empty\" might be clearer.\n\n> I'm not saying this is the exact definition that we want; I'm just\n> providing an example of the level of precision that I think is needed.\n\nYeah, but I think too much precision can be counter productive.\n\n> With regard to the separator character, my concern is not about how to\n> document the rules for this one tool.  It's more about having really\n> well-defined rules that are consistent between reading and writing.  For\n> me it seems silly to let \"git interpret-trailers\" output trailers that\n> it doesn't know how to read back in, and pretty much be a show-stopper\n> if the presence of such trailers makes the tool unable to read other\n> trailers in the same commit message.\n\nWe might allow an option to specify witch separator(s) should be\nallowed in the input messages for example. Right now I think it is\nenough if we support well the default separator, ':' in the input\nmessage.\n\n> So my preference would be to make the format of trailers really strict;\n> for example, only allowing colon separators as in the regexp above.\n> People who want to work with trailers using Git tools will just have to\n> conform to this format.\n\nI don't think we should cast in stone the format for trailers, because\nof the main purpose of trailers.\n\nThe format of the commit header for example is cast in stone, but\nthat's ok because it is mostly for Git internal use. Trailers are\nmostly for external use by users who already have tools expecting\ndifferent formats.\n\nThere are already users who are not happy that they cannot easily have\nother commit headers, and we point them to trailers. If we specify\ntrailers too strictly, where will we point them to?\n\n> But if we must support flexibility in the separator characters, then I\n> think it is important that we be able to read whatever we can write.\n\nAn option like --input-separator might be enough to support this.\n\n> For me this means:\n> \n> * Enumerating a list of allowed separators (e.g., [:=#])\n\nJunio suggested in a message that users might use different separators\nlike '%'.\n \n> * Specifying how it is decided what separator to use when generating\n>   new trailers\n\nThis is already possible with the 'trailer.<token>.key' config\nvariable.\n\n> * Specifying what appends when a trailer is read and then written again:\n>   is its separator preserved, or is the trailer converted to use the\n>   separator configured for that particular key in the config.  And if\n>   the latter, what happens if a key's separator is not configured?\n\nRight now we only accept ':' as input separator for the messages and\n':' and '=' for the --trailer option, and the default output separator\nis ':'. If the user specify a different separator in a key, this\nseparator will be used only in the output for this key.\n\nIf this is not clear in the documentation, please susggest specific\nimprovements.\n\n> * Specifying whether whitespace around a separator is adjusted when\n>   reading then writing a trailer.  For example, is\n> \n>       Foo SP SP : HT bar SP\n> \n>   canonicalized to\n> \n>       Foo: SP bar\n> \n>   (SP=space, HT=tab)?  What about\n> \n>       Fixes SP #33\n> \n>   ?  What if the separator for the \"fixes\" key is not configured?\n\nI tried to be very clear in the doc in v12.\n\n> The reason that I prefer supporting only colons is that more flexibility\n> inevitably raises lots of questions like this, makes the documentation\n> and implementation more complicated, and makes it harder for other\n> implementations to be sure they agree 100% with the reference\n> implementation.\n\nYeah, but we should not forget the main purpose of trailers.\n\n>>> It seems to me that the current behavior (rewriting exactly one existing\n>>> line) is not that useful.  Why not make \"overwrite\" overwrite *all*\n>>> existing matching lines?\n>> \n>> I was thinking that people could use the following template message:\n>> \n>> ---------------\n>> Signed-off-by: \n>> Signed-off-by: YOU-WILL-BE-AUTOMATICALLY-ADDED-HERE\n>> ---------------\n>> \n>> and the following config:\n>> \n>> ---------------\n>> [trailer \"s-o-b\"]\n>> \t key = \"Signed-off-by: \"\n>> \t ifexist = overwrite\n>> \t command = echo \\\"$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>\\\"\n>> ---------------\n>> \n>> This way the user can add other people's s-o-b before the last one\n>> which will always contain his own s-o-b.\n> \n> That seems fragile.  For example, if the user changes the lines to\n> \n>     Signed-off-by: Somebody Else <...>\n> \n> (deleting the \"YOU-WILL-BE\" line, maybe because they don't want to sign\n> off the commit) then not only will their wish be contradicted, but also\n> Somebody Else would be deleted.\n\nI agree that it is fragile, but we can add an overwriteAll option if\nthat suits people needs better. \"overwrite\" is needed anyway for\ntrailers where there should be only one trailer with a given key.\n\n> What about allowing a --draft option, which allows blank trailer values\n> plus comments interspersed in the trailer lines?  (I.e., the equivalent\n> of --trim-empty could be the default and --draft would turn it off plus\n> allow interspersed comments.)\n\nYeah, or --allow-comments. As I said above prefer many orthogonal\noptions, rather than some general options like --draft.\n\n> Then the template could be\n> \n>     # You can add one or more Signed-off-by lines for other people here.\n>     # A Signed-off-by line for you will be appended automatically when\n>     # you commit.\n>     Signed-off-by:\n> \n> Or, even better:\n> \n>     # You can add one or more Signed-off-by lines here:\n>     Signed-off-by:\n>     Signed-off-by:\n>     # You can delete the following line if you don't want it:\n>     Signed-off-by: Me <me@example.com>\n> \n> ; i.e., the Signed-off-by line for the author could be filled in\n> *before* the user is asked to edit the commit message.  There could also\n> be a --cleanup mode that allows blank values and comments on input but\n> removes them from the output.\n\nI would prefer to add --trim-comments rather than a --cleanup mode. \n\n>> [...]\n> \n> Given Git's requirements for backwards compatibility, a specification\n> that we release now will have to be supported forever (because it will\n> be baked into commits and can *never* be changed), and any\n> trailer-handling tools that we release now will have to be supported for\n> years (until at least Git 3.0).\n\nYeah, I know that. So if we are too strict in the specification will\nbe stuck for a long time.\n\n> All in all, I think that there has been a lot of discussion about the\n> interface of this one command, \"git interpret-trailers\", including its\n> quite complicated configuration and a command-line behavior.  And yet it\n> seems to me that not many Git developers have been very engaged in the\n> conversation, and Junio (who has) still doesn't seem satisfied with it.\n>  I (though among the too-little engaged) have the feeling that it is\n> still a ways from maturity.\n\nMy opinion is that many Git developers have been engaged and you can\nsee that in the Cc.\n\nI cannot tell if they are all very happy or not but I suppose that if\nthey were very unhappy they would tell it.\n\n> On the contrary, the data format and semantics of the finished trailers\n> seem to have gotten too little attention, even though they are simpler\n> to define and even more important than the interface of the command used\n> to manipulate trailers.\n> \n> I think it would be really helpful to have a careful specification of\n> the data format, and make sure that everybody agrees on what we want.\n> For example, I think it is crucial that the trailers can be read and\n> written unambiguously.\n> \n> Once that's clear, it will be a lot easier to be sure that the tool(s)\n> for working with trailers conform to the specification.\n\nPlease realize that too much specification is not always good and that\nit cuts both ways...\n\n> Even then, I think it might be prudent to mark \"git interpret-trailers\"\n> as \"experimental\" and/or put it under \"contrib\" rather than among the\n> main Git commands for a couple of releases.  Luckily, it is very loosely\n> coupled to the rest of Git, so I don't see any urgency to having it in\n> core [1].  After people have had time to experiment with it, then it\n> could be moved to core.\n\nYeah, it is very loosely coupled to the rest of Git by design. I\nposted the first version of this series around last November. So\npeople have had a very long time to review it, comment on it,\nexperiment with it, bikeshed many details... And in the same time many\nusers have asked for some features that \"git interpret-trailers\"\nprovides...\n\nRegards,\nChristian.\n"},{"id":"242734","messageId":"53844AEF.1080502@alum.mit.edu","threadId":"36360","inReplyTo":"20140525.103721.1806399553055631284.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2014-05-27T08:21:03Z","receivedAt":"2014-05-27T08:21:03Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"tl;dr: This patch series wants to introduce a permanent new Git data\nformat.  The current version can write trailers in formats that it is\nincapable of reading, which I consider broken.  I advocate a stricter\nspecification of the format of trailers, at least until we get feedback\nfrom users that they need more flexibility.\n\nOn 05/25/2014 10:37 AM, Christian Couder wrote:\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n> [...]\n>> * A way for users to add information to commits for whatever purpose\n>>   they want, without having to convince upstream to built support in.\n> \n> Yeah, I agree this is the main purpose of trailers.\n> \n>> * A standard format for that information, so that all tools can agree\n>>   how to read/write trailers without being confused by or breaking\n>>   trailers that they didn't know about in advance.\n> \n> Yeah, but don't you think this goal can sometimes go against the\n> previous goal?\n> \n> I mean, if some users for their project think that it's better, for\n> example, if they use trailers like \"Fix #42\" instead of \"Fix: 42\",\n> because their bug tracking system supports \"Fix #42\" better, we should\n> let them do what suits them better, even if Git supports them not as\n> well as if they used \"Fix: 42\".\n\nThe flexibility that comes from offering our users a more-or-less\ngeneral key/value store already accomplishes the first goal.  With that\nthe users *could* store their data as \"Fix: 42\" or \"Fixes: #42\" and\nsatisfy their functional requirements.\n\nGiving them the option to use \"Fix #42\" doesn't make *any* new\nfunctionality possible.  It is pure eye-candy.  And it would come at the\nIMO high cost of making it harder for *everybody* to work with the\nmetadata.  It makes the specification more complicated.  It makes the\ncode more complicated.  It makes the configuration more complicated.  It\nmakes it more likely that there will be \"false positives\" (text in a\ncommit message that our code recognizes as key/value data even though it\nwas not meant to be).  And in my opinion it makes the k/v data itself\nharder for a human to read because it is not in a uniform format.\n\nThe only justification I can think of for allowing more flexible formats\nwould be to \"retroactively\" support metadata that people already have in\ntheir history.  Are there \"famous\" or \"important\" existing metadata\nformats that are incompatible with \"Key: Value\"?\n\nMore to the point: do you have a concrete reason for wanting to support\nalternative formats like \"Fix #42\", or is it based more on the feeling\nthat users will want it?\n\nRemember, it would be really easy to release v1 of this feature with a\nstrict format, then wait and see if users clamor for more flexibility.\nWe can always add more flexibility later.  Whereas if v1 already\nsupports more flexible formats, we pretty much have to support them forever.\n\n>> * A format that is straightforward enough that it can be machine-\n>>   readable with minimum ambiguity.\n> \n> Yeah, but again this could go against the main purpose of trailers\n> above.\n\nNo, the users have all the flexibility they need if they can choose\ntheir own key/value schema.  Allowing alternative formats adds very little.\n\nI feel strongly that it would be a bad mistake to leave the\nspecification of trailers so loose that they cannot be machine-readable\nwith a good degree of confidence.  Tools that add trailers should be\ncomposable.  The current scheme is not.  For example, suppose one tool\nwants to add a \"Fix #42\" line and another one wants to add \"Signed-off-by\":\n\n    git config trailer.fix.key \"Fix #\"\n    git config trailer.sign.key \"Signed-off-by: \"\n    git config trailer.sign.ifexists doNothing\n\n    echo \"subject\n\n    Signed-off-by: Alice <alice@example.com>\" |\n        git interpret-trailers --trailer fix=42 |\n        git interpret-trailers --trailer sign=\"Bob <bob@example.com>\"\n    --------- output ------------------------------------------\n    subject\n\n    Signed-off-by: Alice <alice@example.com>\n    Fix #42\n\n    Signed-off-by: Bob <bob@example.com>\n    -----------------------------------------------------------\n\nThe result is that the trailers end up not in one block but in two\n(meaning that the first block is no longer recognized as a trailer\nblock), and the second \"Signed-off-by\" line, which should have been\nomitted because of ifexists=doNothing, was incorrectly added.\n\nOr let's do something like the \"commit template\" example from the\ndocumentation, but using \"Fix #\" instead of \"Fixes: \":\n\n    echo \"***subject***\n\n    ***message***\n\n    Fix #\n    Cc:\n    Reviewed-by:\n    Signed-off-by:\n    \" |\n        sed -Ee 's/(Reviewed-by.*)/\\1me/' |\n        git interpret-trailers --trim-empty --trailer \"git-version: foo\"\n    ---------- output ------------------------------------------\n    ***subject***\n\n    ***message***\n\n    Fix #\n    Cc:\n    Reviewed-by: me\n    Signed-off-by:\n\n    git-version: foo\n    ------------------------------------------------------------\n\nNot only haven't the empty lines been stripped off, but a new trailer\nblock has been created for \"git-version\".\n\nI consider this broken.\n\n> [...]\n>> For example the specification\n>> might look something like:\n>>\n>>     A commit message can optionally end with a block of trailers.\n>>     The trailers, if present, must be separated from the rest of the\n>>     commit message by one or more blank lines (lines that contain only\n>>     whitespace).  There must be no blank lines within the list of\n>>     trailers.  It is allowed to have blank lines after the trailers.\n>>\n>>     Each trailer line must match the following Perl regular\n>>     expression:\n>>\n>>         ^([A-Za-z0-9_-]+)\\s*:\\s*(.*[^\\s])\\s*$\n>>\n>>     The string matching the first group is called the key and the string\n>>     matching the second is called the value.  Keys are considered to be\n>>     case-insensitive [or should they be case-sensitive?].  The\n>>     interpretation of values is left entirely up to the application.\n>>     Values must not be empty.\n> \n> I tried to be clearer in the v12 I just posted, and I think it should\n> be enough to be very clear. We might want to tweak a little the\n> specifications later, so being too strict might be counter productive.\n> \n> And as other tools might already use trailers in a case-sensitive way\n> and yet other tools in a case-insensitive way, I am not sure we would\n> gain anything by specifying if keys or values should be interpreted in\n> a case-sensitive or case-insensitive way. On the contrary we might\n> upset people already using some of these tools for no good reason.\n\nNo sane tool would interpret a trailer differently depending on how the\nkey is capitalized.  So I think that if we ourselves treat the keys\ncase-insensitively but we preserve case when processing metadata, there\nwon't be any problems.\n\n>>     However, in --draft and --cleanup modes, empty values *are*\n>>     allowed, as are comments (lines starting with `core.commentchar`)\n>>     within the trailer block.  In --draft mode such lines are passed\n>>     through unchanged, and in --cleanup mode such lines are removed.\n> \n> I am not sure we should use modes. I think options like\n> \"--trim-empty\", \"--allow-comments\", \"--allow-empty\" might be clearer.\n\nI think we want to train users to think of trailers-with-no-values as\ntemporary helpers that shouldn't end up in commits, just like the\n\"#\"-commented lines in commit message templates.  Why?  Because you\ncan't rely on their being preserved.  As soon as your trailer goes\nthrough a \"--cleanup\" (which might be there because of an unrelated\ntool) it will disappear.  For the user it is a simpler mental model to\nthink of modes: as long as I am in draft mode, there might be comment\nlines and trailer templates in my commit message, but when I commit they\nwill go away.  I would go so far as to say that deleting trailer lines\nwith no values should be a standard part of cleaning commit messages and\nshould maybe be an option offered by \"git stripspace\".\n\n> [...]\n>> So my preference would be to make the format of trailers really strict;\n>> for example, only allowing colon separators as in the regexp above.\n>> People who want to work with trailers using Git tools will just have to\n>> conform to this format.\n> \n> I don't think we should cast in stone the format for trailers, because\n> of the main purpose of trailers.\n> \n> The format of the commit header for example is cast in stone, but\n> that's ok because it is mostly for Git internal use. Trailers are\n> mostly for external use by users who already have tools expecting\n> different formats.\n> \n> There are already users who are not happy that they cannot easily have\n> other commit headers, and we point them to trailers. If we specify\n> trailers too strictly, where will we point them to?\n\nNobody is disagreeing that users should be allowed to choose their own\nkeys and values and assign their own interpretations to them.  If we let\nusers add their own commit headers, we certainly wouldn't let them\ndefine a header formatted like \"Fix #42\", would we?\n\nAgain, the *only* justification for more flexible formats would be if\nthere are a lot of tools that *already* exist out there that don't\nconform to \"Key: value\".  Are there?  New tools will certainly be\nwritten to use whatever format we define, and in exchange they will be\nable to use your awesome new tool and hopefully other upcoming tools for\ndealing with trailers.\n\n>> But if we must support flexibility in the separator characters, then I\n>> think it is important that we be able to read whatever we can write.\n> \n> An option like --input-separator might be enough to support this.\n\nThis would not be adequate because a single commit message might have\nmultiple trailers with different formats:\n\n    Signed-off-by: me\n    Fix #42\n\nEither the input separator would have to be specified for every single\ntrailer (which is impractical because you can't dictate centrally how\ngit clients are configured) or the parsing code would have to be taught\nto read any allowed format.\n\n>> For me this means:\n>>\n>> * Enumerating a list of allowed separators (e.g., [:=#])\n> \n> Junio suggested in a message that users might use different separators\n> like '%'.\n\nThe fewer and stricter the better, to avoid false positive matches to\nnon-trailer text.\n\n>> * Specifying how it is decided what separator to use when generating\n>>   new trailers\n> \n> This is already possible with the 'trailer.<token>.key' config\n> variable.\n\nThis means that if one developer has forgotten to configure the \"Fix\"\ntrailer in one clone, then he will generate trailers that are considered\nmalformed by a colleague who has configured \"Fix #\".\n\n>> * Specifying what appends when a trailer is read and then written again:\n>>   is its separator preserved, or is the trailer converted to use the\n>>   separator configured for that particular key in the config.  And if\n>>   the latter, what happens if a key's separator is not configured?\n> \n> Right now we only accept ':' as input separator for the messages and\n> ':' and '=' for the --trailer option, and the default output separator\n> is ':'. If the user specify a different separator in a key, this\n> separator will be used only in the output for this key.\n\nI consider trailers that can only be written but not read to be broken.\n My questions are to probe one possible alternate reality, namely:\n\"Suppose we want to allow alternative trailer formats.  What would it\ntake to make them readable *and* writable?\"\n\nFor example:\n\n* What if I try to add a Signed-off-by trailer to a message that already\ncontains \"Fix #42\"?  In what form should the \"Fix #42\" line be written\nto the output\n\n  * if I don't have the separator for \"Fix\" configured?\n  * if I have the separator for \"Fix\" configured to be \"=\"?\n\n* What if I *do* have the separator for \"Fix\" set to \" #\", but the input\ncontains \"Fix: 42\"?  How should that line be formatted on output?  Does\nthe answer change if I have \"token.fix.ifexist\" set to \"overwrite\" and\nrun \"git interpret-trailers fix=43\"?\n\nBy asking these questions, I hope to hint that supporting alternative\nformats increases the complexity of the specification and the\nimplementation to an unjustifiable extent and/or it unrealistically\nrelies all developers on a project having their trailer configuration\nset up correctly.\n\n> If this is not clear in the documentation, please susggest specific\n> improvements.\n> [...]\n> I tried to be very clear in the doc in v12.\n\nThe doc only covers writing new trailers in \"alternative\" formats, not\nreading them.\n\n> [...]\n>> Given Git's requirements for backwards compatibility, a specification\n>> that we release now will have to be supported forever (because it will\n>> be baked into commits and can *never* be changed), and any\n>> trailer-handling tools that we release now will have to be supported for\n>> years (until at least Git 3.0).\n> \n> Yeah, I know that. So if we are too strict in the specification will\n> be stuck for a long time.\n\nNo, that's exactly backwards!  We can easily loosen the format in the\nfuture.  Projects don't need to output trailers in the looser format\nuntil everybody involved is using the new Git version.\n\nBut the format can never be made *more* strict, because then the\nmetadata that users have added to their history will stop being readable.\n\n>> All in all, I think that there has been a lot of discussion about the\n>> interface of this one command, \"git interpret-trailers\", including its\n>> quite complicated configuration and a command-line behavior.  And yet it\n>> seems to me that not many Git developers have been very engaged in the\n>> conversation, and Junio (who has) still doesn't seem satisfied with it.\n>>  I (though among the too-little engaged) have the feeling that it is\n>> still a ways from maturity.\n> \n> My opinion is that many Git developers have been engaged and you can\n> see that in the Cc.\n> \n> I cannot tell if they are all very happy or not but I suppose that if\n> they were very unhappy they would tell it.\n> [...]\n\nIt was unfair of me to try to characterize the opinions of other\ndevelopers.  On the other hand, even though many people have commented\non this proposal over its long lifetime, I didn't get the feeling that\nit has won a consensus of +1s in its current form.\n\nI'd love to hear the opinion of others because maybe I'm totally out in\nleft field.\n\nAnd I want to reiterate that the reason I'm so emphatic about this\nproposal is because I think it will be such a great new feature.  I just\nthink that some tweaks would make it a more solid foundation for\nbuilding even more functionality onto.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"242737","messageId":"CALKQrgdMxCP8e+6wJugnJUhLfHvf-t9MDPqdiZvc+HQc+GcBiQ@mail.gmail.com","threadId":"36360","inReplyTo":"53844AEF.1080502@alum.mit.edu","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2014-05-27T09:17:52Z","receivedAt":"2014-05-27T09:17:52Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tue, May 27, 2014 at 10:21 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> tl;dr: This patch series wants to introduce a permanent new Git data\n> format.  The current version can write trailers in formats that it is\n> incapable of reading, which I consider broken.  I advocate a stricter\n> specification of the format of trailers, at least until we get feedback\n> from users that they need more flexibility.\n>\n> On 05/25/2014 10:37 AM, Christian Couder wrote:\n\n[...]\n\n>> My opinion is that many Git developers have been engaged and you can\n>> see that in the Cc.\n>>\n>> I cannot tell if they are all very happy or not but I suppose that if\n>> they were very unhappy they would tell it.\n>> [...]\n>\n> It was unfair of me to try to characterize the opinions of other\n> developers.  On the other hand, even though many people have commented\n> on this proposal over its long lifetime, I didn't get the feeling that\n> it has won a consensus of +1s in its current form.\n>\n> I'd love to hear the opinion of others because maybe I'm totally out in\n> left field.\n\nFWIW, after a quick read, I find myself agreeing very much with\nMichael's arguments for a stricter format (at least in its initial\nversion).\n\nWe are formalizing and applying tools/automation to a part of the\ncommit message that has so far been ad hoc and very informal. There is\nno expectation that _every_ _single_ existing use of (informal)\ntrailers (except the somewhat-formalized support for --signoff) must\nbe supported by git-interpret-trailers.\n\nHowever, there _is_ an expectation that git-interpret-trailers is\nself-consistent and does not stumble over its own trailers. Therefore,\nit makes perfect sense to make v1 very strict in what formats it\nproduce (i.e. a strict \"key: value\" format is enough for now).\n\n> And I want to reiterate that the reason I'm so emphatic about this\n> proposal is because I think it will be such a great new feature.  I just\n> think that some tweaks would make it a more solid foundation for\n> building even more functionality onto.\n\nFully agreed. git-interpret-trailers have come up in several other\ndiscussion, both on the git mailing list and elsewhere, and I have no\ndoubt that this will be a very useful feature that will be put to good\nuse in many projects.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"242768","messageId":"xmqqd2ezf2tf.fsf@gitster.dls.corp.google.com","threadId":"36360","inReplyTo":"20140525.103721.1806399553055631284.chriscool@tuxfamily.org","subject":"Re: [PATCH v10 11/12] Documentation: add documentation for 'git interpret-trailers'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-05-27T19:18:20Z","receivedAt":"2014-05-27T19:18:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n> ...\n> An option like --input-separator might be enough to support this.\n>\n>> For me this means:\n>> \n>> * Enumerating a list of allowed separators (e.g., [:=#])\n>\n> Junio suggested in a message that users might use different separators\n> like '%'.\n\nI actually think we shouldn't go any fancier than \":\" and nothing\nelse, not even \"#\".\n\nI was hoping that you would eventually realize that there are only\ntwo viable extremes when I suggested \"the users may want to use\nother random characters like '%'\" and also \"the users can specify\nthe 'key' with colon and trailing SP\" (in $gmane/245960).\n\n - If you want to give the projects greater control of the format,\n   then you cannot rely on \"separators\" anyway.  Your users can list\n   all possible footer \"keys\" the particular project would use, so\n   that they are recognized by Git, be that \"Fixes: 4a28f16\", \"Bug\n   #12354\", without hard-coding what \"separator\" Git must pay\n   attention to.  You can easily find a run of lines that begin with\n   any of the \"key\" (e.g. \"Fixes: \", \"Signed-off-by: \", \"Bug #\",\n   ...) starting from the tail-end of the log message and that is\n   your footer block.  No need for \"separators\" at all.\n\n - If you want to give the projects freedom to come up with random\n   new kinds of footers without pre-arrangement, then you need to\n   have a reliable way to say if any line you have never seen could\n   be a footer material.  A colon has been used everywhere, and used\n   even in the \"Fixes: 4a28f16\" example you took from the kernel\n   circle.  I think you presented it with '#' but I do not think\n   they even want that, looking at:\n\n   http://lists.linuxfoundation.org/pipermail/ksummit-discuss/2014-May/000618.html\n\nI also think that bug tracking system using \"Bug #12345\" is an\nunrelated issue, as log viewers would want to highlight and make\nlinks out of them anywhere in the log message text, not limited\nto the log footer part.\n\nAs to which one of these two we should take, I tend to think that we\nshould start small and limited; loosening the syntax later is much\neasier than going the other way, i.e. \":\" and nothing else.\n"}]}