threads / patch / 35569

patch, 9 partsAdd interpret-trailers builtin

Subject: [PATCH 0/9] Add interpret-trailers builtin

## tl;dr

17 messages between Dec 24, 2013 and Dec 30, 2013. Diffs are folded; open one to read it.

replies: 16people: 3as markdown or json

Christian Couder· Dec 24, 2013, 06:37 UTC · lore
This patch series implements a new command:
        git interpret-trailers

and an infrastructure to process trailers that can be reused, for example in "commit.c".

1) Rationale:

This command should help with RFC 822 style headers, called "trailers", that are found at the end of commit messages.

(Note that these headers do not follow and are not intended to follow many rules that are in RFC 822. For example they do not follow the line breaking rules, the encoding rules and probably many other rules.)

For a long time, these trailers have become a de facto standard way to add helpful information into commit messages.

Until now git commit has only supported the well known "Signed-off-by: " trailer, that is used by many projects like the Linux kernel and Git.

It is better to implement features for these trailers first in a new command rather than in builtin/commit.c, because this way the prepare-commit-msg and commit-msg hooks can reuse this command.

2) Current state:
Currently the usage string of this command is:
git interpret-trailers [--trim-empty] [--infile=file] [<token[=value]>...]
The following features are implemented:
        - the result is printed on stdout
        - the [<token[=value]>...] arguments are interpreted
        - a commit message passed using the "--infile=file" option is interpreted
        - the "trailer.<token>.key" options in the config are interpreted
        - the "trailer.<token>.where" options are interpreted
        - the "trailer.<token>.ifExist" options are interpreted
        - the "trailer.<token>.ifMissing" options are interpreted
	- there are a lot of tests
The following features are planned but not yet implemented:
	- making "git interpret-trailers" a filter
        - some documentation
        - the "trailer.<token>.command" config option
3) Notes:
* I chose "trailer.<token>.key" over "trailer.<token>.value" and
"trailer.<token>.trailer", as I think it is clearer.
* I focused on a cleaner and more complete implementation than the
PATCH/RFC I sent previously.
Happy end of year!
Christian Couder (9):
  Add data structures and basic functions for commit trailers
  trailer: process trailers from file and arguments
  trailer: read and process config information
  trailer: process command line trailer arguments
  strbuf: add strbuf_isspace()
  trailer: parse trailers from input file
  trailer: put all the processing together and print
  trailer: add interpret-trailers command
  trailer: add tests for "git interpret-trailers"
 .gitignore                    |   1 +
 Makefile                      |   2 +
 builtin.h                     |   1 +
 builtin/interpret-trailers.c  |  36 +++
 git.c                         |   1 +
 strbuf.c                      |   7 +
 strbuf.h                      |   1 +
 t/t7513-interpret-trailers.sh | 206 ++++++++++++++++
 trailer.c                     | 544 ++++++++++++++++++++++++++++++++++++++++++
 trailer.h                     |   6 +
 10 files changed, 805 insertions(+)
 create mode 100644 builtin/interpret-trailers.c
 create mode 100755 t/t7513-interpret-trailers.sh
 create mode 100644 trailer.c
 create mode 100644 trailer.h
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 1/9] Add data structures and basic functions for commit trailers

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>
---
 Makefile  |  1 +
 trailer.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 trailer.c
Show changes to 2 files +48 −0

Makefile, 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..ccbcfb0
--- /dev/null
+++ b/trailer.c
@@ -0,0 +1,47 @@
+#include "cache.h"
+/*
+ * Copyright (c) 2013 Christian Couder <chriscool@tuxfamily.org>
+ */
+
+enum action_where { AFTER, BEFORE };
+enum action_if_exist { EXIST_ADD_IF_DIFFERENT, EXIST_ADD_IF_DIFFERENT_NEIGHBOR,
+		       EXIST_ADD, EXIST_OVERWRITE, EXIST_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_exist if_exist;
+	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]));
+	return len + 1;
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 2/9] trailer: process trailers from file and arguments

This patch implements the logic that process trailers from file and arguments.

At the beginning trailers from file are in their own infile_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 infile_tok list.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 trailer.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 189 insertions(+)
Show changes to trailer.c +189 −0
diff --git a/trailer.c b/trailer.c
index ccbcfb0..bd6595d 100644
--- a/trailer.c
+++ b/trailer.c
@@ -45,3 +45,192 @@ static size_t alnum_len(const char *buf, size_t len) {
 	while (--len >= 0 && !isalnum(buf[len]));
 	return len + 1;
 }
