* [PATCH v11 01/11] trailer: add data structures and basic functions
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 02/11] trailer: process trailers from input message and arguments Christian Couder
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
We will use a doubly linked list to store all information
about trailers and their configuration.
This way we can easily remove or add trailers to or from
trailer lists while traversing the lists in either direction.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 1 +
trailer.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+)
create mode 100644 trailer.c
diff --git a/Makefile b/Makefile
index b4af1e2..ec90feb 100644
--- a/Makefile
+++ b/Makefile
@@ -871,6 +871,7 @@ LIB_OBJS += submodule.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += trace.o
+LIB_OBJS += trailer.o
LIB_OBJS += transport.o
LIB_OBJS += transport-helper.o
LIB_OBJS += tree-diff.o
diff --git a/trailer.c b/trailer.c
new file mode 100644
index 0000000..db93a63
--- /dev/null
+++ b/trailer.c
@@ -0,0 +1,49 @@
+#include "cache.h"
+/*
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ */
+
+enum action_where { WHERE_AFTER, WHERE_BEFORE };
+enum action_if_exists { EXISTS_ADD_IF_DIFFERENT, EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
+ EXISTS_ADD, EXISTS_OVERWRITE, EXISTS_DO_NOTHING };
+enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
+
+struct conf_info {
+ char *name;
+ char *key;
+ char *command;
+ enum action_where where;
+ enum action_if_exists if_exists;
+ enum action_if_missing if_missing;
+};
+
+struct trailer_item {
+ struct trailer_item *previous;
+ struct trailer_item *next;
+ const char *token;
+ const char *value;
+ struct conf_info conf;
+};
+
+static int same_token(struct trailer_item *a, struct trailer_item *b, int alnum_len)
+{
+ return !strncasecmp(a->token, b->token, alnum_len);
+}
+
+static int same_value(struct trailer_item *a, struct trailer_item *b)
+{
+ return !strcasecmp(a->value, b->value);
+}
+
+static int same_trailer(struct trailer_item *a, struct trailer_item *b, int alnum_len)
+{
+ return same_token(a, b, alnum_len) && same_value(a, b);
+}
+
+/* Get the length of buf from its beginning until its last alphanumeric character */
+static size_t alnum_len(const char *buf, size_t len)
+{
+ while (len > 0 && !isalnum(buf[len - 1]))
+ len--;
+ return len;
+}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 02/11] trailer: process trailers from input message and arguments
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
2014-04-25 19:06 ` [PATCH v11 01/11] trailer: add data structures and basic functions Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 03/11] trailer: read and process config information Christian Couder
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Implement the logic to process trailers from the input message
and from arguments.
At the beginning trailers from the input message are in their
own "in_tok" doubly linked list, and trailers from arguments
are in their own "arg_tok" doubly linked list.
The lists are traversed and when an "arg_tok" should be "applied",
it is removed from its list and inserted into the "in_tok" list.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 198 insertions(+)
diff --git a/trailer.c b/trailer.c
index db93a63..52108c2 100644
--- a/trailer.c
+++ b/trailer.c
@@ -47,3 +47,201 @@ static size_t alnum_len(const char *buf, size_t len)
len--;
return len;
}
+
+static void free_trailer_item(struct trailer_item *item)
+{
+ free(item->conf.name);
+ free(item->conf.key);
+ free(item->conf.command);
+ free((char *)item->token);
+ free((char *)item->value);
+ free(item);
+}
+
+static void add_arg_to_input_list(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok)
+{
+ if (arg_tok->conf.where == WHERE_AFTER) {
+ arg_tok->next = in_tok->next;
+ in_tok->next = arg_tok;
+ arg_tok->previous = in_tok;
+ if (arg_tok->next)
+ arg_tok->next->previous = arg_tok;
+ } else {
+ arg_tok->previous = in_tok->previous;
+ in_tok->previous = arg_tok;
+ arg_tok->next = in_tok;
+ if (arg_tok->previous)
+ arg_tok->previous->next = arg_tok;
+ }
+}
+
+static int check_if_different(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok,
+ int alnum_len, int check_all)
+{
+ enum action_where where = arg_tok->conf.where;
+ do {
+ if (!in_tok)
+ return 1;
+ if (same_trailer(in_tok, arg_tok, alnum_len))
+ return 0;
+ /*
+ * if we want to add a trailer after another one,
+ * we have to check those before this one
+ */
+ in_tok = (where == WHERE_AFTER) ? in_tok->previous : in_tok->next;
+ } while (check_all);
+ return 1;
+}
+
+static void apply_arg_if_exists(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok,
+ int alnum_len)
+{
+ switch (arg_tok->conf.if_exists) {
+ case EXISTS_DO_NOTHING:
+ free_trailer_item(arg_tok);
+ break;
+ case EXISTS_OVERWRITE:
+ free((char *)in_tok->value);
+ in_tok->value = xstrdup(arg_tok->value);
+ free_trailer_item(arg_tok);
+ break;
+ case EXISTS_ADD:
+ add_arg_to_input_list(in_tok, arg_tok);
+ break;
+ case EXISTS_ADD_IF_DIFFERENT:
+ if (check_if_different(in_tok, arg_tok, alnum_len, 1))
+ add_arg_to_input_list(in_tok, arg_tok);
+ else
+ free_trailer_item(arg_tok);
+ break;
+ case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
+ if (check_if_different(in_tok, arg_tok, alnum_len, 0))
+ add_arg_to_input_list(in_tok, arg_tok);
+ else
+ free_trailer_item(arg_tok);
+ break;
+ }
+}
+
+static void remove_from_list(struct trailer_item *item,
+ struct trailer_item **first)
+{
+ if (item->next)
+ item->next->previous = item->previous;
+ if (item->previous)
+ item->previous->next = item->next;
+ else
+ *first = item->next;
+}
+
+static struct trailer_item *remove_first(struct trailer_item **first)
+{
+ struct trailer_item *item = *first;
+ *first = item->next;
+ if (item->next) {
+ item->next->previous = NULL;
+ item->next = NULL;
+ }
+ return item;
+}
+
+static void process_input_token(struct trailer_item *in_tok,
+ struct trailer_item **arg_tok_first,
+ enum action_where where)
+{
+ struct trailer_item *arg_tok;
+ struct trailer_item *next_arg;
+
+ int after = where == WHERE_AFTER;
+ int tok_alnum_len = alnum_len(in_tok->token, strlen(in_tok->token));
+
+ for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {
+ next_arg = arg_tok->next;
+ if (!same_token(in_tok, arg_tok, tok_alnum_len))
+ continue;
+ if (arg_tok->conf.where != where)
+ continue;
+ remove_from_list(arg_tok, arg_tok_first);
+ apply_arg_if_exists(in_tok, arg_tok, tok_alnum_len);
+ /*
+ * If arg has been added to input,
+ * then we need to process it too now.
+ */
+ if ((after ? in_tok->next : in_tok->previous) == arg_tok)
+ in_tok = arg_tok;
+ }
+}
+
+static void update_last(struct trailer_item **last)
+{
+ if (*last)
+ while ((*last)->next != NULL)
+ *last = (*last)->next;
+}
+
+static void update_first(struct trailer_item **first)
+{
+ if (*first)
+ while ((*first)->previous != NULL)
+ *first = (*first)->previous;
+}
+
+static void apply_arg_if_missing(struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last,
+ struct trailer_item *arg_tok)
+{
+ struct trailer_item **in_tok;
+ enum action_where where;
+
+ switch (arg_tok->conf.if_missing) {
+ case MISSING_DO_NOTHING:
+ free_trailer_item(arg_tok);
+ break;
+ case MISSING_ADD:
+ where = arg_tok->conf.where;
+ in_tok = (where == WHERE_AFTER) ? in_tok_last : in_tok_first;
+ if (*in_tok) {
+ add_arg_to_input_list(*in_tok, arg_tok);
+ *in_tok = arg_tok;
+ } else {
+ *in_tok_first = arg_tok;
+ *in_tok_last = arg_tok;
+ }
+ break;
+ }
+}
+
+static void process_trailers_lists(struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last,
+ struct trailer_item **arg_tok_first)
+{
+ struct trailer_item *in_tok;
+ struct trailer_item *arg_tok;
+
+ if (!*arg_tok_first)
+ return;
+
+ /* Process input from end to start */
+ for (in_tok = *in_tok_last; in_tok; in_tok = in_tok->previous)
+ process_input_token(in_tok, arg_tok_first, WHERE_AFTER);
+
+ update_last(in_tok_last);
+
+ if (!*arg_tok_first)
+ return;
+
+ /* Process input from start to end */
+ for (in_tok = *in_tok_first; in_tok; in_tok = in_tok->next)
+ process_input_token(in_tok, arg_tok_first, WHERE_BEFORE);
+
+ update_first(in_tok_first);
+
+ /* Process args left */
+ while (*arg_tok_first) {
+ arg_tok = remove_first(arg_tok_first);
+ apply_arg_if_missing(in_tok_first, in_tok_last, arg_tok);
+ }
+}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 03/11] trailer: read and process config information
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
2014-04-25 19:06 ` [PATCH v11 01/11] trailer: add data structures and basic functions Christian Couder
2014-04-25 19:06 ` [PATCH v11 02/11] trailer: process trailers from input message and arguments Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 04/11] trailer: process command line trailer arguments Christian Couder
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Read the configuration to get trailer information, and then process
it and store it in a doubly linked list.
The config information is stored in the list whose first item is
pointed to by:
static struct trailer_item *first_conf_item;
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 146 insertions(+)
diff --git a/trailer.c b/trailer.c
index 52108c2..f376be5 100644
--- a/trailer.c
+++ b/trailer.c
@@ -25,6 +25,8 @@ struct trailer_item {
struct conf_info conf;
};
+static struct trailer_item *first_conf_item;
+
static int same_token(struct trailer_item *a, struct trailer_item *b, int alnum_len)
{
return !strncasecmp(a->token, b->token, alnum_len);
@@ -245,3 +247,147 @@ static void process_trailers_lists(struct trailer_item **in_tok_first,
apply_arg_if_missing(in_tok_first, in_tok_last, arg_tok);
}
}
+
+static int set_where(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("after", value))
+ item->where = WHERE_AFTER;
+ else if (!strcasecmp("before", value))
+ item->where = WHERE_BEFORE;
+ else
+ return -1;
+ return 0;
+}
+
+static int set_if_exists(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("addIfDifferent", value))
+ item->if_exists = EXISTS_ADD_IF_DIFFERENT;
+ else if (!strcasecmp("addIfDifferentNeighbor", value))
+ item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+ else if (!strcasecmp("add", value))
+ item->if_exists = EXISTS_ADD;
+ else if (!strcasecmp("overwrite", value))
+ item->if_exists = EXISTS_OVERWRITE;
+ else if (!strcasecmp("doNothing", value))
+ item->if_exists = EXISTS_DO_NOTHING;
+ else
+ return -1;
+ return 0;
+}
+
+static int set_if_missing(struct conf_info *item, const char *value)
+{
+ if (!strcasecmp("doNothing", value))
+ item->if_missing = MISSING_DO_NOTHING;
+ else if (!strcasecmp("add", value))
+ item->if_missing = MISSING_ADD;
+ else
+ return -1;
+ return 0;
+}
+
+static struct trailer_item *get_conf_item(const char *name)
+{
+ struct trailer_item *item;
+ struct trailer_item *previous;
+
+ /* Look up item with same name */
+ for (previous = NULL, item = first_conf_item;
+ item;
+ previous = item, item = item->next) {
+ if (!strcasecmp(item->conf.name, name))
+ return item;
+ }
+
+ /* Item does not already exists, create it */
+ item = xcalloc(sizeof(struct trailer_item), 1);
+ item->conf.name = xstrdup(name);
+
+ if (!previous)
+ first_conf_item = item;
+ else {
+ previous->next = item;
+ item->previous = previous;
+ }
+
+ return item;
+}
+
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
+ TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+
+static struct {
+ const char *name;
+ enum trailer_info_type type;
+} trailer_config_items[] = {
+ { "key", TRAILER_KEY },
+ { "command", TRAILER_COMMAND },
+ { "where", TRAILER_WHERE },
+ { "ifexists", TRAILER_IF_EXISTS },
+ { "ifmissing", TRAILER_IF_MISSING }
+};
+
+static int git_trailer_config(const char *conf_key, const char *value, void *cb)
+{
+ const char *trailer_item, *variable_name;
+ struct trailer_item *item;
+ struct conf_info *conf;
+ char *name = NULL;
+ enum trailer_info_type type;
+ int i;
+
+ trailer_item = skip_prefix(conf_key, "trailer.");
+ if (!trailer_item)
+ return 0;
+
+ variable_name = strrchr(trailer_item, '.');
+ if (!variable_name) {
+ warning(_("two level trailer config variable %s"), conf_key);
+ return 0;
+ }
+
+ variable_name++;
+ for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
+ if (strcmp(trailer_config_items[i].name, variable_name))
+ continue;
+ name = xstrndup(trailer_item, variable_name - trailer_item - 1);
+ type = trailer_config_items[i].type;
+ break;
+ }
+
+ if (!name)
+ return 0;
+
+ item = get_conf_item(name);
+ conf = &item->conf;
+ free(name);
+
+ switch (type) {
+ case TRAILER_KEY:
+ if (conf->key)
+ warning(_("more than one %s"), conf_key);
+ conf->key = xstrdup(value);
+ break;
+ case TRAILER_COMMAND:
+ if (conf->command)
+ warning(_("more than one %s"), conf_key);
+ conf->command = xstrdup(value);
+ break;
+ case TRAILER_WHERE:
+ if (set_where(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ case TRAILER_IF_EXISTS:
+ if (set_if_exists(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ case TRAILER_IF_MISSING:
+ if (set_if_missing(conf, value))
+ warning(_("unknown value '%s' for key '%s'"), value, conf_key);
+ break;
+ default:
+ die("internal bug in trailer.c");
+ }
+ return 0;
+}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 04/11] trailer: process command line trailer arguments
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (2 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 03/11] trailer: read and process config information Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 05/11] trailer: parse trailers from file or stdin Christian Couder
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Parse the trailer command line arguments and put
the result into an arg_tok doubly linked list.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/trailer.c b/trailer.c
index f376be5..f79a369 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "string-list.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
@@ -391,3 +392,120 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
}
return 0;
}
+
+static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)
+{
+ size_t len = strcspn(trailer, "=:");
+ if (len == 0)
+ return error(_("empty trailer token in trailer '%s'"), trailer);
+ if (len < strlen(trailer)) {
+ strbuf_add(tok, trailer, len);
+ strbuf_trim(tok);
+ strbuf_addstr(val, trailer + len + 1);
+ strbuf_trim(val);
+ } else {
+ strbuf_addstr(tok, trailer);
+ strbuf_trim(tok);
+ }
+ return 0;
+}
+
+
+static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
+{
+ *dst = *src;
+ if (src->name)
+ dst->name = xstrdup(src->name);
+ if (src->key)
+ dst->key = xstrdup(src->key);
+ if (src->command)
+ dst->command = xstrdup(src->command);
+}
+
+static const char *token_from_item(struct trailer_item *item)
+{
+ if (item->conf.key)
+ return item->conf.key;
+
+ return item->conf.name;
+}
+
+static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
+ char *tok, char *val)
+{
+ struct trailer_item *new = xcalloc(sizeof(*new), 1);
+ new->value = val;
+
+ if (conf_item) {
+ duplicate_conf(&new->conf, &conf_item->conf);
+ new->token = xstrdup(token_from_item(conf_item));
+ free(tok);
+ } else
+ new->token = tok;
+
+ return new;
+}
+
+static int token_matches_item(const char *tok, struct trailer_item *item, int alnum_len)
+{
+ if (!strncasecmp(tok, item->conf.name, alnum_len))
+ return 1;
+ return item->conf.key ? !strncasecmp(tok, item->conf.key, alnum_len) : 0;
+}
+
+static struct trailer_item *create_trailer_item(const char *string)
+{
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+ struct trailer_item *item;
+ int tok_alnum_len;
+
+ if (parse_trailer(&tok, &val, string))
+ return NULL;
+
+ tok_alnum_len = alnum_len(tok.buf, tok.len);
+
+ /* Lookup if the token matches something in the config */
+ for (item = first_conf_item; item; item = item->next) {
+ if (token_matches_item(tok.buf, item, tok_alnum_len)) {
+ strbuf_release(&tok);
+ return new_trailer_item(item,
+ NULL,
+ strbuf_detach(&val, NULL));
+ }
+ }
+
+ return new_trailer_item(NULL,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL));
+}
+
+static void add_trailer_item(struct trailer_item **first,
+ struct trailer_item **last,
+ struct trailer_item *new)
+{
+ if (!new)
+ return;
+ if (!*last) {
+ *first = new;
+ *last = new;
+ } else {
+ (*last)->next = new;
+ new->previous = *last;
+ *last = new;
+ }
+}
+
+static struct trailer_item *process_command_line_args(struct string_list *trailers)
+{
+ struct trailer_item *arg_tok_first = NULL;
+ struct trailer_item *arg_tok_last = NULL;
+ struct string_list_item *tr;
+
+ for_each_string_list_item(tr, trailers) {
+ struct trailer_item *new = create_trailer_item(tr->string);
+ add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+ }
+
+ return arg_tok_first;
+}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 05/11] trailer: parse trailers from file or stdin
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (3 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 04/11] trailer: process command line trailer arguments Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 06/11] trailer: put all the processing together and print Christian Couder
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Read trailers from a file or from stdin, parse the trailers and then
put the result into a doubly linked list.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/trailer.c b/trailer.c
index f79a369..4ca9157 100644
--- a/trailer.c
+++ b/trailer.c
@@ -51,6 +51,14 @@ static size_t alnum_len(const char *buf, size_t len)
return len;
}
+static inline int contains_only_spaces(const char *str)
+{
+ const char *s = str;
+ while (*s && isspace(*s))
+ s++;
+ return !*s;
+}
+
static void free_trailer_item(struct trailer_item *item)
{
free(item->conf.name);
@@ -509,3 +517,111 @@ static struct trailer_item *process_command_line_args(struct string_list *traile
return arg_tok_first;
}
+
+static struct strbuf **read_input_file(const char *file)
+{
+ struct strbuf **lines;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (file) {
+ if (strbuf_read_file(&sb, file, 0) < 0)
+ die_errno(_("could not read input file '%s'"), file);
+ } else {
+ if (strbuf_read(&sb, fileno(stdin), 0) < 0)
+ die_errno(_("could not read from stdin"));
+ }
+
+ lines = strbuf_split(&sb, '\n');
+
+ strbuf_release(&sb);
+
+ return lines;
+}
+
+/*
+ * Return the (0 based) index of the start of the patch or the line
+ * count if there is no patch in the message.
+ */
+static int find_patch_start(struct strbuf **lines, int count)
+{
+ int i;
+
+ /* Get the start of the patch part if any */
+ for (i = 0; i < count; i++) {
+ if (starts_with(lines[i]->buf, "---"))
+ return i;
+ }
+
+ return count;
+}
+
+/*
+ * Return the (0 based) index of the first trailer line or count if
+ * there are no trailers. Trailers are searched only in the lines from
+ * index (count - 1) down to index 0. The has_blank_line parameter
+ * tells if there is a blank line before the trailers.
+ */
+static int find_trailer_start(struct strbuf **lines, int count, int *has_blank_line)
+{
+ int start, only_spaces = 1;
+
+ /*
+ * Get the start of the trailers by looking starting from the end
+ * for a line with only spaces before lines with one ':'.
+ */
+ for (start = count - 1; start >= 0; start--) {
+ if (contains_only_spaces(lines[start]->buf)) {
+ if (only_spaces)
+ continue;
+ *has_blank_line = 1;
+ return start + 1;
+ }
+ if (strchr(lines[start]->buf, ':')) {
+ if (only_spaces)
+ only_spaces = 0;
+ continue;
+ }
+ *has_blank_line = start == count - 1 ?
+ 0 : contains_only_spaces(lines[start + 1]->buf);
+ return count;
+ }
+
+ *has_blank_line = only_spaces ? count > 0 : 0;
+ return only_spaces ? count : start + 1;
+}
+
+static void print_lines(struct strbuf **lines, int start, int end)
+{
+ int i;
+ for (i = start; lines[i] && i < end; i++)
+ printf("%s", lines[i]->buf);
+}
+
+static int process_input_file(struct strbuf **lines,
+ struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last)
+{
+ int count = 0;
+ int patch_start, trailer_start, has_blank_line, i;
+
+ /* Get the line count */
+ while (lines[count])
+ count++;
+
+ patch_start = find_patch_start(lines, count);
+ trailer_start = find_trailer_start(lines, patch_start, &has_blank_line);
+
+ /* Print lines before the trailers as is */
+ print_lines(lines, 0, trailer_start);
+
+ if (!has_blank_line)
+ printf("\n");
+
+ /* Parse trailer lines */
+ for (i = trailer_start; i < patch_start; i++) {
+ struct trailer_item *new = create_trailer_item(lines[i]->buf);
+ add_trailer_item(in_tok_first, in_tok_last, new);
+ }
+
+ return patch_start;
+}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 06/11] trailer: put all the processing together and print
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (4 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 05/11] trailer: parse trailers from file or stdin Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 07/11] trailer: add interpret-trailers command Christian Couder
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
This patch adds the process_trailers() function that
calls all the previously added processing functions
and then prints the results on the standard output.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
trailer.h | 6 ++++++
2 files changed, 64 insertions(+)
create mode 100644 trailer.h
diff --git a/trailer.c b/trailer.c
index 4ca9157..feadd3a 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "string-list.h"
+#include "trailer.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
@@ -69,6 +70,26 @@ static void free_trailer_item(struct trailer_item *item)
free(item);
}
+static void print_tok_val(const char *tok, const char *val)
+{
+ char c = tok[strlen(tok) - 1];
+ if (isalnum(c))
+ printf("%s: %s\n", tok, val);
+ else if (isspace(c) || c == '#')
+ printf("%s%s\n", tok, val);
+ else
+ printf("%s %s\n", tok, val);
+}
+
+static void print_all(struct trailer_item *first, int trim_empty)
+{
+ struct trailer_item *item;
+ for (item = first; item; item = item->next) {
+ if (!trim_empty || strlen(item->value) > 0)
+ print_tok_val(item->token, item->value);
+ }
+}
+
static void add_arg_to_input_list(struct trailer_item *in_tok,
struct trailer_item *arg_tok)
{
@@ -625,3 +646,40 @@ static int process_input_file(struct strbuf **lines,
return patch_start;
}
+
+static void free_all(struct trailer_item **first)
+{
+ while (*first) {
+ struct trailer_item *item = remove_first(first);
+ free_trailer_item(item);
+ }
+}
+
+void process_trailers(const char *file, int trim_empty, struct string_list *trailers)
+{
+ struct trailer_item *in_tok_first = NULL;
+ struct trailer_item *in_tok_last = NULL;
+ struct trailer_item *arg_tok_first;
+ struct strbuf **lines;
+ int patch_start;
+
+ git_config(git_trailer_config, NULL);
+
+ lines = read_input_file(file);
+
+ /* Print the lines before the trailers */
+ patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+
+ arg_tok_first = process_command_line_args(trailers);
+
+ process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
+
+ print_all(in_tok_first, trim_empty);
+
+ free_all(&in_tok_first);
+
+ /* Print the lines after the trailers as is */
+ print_lines(lines, patch_start, INT_MAX);
+
+ strbuf_list_free(lines);
+}
diff --git a/trailer.h b/trailer.h
new file mode 100644
index 0000000..8eb25d5
--- /dev/null
+++ b/trailer.h
@@ -0,0 +1,6 @@
+#ifndef TRAILER_H
+#define TRAILER_H
+
+void process_trailers(const char *file, int trim_empty, struct string_list *trailers);
+
+#endif /* TRAILER_H */
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 07/11] trailer: add interpret-trailers command
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (5 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 06/11] trailer: put all the processing together and print Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:06 ` [PATCH v11 08/11] trailer: add tests for "git interpret-trailers" Christian Couder
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
This patch adds the "git interpret-trailers" command.
This command uses the previously added process_trailers()
function in trailer.c.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
.gitignore | 1 +
Makefile | 1 +
builtin.h | 1 +
builtin/interpret-trailers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
git.c | 1 +
5 files changed, 48 insertions(+)
create mode 100644 builtin/interpret-trailers.c
diff --git a/.gitignore b/.gitignore
index b5f9def..c870ada 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@
/git-index-pack
/git-init
/git-init-db
+/git-interpret-trailers
/git-instaweb
/git-log
/git-ls-files
diff --git a/Makefile b/Makefile
index ec90feb..a91465e 100644
--- a/Makefile
+++ b/Makefile
@@ -935,6 +935,7 @@ BUILTIN_OBJS += builtin/hash-object.o
BUILTIN_OBJS += builtin/help.o
BUILTIN_OBJS += builtin/index-pack.o
BUILTIN_OBJS += builtin/init-db.o
+BUILTIN_OBJS += builtin/interpret-trailers.o
BUILTIN_OBJS += builtin/log.o
BUILTIN_OBJS += builtin/ls-files.o
BUILTIN_OBJS += builtin/ls-remote.o
diff --git a/builtin.h b/builtin.h
index c47c110..8ca0065 100644
--- a/builtin.h
+++ b/builtin.h
@@ -73,6 +73,7 @@ extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
extern int cmd_log(int argc, const char **argv, const char *prefix);
extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
new file mode 100644
index 0000000..46838d2
--- /dev/null
+++ b/builtin/interpret-trailers.c
@@ -0,0 +1,44 @@
+/*
+ * Builtin "git interpret-trailers"
+ *
+ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
+ *
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "trailer.h"
+
+static const char * const git_interpret_trailers_usage[] = {
+ N_("git interpret-trailers [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"),
+ NULL
+};
+
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
+{
+ int trim_empty = 0;
+ struct string_list trailers = STRING_LIST_INIT_DUP;
+
+ struct option options[] = {
+ OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
+ OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
+ N_("trailer(s) to add")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ git_interpret_trailers_usage, 0);
+
+ if (argc) {
+ int i;
+ for (i = 0; i < argc; i++)
+ process_trailers(argv[i], trim_empty, &trailers);
+ } else
+ process_trailers(NULL, trim_empty, &trailers);
+
+ string_list_clear(&trailers, 0);
+
+ return 0;
+}
diff --git a/git.c b/git.c
index 7cf2953..63a03eb 100644
--- a/git.c
+++ b/git.c
@@ -380,6 +380,7 @@ static struct cmd_struct commands[] = {
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
+ { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 08/11] trailer: add tests for "git interpret-trailers"
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (6 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 07/11] trailer: add interpret-trailers command Christian Couder
@ 2014-04-25 19:06 ` Christian Couder
2014-04-25 19:07 ` [PATCH v11 09/11] trailer: execute command from 'trailer.<name>.command' Christian Couder
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
t/t7513-interpret-trailers.sh | 418 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 418 insertions(+)
create mode 100755 t/t7513-interpret-trailers.sh
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
new file mode 100755
index 0000000..4506e18
--- /dev/null
+++ b/t/t7513-interpret-trailers.sh
@@ -0,0 +1,418 @@
+#!/bin/sh
+#
+# Copyright (c) 2013, 2014 Christian Couder
+#
+
+test_description='git interpret-trailers'
+
+. ./test-lib.sh
+
+# When we want one trailing space at the end of each line, let's use sed
+# to make sure that these spaces are not removed by any automatic tool.
+
+test_expect_success 'setup' '
+ cat >basic_message <<-\EOF &&
+ subject
+
+ body
+ EOF
+ cat >complex_message_body <<-\EOF &&
+ my subject
+
+ my body which is long
+ and contains some special
+ chars like : = ? !
+
+ EOF
+ sed -e "s/ Z\$/ /" >complex_message_trailers <<-\EOF &&
+ Fixes: Z
+ Acked-by: Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ cat >basic_patch <<-\EOF
+ ---
+ foo.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+ diff --git a/foo.txt b/foo.txt
+ index 0353767..1d91aa1 100644
+ --- a/foo.txt
+ +++ b/foo.txt
+ @@ -1,3 +1,3 @@
+
+ -bar
+ +baz
+
+ --
+ 1.9.rc0.11.ga562ddc
+
+ EOF
+'
+
+test_expect_success 'without config' '
+ sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+ ack: Peff
+ Reviewed-by: Z
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trailer "ack = Peff" --trailer "Reviewed-by" \
+ --trailer "Acked-by: Johan" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '--trim-empty without config' '
+ cat >expected <<-\EOF &&
+
+ ack: Peff
+ Acked-by: Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" \
+ --trailer "Reviewed-by" --trailer "Acked-by: Johan" \
+ --trailer "sob:" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup' '
+ git config trailer.ack.key "Acked-by: " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by: Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by = Peff" >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by :Peff" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and = sign' '
+ git config trailer.ack.key "Acked-by= " &&
+ cat >expected <<-\EOF &&
+
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack = Peff" >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by= Peff" >actual &&
+ test_cmp expected actual &&
+ git interpret-trailers --trim-empty --trailer "Acked-by : Peff" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with config setup and # sign' '
+ git config trailer.bug.key "Bug #" &&
+ cat >expected <<-\EOF &&
+
+ Bug #42
+ EOF
+ git interpret-trailers --trim-empty --trailer "bug = 42" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit basic message' '
+ cat basic_message >expected &&
+ echo >>expected &&
+ git interpret-trailers <basic_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with basic patch' '
+ cat basic_message >input &&
+ cat basic_patch >>input &&
+ cat basic_message >expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers <input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message as argument' '
+ cat complex_message_body complex_message_trailers >complex_message &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with 2 files arguments' '
+ cat basic_message >>expected &&
+ echo >>expected &&
+ cat basic_patch >>expected &&
+ git interpret-trailers complex_message input >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message and trailer args' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Bug #42
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with complex patch, args and --trim-empty' '
+ cat complex_message >complex_patch &&
+ cat basic_patch >>complex_patch &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Acked-by= Peff
+ Bug #42
+ EOF
+ cat basic_patch >>expected &&
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" <complex_patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before"' '
+ git config trailer.bug.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "bug: 42" complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" for a token in the middle of the message' '
+ git config trailer.review.key "Reviewed-by:" &&
+ git config trailer.review.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Johan
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "bug: 42" \
+ --trailer "review: Johan" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" and --trim-empty' '
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #46
+ Bug #42
+ Acked-by= Peff
+ Reviewed-by: Johan
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "bug: 42" --trailer "review: Johan" \
+ --trailer "Bug: 46" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifExists = addIfDifferent"' '
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferent"' '
+ git config trailer.review.ifExists "addIfDifferent" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor"' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
+ git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+ cat complex_message_body >expected &&
+ cat >>expected <<-\EOF &&
+ Bug #42
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ EOF
+ git interpret-trailers --trim-empty --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = add"' '
+ git config trailer.ack.ifExists "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Peff
+ Acked-by= Peff
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "ack: Peff" \
+ --trailer "Acked-by= Peff" --trailer "review:" \
+ --trailer "ack: Junio" --trailer "bug: 42" \
+ --trailer "ack: Peff" <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = overwrite"' '
+ git config trailer.fix.key "Fixes:" &&
+ git config trailer.fix.ifExists "overwrite" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: 22
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" \
+ --trailer "fix=53" --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifExists = doNothing"' '
+ git config trailer.fix.ifExists "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifMissing = add"' '
+ git config trailer.cc.key "Cc: " &&
+ git config trailer.cc.where "before" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Cc: Linus
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = add"' '
+ git config trailer.cc.ifMissing "add" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Cc: Linus
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "ack: Junio" --trailer "fix=22" \
+ --trailer "bug: 42" --trailer "cc=Linus" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = doNothing"' '
+ git config trailer.cc.ifMissing "doNothing" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Bug #42
+ Fixes: Z
+ Acked-by= Z
+ Acked-by= Junio
+ Acked-by= Peff
+ Reviewed-by: Z
+ Signed-off-by: Z
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=53" \
+ --trailer "cc=Linus" --trailer "ack: Junio" \
+ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_done
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 09/11] trailer: execute command from 'trailer.<name>.command'
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (7 preceding siblings ...)
2014-04-25 19:06 ` [PATCH v11 08/11] trailer: add tests for "git interpret-trailers" Christian Couder
@ 2014-04-25 19:07 ` Christian Couder
2014-04-25 19:07 ` [PATCH v11 10/11] trailer: add tests for commands in config file Christian Couder
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:07 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Let the user specify a command that will give on its standard output
the value to use for the specified trailer.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
trailer.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/trailer.c b/trailer.c
index feadd3a..4d32b42 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,5 +1,7 @@
#include "cache.h"
#include "string-list.h"
+#include "run-command.h"
+#include "string-list.h"
#include "trailer.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@ -14,11 +16,14 @@ struct conf_info {
char *name;
char *key;
char *command;
+ unsigned command_uses_arg : 1;
enum action_where where;
enum action_if_exists if_exists;
enum action_if_missing if_missing;
};
+#define TRAILER_ARG_STRING "$ARG"
+
struct trailer_item {
struct trailer_item *previous;
struct trailer_item *next;
@@ -60,6 +65,13 @@ static inline int contains_only_spaces(const char *str)
return !*s;
}
+static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
+{
+ const char *ptr = strstr(sb->buf, a);
+ if (ptr)
+ strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
+}
+
static void free_trailer_item(struct trailer_item *item)
{
free(item->conf.name);
@@ -403,6 +415,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
if (conf->command)
warning(_("more than one %s"), conf_key);
conf->command = xstrdup(value);
+ conf->command_uses_arg = !!strstr(conf->command, TRAILER_ARG_STRING);
break;
case TRAILER_WHERE:
if (set_where(conf, value))
@@ -439,6 +452,45 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *tra
return 0;
}
+static int read_from_command(struct child_process *cp, struct strbuf *buf)
+{
+ if (run_command(cp))
+ return error("running trailer command '%s' failed", cp->argv[0]);
+ if (strbuf_read(buf, cp->out, 1024) < 1)
+ return error("reading from trailer command '%s' failed", cp->argv[0]);
+ strbuf_trim(buf);
+ return 0;
+}
+
+static const char *apply_command(const char *command, const char *arg)
+{
+ struct strbuf cmd = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct child_process cp;
+ const char *argv[] = {NULL, NULL};
+ const char *result;
+
+ strbuf_addstr(&cmd, command);
+ if (arg)
+ strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+
+ argv[0] = cmd.buf;
+ memset(&cp, 0, sizeof(cp));
+ cp.argv = argv;
+ cp.env = local_repo_env;
+ cp.no_stdin = 1;
+ cp.out = -1;
+ cp.use_shell = 1;
+
+ if (read_from_command(&cp, &buf)) {
+ strbuf_release(&buf);
+ result = xstrdup("");
+ } else
+ result = strbuf_detach(&buf, NULL);
+
+ strbuf_release(&cmd);
+ return result;
+}
static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
{
@@ -469,6 +521,10 @@ static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
duplicate_conf(&new->conf, &conf_item->conf);
new->token = xstrdup(token_from_item(conf_item));
free(tok);
+ if (conf_item->conf.command_uses_arg || !val) {
+ new->value = apply_command(conf_item->conf.command, val);
+ free(val);
+ }
} else
new->token = tok;
@@ -530,12 +586,21 @@ static struct trailer_item *process_command_line_args(struct string_list *traile
struct trailer_item *arg_tok_first = NULL;
struct trailer_item *arg_tok_last = NULL;
struct string_list_item *tr;
+ struct trailer_item *item;
for_each_string_list_item(tr, trailers) {
struct trailer_item *new = create_trailer_item(tr->string);
add_trailer_item(&arg_tok_first, &arg_tok_last, new);
}
+ /* Add conf commands that don't use $ARG */
+ for (item = first_conf_item; item; item = item->next) {
+ if (item->conf.command && !item->conf.command_uses_arg) {
+ struct trailer_item *new = new_trailer_item(item, NULL, NULL);
+ add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+ }
+ }
+
return arg_tok_first;
}
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 10/11] trailer: add tests for commands in config file
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (8 preceding siblings ...)
2014-04-25 19:07 ` [PATCH v11 09/11] trailer: execute command from 'trailer.<name>.command' Christian Couder
@ 2014-04-25 19:07 ` Christian Couder
2014-04-25 19:07 ` [PATCH v11 11/11] Documentation: add documentation for 'git interpret-trailers' Christian Couder
2014-04-29 21:43 ` [PATCH v11 00/11] Add interpret-trailers builtin Junio C Hamano
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:07 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
And add a few other tests for some special cases.
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
t/t7513-interpret-trailers.sh | 124 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 4506e18..9aae721 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -415,4 +415,128 @@ test_expect_success 'using "ifMissing = doNothing"' '
test_cmp expected actual
'
+test_expect_success 'with simple command' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using commiter information' '
+ git config trailer.sign.ifExists "addIfDifferent" &&
+ git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Signed-off-by: C O Mitter <committer@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with command using author information' '
+ git config trailer.sign.key "Signed-off-by: " &&
+ git config trailer.sign.where "after" &&
+ git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+ git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=22" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup a commit' '
+ echo "Content of the first commit." > a.txt &&
+ git add a.txt &&
+ git commit -m "Add file a.txt"
+'
+
+test_expect_success 'with command using $ARG' '
+ git config trailer.fix.ifExists "overwrite" &&
+ git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+ FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: $FIXED
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with failing command using $ARG' '
+ git config trailer.fix.ifExists "overwrite" &&
+ git config trailer.fix.command "false \$ARG" &&
+ cat complex_message_body >expected &&
+ sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+ Fixes: Z
+ Acked-by= Z
+ Reviewed-by: Z
+ Signed-off-by: Z
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+ <complex_message >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with empty tokens' '
+ cat >expected <<-EOF &&
+
+ Signed-off-by: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer ":" --trailer ":test" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with command but no key' '
+ git config --unset trailer.sign.key &&
+ cat >expected <<-EOF &&
+
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'with no command and no key' '
+ git config --unset trailer.review.key &&
+ cat >expected <<-EOF &&
+
+ review: Junio
+ sign: A U Thor <author@example.com>
+ EOF
+ git interpret-trailers --trailer "review:Junio" >actual <<-EOF &&
+ EOF
+ test_cmp expected actual
+'
+
test_done
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v11 11/11] Documentation: add documentation for 'git interpret-trailers'
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (9 preceding siblings ...)
2014-04-25 19:07 ` [PATCH v11 10/11] trailer: add tests for commands in config file Christian Couder
@ 2014-04-25 19:07 ` Christian Couder
2014-04-29 21:43 ` [PATCH v11 00/11] Add interpret-trailers builtin Junio C Hamano
11 siblings, 0 replies; 13+ messages in thread
From: Christian Couder @ 2014-04-25 19:07 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
While at it add git-interpret-trailers to "command-list.txt".
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Documentation/git-interpret-trailers.txt | 143 +++++++++++++++++++++++++++++++
command-list.txt | 1 +
2 files changed, 144 insertions(+)
create mode 100644 Documentation/git-interpret-trailers.txt
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
new file mode 100644
index 0000000..450ec54
--- /dev/null
+++ b/Documentation/git-interpret-trailers.txt
@@ -0,0 +1,143 @@
+git-interpret-trailers(1)
+=========================
+
+NAME
+----
+git-interpret-trailers - help add stuctured information into commit messages
+
+SYNOPSIS
+--------
+[verse]
+'git interpret-trailers' [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]
+
+DESCRIPTION
+-----------
+Help adding 'trailers' lines, that look similar to RFC 822 e-mail
+headers, at the end of the otherwise free-form part of a commit
+message.
+
+This command reads some patches or commit messages from either the
+<file> arguments or the standard input if no <file> is specified. Then
+this command applies the arguments passed using the `--trailer`
+option, if any, to the commit message part of each input file. The
+result is emitted on the standard output.
+
+Some configuration variables control the way the `--trailer` arguments
+are applied to each commit message and the way any existing trailer in
+the commit message is changed. They also make it possible to
+automatically add some trailers.
+
+By default, a '<token>=<value>' or '<token>:<value>' argument given
+using `--trailer` will be added only if no trailer with the same
+(<token>, <value>) pair is already in the message. The <token> and
+<value> parts will be trimmed to remove starting and trailing
+whitespace, and the resulting trimmed <token> and <value> will appear
+in the message like this:
+
+------------------------------------------------
+token: value
+------------------------------------------------
+
+By default, if there are already trailers with the same <token>, the
+new trailer will appear just after the last trailer with the same
+<token>. Otherwise it will appear at the end of the message.
+
+The trailers are recognized in the input commit message using the
+following rules:
+
+* only lines that contains a ':' are considered trailers,
+* the trailer lines must all be next to each other,
+* after them it's only possible to have some lines that contain only spaces,
+* before them there must be at least one line with only spaces.
+
+Note that 'trailers' do not follow and are not intended to follow many
+rules for RFC 822 headers. For example they do not follow the line
+folding rules, the encoding rules and probably many other rules.
+
+OPTIONS
+-------
+--trim-empty::
+ If the <value> part of any trailer contains only whitespace,
+ the whole trailer will be removed from the resulting message.
+ This apply to existing trailers as well as new trailers.
+
+--trailer <token>[(=|:)<value>]::
+ Specify a (<token>, <value>) pair that should be applied as a
+ trailer to the input messages. See the description of this
+ command.
+
+CONFIGURATION VARIABLES
+-----------------------
+
+trailer.<token>.key::
+ This `key` will be used instead of <token> in the
+ trailer. After the last alphanumeric character, it can contain
+ some non alphanumeric characters like ':', '=' or '#' that
+ will be used instead of ':' to separate the <token> from the
+ <value> in the trailer, though the default ':' is more
+ standard.
+
+trailer.<token>.where::
+ This can be either `after`, which is the default, or
+ `before`. If it is `before`, then a trailer with the specified
+ <token>, will appear before, instead of after, other trailers
+ with the same <token>, or otherwise at the beginning, instead
+ of at the end, of all the trailers.
+
+trailer.<token>.ifexist::
+ This option makes it possible to choose what action will be
+ performed when there is already at least one trailer with the
+ same <token> in the message.
++
+The valid values for this option are: `addIfDifferent` (this is the
+default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.
++
+With `addIfDifferent`, a new trailer will be added only if no trailer
+with the same (<token>, <value>) pair is already in the message.
++
+With `addIfDifferentNeighbor`, a new trailer will be added only if no
+trailer with the same (<token>, <value>) pair is above or below the line
+where the new trailer will be added.
++
+With `add`, a new trailer will be added, even if some trailers with
+the same (<token>, <value>) pair are already in the message.
++
+With `overwrite`, the new trailer will overwrite an existing trailer
+with the same <token>.
++
+With `doNothing`, nothing will be done; that is no new trailer will be
+added if there is already one with the same <token> in the message.
+
+trailer.<token>.ifmissing::
+ This option makes it possible to choose what action will be
+ performed when there is not yet any trailer with the same
+ <token> in the message.
++
+The valid values for this option are: `add` (this is the default) and
+`doNothing`.
++
+With `add`, a new trailer will be added.
++
+With `doNothing`, nothing will be done.
+
+trailer.<token>.command::
+ This option can be used to specify a shell command that will
+ be used to automatically add or modify a trailer with the
+ specified <token>.
++
+When this option is specified, the behavior is as if a special
+'<token>=<value>' argument were added at the end of the command line,
+where <value> is taken to be the standard output of the specified
+command with any leading and trailing whitespace trimmed off.
++
+If the command contains the `$ARG` string, this string will be
+replaced with the <value> part of an existing trailer with the same
+<token>, if any, before the command is launched.
+
+SEE ALSO
+--------
+linkgit:git-commit[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/command-list.txt b/command-list.txt
index cf36c3d..d5e0bed 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -62,6 +62,7 @@ git-imap-send foreignscminterface
git-index-pack plumbingmanipulators
git-init mainporcelain common
git-instaweb ancillaryinterrogators
+git-interpret-trailers purehelpers
gitk mainporcelain
git-log mainporcelain common
git-ls-files plumbinginterrogators
--
1.9.1.636.g20d5f34
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v11 00/11] Add interpret-trailers builtin
2014-04-25 19:06 [PATCH v11 00/11] Add interpret-trailers builtin Christian Couder
` (10 preceding siblings ...)
2014-04-25 19:07 ` [PATCH v11 11/11] Documentation: add documentation for 'git interpret-trailers' Christian Couder
@ 2014-04-29 21:43 ` Junio C Hamano
11 siblings, 0 replies; 13+ messages in thread
From: Junio C Hamano @ 2014-04-29 21:43 UTC (permalink / raw)
To: Christian Couder
Cc: git, Johan Herland, Josh Triplett, Thomas Rast, Michael Haggerty,
Dan Carpenter, Greg Kroah-Hartman, Jeff King, Eric Sunshine,
Ramsay Jones, Jonathan Nieder
Thanks and sorry for taking a bit longer than usual; will push this
series out, replacing the previous round, when I am done for today's
integration cycle.
^ permalink raw reply [flat|nested] 13+ messages in thread