+
+static void add_arg_to_infile(struct trailer_item *infile_tok,
+			      struct trailer_item *arg_tok)
+{
+	if (arg_tok->conf->where == AFTER) {
+		arg_tok->next = infile_tok->next;
+		infile_tok->next = arg_tok;
+		arg_tok->previous = infile_tok;
+		if (arg_tok->next)
+			arg_tok->next->previous = arg_tok;
+	} else {
+		arg_tok->previous = infile_tok->previous;
+		infile_tok->previous = arg_tok;
+		arg_tok->next = infile_tok;
+		if (arg_tok->previous)
+			arg_tok->previous->next = arg_tok;
+	}
+}
+
+static int check_if_different(struct trailer_item *infile_tok,
+			      struct trailer_item *arg_tok,
+			      int alnum_len, int check_all)
+{
+	enum action_where where = arg_tok->conf->where;
+	do {
+		if (!infile_tok)
+			return 1;
+		if (same_trailer(infile_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
+		 */
+		infile_tok = (where == AFTER) ? infile_tok->previous : infile_tok->next;
+	} while (check_all);
+	return 1;
+}
+
+static void apply_arg_if_exist(struct trailer_item *infile_tok,
+			       struct trailer_item *arg_tok,
+			       int alnum_len)
+{
+	switch(arg_tok->conf->if_exist) {
+	case EXIST_DO_NOTHING:
+		free(arg_tok);
+		break;
+	case EXIST_OVERWRITE:
+		free((char *)infile_tok->value);
+		infile_tok->value = xstrdup(arg_tok->value);
+		free(arg_tok);
+		break;
+	case EXIST_ADD:
+		add_arg_to_infile(infile_tok, arg_tok);
+		break;
+	case EXIST_ADD_IF_DIFFERENT:
+		if (check_if_different(infile_tok, arg_tok, alnum_len, 1))
+			add_arg_to_infile(infile_tok, arg_tok);
+		else
+			free(arg_tok);
+		break;
+	case EXIST_ADD_IF_DIFFERENT_NEIGHBOR:
+		if (check_if_different(infile_tok, arg_tok, alnum_len, 0))
+			add_arg_to_infile(infile_tok, arg_tok);
+		else
+			free(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_infile_tok(struct trailer_item *infile_tok,
+			       struct trailer_item **arg_tok_first,
+			       enum action_where where)
+{
+	struct trailer_item *arg_tok;
+	struct trailer_item *next_arg;
+
+	int tok_alnum_len = alnum_len(infile_tok->token, strlen(infile_tok->token));
+	for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {
+		next_arg = arg_tok->next;
+		if (same_token(infile_tok, arg_tok, tok_alnum_len) &&
+		    arg_tok->conf->where == where) {
+			/* Remove arg_tok from list */
+			remove_from_list(arg_tok, arg_tok_first);
+			/* Apply arg */
+			apply_arg_if_exist(infile_tok, arg_tok, tok_alnum_len);
+			/*
+			 * If arg has been added to infile,
+			 * then we need to process it too now.
+			 */
+			if ((where == AFTER ? infile_tok->next : infile_tok->previous) == arg_tok)
+				infile_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 **infile_tok_first,
+				 struct trailer_item **infile_tok_last,
+				 struct trailer_item *arg_tok)
+{
+	struct trailer_item **infile_tok;
+	enum action_where where;
+
+	switch(arg_tok->conf->if_missing) {
+	case MISSING_DO_NOTHING:
+		free(arg_tok);
+		break;
+	case MISSING_ADD:
+		where = arg_tok->conf->where;
+		infile_tok = (where == AFTER) ? infile_tok_last : infile_tok_first;
+		if (*infile_tok) {
+			add_arg_to_infile(*infile_tok, arg_tok);
+			*infile_tok = arg_tok;
+		} else {
+			*infile_tok_first = arg_tok;
+			*infile_tok_last = arg_tok;
+		}
+		break;
+	}
+}
+
+static void process_trailers_lists(struct trailer_item **infile_tok_first,
+				   struct trailer_item **infile_tok_last,
+				   struct trailer_item **arg_tok_first)
+{
+	struct trailer_item *infile_tok;
+	struct trailer_item *arg_tok;
+
+	if (!*arg_tok_first)
+		return;
+
+	/* Process infile from end to start */
+	for (infile_tok = *infile_tok_last; infile_tok; infile_tok = infile_tok->previous) {
+		process_infile_tok(infile_tok, arg_tok_first, AFTER);
+	}
+
+	update_last(infile_tok_last);
+
+	if (!*arg_tok_first)
+		return;
+
+	/* Process infile from start to end */
+	for (infile_tok = *infile_tok_first; infile_tok; infile_tok = infile_tok->next) {
+		process_infile_tok(infile_tok, arg_tok_first, BEFORE);
+	}
+
+	update_first(infile_tok_first);
+
+	/* Process args left */
+	while (*arg_tok_first) {
+		arg_tok = remove_first(arg_tok_first);
+		apply_arg_if_missing(infile_tok_first, infile_tok_last, arg_tok);
+	}
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 3/9] trailer: read and process config information

This patch implements reading the configuration to get trailer information, and then processing it and storing 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>
---
 trailer.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)
Show changes to trailer.c +129 −0
diff --git a/trailer.c b/trailer.c
index bd6595d..6aba40e 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);
@@ -234,3 +236,130 @@ static void process_trailers_lists(struct trailer_item **infile_tok_first,
 		apply_arg_if_missing(infile_tok_first, infile_tok_last, arg_tok);
 	}
 }
+
+static int set_where(struct conf_info *item, const char *value)
+{
+	if (!strcasecmp("after", value)) {
+		item->where = AFTER;
+	} else if (!strcasecmp("before", value)) {
+		item->where = BEFORE;
+	} else
+		return 1;
+	return 0;
+}
+
+static int set_if_exist(struct conf_info *item, const char *value)
+{
+	if (!strcasecmp("addIfDifferent", value)) {
+		item->if_exist = EXIST_ADD_IF_DIFFERENT;
+	} else if (!strcasecmp("addIfDifferentNeighbor", value)) {
+		item->if_exist = EXIST_ADD_IF_DIFFERENT_NEIGHBOR;
+	} else if (!strcasecmp("add", value)) {
+		item->if_exist = EXIST_ADD;
+	} else if (!strcasecmp("overwrite", value)) {
+		item->if_exist = EXIST_OVERWRITE;
+	} else if (!strcasecmp("doNothing", value)) {
+		item->if_exist = EXIST_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;
+}
+
+enum trailer_info_type { TRAILER_VALUE, TRAILER_COMMAND, TRAILER_WHERE,
+			 TRAILER_IF_EXIST, TRAILER_IF_MISSING };
+
+static int set_name_and_type(const char *conf_key, const char *suffix,
+			     enum trailer_info_type type,
+			     char **pname, enum trailer_info_type *ptype)
+{
+	int ret = ends_with(conf_key, suffix);
+	if (ret) {
+		*pname = xstrndup(conf_key, strlen(conf_key) - strlen(suffix));
+		*ptype = type;
+	}
+	return ret;
+}
+
+static struct trailer_item *get_conf_item(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 = xcalloc(sizeof(struct conf_info), 1);
+	item->conf->name = xstrdup(name);
+
+	if (!previous) {
+		first_conf_item = item;
+	} else {
+		previous->next = item;
+		item->previous = previous;
+	}
+
+	return item;
+}
+
+static int git_trailer_config(const char *conf_key, const char *value, void *cb)
+{
+	if (starts_with(conf_key, "trailer.")) {
+		const char *orig_conf_key = conf_key;
+		struct trailer_item *item;
+		struct conf_info *conf;
+		char *name;
+		enum trailer_info_type type;
+
+		conf_key += 8;
+		if (!set_name_and_type(conf_key, ".key", TRAILER_VALUE, &name, &type) &&
+		    !set_name_and_type(conf_key, ".command", TRAILER_COMMAND, &name, &type) &&
+		    !set_name_and_type(conf_key, ".where", TRAILER_WHERE, &name, &type) &&
+		    !set_name_and_type(conf_key, ".ifexist", TRAILER_IF_EXIST, &name, &type) &&
+		    !set_name_and_type(conf_key, ".ifmissing", TRAILER_IF_MISSING, &name, &type))
+			return 0;
+
+		item = get_conf_item(name);
+		conf = item->conf;
+
+		if (type == TRAILER_VALUE) {
+			if (conf->key)
+				warning(_("more than one %s"), orig_conf_key);
+			conf->key = xstrdup(value);
+		} else if (type == TRAILER_COMMAND) {
+			if (conf->command)
+				warning(_("more than one %s"), orig_conf_key);
+			conf->command = xstrdup(value);
+		} else if (type == TRAILER_WHERE) {
+			if (set_where(conf, value))
+				warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);
+		} else if (type == TRAILER_IF_EXIST) {
+			if (set_if_exist(conf, value))
+				warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);
+		} else if (type == TRAILER_IF_MISSING) {
+			if (set_if_missing(conf, value))
+				warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);
+		} else {
+			die("internal bug in trailer.c");
+		}
+	}
+	return 0;
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 13:47 UTC · re: Christian Couder · lore

Re: [PATCH 3/9] trailer: read and process config information

On Tue, Dec 24, 2013 at 7:37 AM, Christian Couder <chriscool@tuxfamily.org> wrote:

Show 32 quoted lines
>
> +static int git_trailer_config(const char *conf_key, const char *value, void *cb)
> +{
> +       if (starts_with(conf_key, "trailer.")) {
> +               const char *orig_conf_key = conf_key;
> +               struct trailer_item *item;
> +               struct conf_info *conf;
> +               char *name;
> +               enum trailer_info_type type;
> +
> +               conf_key += 8;
> +               if (!set_name_and_type(conf_key, ".key", TRAILER_VALUE, &name, &type) &&
> +                   !set_name_and_type(conf_key, ".command", TRAILER_COMMAND, &name, &type) &&
> +                   !set_name_and_type(conf_key, ".where", TRAILER_WHERE, &name, &type) &&
> +                   !set_name_and_type(conf_key, ".ifexist", TRAILER_IF_EXIST, &name, &type) &&
> +                   !set_name_and_type(conf_key, ".ifmissing", TRAILER_IF_MISSING, &name, &type))
> +                       return 0;
> +
> +               item = get_conf_item(name);
> +               conf = item->conf;
> +
> +               if (type == TRAILER_VALUE) {
> +                       if (conf->key)
> +                               warning(_("more than one %s"), orig_conf_key);
> +                       conf->key = xstrdup(value);
> +               } else if (type == TRAILER_COMMAND) {
> +                       if (conf->command)
> +                               warning(_("more than one %s"), orig_conf_key);
> +                       conf->command = xstrdup(value);
> +               } else if (type == TRAILER_WHERE) {
> +                       if (set_where(conf, value))
> +                               warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);

I realize that I forgot to s/unknow/unknown/. Sorry about that. It will be in the next version.

Show 12 quoted lines
> +               } else if (type == TRAILER_IF_EXIST) {
> +                       if (set_if_exist(conf, value))
> +                               warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);
> +               } else if (type == TRAILER_IF_MISSING) {
> +                       if (set_if_missing(conf, value))
> +                               warning(_("unknow value '%s' for key '%s'"), value, orig_conf_key);
> +               } else {
> +                       die("internal bug in trailer.c");
> +               }
> +       }
> +       return 0;
> +}
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 4/9] trailer: process command line trailer arguments

This patch parses the trailer command line arguments and put the result into an arg_tok doubly linked list.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 trailer.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)
Show changes to trailer.c +77 −0
diff --git a/trailer.c b/trailer.c
index 6aba40e..b572c44 100644
--- a/trailer.c
+++ b/trailer.c
@@ -363,3 +363,80 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
 	}
 	return 0;
 }
+
+static void parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)
+{
+	char *end = strchr(trailer, '=');
+	if (!end)
+		end = strchr(trailer, ':');
+	if (end) {
+		strbuf_add(tok, trailer, end - trailer);
+		strbuf_trim(tok);
+		strbuf_addstr(val, end + 1);
+		strbuf_trim(val);
+	} else {
+		strbuf_addstr(tok, trailer);
+		strbuf_trim(tok);
+	}
+}
+
+static struct trailer_item *create_trailer_item(const char *string)
+{
+	struct strbuf tok = STRBUF_INIT;
+	struct strbuf val = STRBUF_INIT;
+	struct trailer_item *new;
+
+	parse_trailer(&tok, &val, string);
+
+	int tok_alnum_len = alnum_len(tok.buf, tok.len);
+
+	/* Lookup if the token matches something in the config */
+	struct trailer_item *item;
+	for (item = first_conf_item; item; item = item->next)
+	{
+		if (!strncasecmp(tok.buf, item->conf->key, tok_alnum_len) ||
+		    !strncasecmp(tok.buf, item->conf->name, tok_alnum_len)) {
+			new = xcalloc(sizeof(struct trailer_item), 1);
+			new->conf = item->conf;
+			new->token = xstrdup(item->conf->key);
+			new->value = strbuf_detach(&val, NULL);
+			strbuf_release(&tok);
+			return new;
+		}
+	}
+
+	new = xcalloc(sizeof(struct trailer_item), 1);
+	new->conf = xcalloc(sizeof(struct conf_info), 1);
+	new->token = strbuf_detach(&tok, NULL);
+	new->value = strbuf_detach(&val, NULL);
+
+	return new;
+}
+
+static void add_trailer_item(struct trailer_item **first,
+			     struct trailer_item **last,
+			     struct trailer_item *new)
+{
+	if (!*last) {
+		*first = new;
+		*last = new;
+	} else {
+		(*last)->next = new;
+		new->previous = *last;
+		*last = new;
+	}
+}
+
+static struct trailer_item *process_command_line_args(int argc, const char **argv)
+{
+	int i;
+	struct trailer_item *arg_tok_first = NULL;
+	struct trailer_item *arg_tok_last = NULL;
+
+	for (i = 0; i < argc; i++) {
+		struct trailer_item *new = create_trailer_item(argv[i]);
+		add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+	}
+
+	return arg_tok_first;
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 5/9] strbuf: add strbuf_isspace()

This helper function checks if a strbuf contains only space chars or not.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 strbuf.c | 7 +++++++
 strbuf.h | 1 +
 2 files changed, 8 insertions(+)
Show changes to 2 files +8 −0

strbuf.c, strbuf.h

diff --git a/strbuf.c b/strbuf.c
index 83caf4a..2124bb8 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -124,6 +124,13 @@ void strbuf_ltrim(struct strbuf *sb)
 	sb->buf[sb->len] = '\0';
 }
 
+int strbuf_isspace(struct strbuf *sb)
+{
+	char *b;
+	for (b = sb->buf; *b && isspace(*b); b++);
+	return !*b;
+}
+
 struct strbuf **strbuf_split_buf(const char *str, size_t slen,
 				 int terminator, int max)
 {
diff --git a/strbuf.h b/strbuf.h
index 73e80ce..02bff3a 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -42,6 +42,7 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
 extern void strbuf_trim(struct strbuf *);
 extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
+extern int strbuf_isspace(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
 /*
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 6/9] trailer: parse trailers from input file

This patch reads trailers from an input file, parses them and puts the result into a doubly linked list.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 trailer.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
Show changes to trailer.c +62 −0
diff --git a/trailer.c b/trailer.c
index b572c44..22d3ea3 100644
--- a/trailer.c
+++ b/trailer.c
@@ -440,3 +440,65 @@ static struct trailer_item *process_command_line_args(int argc, const char **arg
 
 	return arg_tok_first;
 }
+
+static struct strbuf **read_input_file(const char *infile)
+{
+	struct strbuf sb = STRBUF_INIT;
+
+	if (strbuf_read_file(&sb, infile, 0) < 0)
+		die_errno(_("could not read input file '%s'"), infile);
+
+	return strbuf_split(&sb, '\n');
+}
+
+/*
+ * Return the the (0 based) index of the first trailer line
+ * or the line count if there are no trailers.
+ */
+static int find_trailer_start(struct strbuf **lines)
+{
+	int count, start, empty = 1;
+
+	/* Get the line count */
+	for (count = 0; lines[count]; count++);
+
+	/*
+	 * 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 (strbuf_isspace(lines[start])) {
+			if (empty)
+				continue;
+			return start + 1;
+		}
+		if (strchr(lines[start]->buf, ':')) {
+			if (empty)
+				empty = 0;
+			continue;
+		}
+		return count;
+	}
+
+	return empty ? count : start + 1;
+}
+
+static void process_input_file(const char *infile,
+			       struct trailer_item **infile_tok_first,
+			       struct trailer_item **infile_tok_last)
+{
+	struct strbuf **lines = read_input_file(infile);
+	int start = find_trailer_start(lines);
+	int i;
+
+	/* Print non trailer lines as is */
+	for (i = 0; lines[i] && i < start; i++) {
+		printf("%s", lines[i]->buf);
+	}
+
+	/* Parse trailer lines */
+	for (i = start; lines[i]; i++) {
+		struct trailer_item *new = create_trailer_item(lines[i]->buf);
+		add_trailer_item(infile_tok_first, infile_tok_last, new);
+	}
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 7/9] trailer: put all the processing together and print

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>
---
 trailer.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
Show changes to trailer.c +40 −0
diff --git a/trailer.c b/trailer.c
index 22d3ea3..33aa907 100644
--- a/trailer.c
+++ b/trailer.c
@@ -48,6 +48,26 @@ static size_t alnum_len(const char *buf, size_t len) {
 	return len + 1;
 }
 
+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_infile(struct trailer_item *infile_tok,
 			      struct trailer_item *arg_tok)
 {
@@ -502,3 +522,23 @@ static void process_input_file(const char *infile,
 		add_trailer_item(infile_tok_first, infile_tok_last, new);
 	}
 }
+
+void process_trailers(const char *infile, int trim_empty, int argc, const char **argv)
+{
+	struct trailer_item *infile_tok_first = NULL;
+	struct trailer_item *infile_tok_last = NULL;
+	struct trailer_item *arg_tok_first;
+
+	git_config(git_trailer_config, NULL);
+
+	/* Print the non trailer part of infile */
+	if (infile) {
+		process_input_file(infile, &infile_tok_first, &infile_tok_last);
+	}
+
+	arg_tok_first = process_command_line_args(argc, argv);
+
+	process_trailers_lists(&infile_tok_first, &infile_tok_last, &arg_tok_first);
+
+	print_all(infile_tok_first, trim_empty);
+}
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 8/9] trailer: add interpret-trailers command

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>
---
 .gitignore                   |  1 +
 Makefile                     |  1 +
 builtin.h                    |  1 +
 builtin/interpret-trailers.c | 36 ++++++++++++++++++++++++++++++++++++
 git.c                        |  1 +
 trailer.h                    |  6 ++++++
 6 files changed, 46 insertions(+)
 create mode 100644 builtin/interpret-trailers.c
 create mode 100644 trailer.h
Show changes to 6 files +46 −0

.gitignore, Makefile, builtin.h, builtin/interpret-trailers.c, git.c, trailer.h

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 d4afbfe..30f4c30 100644
--- a/builtin.h
+++ b/builtin.h
@@ -71,6 +71,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..f79bffa
--- /dev/null
+++ b/builtin/interpret-trailers.c
@@ -0,0 +1,36 @@
+/*
+ * Builtin "git interpret-trailers"
+ *
+ * Copyright (c) 2013 Christian Couder <chriscool@tuxfamily.org>
+ *
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "strbuf.h"
+#include "trailer.h"
+
+static const char * const git_interpret_trailers_usage[] = {
+	N_("git interpret-trailers [--trim-empty] [--infile=file] [<token[=value]>...]"),
+	NULL
+};
+
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
+{
+	const char *infile = NULL;
+	int trim_empty = 0;
+
+	struct option options[] = {
+		OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")),
+		OPT_FILENAME(0, "infile", &infile, N_("use message from file")),
+		OPT_END()
+	};
+
+	argc = parse_options(argc, argv, prefix, options,
+			     git_interpret_trailers_usage, 0);
+
+	process_trailers(infile, trim_empty, argc, argv);
+
+	return 0;
+}
diff --git a/git.c b/git.c
index 3799514..1420b58 100644
--- a/git.c
+++ b/git.c
@@ -383,6 +383,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "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 },
diff --git a/trailer.h b/trailer.h
new file mode 100644
index 0000000..9db4459
--- /dev/null
+++ b/trailer.h
@@ -0,0 +1,6 @@
+#ifndef TRAILER_H
+#define TRAILER_H
+
+void process_trailers(const char *infile, int trim_empty, int argc, const char **argv);
+
+#endif /* TRAILER_H */
-- 
1.8.4.1.616.g07f5c81
Christian Couder· Dec 24, 2013, 06:37 UTC · re: Christian Couder · lore

[PATCH 9/9] trailer: add tests for "git interpret-trailers"

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 t/t7513-interpret-trailers.sh | 206 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 206 insertions(+)
 create mode 100755 t/t7513-interpret-trailers.sh
Show changes to t/t7513-interpret-trailers.sh +206 −0
diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
new file mode 100755
index 0000000..beb58fe
--- /dev/null
+++ b/t/t7513-interpret-trailers.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Christian Couder
+#
+
+test_description='git interpret-trailers'
+
+. ./test-lib.sh
+
+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
+
+# Do not remove trailing spaces below!
+cat >complex_message_trailers <<'EOF'
+Fixes: 
+Acked-by: 
+Reviewed-by: 
+Signed-off-by: 
+EOF
+
+test_expect_success 'without config' '
+	printf "ack: Peff\nReviewed-by: \nAcked-by: Johan\n" >expected &&
+	git interpret-trailers "ack = Peff" "Reviewed-by" "Acked-by: Johan" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success '--trim-empty without config' '
+	printf "ack: Peff\nAcked-by: Johan\n" >expected &&
+	git interpret-trailers --trim-empty "ack = Peff" "Reviewed-by" "Acked-by: Johan" "sob:" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with config setup' '
+	git config trailer.ack.key "Acked-by: " &&
+	printf "Acked-by: Peff\n" >expected &&
+	git interpret-trailers --trim-empty "ack = Peff" >actual &&
+	test_cmp expected actual &&
+	git interpret-trailers --trim-empty "Acked-by = Peff" >actual &&
+	test_cmp expected actual &&
+	git interpret-trailers --trim-empty "Acked-by :Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with config setup and = sign' '
+	git config trailer.ack.key "Acked-by= " &&
+	printf "Acked-by= Peff\n" >expected &&
+	git interpret-trailers --trim-empty "ack = Peff" >actual &&
+	test_cmp expected actual &&
+	git interpret-trailers --trim-empty "Acked-by= Peff" >actual &&
+	test_cmp expected actual &&
+	git interpret-trailers --trim-empty "Acked-by : Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with config setup and # sign' '
+	git config trailer.bug.key "Bug #" &&
+	printf "Bug #42\n" >expected &&
+	git interpret-trailers --trim-empty "bug = 42" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with commit basic message' '
+	git interpret-trailers --infile basic_message >actual &&
+	test_cmp basic_message actual
+'
+
+test_expect_success 'with commit complex message' '
+	cat complex_message_body complex_message_trailers >complex_message &&
+	cat complex_message_body >expected &&
+	printf "Fixes: \nAcked-by= \nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message and args' '
+	cat complex_message_body >expected &&
+	printf "Fixes: \nAcked-by= \nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \nBug #42\n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "bug: 42" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message, args and --trim-empty' '
+	cat complex_message_body >expected &&
+	printf "Acked-by= Peff\nBug #42\n" >>expected &&
+	git interpret-trailers --trim-empty --infile complex_message "ack: Peff" "bug: 42" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "where = before"' '
+	git config trailer.bug.where "before" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "bug: 42" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" for a token in the middle of infile' '
+	git config trailer.review.key "Reviewed-by:" &&
+	git config trailer.review.where "before" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nReviewed-by: Johan\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "bug: 42" "review: Johan" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "where = before" and --trim-empty' '
+	cat complex_message_body >expected &&
+	printf "Bug #46\nBug #42\nAcked-by= Peff\nReviewed-by: Johan\n" >>expected &&
+	git interpret-trailers --infile complex_message --trim-empty "ack: Peff" "bug: 42" "review: Johan" "Bug: 46"  >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'the default is "ifExist = addIfDifferent"' '
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "review:" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = addIfDifferent"' '
+	git config trailer.review.ifExist "addIfDifferent" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "review:" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = addIfDifferentNeighbor"' '
+	git config trailer.ack.ifExist "addIfDifferentNeighbor" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "review:" "ack: Junio" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = addIfDifferentNeighbor" and --trim-empty' '
+	git config trailer.ack.ifExist "addIfDifferentNeighbor" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nAcked-by= Peff\nAcked-by= Junio\nAcked-by= Peff\n" >>expected &&
+	git interpret-trailers --infile complex_message --trim-empty "ack: Peff" "Acked-by= Peff" "review:" "ack: Junio" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = add"' '
+	git config trailer.ack.ifExist "add" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Peff\nAcked-by= Peff\nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "ack: Peff" "Acked-by= Peff" "review:" "ack: Junio" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = overwrite"' '
+	git config trailer.fix.key "Fixes:" &&
+	git config trailer.fix.ifExist "overwrite" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: 22\nAcked-by= \nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "review:" "fix=53" "ack: Junio" "fix=22" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifExist = doNothing"' '
+	git config trailer.fix.ifExist "doNothing" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "review:" "fix=53" "ack: Junio" "fix=22" "bug: 42" "ack: Peff" >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 &&
+	printf "Bug #42\nCc: Linus\nFixes: \nAcked-by= \nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "review:" "fix=53" "cc=Linus" "ack: Junio" "fix=22" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = add"' '
+	git config trailer.Cc.ifMissing "add" &&
+	cat complex_message_body >expected &&
+	printf "Cc: Linus\nBug #42\nFixes: \nAcked-by= \nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "review:" "fix=53" "ack: Junio" "fix=22" "bug: 42" "cc=Linus" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'using "ifMissing = doNothing"' '
+	git config trailer.Cc.ifMissing "doNothing" &&
+	cat complex_message_body >expected &&
+	printf "Bug #42\nFixes: \nAcked-by= \nAcked-by= Junio\nAcked-by= Peff\nReviewed-by: \nSigned-off-by: \n" >>expected &&
+	git interpret-trailers --infile complex_message "review:" "fix=53" "cc=Linus" "ack: Junio" "fix=22" "bug: 42" "ack: Peff" >actual &&
+	test_cmp expected actual
+'
+
+test_done
-- 
1.8.4.1.616.g07f5c81
Junio C Hamano· Dec 30, 2013, 17:19 UTC · re: Christian Couder · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

Christian Couder <chriscool@tuxfamily.org> writes:
Show 7 quoted lines
> +# Do not remove trailing spaces below!
> +cat >complex_message_trailers <<'EOF'
> +Fixes: 
> +Acked-by: 
> +Reviewed-by: 
> +Signed-off-by: 
> +EOF

Just a hint. I think it is far safer and robust over time to do something like this:

	sed -e 's/ Z$/ /' <<-\EOF
        Fixes: Z
        Acked-by: Z
        EOF

instead of a comment, which can warn human developers but does not do anything to prevent their editors' auto-fix features from kicking in.

Josh Triplett· Dec 30, 2013, 20:20 UTC · re: Junio C Hamano · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

On Mon, Dec 30, 2013 at 09:19:55AM -0800, Junio C Hamano wrote:
Show 21 quoted lines
> Christian Couder <chriscool@tuxfamily.org> writes:
> 
> > +# Do not remove trailing spaces below!
> > +cat >complex_message_trailers <<'EOF'
> > +Fixes: 
> > +Acked-by: 
> > +Reviewed-by: 
> > +Signed-off-by: 
> > +EOF
> 
> Just a hint.  I think it is far safer and robust over time to do
> something like this:
> 
> 	sed -e 's/ Z$/ /' <<-\EOF
>         Fixes: Z
>         Acked-by: Z
>         EOF
> 
> instead of a comment, which can warn human developers but does not
> do anything to prevent their editors' auto-fix features from kicking
> in.

This, but for simplicity, since every line needs the trailing space, why not just use 's/$/ /' and drop the ' Z' on every line?

</bikeshed>
- Josh Triplett
Junio C Hamano· Dec 30, 2013, 20:46 UTC · re: Josh Triplett · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

Josh Triplett <josh@joshtriplett.org> writes:
Show 29 quoted lines
> On Mon, Dec 30, 2013 at 09:19:55AM -0800, Junio C Hamano wrote:
>> Christian Couder <chriscool@tuxfamily.org> writes:
>> 
>> > +# Do not remove trailing spaces below!
>> > +cat >complex_message_trailers <<'EOF'
>> > +Fixes: 
>> > +Acked-by: 
>> > +Reviewed-by: 
>> > +Signed-off-by: 
>> > +EOF
>> 
>> Just a hint.  I think it is far safer and robust over time to do
>> something like this:
>> 
>> 	sed -e 's/ Z$/ /' <<-\EOF
>>         Fixes: Z
>>         Acked-by: Z
>>         EOF
>> 
>> instead of a comment, which can warn human developers but does not
>> do anything to prevent their editors' auto-fix features from kicking
>> in.
>
> This, but for simplicity, since every line needs the trailing space, why
> not just use 's/$/ /' and drop the ' Z' on every line?
>
> </bikeshed>
>
> - Josh Triplett
A few reasons:
 - The "everybody will have a single SP at the end" may or may not
   last forever;
 - With your scheme, if you already had _one_ trailing SPs in the
   input, it would be hard to spot in the source;
 - It makes it visually very clear that we expect a SP after these
   colons.  This is especially true if you replace 'Z' with
   something more readable (e.g. '|').
Josh Triplett· Dec 30, 2013, 20:52 UTC · re: Junio C Hamano · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

On Mon, Dec 30, 2013 at 12:46:33PM -0800, Junio C Hamano wrote:
Show 36 quoted lines
> Josh Triplett <josh@joshtriplett.org> writes:
> 
> > On Mon, Dec 30, 2013 at 09:19:55AM -0800, Junio C Hamano wrote:
> >> Christian Couder <chriscool@tuxfamily.org> writes:
> >> 
> >> > +# Do not remove trailing spaces below!
> >> > +cat >complex_message_trailers <<'EOF'
> >> > +Fixes: 
> >> > +Acked-by: 
> >> > +Reviewed-by: 
> >> > +Signed-off-by: 
> >> > +EOF
> >> 
> >> Just a hint.  I think it is far safer and robust over time to do
> >> something like this:
> >> 
> >> 	sed -e 's/ Z$/ /' <<-\EOF
> >>         Fixes: Z
> >>         Acked-by: Z
> >>         EOF
> >> 
> >> instead of a comment, which can warn human developers but does not
> >> do anything to prevent their editors' auto-fix features from kicking
> >> in.
> >
> > This, but for simplicity, since every line needs the trailing space, why
> > not just use 's/$/ /' and drop the ' Z' on every line?
> >
> > </bikeshed>
> >
> > - Josh Triplett
> 
> A few reasons:
> 
>  - The "everybody will have a single SP at the end" may or may not
>    last forever;

Trivially fixed if that ever changes, but given the nature of all of these, that seems unlikely.

>  - With your scheme, if you already had _one_ trailing SPs in the
>    input, it would be hard to spot in the source;
Git makes them quite difficult to miss. :)
- Josh Triplett
Junio C Hamano· Dec 30, 2013, 21:05 UTC · re: Josh Triplett · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

Josh Triplett <josh@joshtriplett.org> writes:
Show 5 quoted lines
>>  - The "everybody will have a single SP at the end" may or may not
>>    last forever;
>
> Trivially fixed if that ever changes, but given the nature of all of
> these, that seems unlikely.

Why? Because we encourage to write tests that are expected to find breakages, some of these test vector lines may have to show a broken line that lacks SP after label + colon.

>>  - With your scheme, if you already had _one_ trailing SPs in the
>>    input, it would be hard to spot in the source;
>
> Git makes them quite difficult to miss. :)
That is irrelevant, isn't it?

This is about protecting the source in the editor, before you run "git show --whitespace=trailing-space", "git diff --check", etc.

Josh Triplett· Dec 30, 2013, 22:27 UTC · re: Junio C Hamano · lore

Re: [PATCH 9/9] trailer: add tests for "git interpret-trailers"

On Mon, Dec 30, 2013 at 01:05:25PM -0800, Junio C Hamano wrote:
Show 10 quoted lines
> Josh Triplett <josh@joshtriplett.org> writes:
> >>  - With your scheme, if you already had _one_ trailing SPs in the
> >>    input, it would be hard to spot in the source;
> >
> > Git makes them quite difficult to miss. :)
> 
> That is irrelevant, isn't it?
> 
> This is about protecting the source in the editor, before you run
> "git show --whitespace=trailing-space", "git diff --check", etc.

That was exactly my point: such lines shouldn't exist, and rather than including the trailing space and following it with a character that then needs removing, it seems more sensible to me to omit the trailing space and insert it via an almost identical sed line. Git already helps ensure that trailing space won't exist on *any* line, including those; I don't see how an extra character after the space (making it no longer trailing space) makes it any more or less likely that those lines would have trailing space.

In any case, I don't care enough to argue the point further; it was just a style suggestion.

- Josh Triplett

← back to recent threads