allow non-trailers and multiple-line trailers
cover allow non-trailers and multiple-line trailers
In [1], Junio explained a possible redesign of trailer support in interpret-trailers and cherry-pick, something along these lines:
1. Support non-trailers and multi-line trailers in interpret-trailers
2. Create a helper function so that the new interpretation can be used elsewhere (e.g. in sequencer)
3. Modify "Signed-off-by" and "(cherry picked by" to use the helper function
My current plans for step 1 and step 2 require relatively significant refactoring in trailer.c, so I thought that I should send out a patch set covering only step 1 first for discussion, before continuing with my work on top of this patch set.
Support for steps 2 and 3, including my original use case of being looser in the definition of a trailer when invoking "cherry-pick -x" (and thus suppressing the insertion of a newline) will come in a subsequent patch set.
[1] Message ID <[email protected]>
Jonathan Tan (5):
trailer: use singly-linked list, not doubly
trailer: streamline trailer item create and add
trailer: make args have their own struct
trailer: allow non-trailers in trailer block
trailer: support values folded to multiple lines
Documentation/git-interpret-trailers.txt | 10 +- t/t7513-interpret-trailers.sh | 174 +++++++++ trailer.c | 638 +++++++++++++++---------------- 3 files changed, 480 insertions(+), 342 deletions(-)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH 1/5 trailer: use singly-linked list, not doubly
Use singly-linked lists (instead of doubly-linked lists) in trailer to keep track of arguments (whether implicit from configuration or explicit from the command line) and trailer items.
This change significantly reduces the code length and simplifies the code. There are now fewer pointers to be manipulated, but most trailer manipulations now require seeking from beginning to end, so there might be a slight net decrease in performance; however the number of trailers is usually small (10 to 15 at the most) so this should not cause a big impact.
--- trailer.c | 357 ++++++++++++++++++++++---------------------------------------- 1 file changed, 125 insertions(+), 232 deletions(-)
trailer.c +125 -232
@@ -25,7 +25,6 @@ struct conf_info {static struct conf_info default_conf_info;struct trailer_item { -struct trailer_item *previous;struct trailer_item *next;const char *token;const char *value;
@@ -56,7 +55,8 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (struct trailer_item *a,struct trailer_item *b) +static int same_token (const struct trailer_item *a, +const struct trailer_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -65,12 +65,14 @@ static int same_token(struct trailer_item *a, struct trailer_item *b)return !strncasecmp (a->token, b->token, min_len); } -static int same_value (struct trailer_item *a,struct trailer_item *b) +static int same_value (const struct trailer_item *a, +const struct trailer_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (struct trailer_item *a,struct trailer_item *b) +static int same_trailer (const struct trailer_item *a, +const struct trailer_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -129,92 +131,6 @@ static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty) } } -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 add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok, -struct trailer_item **first, -struct trailer_item **last) -{ -if (after_or_end (arg_tok->conf.where)) { - arg_tok->next = on_tok->next; - on_tok->next = arg_tok; - arg_tok->previous = on_tok; -if (arg_tok->next) - arg_tok->next->previous = arg_tok; -update_last (last); - }else { - arg_tok->previous = on_tok->previous; - on_tok->previous = arg_tok; - arg_tok->next = on_tok; -if (arg_tok->previous) - arg_tok->previous->next = arg_tok; -update_first (first); - } -} - -static int check_if_different (struct trailer_item *in_tok, -struct trailer_item *arg_tok, -int check_all) -{ -enum action_where where = arg_tok->conf.where; -do { -if (!in_tok) -return 1 ; -if (same_trailer (in_tok, arg_tok)) -return 0 ; -/* -* if we want to add a trailer after another one, -* we have to check those before this one -*/ - in_tok =after_or_end (where) ? in_tok->previous : in_tok->next; - }while (check_all); -return 1 ; -} - -static void remove_from_list (struct trailer_item *item, -struct trailer_item **first, -struct trailer_item **last) -{ -struct trailer_item *next = item->next; -struct trailer_item *previous = item->previous; - -if (next) { - item->next->previous = previous; - item->next =NULL ; - }else if (last) - *last = previous; - -if (previous) { - item->previous->next = next; - item->previous =NULL ; - }else if (first) - *first = 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 const char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;
@@ -263,122 +179,116 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item } } -static void apply_arg_if_exists (struct trailer_item *in_tok, -struct trailer_item *arg_tok, -struct trailer_item *on_tok, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +static void apply_existing_arg (struct trailer_item **found_next, +struct trailer_item *arg_tok, +struct trailer_item **position_to_add, +const struct trailer_item *in_tok_head, +const struct trailer_item *neighbor) { -switch (arg_tok->conf.if_exists) { -case EXISTS_DO_NOTHING : +if (arg_tok->conf.if_exists ==EXISTS_DO_NOTHING ) {free_trailer_item (arg_tok); -break ; -case EXISTS_REPLACE : -apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -remove_from_list (in_tok, in_tok_first, in_tok_last); -free_trailer_item (in_tok); -break ; -case EXISTS_ADD : -apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -break ; -case EXISTS_ADD_IF_DIFFERENT : -apply_item_command (in_tok, arg_tok); -if (check_if_different (in_tok, arg_tok,1 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -else -free_trailer_item (arg_tok); -break ; -case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR : -apply_item_command (in_tok, arg_tok); -if (check_if_different (on_tok, arg_tok,0 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -else +return ; + } + +apply_item_command (*found_next, arg_tok); + +if (arg_tok->conf.if_exists ==EXISTS_ADD_IF_DIFFERENT_NEIGHBOR ) { +if (neighbor &&same_trailer (neighbor, arg_tok)) {free_trailer_item (arg_tok); -break ; +return ; + } + }else if (arg_tok->conf.if_exists ==EXISTS_ADD_IF_DIFFERENT ) { +const struct trailer_item *in_tok; +for (in_tok = in_tok_head; in_tok; in_tok = in_tok->next) { +if (same_trailer (in_tok, arg_tok)) { +free_trailer_item (arg_tok); +return ; + } + } + } + + arg_tok->next = *position_to_add; + *position_to_add = arg_tok; + +if (arg_tok->conf.if_exists ==EXISTS_REPLACE ) { +struct trailer_item *new_next = (*found_next)->next; +free_trailer_item (*found_next); + *found_next = new_next; } } -static void apply_arg_if_missing (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item *arg_tok) +static void apply_missing_arg (struct trailer_item *arg_tok, +struct trailer_item **position_to_add) { -struct trailer_item **in_tok; -enum action_where where; - -switch (arg_tok->conf.if_missing) { -case MISSING_DO_NOTHING : +if (arg_tok->conf.if_missing ==MISSING_DO_NOTHING ) {free_trailer_item (arg_tok); -break ; -case MISSING_ADD : - where = arg_tok->conf.where; - in_tok =after_or_end (where) ? in_tok_last : in_tok_first; -apply_item_command (NULL , arg_tok); -if (*in_tok) { -add_arg_to_input_list (*in_tok, arg_tok, - in_tok_first, in_tok_last); - }else { - *in_tok_first = arg_tok; - *in_tok_last = arg_tok; - } -break ; +return ; } + +apply_item_command (NULL , arg_tok); + + arg_tok->next = *position_to_add; + *position_to_add = arg_tok; } -static int find_same_and_apply_arg (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item *arg_tok) +static void apply_arg (struct trailer_item **in_tok_head, +struct trailer_item *arg_tok) { -struct trailer_item *in_tok; -struct trailer_item *on_tok; -struct trailer_item *following_tok; - +struct trailer_item **next, **found_next =NULL , *last =NULL ;enum action_where where = arg_tok->conf.where; -int middle = (where ==WHERE_AFTER ) || (where ==WHERE_BEFORE );int backwards =after_or_end (where); -struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first; -for (in_tok = start_tok; in_tok; in_tok = following_tok) { - following_tok = backwards ? in_tok->previous : in_tok->next; -if (!same_token (in_tok, arg_tok)) -continue ; - on_tok = middle ? in_tok : start_tok; -apply_arg_if_exists (in_tok, arg_tok, on_tok, - in_tok_first, in_tok_last); -return 1 ; +for (next = in_tok_head; *next; next = &(*next)->next) { + last = *next; +if ((!found_next || backwards) && +same_token (*next, arg_tok)) + found_next = next; + } + +if (found_next) { +struct trailer_item **position_to_add, *neighbor; +switch (where) { +case WHERE_START : + position_to_add = in_tok_head; + neighbor = *in_tok_head; +break ; +case WHERE_BEFORE : + position_to_add = found_next; + neighbor = *found_next; +break ; +case WHERE_AFTER : + position_to_add = &(*found_next)->next; + neighbor = *found_next; +break ; +default : + position_to_add = next; + neighbor = last; +break ; + } +apply_existing_arg (found_next, arg_tok, position_to_add, + *in_tok_head, neighbor); + }else { +struct trailer_item **position_to_add; +switch (where) { +case WHERE_START : +case WHERE_BEFORE : + position_to_add = in_tok_head; +break ; +default : + position_to_add = next; +break ; + } +apply_missing_arg (arg_tok, position_to_add); } -return 0 ; } -static void process_trailers_lists (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item **arg_tok_first) +static void process_trailers_lists (struct trailer_item **in_tok_head, +struct trailer_item *arg_tok_head) { -struct trailer_item *arg_tok; -struct trailer_item *next_arg; - -if (!*arg_tok_first) -return ; - -for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { -int applied =0 ; - - next_arg = arg_tok->next; -remove_from_list (arg_tok, arg_tok_first,NULL ); - - applied =find_same_and_apply_arg (in_tok_first, - in_tok_last, - arg_tok); - -if (!applied) -apply_arg_if_missing (in_tok_first, - in_tok_last, - arg_tok); +struct trailer_item *arg_tok, *next; +for (arg_tok = arg_tok_head; arg_tok; arg_tok = next) { + next = arg_tok->next; +apply_arg (in_tok_head, arg_tok); } }
@@ -438,29 +348,19 @@ static void duplicate_conf(struct conf_info *dst, struct conf_info *src)static struct trailer_item *get_conf_item (const char *name) { +struct trailer_item **next = &first_conf_item;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; - } +for (next = &first_conf_item; *next; next = &(*next)->next) +if (!strcasecmp ((*next)->conf.name, name)) +return *next;/* Item does not already exists, create it */ item =xcalloc (sizeof (struct trailer_item ),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); - -if (!previous) - first_conf_item = item; -else { - previous->next = item; - item->previous = previous; - } - + *next = item;return item; }
@@ -652,26 +552,19 @@ static struct trailer_item *create_trailer_item(const char *string) strbuf_detach(&val, NULL)); } -static void add_trailer_item (struct trailer_item **first, -struct trailer_item **last, +static void add_trailer_item (struct trailer_item ***tail,struct trailer_item *new) {if (!new)return ; -if (!*last) { - *first = new; - *last = new; - }else { - (*last)->next = new; - new->previous = *last; - *last = new; - } + **tail = new; + *tail = &new->next; }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 trailer_item *arg_tok_head =NULL ; +struct trailer_item **arg_tok_tail = &arg_tok_head;struct string_list_item *tr;struct trailer_item *item;
@@ -679,17 +572,17 @@ static struct trailer_item *process_command_line_args(struct string_list *trailefor (item = first_conf_item; item; item = item->next) {if (item->conf.command) {struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_first, &arg_tok_last, new); +add_trailer_item (&arg_tok_tail, new); } }/* Add a trailer item for each trailer on the command line */ 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_trailer_item (&arg_tok_tail, new); } -return arg_tok_first; +return arg_tok_head; }static struct strbuf **read_input_file (const char *file)
@@ -805,8 +698,7 @@ static void print_lines(FILE *outfile, struct strbuf **lines, int start, int endstatic int process_input_file (FILE *outfile,struct strbuf **lines, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct trailer_item ***in_tok_tail) {int count =0 ;int patch_start, trailer_start, trailer_end, i;
@@ -829,18 +721,19 @@ static int process_input_file(FILE *outfile,for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char) {struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_first, in_tok_last, new); +add_trailer_item (in_tok_tail, new); } }return trailer_end; } -static void free_all (struct trailer_item **first) +static void free_all (struct trailer_item *head) { -while (*first) { -struct trailer_item *item =remove_first (first); -free_trailer_item (item); +while (head) { +struct trailer_item *next = head->next; +free_trailer_item (head); + head = next; } }
@@ -877,9 +770,9 @@ static FILE *create_in_place_tempfile(const char *file)void process_trailers (const char *file,int in_place,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 trailer_item *in_tok_head =NULL ; +struct trailer_item **in_tok_tail = &in_tok_head; +struct trailer_item *arg_tok_head;struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
@@ -894,15 +787,15 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str outfile =create_in_place_tempfile (file);/* Print the lines before the trailers */ - trailer_end =process_input_file (outfile, lines, &in_tok_first, &in_tok_last); + trailer_end =process_input_file (outfile, lines, &in_tok_tail); - arg_tok_first =process_command_line_args (trailers); + arg_tok_head =process_command_line_args (trailers); -process_trailers_lists (&in_tok_first, &in_tok_last, &arg_tok_first); +process_trailers_lists (&in_tok_head, arg_tok_head); -print_all (outfile, in_tok_first, trim_empty); +print_all (outfile, in_tok_head, trim_empty); -free_all (&in_tok_first); +free_all (in_tok_head);/* Print the lines after the trailers as is */ print_lines (outfile, lines, trailer_end,INT_MAX );
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH 1/5] trailer: use singly-linked list, not doubly
Quoted 10 lines · Jonathan Tan
Use singly-linked lists (instead of doubly-linked lists) in trailer to keep track of arguments (whether implicit from configuration or explicit from the command line) and trailer items.
This change significantly reduces the code length and simplifies the code. There are now fewer pointers to be manipulated, but most trailer manipulations now require seeking from beginning to end, so there might be a slight net decrease in performance; however the number of trailers is usually small (10 to 15 at the most) so this should not cause a big impact.
It is overall a very good change, but can you split this into two independent patches? s/struct trailer_item/const &/ sprinkled all over the place is more or less unrelated change and it is very distracting to see the primary change of the way lists are handled.
Re: [PATCH 1/5] trailer: use singly-linked list, not doubly
In reply to Jonathan TanQuoted 5 lines · Jonathan Tan
Use singly-linked lists (instead of doubly-linked lists) in trailer to keep track of arguments (whether implicit from configuration or explicit from the command line) and trailer items.
This change significantly reduces the code length and simplifies the code.
It's true that the code can be simplified a lot by using a singly-linked list, but if we already had and used some generic functions or macros to handle doubly-linked list, I doubt there would be a significant simplification (as the generic code could not be deleted in this case).
Quoted 5 lines
There are now fewer pointers to be manipulated, but most trailer manipulations now require seeking from beginning to end, so there might be a slight net decrease in performance; however the number of trailers is usually small (10 to 15 at the most) so this should not cause a big impact.
By default we append new trailers at the end of the trailer list, so a singly-linked list is theoretically not well suited at all for handling trailer lists.
You say that at most there is a small number of trailers, and that may be true indeed for the Linux kernel and most projects these days, but I am not sure it is a good assumption to make in general.
For example if some projects use or start using "CC: *" trailers and tools to automatically append such trailers (perhaps using 'git interpret-trailers' for that purpose by the way) based on people who touched the same code, then it could very well be a common thing to have 20 or more trailers on patches/commits for these projects.
There could also be automated testing tools that add their own "Tested-by: *" trailers, and projects that require many people to add their "Reviewed-by: *" trailers to each patch/commit. And in general with millions of users, it is not very safe to make assumptions that they will all be "reasonable" in the way they use a feature.
Another thing is that when Git started, I doubt many people would have thought that there would often be more than just one or two "Signed-off-by: *" trailer, and now 10 or 15 doesn't seem unthinkable. In fact 'git interpret-trailers' has been made precisely because there are more and more trailers, so there is more and more a need for a tool to properly add and manage them.
We recently had a discussion on the list to increase the default abbreviation length because it was not foreseen at the beginning of Git that people would need a larger number of characters as projects grow.
So even for the Linux kernel, I am not sure that it is safe to assume that the number of trailers will not grow much over the years, especially if we work on the tool that can make it easy to easily and automatically add them.
Thanks,
Christian.
Re: [PATCH 1/5] trailer: use singly-linked list, not doubly
Quoted 5 lines · Christian Couder
Quoted 4 lines · Jonathan Tan
Use singly-linked lists (instead of doubly-linked lists) in trailer to keep track of arguments (whether implicit from configuration or explicit from the command line) and trailer items.
Quoted 7 lines
Quoted 1 line
This change significantly reduces the code length and simplifies the code.
It's true that the code can be simplified a lot by using a singly-linked list, but if we already had and used some generic functions or macros to handle doubly-linked list, I doubt there would be a significant simplification (as the generic code could not be deleted in this case).
We didn't have such generic macros when you wrote the trailer code originally, but we do now, in list.h (they come from the kernel's doubly-linked list implementation). I used them recently in a series and found them pretty pleasant and complete.
Maybe it's worth trying the conversion here to see if it simplifies the code.
Quoted 6 lines
Quoted 5 lines
There are now fewer pointers to be manipulated, but most trailer manipulations now require seeking from beginning to end, so there might be a slight net decrease in performance; however the number of trailers is usually small (10 to 15 at the most) so this should not cause a big impact.
Quoted 7 lines
By default we append new trailers at the end of the trailer list, so a singly-linked list is theoretically not well suited at all for handling trailer lists.
You say that at most there is a small number of trailers, and that may be true indeed for the Linux kernel and most projects these days, but I am not sure it is a good assumption to make in general.
I agree. As somebody who has fixed quite a number of accidentally quadratic cases in the number of refs, width of the commit graph, etc, over the years, these things have a way of creeping up or finding pathological cases.
You _can_ append to a singly linked list in O(1) if you keep a tail pointer, but I think using list.h would be even nicer.
-Peff
PATCH 2/5 trailer: streamline trailer item create and add
In reply to Jonathan TanCurrently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.
--- trailer.c | 135 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 62 insertions(+), 73 deletions(-)
trailer.c +62 -73
@@ -335,7 +335,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name)
@@ -467,10 +467,30 @@ 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) +static const char *token_from_item (struct trailer_item *item,char *tok) +{ +if (item->conf.key) +return item->conf.key; +if (tok) +return tok; +return item->conf.name; +} + +static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +{ +if (!strncasecmp (tok, item->conf.name, tok_len)) +return 1 ; +return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; +} + +static int parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer) {size_t len;struct strbuf seps =STRBUF_INIT ; +struct trailer_item *item; +int tok_len; +strbuf_addstr (&seps, separators);strbuf_addch (&seps,'=' ); len =strcspn (trailer, seps.buf);
@@ -490,73 +510,31 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trastrbuf_addstr (tok, trailer);strbuf_trim (tok); } -return 0 ; -} - -static const char *token_from_item (struct trailer_item *item,char *tok) -{ -if (item->conf.key) -return item->conf.key; -if (tok) -return tok; -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 ? val :xstrdup ("" ); - -if (conf_item) { -duplicate_conf (&new->conf, &conf_item->conf); - new->token =xstrdup (token_from_item (conf_item, tok)); -free (tok); - }else { -duplicate_conf (&new->conf, &default_conf_info); - new->token = tok; - } - -return new; -} - -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) -{ -if (!strncasecmp (tok, item->conf.name, tok_len)) -return 1 ; -return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_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_len; - -if (parse_trailer (&tok, &val, string)) -return NULL ; - - tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ + tok_len =token_len_without_separator (tok->buf, tok->len); + *conf = &default_conf_info;for (item = first_conf_item; item; item = item->next) { -if (token_matches_item (tok.buf, item, tok_len)) -return new_trailer_item (item, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +if (token_matches_item (tok->buf, item, tok_len)) { +char *tok_buf =strbuf_detach (tok,NULL ); + *conf = &item->conf; +strbuf_addstr (tok,token_from_item (item, tok_buf)); +free (tok_buf); +break ; + } } -return new_trailer_item (NULL , -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +return 0 ; } -static void add_trailer_item (struct trailer_item ***tail, -struct trailer_item *new) +static void add_trailer_item (struct trailer_item ***tail,char *tok,char *val, +const struct conf_info *conf) { -if (!new) -return ; +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf); + **tail = new; *tail = &new->next; }
@@ -567,19 +545,25 @@ static struct trailer_item *process_command_line_args(struct string_list *trailestruct trailer_item **arg_tok_tail = &arg_tok_head;struct string_list_item *tr;struct trailer_item *item; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Add a trailer item for each configured trailer with a command */ -for (item = first_conf_item; item; item = item->next) { -if (item->conf.command) { -struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_tail, new); - } - } +for (item = first_conf_item; item; item = item->next) +if (item->conf.command) +add_trailer_item (&arg_tok_tail, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf);/* Add a trailer item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -struct trailer_item *new =create_trailer_item (tr->string); -add_trailer_item (&arg_tok_tail, new); +if (!parse_trailer (&tok, &val, &conf, tr->string)) +add_trailer_item (&arg_tok_tail, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return arg_tok_head;
@@ -702,6 +686,9 @@ static int process_input_file(FILE *outfile, {int count =0 ;int patch_start, trailer_start, trailer_end, i; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -719,10 +706,12 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char) { -struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_tail, new); - } +if (lines[i]->buf[0 ] != comment_line_char && + !parse_trailer (&tok, &val, &conf, lines[i]->buf)) +add_trailer_item (in_tok_tail, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH 3/5 trailer: make args have their own struct
In reply to Jonathan TanImprove type safety by making arguments (whether from configuration or from the command line) have their own "struct arg_item" type, separate from the "struct trailer_item" type used to represent the trailers in the buffer being manipulated.
Also take the opportunity to refine the "struct trailer_item" definition by removing the conf (which is used only by arguments) and by removing const on the string fields, since those fields are owned by the struct.
This change also prepares "struct trailer_item" to be further differentiated from "struct arg_item" in future patches.
--- trailer.c | 161 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 62 deletions(-)
trailer.c +99 -62
@@ -26,12 +26,18 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value; +}; + +struct arg_item { +struct arg_item *next; +char *token; +char *value;struct conf_info conf; }; -static struct trailer_item *first_conf_item; +static struct arg_item *first_conf_item;static char *separators =":" ;
@@ -55,8 +61,7 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (const struct trailer_item *a, -const struct trailer_item *b) +static int same_token (const struct trailer_item *a,const struct arg_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -65,14 +70,12 @@ static int same_token(const struct trailer_item *a,return !strncasecmp (a->token, b->token, min_len); } -static int same_value (const struct trailer_item *a, -const struct trailer_item *b) +static int same_value (const struct trailer_item *a,const struct arg_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (const struct trailer_item *a, -const struct trailer_item *b) +static int same_trailer (const struct trailer_item *a,const struct arg_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -94,11 +97,18 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) { +free (item->token); +free (item->value); +free (item); +} + +static void free_arg_item (struct arg_item *item) +{free (item->conf.name);free (item->conf.key);free (item->conf.command); -free ((char *)item->token); -free ((char *)item->value); +free (item->token); +free (item->value);free (item); }
@@ -131,13 +141,13 @@ static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty) } } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)
@@ -162,7 +172,7 @@ static const char *apply_command(const char *command, const char *arg)return result; } -static void apply_item_command (struct trailer_item *in_tok,struct trailer_item *arg_tok) +static void apply_item_command (struct trailer_item *in_tok,struct arg_item *arg_tok) {if (arg_tok->conf.command) {const char *arg;
@@ -179,60 +189,77 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item } } +static struct trailer_item *trailer_from_arg (struct arg_item *arg_tok) +{ +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = arg_tok->token; + new->value = arg_tok->value; + arg_tok->token = arg_tok->value =NULL ; +free_arg_item (arg_tok); +return new; +} +static void apply_existing_arg (struct trailer_item **found_next, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,struct trailer_item **position_to_add,const struct trailer_item *in_tok_head,const struct trailer_item *neighbor) { -if (arg_tok->conf.if_exists ==EXISTS_DO_NOTHING ) { -free_trailer_item (arg_tok); +enum action_if_exists if_exists = arg_tok->conf.if_exists; +struct trailer_item *to_add; + +if (if_exists ==EXISTS_DO_NOTHING ) { +free_arg_item (arg_tok);return ; }apply_item_command (*found_next, arg_tok); -if (arg_tok->conf.if_exists ==EXISTS_ADD_IF_DIFFERENT_NEIGHBOR ) { +if (if_exists ==EXISTS_ADD_IF_DIFFERENT_NEIGHBOR ) {if (neighbor &&same_trailer (neighbor, arg_tok)) { -free_trailer_item (arg_tok); +free_arg_item (arg_tok);return ; } - }else if (arg_tok->conf.if_exists ==EXISTS_ADD_IF_DIFFERENT ) { + }else if (if_exists ==EXISTS_ADD_IF_DIFFERENT ) {const struct trailer_item *in_tok;for (in_tok = in_tok_head; in_tok; in_tok = in_tok->next) {if (same_trailer (in_tok, arg_tok)) { -free_trailer_item (arg_tok); +free_arg_item (arg_tok);return ; } } } - arg_tok->next = *position_to_add; - *position_to_add = arg_tok; + to_add =trailer_from_arg (arg_tok); + to_add->next = *position_to_add; + *position_to_add = to_add; -if (arg_tok->conf.if_exists ==EXISTS_REPLACE ) { +if (if_exists ==EXISTS_REPLACE ) {struct trailer_item *new_next = (*found_next)->next;free_trailer_item (*found_next); *found_next = new_next; } } -static void apply_missing_arg (struct trailer_item *arg_tok, +static void apply_missing_arg (struct arg_item *arg_tok,struct trailer_item **position_to_add) { +struct trailer_item *to_add; +if (arg_tok->conf.if_missing ==MISSING_DO_NOTHING ) { -free_trailer_item (arg_tok); +free_arg_item (arg_tok);return ; }apply_item_command (NULL , arg_tok); - arg_tok->next = *position_to_add; - *position_to_add = arg_tok; + to_add =trailer_from_arg (arg_tok); + to_add->next = *position_to_add; + *position_to_add = to_add; }static void apply_arg (struct trailer_item **in_tok_head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {struct trailer_item **next, **found_next =NULL , *last =NULL ;enum action_where where = arg_tok->conf.where;
@@ -283,9 +310,9 @@ static void apply_arg(struct trailer_item **in_tok_head, }static void process_trailers_lists (struct trailer_item **in_tok_head, -struct trailer_item *arg_tok_head) +struct arg_item *arg_tok_head) { -struct trailer_item *arg_tok, *next; +struct arg_item *arg_tok, *next;for (arg_tok = arg_tok_head; arg_tok; arg_tok = next) { next = arg_tok->next;apply_arg (in_tok_head, arg_tok);
@@ -346,10 +373,10 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src) dst->command =xstrdup (src->command); } -static struct trailer_item *get_conf_item (const char *name) +static struct arg_item *get_conf_item (const char *name) { -struct trailer_item **next = &first_conf_item; -struct trailer_item *item; +struct arg_item **next = &first_conf_item; +struct arg_item *item;/* Look up item with same name */ for (next = &first_conf_item; *next; next = &(*next)->next)
@@ -357,7 +384,7 @@ static struct trailer_item *get_conf_item(const char *name)return *next;/* Item does not already exists, create it */ - item =xcalloc (sizeof (struct trailer_item ),1 ); + item =xcalloc (sizeof (*item),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); *next = item;
@@ -409,7 +436,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value, vstatic int git_trailer_config (const char *conf_key,const char *value,void *cb) {const char *trailer_item, *variable_name; -struct trailer_item *item; +struct arg_item *item;struct conf_info *conf;char *name =NULL ;enum trailer_info_type type;
@@ -467,7 +494,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)return 0 ; } -static const char *token_from_item (struct trailer_item *item,char *tok) +static const char *token_from_item (struct arg_item *item,char *tok) {if (item->conf.key)return item->conf.key;
@@ -476,7 +503,7 @@ static const char *token_from_item(struct trailer_item *item, char *tok)return item->conf.name; } -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +static int token_matches_item (const char *tok,struct arg_item *item,int tok_len) {if (!strncasecmp (tok, item->conf.name, tok_len))return 1 ;
@@ -488,7 +515,7 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, {size_t len;struct strbuf seps =STRBUF_INIT ; -struct trailer_item *item; +struct arg_item *item;int tok_len;strbuf_addstr (&seps, separators);
@@ -513,11 +540,13 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,/* Lookup if the token matches something in the config */ tok_len =token_len_without_separator (tok->buf, tok->len); - *conf = &default_conf_info; +if (conf) + *conf = &default_conf_info;for (item = first_conf_item; item; item = item->next) {if (token_matches_item (tok->buf, item, tok_len)) {char *tok_buf =strbuf_detach (tok,NULL ); - *conf = &item->conf; +if (conf) + *conf = &item->conf;strbuf_addstr (tok,token_from_item (item, tok_buf));free (tok_buf);break ;
@@ -527,43 +556,53 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct trailer_item ***tail,char *tok,char *val, -const struct conf_info *conf) +static void add_trailer_item (struct trailer_item ***tail,char *tok,char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val; + + **tail = new; + *tail = &new->next; +} + +static void add_arg_item (struct arg_item ***tail,char *tok,char *val, +const struct conf_info *conf) +{ +struct arg_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val;duplicate_conf (&new->conf, conf); **tail = new; *tail = &new->next; } -static struct trailer_item *process_command_line_args (struct string_list *trailers) +static struct arg_item *process_command_line_args (struct string_list *trailers) { -struct trailer_item *arg_tok_head =NULL ; -struct trailer_item **arg_tok_tail = &arg_tok_head; +struct arg_item *arg_tok_head =NULL ; +struct arg_item **arg_tok_tail = &arg_tok_head;struct string_list_item *tr; -struct trailer_item *item; +struct arg_item *item;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ;const struct conf_info *conf; -/* Add a trailer item for each configured trailer with a command */ +/* Add an arg item for each configured trailer with a command */ for (item = first_conf_item; item; item = item->next)if (item->conf.command) -add_trailer_item (&arg_tok_tail, -xstrdup (token_from_item (item,NULL )), -xstrdup ("" ), - &item->conf); +add_arg_item (&arg_tok_tail, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); -/* Add a trailer item for each trailer on the command line */ +/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) {if (!parse_trailer (&tok, &val, &conf, tr->string)) -add_trailer_item (&arg_tok_tail, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +add_arg_item (&arg_tok_tail, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return arg_tok_head;
@@ -688,7 +727,6 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; -const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -707,11 +745,10 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val, &conf, lines[i]->buf)) + !parse_trailer (&tok, &val,NULL , lines[i]->buf))add_trailer_item (in_tok_tail,strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +strbuf_detach (&val,NULL )); }return trailer_end;
@@ -761,7 +798,7 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str {struct trailer_item *in_tok_head =NULL ;struct trailer_item **in_tok_tail = &in_tok_head; -struct trailer_item *arg_tok_head; +struct arg_item *arg_tok_head;struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH 4/5 trailer: allow non-trailers in trailer block
In reply to Jonathan TanCurrently, interpret-trailers requires all lines of a trailer block to be trailers (or comments) - if not it would not identify that block as a trailer block, and thus create its own trailer block, inserting a blank line. For example:
echo -e "\na: b\nnot trailer" | git interpret-trailers --trailer "c: d"
would result in:
a: b
not trailer
c: d
Relax the definition of a trailer block to only require 1 trailer, so that trailers can be directly added to such blocks, resulting in:
a: b
not trailer
c: d
This allows arbitrary lines to be included in trailer blocks, like those in [1], and still allow interpret-trailers to be used.
This change also makes comments in the trailer block be treated as any other non-trailer line, preserving them in the output of interpret-trailers.
There are some possible inaccuracies in the master branch's find_trailer_start (in particular, handling of lines that *start* with a separator, which should not be considered a trailer line) - this patch preserves the existing behavior.
Documentation/git-interpret-trailers.txt | 3 +- t/t7513-interpret-trailers.sh | 35 +++++++++++++++ trailer.c | 77 ++++++++++++++++++++++---------- 3 files changed, 90 insertions(+), 25 deletions(-)
Documentation/git-interpret-trailers.txt +2 -1
@@ -48,7 +48,8 @@ with only spaces at the end of the commit message part, one blank line will be added before the new trailer. Existing trailers are extracted from the input message by looking for -a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), where the group is preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three
t/t7513-interpret-trailers.sh +35 -0
@@ -126,6 +126,37 @@ test_expect_success 'with multiline title in the message' ' test_cmp expected actual ' +test_expect_success 'with non-trailer lines mixed with trailer lines' ' + cat >patch <<-\EOF && + + this: is a trailer + this is not a trailer + EOF + cat >expected <<-\EOF && + + this: is a trailer + this is not a trailer + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines only' ' + cat >patch <<-\EOF && + + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF &&
@@ -257,6 +288,8 @@ test_expect_success 'with message that has comments' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
@@ -286,6 +319,8 @@ test_expect_success 'with message that has an old style conflict block' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
trailer.c +53 -24
152 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH 5/5 trailer: support values folded to multiple lines
In reply to Jonathan TanCurrently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
--- Documentation/git-interpret-trailers.txt | 7 +- t/t7513-interpret-trailers.sh | 139 +++++++++++++++++++++++++++++++ trailer.c | 40 ++++++--- 3 files changed, 170 insertions(+), 16 deletions(-)
Documentation/git-interpret-trailers.txt +4 -3
@@ -57,11 +57,12 @@ minus signs start the patch part of the message. When reading trailers, there can be whitespaces before and after the token, the separator and the value. There can also be whitespaces -inside the token and the value. +inside the token and the value. The value may be split over multiple lines with +each subsequent line starting with whitespace, like the "folding" in RFC 822. 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. +rules for RFC 822 headers. For example they do not follow +the encoding rules and probably many other rules. OPTIONS -------
t/t7513-interpret-trailers.sh +139 -0
145 lines not on this page · raw message
trailer.c +27 -13
@@ -31,7 +31,7 @@ struct trailer_item {* (excluding the terminating newline) and token is NULL. */ char *token; -char *value; +struct strbuf value; };struct arg_item {
@@ -81,7 +81,7 @@ static int same_token(const struct trailer_item *a, const struct arg_item *b)static int same_value (const struct trailer_item *a,const struct arg_item *b) { -return !strcasecmp (a->value, b->value); +return !strcasecmp (a->value.buf, b->value); }static int same_trailer (const struct trailer_item *a,const struct arg_item *b)
@@ -107,7 +107,7 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) {free (item->token); -free (item->value); +strbuf_release (&item->value);free (item); }
@@ -152,8 +152,8 @@ static void print_all(FILE *outfile, 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 (outfile, item->token, item->value); +if (!trim_empty || item->value.len >0 ) +print_tok_val (outfile, item->token, item->value.buf); } }
@@ -195,8 +195,8 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *argif (arg_tok->value && arg_tok->value[0 ]) { arg = arg_tok->value; }else { -if (in_tok && in_tok->value) - arg =xstrdup (in_tok->value); +if (in_tok) + arg =xstrdup (in_tok->value.buf);else arg =xstrdup ("" ); }
@@ -209,7 +209,9 @@ static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = arg_tok->token; - new->value = arg_tok->value; +strbuf_init (&new->value,0 ); +strbuf_attach (&new->value, arg_tok->value,strlen (arg_tok->value), +strlen (arg_tok->value)); arg_tok->token = arg_tok->value =NULL ;free_arg_item (arg_tok);return new;
@@ -587,14 +589,17 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct trailer_item ***tail,char *tok,char *val) +static struct trailer_item *add_trailer_item (struct trailer_item ***tail,char *tok, +char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; - new->value = val; +strbuf_init (&new->value,0 ); +strbuf_attach (&new->value, val,strlen (val),strlen (val)); **tail = new; *tail = &new->next; +return new; } static void add_arg_item(struct arg_item ***tail, char *tok, char *val,
@@ -750,6 +755,7 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; +struct trailer_item *last =NULL ;/* Get the line count */ while (lines[count])
@@ -767,16 +773,24 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { +if (last &&isspace (lines[i]->buf[0 ])) { +/* continuation line of the last trailer item */ +strbuf_addch (&last->value,'\n' ); +strbuf_addbuf (&last->value, lines[i]); +strbuf_strip_suffix (&last->value,"\n" ); +continue ; + }if (!parse_trailer (&tok, &val,NULL , lines[i]->buf,0 )) -add_trailer_item (in_tok_tail, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); + last =add_trailer_item (in_tok_tail, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ));else {strbuf_addbuf (&val, lines[i]);strbuf_strip_suffix (&val,"\n" );add_trailer_item (in_tok_tail,NULL ,strbuf_detach (&val,NULL )); + last =NULL ; } }
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH 5/5] trailer: support values folded to multiple lines
Quoted 10 lines · Jonathan Tan
Currently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second linewould be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
Let's see how the code handles one minor detail when we see 822 folding, namely, "what happens to the leading whitespace that signals the beginning of the second and subsequent lines?".
trailer.c struct trailer_item { · line 35
Quoted 11 lines
trailer.c +1
-1@@ -31,7 +31,7 @@ struct trailer_item {* (excluding the terminating newline) and token is NULL. */ char *token; -char *value; +struct strbuf value; };
Is the length of value very frequently used once the list of trailer lines are fully parsed? If not, I'd rather not to have "struct strbuf" in a long-living structure like this one and instead prefer keeping it a simple and stupid "char *value".
Yes, I know the existing code in trailers overuses strbuf when there is no need, primarily because it uses the lazy "split into an array of strbufs" function. We shouldn't make it worse.
trailer.c static int process_input_file(FILE *outfile, · line 776
Quoted 5 lines
trailer.c +1
-0@@ -767,16 +773,24 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { +if (last &&isspace (lines[i]->buf[0 ])) {
It is convenient if "value" is a strbuf to do this,
trailer.c static int process_input_file(FILE *outfile, · line 780
Quoted 4 lines
trailer.c +4
-0+/* continuation line of the last trailer item */ +strbuf_addch (&last->value,'\n' ); +strbuf_addbuf (&last->value, lines[i]); +strbuf_strip_suffix (&last->value,"\n" );
but it is easy to introduce a temporary strbuf in this scope and use it only to create the final value and detach it to last->value, i.e.
if (last && isspace(*lines[i]->buf)) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\n%s", last->value, lines[i]->buf);
strbuf_strip_suffix(&buf, "\n");
free(last->value);
last->value = strbuf_detach(&buf, NULL);By the way, I now see that the code handles the "minor detail" to keep the leading whitespace, which is good.
Thanks.
v2 cover allow non-trailers and multiple-line trailers
In reply to Jonathan TanThanks, Peff, for the pointer to list.h. Using list.h does simplify the code by a similar amount to switching it to a singly-linked list, so I have done that (replacing my earlier "trailer: use singly-linked list, not doubly" patch). Another advantage is that I no longer need to change the algorithm, making for a smaller patch.
(There are some quirks resulting from list.h implementing a circular list, like needing to pass "head" as a sentinel when iterating from the middle of the list, but those are minor, and my original singly-linked list implementation had quirks too anyway like needing to pass a pointer to the next pointer.)
Updates:
(-> 1/6)
- Added separate patch for const correctness changes (1/5 -> 2/6)
- Dropped singly-linked list patch, instead replacing existing doubly-linked list implementation with list.h
(5/5 -> 6/6)
- Used "char *" instead of "struct strbuf"
- Modified test slightly to test whitespace at beginning of line
Jonathan Tan (6):
trailer: improve const correctness
trailer: use list.h for doubly-linked list
trailer: streamline trailer item create and add
trailer: make args have their own struct
trailer: allow non-trailers in trailer block
trailer: support values folded to multiple lines
Documentation/git-interpret-trailers.txt | 10 +- t/t7513-interpret-trailers.sh | 174 ++++++++++ trailer.c | 538 +++++++++++++++---------------- 3 files changed, 444 insertions(+), 278 deletions(-)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v2 1/6 trailer: improve const correctness
Change "const char *" to "char *" in struct trailer_item and in the return value of apply_command (since those strings are owned strings).
Change "struct conf_info *" to "const struct conf_info *" (since that struct is not modified).
--- trailer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
trailer.c +7 -7
@@ -27,8 +27,8 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *previous;struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value;struct conf_info conf; };
@@ -95,8 +95,8 @@ 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->token); +free (item->value);free (item); }
@@ -215,13 +215,13 @@ static struct trailer_item *remove_first(struct trailer_item **first)return item; } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)
@@ -425,7 +425,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v2 2/6 trailer: use list.h for doubly-linked list
In reply to Jonathan TanReplace the existing handwritten implementation of a doubly-linked list in trailer.c with the functions and macros from list.h. This significantly simplifies the code.
--- trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
trailer.c +91 -167
@@ -4,6 +4,7 @@#include "commit.h" #include "tempfile.h" #include "trailer.h" +#include "list.h" /* * Copyright (c) 2013, 2014 Christian Couder <[email protected]> */
@@ -25,19 +26,24 @@ struct conf_info {static struct conf_info default_conf_info;struct trailer_item { -struct trailer_item *previous; -struct trailer_item *next; +struct list_head list;char *token;char *value;struct conf_info conf; }; -static struct trailer_item *first_conf_item; +LIST_HEAD (conf_head);static char *separators =":" ;#define TRAILER_ARG_STRING "$ARG" +/* Iterate over the elements of the list. */ +#define list_for_each_dir (pos, head, is_reverse) \ + for (pos = is_reverse ? (head)->prev : (head)->next; \ + pos != (head); \ + pos = is_reverse ? pos->prev : pos->next) +static int after_or_end (enum action_where where) {return (where ==WHERE_AFTER ) || (where ==WHERE_END );
@@ -120,101 +126,49 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)fprintf (outfile,"%s%c %s\n" , tok, separators[0 ], val); } -static void print_all (FILE *outfile,struct trailer_item *first,int trim_empty) +static void print_all (FILE *outfile,struct list_head *head,int trim_empty) { +struct list_head *pos;struct trailer_item *item; -for (item = first; item; item = item->next) { +list_for_each (pos, head) { + item = list_entry(pos, struct trailer_item, list);if (!trim_empty ||strlen (item->value) >0 )print_tok_val (outfile, item->token, item->value); } } -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 add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok, -struct trailer_item **first, -struct trailer_item **last) -{ -if (after_or_end (arg_tok->conf.where)) { - arg_tok->next = on_tok->next; - on_tok->next = arg_tok; - arg_tok->previous = on_tok; -if (arg_tok->next) - arg_tok->next->previous = arg_tok; -update_last (last); - }else { - arg_tok->previous = on_tok->previous; - on_tok->previous = arg_tok; - arg_tok->next = on_tok; -if (arg_tok->previous) - arg_tok->previous->next = arg_tok; -update_first (first); - } +struct trailer_item *arg_tok) +{ +if (after_or_end (arg_tok->conf.where)) +list_add (&arg_tok->list, &on_tok->list); +else +list_add_tail (&arg_tok->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok,struct trailer_item *arg_tok, -int check_all) +int check_all, +struct list_head *head) {enum action_where where = arg_tok->conf.where; +struct list_head *next_head;do { -if (!in_tok) -return 1 ;if (same_trailer (in_tok, arg_tok))return 0 ;/* * if we want to add a trailer after another one, * we have to check those before this one */ - in_tok =after_or_end (where) ? in_tok->previous : in_tok->next; + next_head =after_or_end (where) ? in_tok->list.prev + : in_tok->list.next; +if (next_head == head) +break ; + in_tok = list_entry(next_head, struct trailer_item, list); }while (check_all);return 1 ; } -static void remove_from_list (struct trailer_item *item, -struct trailer_item **first, -struct trailer_item **last) -{ -struct trailer_item *next = item->next; -struct trailer_item *previous = item->previous; - -if (next) { - item->next->previous = previous; - item->next =NULL ; - }else if (last) - *last = previous; - -if (previous) { - item->previous->next = next; - item->previous =NULL ; - }else if (first) - *first = 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 char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;
@@ -266,8 +220,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_itemstatic void apply_arg_if_exists (struct trailer_item *in_tok,struct trailer_item *arg_tok,struct trailer_item *on_tok, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING :
@@ -275,40 +228,34 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -remove_from_list (in_tok, in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok); +list_del (&in_tok->list);free_trailer_item (in_tok);break ;case EXISTS_ADD :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT :apply_item_command (in_tok, arg_tok); -if (check_if_different (in_tok, arg_tok,1 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (in_tok, arg_tok,1 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok); -if (check_if_different (on_tok, arg_tok,0 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (on_tok, arg_tok,0 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ; } } -static void apply_arg_if_missing (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static void apply_arg_if_missing (struct list_head *head,struct trailer_item *arg_tok) { -struct trailer_item **in_tok;enum action_where where;switch (arg_tok->conf.if_missing) {
@@ -317,68 +264,60 @@ static void apply_arg_if_missing(struct trailer_item **in_tok_first,break ;case MISSING_ADD : where = arg_tok->conf.where; - in_tok =after_or_end (where) ? in_tok_last : in_tok_first;apply_item_command (NULL , arg_tok); -if (*in_tok) { -add_arg_to_input_list (*in_tok, arg_tok, - in_tok_first, in_tok_last); - }else { - *in_tok_first = arg_tok; - *in_tok_last = arg_tok; - } -break ; +if (after_or_end (where)) +list_add_tail (&arg_tok->list, head); +else +list_add (&arg_tok->list, head); } } -static int find_same_and_apply_arg (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static int find_same_and_apply_arg (struct list_head *head,struct trailer_item *arg_tok) { +struct list_head *pos;struct trailer_item *in_tok;struct trailer_item *on_tok; -struct trailer_item *following_tok;enum action_where where = arg_tok->conf.where;int middle = (where ==WHERE_AFTER ) || (where ==WHERE_BEFORE );int backwards =after_or_end (where); -struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first; +struct trailer_item *start_tok; -for (in_tok = start_tok; in_tok; in_tok = following_tok) { - following_tok = backwards ? in_tok->previous : in_tok->next; +if (list_empty (head)) +return 0 ; + + start_tok =list_entry (backwards ? head->prev : head->next, + struct trailer_item, + list); + +list_for_each_dir (pos, head, backwards) { + in_tok = list_entry(pos, struct trailer_item, list);if (!same_token (in_tok, arg_tok))continue ; on_tok = middle ? in_tok : start_tok; -apply_arg_if_exists (in_tok, arg_tok, on_tok, - in_tok_first, in_tok_last); +apply_arg_if_exists (in_tok, arg_tok, on_tok, head);return 1 ; }return 0 ; } -static void process_trailers_lists (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item **arg_tok_first) +static void process_trailers_lists (struct list_head *head, +struct list_head *arg_head) { +struct list_head *pos, *p;struct trailer_item *arg_tok; -struct trailer_item *next_arg; - -if (!*arg_tok_first) -return ; -for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { +list_for_each_safe (pos, p, arg_head) {int applied =0 ; + arg_tok = list_entry(pos, struct trailer_item, list); - next_arg = arg_tok->next; -remove_from_list (arg_tok, arg_tok_first,NULL ); +list_del (pos); - applied =find_same_and_apply_arg (in_tok_first, - in_tok_last, - arg_tok); + applied =find_same_and_apply_arg (head, arg_tok);if (!applied) -apply_arg_if_missing (in_tok_first, - in_tok_last, - arg_tok); +apply_arg_if_missing (head, arg_tok); } }
@@ -438,13 +377,12 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)static struct trailer_item *get_conf_item (const char *name) { +struct list_head *pos;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) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (!strcasecmp (item->conf.name, name))return item; }
@@ -454,12 +392,7 @@ static struct trailer_item *get_conf_item(const char *name)duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); -if (!previous) - first_conf_item = item; -else { - previous->next = item; - item->previous = previous; - } +list_add_tail (&item->list, &conf_head);return item; }
@@ -633,6 +566,7 @@ static struct trailer_item *create_trailer_item(const char *string)struct strbuf val =STRBUF_INIT ;struct trailer_item *item;int tok_len; +struct list_head *pos;if (parse_trailer (&tok, &val, string))return NULL ;
@@ -640,7 +574,8 @@ static struct trailer_item *create_trailer_item(const char *string) tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (token_matches_item (tok.buf, item, tok_len)) return new_trailer_item(item, strbuf_detach(&tok, NULL),
@@ -652,44 +587,34 @@ static struct trailer_item *create_trailer_item(const char *string) strbuf_detach(&val, NULL)); } -static void add_trailer_item (struct trailer_item **first, -struct trailer_item **last, -struct trailer_item *new) +static void add_trailer_item (struct list_head *head,struct trailer_item *new) {if (!new)return ; -if (!*last) { - *first = new; - *last = new; - }else { - (*last)->next = new; - new->previous = *last; - *last = new; - } +list_add_tail (&new->list, head); } -static struct trailer_item *process_command_line_args (struct string_list *trailers) +static void process_command_line_args (struct list_head *arg_head, +struct string_list *trailers) { -struct trailer_item *arg_tok_first =NULL ; -struct trailer_item *arg_tok_last =NULL ;struct string_list_item *tr;struct trailer_item *item; +struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (item->conf.command) {struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_first, &arg_tok_last, new); +add_trailer_item (arg_head, new); } }/* Add a trailer item for each trailer on the command line */ 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_trailer_item (arg_head, new); } - -return arg_tok_first; }static struct strbuf **read_input_file (const char *file)
@@ -805,8 +730,7 @@ static void print_lines(FILE *outfile, struct strbuf **lines, int start, int endstatic int process_input_file (FILE *outfile,struct strbuf **lines, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {int count =0 ;int patch_start, trailer_start, trailer_end, i;
@@ -829,18 +753,19 @@ static int process_input_file(FILE *outfile,for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char) {struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_first, in_tok_last, new); +add_trailer_item (head, new); } }return trailer_end; } -static void free_all (struct trailer_item **first) +static void free_all (struct list_head *head) { -while (*first) { -struct trailer_item *item =remove_first (first); -free_trailer_item (item); +struct list_head *pos, *p; +list_for_each_safe (pos, p, head) { +list_del (pos); +free_trailer_item (list_entry(pos ,struct trailer_item ,list )); } }
@@ -877,9 +802,8 @@ static FILE *create_in_place_tempfile(const char *file)void process_trailers (const char *file,int in_place,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; +LIST_HEAD (head); +LIST_HEAD (arg_head);struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
@@ -894,15 +818,15 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str outfile =create_in_place_tempfile (file);/* Print the lines before the trailers */ - trailer_end =process_input_file (outfile, lines, &in_tok_first, &in_tok_last); + trailer_end =process_input_file (outfile, lines, &head); - arg_tok_first =process_command_line_args (trailers); +process_command_line_args (&arg_head, trailers); -process_trailers_lists (&in_tok_first, &in_tok_last, &arg_tok_first); +process_trailers_lists (&head, &arg_head); -print_all (outfile, in_tok_first, trim_empty); +print_all (outfile, &head, trim_empty); -free_all (&in_tok_first); +free_all (&head);/* Print the lines after the trailers as is */ print_lines (outfile, lines, trailer_end,INT_MAX );
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v2 2/6] trailer: use list.h for doubly-linked list
Quoted 4 lines · Jonathan Tan
Replace the existing handwritten implementation of a doubly-linked list in
trailer.cwith the functions and macros fromlist.h. This significantly simplifies the code.
---
The handcrafted one in trailer.c somehow did not use the common practice of using a doubly-linked cycle as a doubly-linked list with a designated fake element as the pointers to the first and to the last elements of the list (instead it used NULL as the "this is the end in this direction" convention just like a common singly-linked list), and this update removes the need for special cases handling the elements at the beginning and at the end that comes from that choice by switching to list.h macros. update_last/update_first can go, two parameters that were passed to point at the variables for the beginning and the end can go, the special cases for the initial condition in add_trailer_item() can go, all thanks to this change.
Very nice.
Quoted 2 lines
trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
Re: [PATCH v2 2/6] trailer: use list.h for doubly-linked list
In reply to Jonathan TanW dniu 13.10.2016 o 01:40, Jonathan Tan pisze:
Quoted 6 lines
Replace the existing handwritten implementation of a doubly-linked list in
trailer.cwith the functions and macros fromlist.h. This significantly simplifies the code.--- trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
Very nice gains!
BTW. could you sign (Signed-off-by) your patches? TIA.
Best,
Signature · 1 line
Jakub Narębski
PATCH v2 3/6 trailer: streamline trailer item create and add
In reply to Jonathan TanCurrently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.
--- trailer.c | 130 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 70 deletions(-)
trailer.c +60 -70
@@ -500,10 +500,31 @@ 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) +static const char *token_from_item (struct trailer_item *item,char *tok) +{ +if (item->conf.key) +return item->conf.key; +if (tok) +return tok; +return item->conf.name; +} + +static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +{ +if (!strncasecmp (tok, item->conf.name, tok_len)) +return 1 ; +return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; +} + +static int parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer) {size_t len;struct strbuf seps =STRBUF_INIT ; +struct trailer_item *item; +int tok_len; +struct list_head *pos; +strbuf_addstr (&seps, separators);strbuf_addch (&seps,'=' ); len =strcspn (trailer, seps.buf);
@@ -523,74 +544,31 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trastrbuf_addstr (tok, trailer);strbuf_trim (tok); } -return 0 ; -} - -static const char *token_from_item (struct trailer_item *item,char *tok) -{ -if (item->conf.key) -return item->conf.key; -if (tok) -return tok; -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 ? val :xstrdup ("" ); - -if (conf_item) { -duplicate_conf (&new->conf, &conf_item->conf); - new->token =xstrdup (token_from_item (conf_item, tok)); -free (tok); - }else { -duplicate_conf (&new->conf, &default_conf_info); - new->token = tok; - } - -return new; -} - -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) -{ -if (!strncasecmp (tok, item->conf.name, tok_len)) -return 1 ; -return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_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_len; -struct list_head *pos; - -if (parse_trailer (&tok, &val, string)) -return NULL ; - - tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ + tok_len =token_len_without_separator (tok->buf, tok->len); + *conf = &default_conf_info;list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (token_matches_item (tok.buf, item, tok_len)) -return new_trailer_item (item, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +if (token_matches_item (tok->buf, item, tok_len)) { +char *tok_buf =strbuf_detach (tok,NULL ); + *conf = &item->conf; +strbuf_addstr (tok,token_from_item (item, tok_buf)); +free (tok_buf); +break ; + } } -return new_trailer_item (NULL , -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +return 0 ; } -static void add_trailer_item (struct list_head *head,struct trailer_item *new) +static void add_trailer_item (struct list_head *head,char *tok,char *val, +const struct conf_info *conf) { -if (!new) -return ; +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); }
@@ -599,21 +577,28 @@ static void process_command_line_args(struct list_head *arg_head, {struct string_list_item *tr;struct trailer_item *item; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (item->conf.command) { -struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (arg_head, new); - } +if (item->conf.command) +add_trailer_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); }/* Add a trailer item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -struct trailer_item *new =create_trailer_item (tr->string); -add_trailer_item (arg_head, new); +if (!parse_trailer (&tok, &val, &conf, tr->string)) +add_trailer_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -734,6 +719,9 @@ static int process_input_file(FILE *outfile, {int count =0 ;int patch_start, trailer_start, trailer_end, i; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -751,10 +739,12 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char) { -struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (head, new); - } +if (lines[i]->buf[0 ] != comment_line_char && + !parse_trailer (&tok, &val, &conf, lines[i]->buf)) +add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v2 4/6 trailer: make args have their own struct
In reply to Jonathan TanImprove type safety by making arguments (whether from configuration or from the command line) have their own "struct arg_item" type, separate from the "struct trailer_item" type used to represent the trailers in the buffer being manipulated.
This change also prepares "struct trailer_item" to be further differentiated from "struct arg_item" in future patches.
--- trailer.c | 135 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 50 deletions(-)
trailer.c +85 -50
@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;struct conf_info conf; };
@@ -62,7 +68,7 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (struct trailer_item *a,struct trailer_item *b) +static int same_token (struct trailer_item *a,struct arg_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -71,12 +77,12 @@ static int same_token(struct trailer_item *a, struct trailer_item *b)return !strncasecmp (a->token, b->token, min_len); } -static int same_value (struct trailer_item *a,struct trailer_item *b) +static int same_value (struct trailer_item *a,struct arg_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (struct trailer_item *a,struct trailer_item *b) +static int same_trailer (struct trailer_item *a,struct arg_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -98,6 +104,13 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) { +free (item->token); +free (item->value); +free (item); +} + +static void free_arg_item (struct arg_item *item) +{free (item->conf.name);free (item->conf.key);free (item->conf.command);
@@ -137,17 +150,29 @@ static void print_all(FILE *outfile, struct list_head *head, int trim_empty) } } +static struct trailer_item *trailer_from_arg (struct arg_item *arg_tok) +{ +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = arg_tok->token; + new->value = arg_tok->value; + arg_tok->token = arg_tok->value =NULL ; +free_arg_item (arg_tok); +return new; +} +static void add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) { -if (after_or_end (arg_tok->conf.where)) -list_add (&arg_tok->list, &on_tok->list); +int aoe =after_or_end (arg_tok->conf.where); +struct trailer_item *to_add =trailer_from_arg (arg_tok); +if (aoe) +list_add (&to_add->list, &on_tok->list);else -list_add_tail (&arg_tok->list, &on_tok->list); +list_add_tail (&to_add->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,int check_all,struct list_head *head) {
@@ -200,7 +225,7 @@ static char *apply_command(const char *command, const char *arg)return result; } -static void apply_item_command (struct trailer_item *in_tok,struct trailer_item *arg_tok) +static void apply_item_command (struct trailer_item *in_tok,struct arg_item *arg_tok) {if (arg_tok->conf.command) {const char *arg;
@@ -218,13 +243,13 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item }static void apply_arg_if_exists (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,struct trailer_item *on_tok,struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok);
@@ -241,39 +266,41 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,if (check_if_different (in_tok, arg_tok,1 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok);if (check_if_different (on_tok, arg_tok,0 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ; } }static void apply_arg_if_missing (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {enum action_where where; +struct trailer_item *to_add;switch (arg_tok->conf.if_missing) {case MISSING_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case MISSING_ADD : where = arg_tok->conf.where;apply_item_command (NULL , arg_tok); + to_add =trailer_from_arg (arg_tok);if (after_or_end (where)) -list_add_tail (&arg_tok->list, head); +list_add_tail (&to_add->list, head);else -list_add (&arg_tok->list, head); +list_add (&to_add->list, head); } }static int find_same_and_apply_arg (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {struct list_head *pos;struct trailer_item *in_tok;
@@ -306,11 +333,11 @@ static void process_trailers_lists(struct list_head *head, struct list_head *arg_head) {struct list_head *pos, *p; -struct trailer_item *arg_tok; +struct arg_item *arg_tok;list_for_each_safe (pos, p, arg_head) {int applied =0 ; - arg_tok = list_entry(pos, struct trailer_item, list); + arg_tok = list_entry(pos, struct arg_item, list);list_del (pos);
@@ -375,20 +402,20 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src) dst->command =xstrdup (src->command); } -static struct trailer_item *get_conf_item (const char *name) +static struct arg_item *get_conf_item (const char *name) {struct list_head *pos; -struct trailer_item *item; +struct arg_item *item;/* Look up item with same name */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (!strcasecmp (item->conf.name, name))return item; }/* Item does not already exists, create it */ - item =xcalloc (sizeof (struct trailer_item ),1 ); + item =xcalloc (sizeof (*item),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name);
@@ -442,7 +469,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value, vstatic int git_trailer_config (const char *conf_key,const char *value,void *cb) {const char *trailer_item, *variable_name; -struct trailer_item *item; +struct arg_item *item;struct conf_info *conf;char *name =NULL ;enum trailer_info_type type;
@@ -500,7 +527,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)return 0 ; } -static const char *token_from_item (struct trailer_item *item,char *tok) +static const char *token_from_item (struct arg_item *item,char *tok) {if (item->conf.key)return item->conf.key;
@@ -509,7 +536,7 @@ static const char *token_from_item(struct trailer_item *item, char *tok)return item->conf.name; } -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +static int token_matches_item (const char *tok,struct arg_item *item,int tok_len) {if (!strncasecmp (tok, item->conf.name, tok_len))return 1 ;
@@ -521,7 +548,7 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, {size_t len;struct strbuf seps =STRBUF_INIT ; -struct trailer_item *item; +struct arg_item *item;int tok_len;struct list_head *pos;
@@ -547,12 +574,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,/* Lookup if the token matches something in the config */ tok_len =token_len_without_separator (tok->buf, tok->len); - *conf = &default_conf_info; +if (conf) + *conf = &default_conf_info;list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (token_matches_item (tok->buf, item, tok_len)) {char *tok_buf =strbuf_detach (tok,NULL ); - *conf = &item->conf; +if (conf) + *conf = &item->conf;strbuf_addstr (tok,token_from_item (item, tok_buf));free (tok_buf);break ;
@@ -562,43 +591,51 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val, -const struct conf_info *conf) +static void add_trailer_item (struct list_head *head,char *tok,char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val; -duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); } +static void add_arg_item (struct list_head *arg_head,char *tok,char *val, +const struct conf_info *conf) +{ +struct arg_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf); +list_add_tail (&new->list, arg_head); +} +static void process_command_line_args (struct list_head *arg_head,struct string_list *trailers) {struct string_list_item *tr; -struct trailer_item *item; +struct arg_item *item;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ;const struct conf_info *conf;struct list_head *pos; -/* Add a trailer item for each configured trailer with a command */ +/* Add an arg item for each configured trailer with a command */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (item->conf.command) -add_trailer_item (arg_head, -xstrdup (token_from_item (item,NULL )), -xstrdup ("" ), - &item->conf); +add_arg_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); } -/* Add a trailer item for each trailer on the command line */ +/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) {if (!parse_trailer (&tok, &val, &conf, tr->string)) -add_trailer_item (arg_head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +add_arg_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -721,7 +758,6 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; -const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -740,11 +776,10 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val, &conf, lines[i]->buf)) + !parse_trailer (&tok, &val,NULL , lines[i]->buf))add_trailer_item (head,strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +strbuf_detach (&val,NULL )); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v2 5/6 trailer: allow non-trailers in trailer block
In reply to Jonathan TanCurrently, interpret-trailers requires all lines of a trailer block to be trailers (or comments) - if not it would not identify that block as a trailer block, and thus create its own trailer block, inserting a blank line. For example:
echo -e "\na: b\nnot trailer" | git interpret-trailers --trailer "c: d"
would result in:
a: b
not trailer
c: d
Relax the definition of a trailer block to only require 1 trailer, so that trailers can be directly added to such blocks, resulting in:
a: b
not trailer
c: d
This allows arbitrary lines to be included in trailer blocks, like those in [1], and still allow interpret-trailers to be used.
This change also makes comments in the trailer block be treated as any other non-trailer line, preserving them in the output of interpret-trailers.
--- Documentation/git-interpret-trailers.txt | 3 +- t/t7513-interpret-trailers.sh | 35 +++++++++++++++ trailer.c | 77 ++++++++++++++++++++++---------- 3 files changed, 90 insertions(+), 25 deletions(-)
Documentation/git-interpret-trailers.txt +2 -1
@@ -48,7 +48,8 @@ with only spaces at the end of the commit message part, one blank line will be added before the new trailer. Existing trailers are extracted from the input message by looking for -a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), where the group is preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three
t/t7513-interpret-trailers.sh +35 -0
@@ -126,6 +126,37 @@ test_expect_success 'with multiline title in the message' ' test_cmp expected actual ' +test_expect_success 'with non-trailer lines mixed with trailer lines' ' + cat >patch <<-\EOF && + + this: is a trailer + this is not a trailer + EOF + cat >expected <<-\EOF && + + this: is a trailer + this is not a trailer + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines only' ' + cat >patch <<-\EOF && + + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF &&
@@ -257,6 +288,8 @@ test_expect_success 'with message that has comments' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
@@ -286,6 +319,8 @@ test_expect_success 'with message that has an old style conflict block' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
trailer.c +53 -24
152 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v2 6/6 trailer: support values folded to multiple lines
In reply to Jonathan TanCurrently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
--- Documentation/git-interpret-trailers.txt | 7 +- t/t7513-interpret-trailers.sh | 139 +++++++++++++++++++++++++++++++ trailer.c | 22 +++-- 3 files changed, 160 insertions(+), 8 deletions(-)
Documentation/git-interpret-trailers.txt +4 -3
@@ -57,11 +57,12 @@ minus signs start the patch part of the message. When reading trailers, there can be whitespaces before and after the token, the separator and the value. There can also be whitespaces -inside the token and the value. +inside the token and the value. The value may be split over multiple lines with +each subsequent line starting with whitespace, like the "folding" in RFC 822. 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. +rules for RFC 822 headers. For example they do not follow +the encoding rules and probably many other rules. OPTIONS -------
t/t7513-interpret-trailers.sh +139 -0
145 lines not on this page · raw message
trailer.c +17 -5
@@ -248,7 +248,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *argif (arg_tok->value && arg_tok->value[0 ]) { arg = arg_tok->value; }else { -if (in_tok && in_tok->value) +if (in_tok) arg =xstrdup (in_tok->value);else arg =xstrdup ("" );
@@ -622,12 +622,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val) +static struct trailer_item *add_trailer_item (struct list_head *head,char *tok, +char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val;list_add_tail (&new->list, head); +return new; } static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
@@ -781,6 +783,7 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; +struct trailer_item *last =NULL ;/* Get the line count */ while (lines[count])
@@ -798,16 +801,25 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { +if (last &&isspace (lines[i]->buf[0 ])) { +struct strbuf sb =STRBUF_INIT ; +strbuf_addf (&sb,"%s\n%s" , last->value, lines[i]->buf); +strbuf_strip_suffix (&sb,"\n" ); +free (last->value); + last->value =strbuf_detach (&sb,NULL ); +continue ; + }if (!parse_trailer (&tok, &val,NULL , lines[i]->buf,0 )) -add_trailer_item (head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); + last =add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ));else {strbuf_addbuf (&val, lines[i]);strbuf_strip_suffix (&val,"\n" );add_trailer_item (head,NULL ,strbuf_detach (&val,NULL )); + last =NULL ; } }
Signature · 1 line
2.8.0.rc3.226.g39d4020
v3 cover allow non-trailers and multiple-line trailers
In reply to Jonathan TanAh, I knew I forgot something. These are exactly the same as v2, except that these are signed off.
Jonathan Tan (6):
trailer: improve const correctness
trailer: use list.h for doubly-linked list
trailer: streamline trailer item create and add
trailer: make args have their own struct
trailer: allow non-trailers in trailer block
trailer: support values folded to multiple lines
Documentation/git-interpret-trailers.txt | 10 +- t/t7513-interpret-trailers.sh | 174 ++++++++++ trailer.c | 538 +++++++++++++++---------------- 3 files changed, 444 insertions(+), 278 deletions(-)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v3 1/6 trailer: improve const correctness
Change "const char *" to "char *" in struct trailer_item and in the return value of apply_command (since those strings are owned strings).
Change "struct conf_info *" to "const struct conf_info *" (since that struct is not modified).
--- trailer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
trailer.c +7 -7
@@ -27,8 +27,8 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *previous;struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value;struct conf_info conf; };
@@ -95,8 +95,8 @@ 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->token); +free (item->value);free (item); }
@@ -215,13 +215,13 @@ static struct trailer_item *remove_first(struct trailer_item **first)return item; } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)
@@ -425,7 +425,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name)
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v3 1/6] trailer: improve const correctness
Quoted 7 lines · Jonathan Tan
Change "const char *" to "char *" in struct
trailer_itemand in the return value ofapply_command(since those strings are owned strings).Change "struct
conf_info*" to "const structconf_info*" (since that struct is not modified).Signed-off-by Jonathan Tan <[email protected]>
Reviewed-by Stefan Beller <[email protected]>
Quoted 58 lines
--- trailer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)trailer.c +7
-8@@ -27,8 +27,8 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *previous;struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value;struct conf_info conf; };@@ -95,8 +95,8 @@ 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->token); +free (item->value);free (item); }@@ -215,13 +215,13 @@ static struct trailer_item *remove_first(struct trailer_item **first)return item; } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)@@ -425,7 +425,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name) --2.8.0.rc3.226.g39d4020
PATCH v3 2/6 trailer: use list.h for doubly-linked list
In reply to Jonathan TanReplace the existing handwritten implementation of a doubly-linked list in trailer.c with the functions and macros from list.h. This significantly simplifies the code.
--- trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
trailer.c +91 -167
@@ -4,6 +4,7 @@#include "commit.h" #include "tempfile.h" #include "trailer.h" +#include "list.h" /* * Copyright (c) 2013, 2014 Christian Couder <[email protected]> */
@@ -25,19 +26,24 @@ struct conf_info {static struct conf_info default_conf_info;struct trailer_item { -struct trailer_item *previous; -struct trailer_item *next; +struct list_head list;char *token;char *value;struct conf_info conf; }; -static struct trailer_item *first_conf_item; +LIST_HEAD (conf_head);static char *separators =":" ;#define TRAILER_ARG_STRING "$ARG" +/* Iterate over the elements of the list. */ +#define list_for_each_dir (pos, head, is_reverse) \ + for (pos = is_reverse ? (head)->prev : (head)->next; \ + pos != (head); \ + pos = is_reverse ? pos->prev : pos->next) +static int after_or_end (enum action_where where) {return (where ==WHERE_AFTER ) || (where ==WHERE_END );
@@ -120,101 +126,49 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)fprintf (outfile,"%s%c %s\n" , tok, separators[0 ], val); } -static void print_all (FILE *outfile,struct trailer_item *first,int trim_empty) +static void print_all (FILE *outfile,struct list_head *head,int trim_empty) { +struct list_head *pos;struct trailer_item *item; -for (item = first; item; item = item->next) { +list_for_each (pos, head) { + item = list_entry(pos, struct trailer_item, list);if (!trim_empty ||strlen (item->value) >0 )print_tok_val (outfile, item->token, item->value); } } -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 add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok, -struct trailer_item **first, -struct trailer_item **last) -{ -if (after_or_end (arg_tok->conf.where)) { - arg_tok->next = on_tok->next; - on_tok->next = arg_tok; - arg_tok->previous = on_tok; -if (arg_tok->next) - arg_tok->next->previous = arg_tok; -update_last (last); - }else { - arg_tok->previous = on_tok->previous; - on_tok->previous = arg_tok; - arg_tok->next = on_tok; -if (arg_tok->previous) - arg_tok->previous->next = arg_tok; -update_first (first); - } +struct trailer_item *arg_tok) +{ +if (after_or_end (arg_tok->conf.where)) +list_add (&arg_tok->list, &on_tok->list); +else +list_add_tail (&arg_tok->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok,struct trailer_item *arg_tok, -int check_all) +int check_all, +struct list_head *head) {enum action_where where = arg_tok->conf.where; +struct list_head *next_head;do { -if (!in_tok) -return 1 ;if (same_trailer (in_tok, arg_tok))return 0 ;/* * if we want to add a trailer after another one, * we have to check those before this one */ - in_tok =after_or_end (where) ? in_tok->previous : in_tok->next; + next_head =after_or_end (where) ? in_tok->list.prev + : in_tok->list.next; +if (next_head == head) +break ; + in_tok = list_entry(next_head, struct trailer_item, list); }while (check_all);return 1 ; } -static void remove_from_list (struct trailer_item *item, -struct trailer_item **first, -struct trailer_item **last) -{ -struct trailer_item *next = item->next; -struct trailer_item *previous = item->previous; - -if (next) { - item->next->previous = previous; - item->next =NULL ; - }else if (last) - *last = previous; - -if (previous) { - item->previous->next = next; - item->previous =NULL ; - }else if (first) - *first = 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 char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;
@@ -266,8 +220,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_itemstatic void apply_arg_if_exists (struct trailer_item *in_tok,struct trailer_item *arg_tok,struct trailer_item *on_tok, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING :
@@ -275,40 +228,34 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -remove_from_list (in_tok, in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok); +list_del (&in_tok->list);free_trailer_item (in_tok);break ;case EXISTS_ADD :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT :apply_item_command (in_tok, arg_tok); -if (check_if_different (in_tok, arg_tok,1 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (in_tok, arg_tok,1 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok); -if (check_if_different (on_tok, arg_tok,0 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (on_tok, arg_tok,0 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ; } } -static void apply_arg_if_missing (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static void apply_arg_if_missing (struct list_head *head,struct trailer_item *arg_tok) { -struct trailer_item **in_tok;enum action_where where;switch (arg_tok->conf.if_missing) {
@@ -317,68 +264,60 @@ static void apply_arg_if_missing(struct trailer_item **in_tok_first,break ;case MISSING_ADD : where = arg_tok->conf.where; - in_tok =after_or_end (where) ? in_tok_last : in_tok_first;apply_item_command (NULL , arg_tok); -if (*in_tok) { -add_arg_to_input_list (*in_tok, arg_tok, - in_tok_first, in_tok_last); - }else { - *in_tok_first = arg_tok; - *in_tok_last = arg_tok; - } -break ; +if (after_or_end (where)) +list_add_tail (&arg_tok->list, head); +else +list_add (&arg_tok->list, head); } } -static int find_same_and_apply_arg (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static int find_same_and_apply_arg (struct list_head *head,struct trailer_item *arg_tok) { +struct list_head *pos;struct trailer_item *in_tok;struct trailer_item *on_tok; -struct trailer_item *following_tok;enum action_where where = arg_tok->conf.where;int middle = (where ==WHERE_AFTER ) || (where ==WHERE_BEFORE );int backwards =after_or_end (where); -struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first; +struct trailer_item *start_tok; -for (in_tok = start_tok; in_tok; in_tok = following_tok) { - following_tok = backwards ? in_tok->previous : in_tok->next; +if (list_empty (head)) +return 0 ; + + start_tok =list_entry (backwards ? head->prev : head->next, + struct trailer_item, + list); + +list_for_each_dir (pos, head, backwards) { + in_tok = list_entry(pos, struct trailer_item, list);if (!same_token (in_tok, arg_tok))continue ; on_tok = middle ? in_tok : start_tok; -apply_arg_if_exists (in_tok, arg_tok, on_tok, - in_tok_first, in_tok_last); +apply_arg_if_exists (in_tok, arg_tok, on_tok, head);return 1 ; }return 0 ; } -static void process_trailers_lists (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item **arg_tok_first) +static void process_trailers_lists (struct list_head *head, +struct list_head *arg_head) { +struct list_head *pos, *p;struct trailer_item *arg_tok; -struct trailer_item *next_arg; - -if (!*arg_tok_first) -return ; -for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { +list_for_each_safe (pos, p, arg_head) {int applied =0 ; + arg_tok = list_entry(pos, struct trailer_item, list); - next_arg = arg_tok->next; -remove_from_list (arg_tok, arg_tok_first,NULL ); +list_del (pos); - applied =find_same_and_apply_arg (in_tok_first, - in_tok_last, - arg_tok); + applied =find_same_and_apply_arg (head, arg_tok);if (!applied) -apply_arg_if_missing (in_tok_first, - in_tok_last, - arg_tok); +apply_arg_if_missing (head, arg_tok); } }
@@ -438,13 +377,12 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)static struct trailer_item *get_conf_item (const char *name) { +struct list_head *pos;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) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (!strcasecmp (item->conf.name, name))return item; }
@@ -454,12 +392,7 @@ static struct trailer_item *get_conf_item(const char *name)duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); -if (!previous) - first_conf_item = item; -else { - previous->next = item; - item->previous = previous; - } +list_add_tail (&item->list, &conf_head);return item; }
@@ -633,6 +566,7 @@ static struct trailer_item *create_trailer_item(const char *string)struct strbuf val =STRBUF_INIT ;struct trailer_item *item;int tok_len; +struct list_head *pos;if (parse_trailer (&tok, &val, string))return NULL ;
@@ -640,7 +574,8 @@ static struct trailer_item *create_trailer_item(const char *string) tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (token_matches_item (tok.buf, item, tok_len)) return new_trailer_item(item, strbuf_detach(&tok, NULL),
@@ -652,44 +587,34 @@ static struct trailer_item *create_trailer_item(const char *string) strbuf_detach(&val, NULL)); } -static void add_trailer_item (struct trailer_item **first, -struct trailer_item **last, -struct trailer_item *new) +static void add_trailer_item (struct list_head *head,struct trailer_item *new) {if (!new)return ; -if (!*last) { - *first = new; - *last = new; - }else { - (*last)->next = new; - new->previous = *last; - *last = new; - } +list_add_tail (&new->list, head); } -static struct trailer_item *process_command_line_args (struct string_list *trailers) +static void process_command_line_args (struct list_head *arg_head, +struct string_list *trailers) { -struct trailer_item *arg_tok_first =NULL ; -struct trailer_item *arg_tok_last =NULL ;struct string_list_item *tr;struct trailer_item *item; +struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (item->conf.command) {struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_first, &arg_tok_last, new); +add_trailer_item (arg_head, new); } }/* Add a trailer item for each trailer on the command line */ 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_trailer_item (arg_head, new); } - -return arg_tok_first; }static struct strbuf **read_input_file (const char *file)
@@ -805,8 +730,7 @@ static void print_lines(FILE *outfile, struct strbuf **lines, int start, int endstatic int process_input_file (FILE *outfile,struct strbuf **lines, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {int count =0 ;int patch_start, trailer_start, trailer_end, i;
@@ -829,18 +753,19 @@ static int process_input_file(FILE *outfile,for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char) {struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_first, in_tok_last, new); +add_trailer_item (head, new); } }return trailer_end; } -static void free_all (struct trailer_item **first) +static void free_all (struct list_head *head) { -while (*first) { -struct trailer_item *item =remove_first (first); -free_trailer_item (item); +struct list_head *pos, *p; +list_for_each_safe (pos, p, head) { +list_del (pos); +free_trailer_item (list_entry(pos ,struct trailer_item ,list )); } }
@@ -877,9 +802,8 @@ static FILE *create_in_place_tempfile(const char *file)void process_trailers (const char *file,int in_place,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; +LIST_HEAD (head); +LIST_HEAD (arg_head);struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
@@ -894,15 +818,15 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str outfile =create_in_place_tempfile (file);/* Print the lines before the trailers */ - trailer_end =process_input_file (outfile, lines, &in_tok_first, &in_tok_last); + trailer_end =process_input_file (outfile, lines, &head); - arg_tok_first =process_command_line_args (trailers); +process_command_line_args (&arg_head, trailers); -process_trailers_lists (&in_tok_first, &in_tok_last, &arg_tok_first); +process_trailers_lists (&head, &arg_head); -print_all (outfile, in_tok_first, trim_empty); +print_all (outfile, &head, trim_empty); -free_all (&in_tok_first); +free_all (&head);/* Print the lines after the trailers as is */ print_lines (outfile, lines, trailer_end,INT_MAX );
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v3 3/6 trailer: streamline trailer item create and add
In reply to Jonathan TanCurrently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.
--- trailer.c | 130 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 70 deletions(-)
trailer.c +60 -70
@@ -500,10 +500,31 @@ 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) +static const char *token_from_item (struct trailer_item *item,char *tok) +{ +if (item->conf.key) +return item->conf.key; +if (tok) +return tok; +return item->conf.name; +} + +static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +{ +if (!strncasecmp (tok, item->conf.name, tok_len)) +return 1 ; +return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; +} + +static int parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer) {size_t len;struct strbuf seps =STRBUF_INIT ; +struct trailer_item *item; +int tok_len; +struct list_head *pos; +strbuf_addstr (&seps, separators);strbuf_addch (&seps,'=' ); len =strcspn (trailer, seps.buf);
@@ -523,74 +544,31 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trastrbuf_addstr (tok, trailer);strbuf_trim (tok); } -return 0 ; -} - -static const char *token_from_item (struct trailer_item *item,char *tok) -{ -if (item->conf.key) -return item->conf.key; -if (tok) -return tok; -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 ? val :xstrdup ("" ); - -if (conf_item) { -duplicate_conf (&new->conf, &conf_item->conf); - new->token =xstrdup (token_from_item (conf_item, tok)); -free (tok); - }else { -duplicate_conf (&new->conf, &default_conf_info); - new->token = tok; - } - -return new; -} - -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) -{ -if (!strncasecmp (tok, item->conf.name, tok_len)) -return 1 ; -return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_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_len; -struct list_head *pos; - -if (parse_trailer (&tok, &val, string)) -return NULL ; - - tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ + tok_len =token_len_without_separator (tok->buf, tok->len); + *conf = &default_conf_info;list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (token_matches_item (tok.buf, item, tok_len)) -return new_trailer_item (item, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +if (token_matches_item (tok->buf, item, tok_len)) { +char *tok_buf =strbuf_detach (tok,NULL ); + *conf = &item->conf; +strbuf_addstr (tok,token_from_item (item, tok_buf)); +free (tok_buf); +break ; + } } -return new_trailer_item (NULL , -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +return 0 ; } -static void add_trailer_item (struct list_head *head,struct trailer_item *new) +static void add_trailer_item (struct list_head *head,char *tok,char *val, +const struct conf_info *conf) { -if (!new) -return ; +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); }
@@ -599,21 +577,28 @@ static void process_command_line_args(struct list_head *arg_head, {struct string_list_item *tr;struct trailer_item *item; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (item->conf.command) { -struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (arg_head, new); - } +if (item->conf.command) +add_trailer_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); }/* Add a trailer item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -struct trailer_item *new =create_trailer_item (tr->string); -add_trailer_item (arg_head, new); +if (!parse_trailer (&tok, &val, &conf, tr->string)) +add_trailer_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -734,6 +719,9 @@ static int process_input_file(FILE *outfile, {int count =0 ;int patch_start, trailer_start, trailer_end, i; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -751,10 +739,12 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char) { -struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (head, new); - } +if (lines[i]->buf[0 ] != comment_line_char && + !parse_trailer (&tok, &val, &conf, lines[i]->buf)) +add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v3 3/6] trailer: streamline trailer item create and add
Quoted 6 lines · Jonathan Tan
Currently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.Signed-off-by Jonathan Tan <[email protected]>
PATCH v3 4/6 trailer: make args have their own struct
In reply to Jonathan TanImprove type safety by making arguments (whether from configuration or from the command line) have their own "struct arg_item" type, separate from the "struct trailer_item" type used to represent the trailers in the buffer being manipulated.
This change also prepares "struct trailer_item" to be further differentiated from "struct arg_item" in future patches.
--- trailer.c | 135 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 50 deletions(-)
trailer.c +85 -50
@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;struct conf_info conf; };
@@ -62,7 +68,7 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (struct trailer_item *a,struct trailer_item *b) +static int same_token (struct trailer_item *a,struct arg_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -71,12 +77,12 @@ static int same_token(struct trailer_item *a, struct trailer_item *b)return !strncasecmp (a->token, b->token, min_len); } -static int same_value (struct trailer_item *a,struct trailer_item *b) +static int same_value (struct trailer_item *a,struct arg_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (struct trailer_item *a,struct trailer_item *b) +static int same_trailer (struct trailer_item *a,struct arg_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -98,6 +104,13 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) { +free (item->token); +free (item->value); +free (item); +} + +static void free_arg_item (struct arg_item *item) +{free (item->conf.name);free (item->conf.key);free (item->conf.command);
@@ -137,17 +150,29 @@ static void print_all(FILE *outfile, struct list_head *head, int trim_empty) } } +static struct trailer_item *trailer_from_arg (struct arg_item *arg_tok) +{ +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = arg_tok->token; + new->value = arg_tok->value; + arg_tok->token = arg_tok->value =NULL ; +free_arg_item (arg_tok); +return new; +} +static void add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) { -if (after_or_end (arg_tok->conf.where)) -list_add (&arg_tok->list, &on_tok->list); +int aoe =after_or_end (arg_tok->conf.where); +struct trailer_item *to_add =trailer_from_arg (arg_tok); +if (aoe) +list_add (&to_add->list, &on_tok->list);else -list_add_tail (&arg_tok->list, &on_tok->list); +list_add_tail (&to_add->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,int check_all,struct list_head *head) {
@@ -200,7 +225,7 @@ static char *apply_command(const char *command, const char *arg)return result; } -static void apply_item_command (struct trailer_item *in_tok,struct trailer_item *arg_tok) +static void apply_item_command (struct trailer_item *in_tok,struct arg_item *arg_tok) {if (arg_tok->conf.command) {const char *arg;
@@ -218,13 +243,13 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item }static void apply_arg_if_exists (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,struct trailer_item *on_tok,struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok);
@@ -241,39 +266,41 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,if (check_if_different (in_tok, arg_tok,1 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok);if (check_if_different (on_tok, arg_tok,0 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ; } }static void apply_arg_if_missing (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {enum action_where where; +struct trailer_item *to_add;switch (arg_tok->conf.if_missing) {case MISSING_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case MISSING_ADD : where = arg_tok->conf.where;apply_item_command (NULL , arg_tok); + to_add =trailer_from_arg (arg_tok);if (after_or_end (where)) -list_add_tail (&arg_tok->list, head); +list_add_tail (&to_add->list, head);else -list_add (&arg_tok->list, head); +list_add (&to_add->list, head); } }static int find_same_and_apply_arg (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {struct list_head *pos;struct trailer_item *in_tok;
@@ -306,11 +333,11 @@ static void process_trailers_lists(struct list_head *head, struct list_head *arg_head) {struct list_head *pos, *p; -struct trailer_item *arg_tok; +struct arg_item *arg_tok;list_for_each_safe (pos, p, arg_head) {int applied =0 ; - arg_tok = list_entry(pos, struct trailer_item, list); + arg_tok = list_entry(pos, struct arg_item, list);list_del (pos);
@@ -375,20 +402,20 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src) dst->command =xstrdup (src->command); } -static struct trailer_item *get_conf_item (const char *name) +static struct arg_item *get_conf_item (const char *name) {struct list_head *pos; -struct trailer_item *item; +struct arg_item *item;/* Look up item with same name */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (!strcasecmp (item->conf.name, name))return item; }/* Item does not already exists, create it */ - item =xcalloc (sizeof (struct trailer_item ),1 ); + item =xcalloc (sizeof (*item),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name);
@@ -442,7 +469,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value, vstatic int git_trailer_config (const char *conf_key,const char *value,void *cb) {const char *trailer_item, *variable_name; -struct trailer_item *item; +struct arg_item *item;struct conf_info *conf;char *name =NULL ;enum trailer_info_type type;
@@ -500,7 +527,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)return 0 ; } -static const char *token_from_item (struct trailer_item *item,char *tok) +static const char *token_from_item (struct arg_item *item,char *tok) {if (item->conf.key)return item->conf.key;
@@ -509,7 +536,7 @@ static const char *token_from_item(struct trailer_item *item, char *tok)return item->conf.name; } -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +static int token_matches_item (const char *tok,struct arg_item *item,int tok_len) {if (!strncasecmp (tok, item->conf.name, tok_len))return 1 ;
@@ -521,7 +548,7 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, {size_t len;struct strbuf seps =STRBUF_INIT ; -struct trailer_item *item; +struct arg_item *item;int tok_len;struct list_head *pos;
@@ -547,12 +574,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,/* Lookup if the token matches something in the config */ tok_len =token_len_without_separator (tok->buf, tok->len); - *conf = &default_conf_info; +if (conf) + *conf = &default_conf_info;list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (token_matches_item (tok->buf, item, tok_len)) {char *tok_buf =strbuf_detach (tok,NULL ); - *conf = &item->conf; +if (conf) + *conf = &item->conf;strbuf_addstr (tok,token_from_item (item, tok_buf));free (tok_buf);break ;
@@ -562,43 +591,51 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val, -const struct conf_info *conf) +static void add_trailer_item (struct list_head *head,char *tok,char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val; -duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); } +static void add_arg_item (struct list_head *arg_head,char *tok,char *val, +const struct conf_info *conf) +{ +struct arg_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf); +list_add_tail (&new->list, arg_head); +} +static void process_command_line_args (struct list_head *arg_head,struct string_list *trailers) {struct string_list_item *tr; -struct trailer_item *item; +struct arg_item *item;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ;const struct conf_info *conf;struct list_head *pos; -/* Add a trailer item for each configured trailer with a command */ +/* Add an arg item for each configured trailer with a command */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (item->conf.command) -add_trailer_item (arg_head, -xstrdup (token_from_item (item,NULL )), -xstrdup ("" ), - &item->conf); +add_arg_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); } -/* Add a trailer item for each trailer on the command line */ +/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) {if (!parse_trailer (&tok, &val, &conf, tr->string)) -add_trailer_item (arg_head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +add_arg_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -721,7 +758,6 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; -const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -740,11 +776,10 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val, &conf, lines[i]->buf)) + !parse_trailer (&tok, &val,NULL , lines[i]->buf))add_trailer_item (head,strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +strbuf_detach (&val,NULL )); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v3 4/6] trailer: make args have their own struct
Quoted 19 lines · Jonathan Tan
Improve type safety by making arguments (whether from configuration or from the command line) have their own "struct
arg_item" type, separate from the "structtrailer_item" type used to represent the trailers in the buffer being manipulated.This change also prepares "struct
trailer_item" to be further differentiated from "structarg_item" in future patches.Signed-off-by Jonathan Tan <[email protected]>--- trailer.c | 135 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 50 deletions(-)trailer.c +0
-0@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;
trailer.c struct trailer_item { · line 39
Quoted 10 lines
trailer.c +6
-0char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;struct conf_info conf; };
(Unrelated side note:) When first seeing this diff, I assumed the diff heuristic is going wild, because it doesn't add a full struct. But on a second closer look, I realize this is the only correct diff, because we do not account for moved lines from one struct to the other.
trailer.c static void print_all(FILE *outfile, struct list_head *head, int trim_empty) · line 168
Quoted 9 lines
trailer.c +4
-3static void add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) { -if (after_or_end (arg_tok->conf.where)) -list_add (&arg_tok->list, &on_tok->list); +int aoe =after_or_end (arg_tok->conf.where); +struct trailer_item *to_add =trailer_from_arg (arg_tok); +if (aoe)
The use of an extra variable here is more readable than my imagined version of inlining to_add into the list_add calls just to save aoe.
Looks good to me.
Stefan
Re: [PATCH v3 4/6] trailer: make args have their own struct
Quoted 10 lines · Stefan Beller
Quoted 10 lines
trailer.c +6
-0@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;
Quoted 8 lines
trailer.c struct trailer_item { · line 39
Quoted 2 lines
trailer.c +0
-0struct conf_info conf; };(Unrelated side note:) When first seeing this diff, I assumed the diff heuristic is going wild, because it doesn't add a full struct. But on a second closer look, I realize this is the only correct diff, because we do not account for moved lines from one struct to the other.
It probably is not "the only" correct diff, as you actually could shift it the other way by one to three lines. I am not sure which one among four possible diff is the easiest to grok, though. Both the above (picking the highest possible position) and the below (the other extreme) are probably easier to read than anything in between.
struct trailer_item {
+ struct list_head list;
+ char *token;
+ char *value;
+};
+
+struct arg_item {
struct list_head list;
char *token;
char *value;
struct conf_info conf;
};
PATCH v3 5/6 trailer: allow non-trailers in trailer block
In reply to Jonathan TanCurrently, interpret-trailers requires all lines of a trailer block to be trailers (or comments) - if not it would not identify that block as a trailer block, and thus create its own trailer block, inserting a blank line. For example:
echo -e "\na: b\nnot trailer" | git interpret-trailers --trailer "c: d"
would result in:
a: b
not trailer
c: d
Relax the definition of a trailer block to only require 1 trailer, so that trailers can be directly added to such blocks, resulting in:
a: b
not trailer
c: d
This allows arbitrary lines to be included in trailer blocks, like those in [1], and still allow interpret-trailers to be used.
This change also makes comments in the trailer block be treated as any other non-trailer line, preserving them in the output of interpret-trailers.
--- Documentation/git-interpret-trailers.txt | 3 +- t/t7513-interpret-trailers.sh | 35 +++++++++++++++ trailer.c | 77 ++++++++++++++++++++++---------- 3 files changed, 90 insertions(+), 25 deletions(-)
Documentation/git-interpret-trailers.txt +2 -1
@@ -48,7 +48,8 @@ with only spaces at the end of the commit message part, one blank line will be added before the new trailer. Existing trailers are extracted from the input message by looking for -a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), where the group is preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three
t/t7513-interpret-trailers.sh +35 -0
@@ -126,6 +126,37 @@ test_expect_success 'with multiline title in the message' ' test_cmp expected actual ' +test_expect_success 'with non-trailer lines mixed with trailer lines' ' + cat >patch <<-\EOF && + + this: is a trailer + this is not a trailer + EOF + cat >expected <<-\EOF && + + this: is a trailer + this is not a trailer + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines only' ' + cat >patch <<-\EOF && + + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF &&
@@ -257,6 +288,8 @@ test_expect_success 'with message that has comments' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
@@ -286,6 +319,8 @@ test_expect_success 'with message that has an old style conflict block' ' cat >>expected <<-\EOF && # comment + # other comment + # yet another comment Reviewed-by: Johan Cc: Peff # last comment
trailer.c +53 -24
152 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Documentation/git-interpret-trailers.txt with only spaces at the end of the commit message part, one blank line · line 52
Quoted 5 lines · Jonathan Tan
Existing trailers are extracted from the input message by looking for
Documentation/git-interpret-trailers.txt +2
-1-a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), where
Please see commit 578e6021c0819d7be1179e05e7ce0e6fdb2a01b7 for an example where I think this is overly broad.
Another made up example, that I'd want to feed
in commit -s eventually:
--8<--
demonstrate colons in Java
First paragraph is not interesting.
Also if using another Language such as Java, where I point out Class::function() to be problematic
--8<--
This would lack the white space between the last paragraph and the Sign off ?
So for this patch I am mostly concerned about false positives hidden in actual text.
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Quoted 10 lines · Stefan Beller
Documentation/git-interpret-trailers.txt with only spaces at the end of the commit message part, one blank line · line 52
Quoted 5 lines · Jonathan Tan
Existing trailers are extracted from the input message by looking for
Documentation/git-interpret-trailers.txt +2
-1-a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), wherePlease see commit
578e6021c0819d7be1179e05e7ce0e6fdb2a01b7for an example where I think this is overly broad.
Hmph. That's a merge.
Merge branch 'rs/c-auto-resets-attributes'
When "%C(auto)" appears at the very beginning of the pretty format string, it did not need to issue the reset sequence, but it did.
* rs/c-auto-resets-attributes:
pretty: avoid adding reset for %C(auto) if output is empty
And neither of the two colon containing line remotely resembles how a typical RFC-822 header is formatted. So that may serve as a hint to how we can tighten it without introducing false negative.
Quoted 12 lines
Another made up example, that I'd want to feed
in commit -s eventually:--8<--
demonstrate colons in JavaFirst paragraph is not interesting.
Also if using another Language such as Java, where I point out
Class::function()to be problematic
--8<--
Quoted 5 lines
This would lack the white space between the last paragraph and the Sign off ?
So for this patch I am mostly concerned about false positives hidden in actual text.
Yes.
These are exactly why I mentioned "if certian number or percentage" in my earlier suggestion.
I think in practice, "A paragraph with at least one Signed-off-by:
line, and has no more than 3/4 of the (logical) lines that do not resemble how a typical RFC-822 header is formatted" or something along that line would give us a reasonable safety.
Your Java example will fail the criteria in two ways, so we'd be safe ;-)
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Quoted 20 lines · Junio C Hamano
Quoted 10 lines · Stefan Beller
Documentation/git-interpret-trailers.txt with only spaces at the end of the commit message part, one blank line · line 52
Quoted 5 lines · Jonathan Tan
Existing trailers are extracted from the input message by looking for
Documentation/git-interpret-trailers.txt +2
-1-a group of one or more lines that contain a colon (by default), where +a group of one or more lines in which at least one line contains a +colon (by default), wherePlease see commit
578e6021c0819d7be1179e05e7ce0e6fdb2a01b7for an example where I think this is overly broad.Hmph. That's a merge.
Merge branch 'rs/c-auto-resets-attributes'
When "%C(auto)" appears at the very beginning of the pretty format string, it did not need to issue the reset sequence, but it did.
Quoted 6 lines
* rs/c-auto-resets-attributes:
pretty: avoid adding reset for %C(auto) if output is emptyAnd neither of the two colon containing line remotely resembles how a typical RFC-822 header is formatted. So that may serve as a hint to how we can tighten it without introducing false negative.
The only "offending" character is the space (according to RFC 822), but that sounds like a good rule to have.
Quoted 20 lines
Quoted 12 lines
Another made up example, that I'd want to feed
in commit -s eventually:--8<--
demonstrate colons in JavaFirst paragraph is not interesting.
Also if using another Language such as Java, where I point out
Class::function()to be problematic
--8<--Quoted 5 lines
This would lack the white space between the last paragraph and the Sign off ?
So for this patch I am mostly concerned about false positives hidden in actual text.
Yes.
Quoted 7 lines
These are exactly why I mentioned "if certian number or percentage" in my earlier suggestion.
I think in practice, "A paragraph with at least one Signed-off-by:
line, and has no more than 3/4 of the (logical) lines that do not resemble how a typical RFC-822 header is formatted" or something along that line would give us a reasonable safety.
I think that "Signed-off-by:" is not guaranteed to be present. Defining a trailer line as "a line starting with a token, then optional whitespace, then separator", maybe the following rule:
- at least one trailer line generated by Git ("(cherry picked by" or "Signed-off-by") or configured in the "trailer" section in gitconfig
OR
- at least 3/4 logical trailer lines (I'm wondering if this should be 100% trailer lines)
?
Quoted 2 lines
Your Java example will fail the criteria in two ways, so we'd be safe ;-)
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Quoted 9 lines · Jonathan Tan
Quoted 6 lines
* rs/c-auto-resets-attributes:
pretty: avoid adding reset for %C(auto) if output is emptyAnd neither of the two colon containing line remotely resembles how a typical RFC-822 header is formatted. So that may serve as a hint to how we can tighten it without introducing false negative.
The only "offending" character is the space (according to RFC 822), but that sounds like a good rule to have.
I suspect that we should be willing to deviate from the letter of RFC to reject misidentification. I saw things like
Thanks to: Jonathan Tan <[email protected]>
in the wild (notice the SP between Thanks and to), for example. Rejecting leading whitespace as a line that does *not* start the header (hence its colon does not count) may be a good compromise.
Quoted 2 lines
I think that "Signed-off-by:" is not guaranteed to be present.
But do we really care about that case where there is no S-o-b:? I personally do not think so.
Quoted 7 lines
Defining a trailer line as "a line starting with a token, then optional whitespace, then separator", maybe the following rule:
- at least one trailer line generated by Git ("(cherry picked by" or "Signed-off-by") or configured in the "trailer" section in gitconfig OR
- at least 3/4 logical trailer lines (I'm wondering if this should be 100% trailer lines)
I'd strongly suggest turning that OR to AND. We will not safely be able to write a commit log message that describes how S-o-b lines are handled in its last paragraph otherwise.
I do not care too deeply about 3/4, but I meant to allow 75% cruft but no more than that, and the fact that the threashold is set at way more than 50% is important. IOW, if you have
Ordinary log message here...
S-o-b: A U Thor <[email protected]>
[a short description that is typically a single liner in the real world use pattern we saw in the world, but could overflow to become multi line cruft]
S-o-b: R E Layer <[email protected]>
"last paragraph" is 5 lines long, among which 60% are cruft that is below the 75% threshold, and "am -s" can still add the S-o-b of the committer at the end of that existing last paragraph. Making it too strict would raise the false negative ratio.
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Quoted 15 lines · Junio C Hamano
Quoted 9 lines · Jonathan Tan
Quoted 6 lines
* rs/c-auto-resets-attributes:
pretty: avoid adding reset for %C(auto) if output is emptyAnd neither of the two colon containing line remotely resembles how a typical RFC-822 header is formatted. So that may serve as a hint to how we can tighten it without introducing false negative.
The only "offending" character is the space (according to RFC 822), but that sounds like a good rule to have.
I suspect that we should be willing to deviate from the letter of RFC to reject misidentification. I saw things like
Quoted 6 lines
Thanks to: Jonathan Tan <[email protected]>
Signed-off-by A U Thor <[email protected]>in the wild (notice the SP between Thanks and to), for example. Rejecting leading whitespace as a line that does *not* start the header (hence its colon does not count) may be a good compromise.
Good point.
Quoted 5 lines
Quoted 2 lines
I think that "Signed-off-by:" is not guaranteed to be present.
But do we really care about that case where there is no S-o-b:? I personally do not think so.
I think we should - the use case I had in mind when I started this is the Android Git repository, which does not use Signed-off-by. For example, I quoted this commit in an earlier e-mail [1]:
https://android.googlesource.com/platform/frameworks/base/+/4c5281862f750cbc9d7355a07ef1a5545b9b3523
which has the footer:
Test: readelf --dyn-sym app_process32 and check that bsd_signal is
exported
readelf --dyn-sym app_process64 and check that bsd_signal is
not exported
Change-Id: Iec584070b42bc7fa43b114c0f884aff2db5a6858
Quoted 24 lines
Quoted 7 lines
Defining a trailer line as "a line starting with a token, then optional whitespace, then separator", maybe the following rule:
- at least one trailer line generated by Git ("(cherry picked by" or "Signed-off-by") or configured in the "trailer" section in gitconfig OR
- at least 3/4 logical trailer lines (I'm wondering if this should be 100% trailer lines)I'd strongly suggest turning that OR to AND. We will not safely be able to write a commit log message that describes how S-o-b lines are handled in its last paragraph otherwise.
I do not care too deeply about 3/4, but I meant to allow 75% cruft but no more than that, and the fact that the threashold is set at way more than 50% is important. IOW, if you have
Ordinary log message here...
S-o-b: A U Thor <[email protected]>
[a short description that is typically a single liner in the real world use pattern we saw in the world, but could overflow to become multi line cruft]
S-o-b: R E Layer <[email protected]>
Quoted 4 lines
"last paragraph" is 5 lines long, among which 60% are cruft that is below the 75% threshold, and "am -s" can still add the S-o-b of the committer at the end of that existing last paragraph. Making it too strict would raise the false negative ratio.
Ah, sorry, I misread your original suggestion.
Would this work then:
- at least one trailer line generated by Git ("(cherry picked by" or "Signed-off-by: ") or configured in the "trailer" section in git config AND at least 25% logical trailer lines
OR
- 100% logical trailer lines
The first part is your original suggestion except that I think that it is more consistent to allow other trailer lines as well (but I do not feel too strongly about this). The second part would satisfy the Android Git use case above, and also retain existing behavior when "Signed-off-by" (for example) is added to an existing footer that does not contain "Signed-off-by" yet.
What do you think?
[1] Message ID <[email protected]>
Re: [PATCH v3 5/6] trailer: allow non-trailers in trailer block
Quoted 6 lines · Jonathan Tan
Would this work then:
- at least one trailer line generated by Git ("(cherry picked by" or "Signed-off-by: ") or configured in the "trailer" section in git config AND at least 25% logical trailer lines
OR
- 100% logical trailer lines
Sure.
At that point, I doubt that the latter "100% logical trailer" makes much difference, though, because at least one of them is likely to be in the former set, or the users can easily make it so by throwing what they use like "Bug: ", "Test: " and "Change-ID: " in the "configured in the trailer section" category. FWIW, I do not think we mind terribly to tweak the definition of "generated by Git" class to "commonly used in Git managed projects" to include "Change-ID:" and friends.
PATCH v3 6/6 trailer: support values folded to multiple lines
In reply to Jonathan TanCurrently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
--- Documentation/git-interpret-trailers.txt | 7 +- t/t7513-interpret-trailers.sh | 139 +++++++++++++++++++++++++++++++ trailer.c | 22 +++-- 3 files changed, 160 insertions(+), 8 deletions(-)
Documentation/git-interpret-trailers.txt +4 -3
@@ -57,11 +57,12 @@ minus signs start the patch part of the message. When reading trailers, there can be whitespaces before and after the token, the separator and the value. There can also be whitespaces -inside the token and the value. +inside the token and the value. The value may be split over multiple lines with +each subsequent line starting with whitespace, like the "folding" in RFC 822. 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. +rules for RFC 822 headers. For example they do not follow +the encoding rules and probably many other rules. OPTIONS -------
t/t7513-interpret-trailers.sh +139 -0
145 lines not on this page · raw message
trailer.c +17 -5
@@ -248,7 +248,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct arg_item *argif (arg_tok->value && arg_tok->value[0 ]) { arg = arg_tok->value; }else { -if (in_tok && in_tok->value) +if (in_tok) arg =xstrdup (in_tok->value);else arg =xstrdup ("" );
@@ -622,12 +622,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val) +static struct trailer_item *add_trailer_item (struct list_head *head,char *tok, +char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val;list_add_tail (&new->list, head); +return new; } static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
@@ -781,6 +783,7 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; +struct trailer_item *last =NULL ;/* Get the line count */ while (lines[count])
@@ -798,16 +801,25 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { +if (last &&isspace (lines[i]->buf[0 ])) { +struct strbuf sb =STRBUF_INIT ; +strbuf_addf (&sb,"%s\n%s" , last->value, lines[i]->buf); +strbuf_strip_suffix (&sb,"\n" ); +free (last->value); + last->value =strbuf_detach (&sb,NULL ); +continue ; + }if (!parse_trailer (&tok, &val,NULL , lines[i]->buf,0 )) -add_trailer_item (head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); + last =add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ));else {strbuf_addbuf (&val, lines[i]);strbuf_strip_suffix (&val,"\n" );add_trailer_item (head,NULL ,strbuf_detach (&val,NULL )); + last =NULL ; } }
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v3 6/6] trailer: support values folded to multiple lines
Quoted 6 lines · Jonathan Tan
Currently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
Quoted 7 lines
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
Signed-off-by Jonathan Tan <[email protected]>---
Looks good,
Thanks,
Stefan
v4 cover allow non-trailers and multiple-line trailers
In reply to Jonathan TanMain changes are:
- implemented the previously discussed trailer block recognizing rule (recognized trailer + 25% trailers or 100% trailers)
- forbidding leading whitespace in trailers to avoid false positives
Once the recognized trailer + 25% trailers rule is implemented, implementing the 100% trailer rule gives us backwards compatibility and is only a few lines of code, so I included it.
Summary of changes from v3:
2/6->2/8:
- squashed Ramsay Jones's "static" patch
new->5/8:
- new patch
5/6->6/8:
- new trailer block recognizing rule
- reverted to the existing behavior of ignoring comments, since the number of trailers and non-trailers in the trailer block now matters more
new->7/8:
- new patch
6/6->8/8:
- updated trailer block recognizing code, since the continuation lines must not be counted if they follow a trailer line
Jonathan Tan (8):
trailer: improve const correctness
trailer: use list.h for doubly-linked list
trailer: streamline trailer item create and add
trailer: make args have their own struct
trailer: clarify failure modes in parse_trailer
trailer: allow non-trailers in trailer block
trailer: forbid leading whitespace in trailers
trailer: support values folded to multiple lines
Documentation/git-interpret-trailers.txt | 14 +- t/t7513-interpret-trailers.sh | 299 +++++++++++++++ trailer.c | 619 +++++++++++++++++-------------- 3 files changed, 651 insertions(+), 281 deletions(-)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 1/8 trailer: improve const correctness
Change "const char *" to "char *" in struct trailer_item and in the return value of apply_command (since those strings are owned strings).
Change "struct conf_info *" to "const struct conf_info *" (since that struct is not modified).
--- trailer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
trailer.c +7 -7
@@ -27,8 +27,8 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *previous;struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value;struct conf_info conf; };
@@ -95,8 +95,8 @@ 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->token); +free (item->value);free (item); }
@@ -215,13 +215,13 @@ static struct trailer_item *remove_first(struct trailer_item **first)return item; } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)
@@ -425,7 +425,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 2/8 trailer: use list.h for doubly-linked list
In reply to Jonathan TanReplace the existing handwritten implementation of a doubly-linked list in trailer.c with the functions and macros from list.h. This significantly simplifies the code.
--- trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
trailer.c +91 -167
@@ -4,6 +4,7 @@#include "commit.h" #include "tempfile.h" #include "trailer.h" +#include "list.h" /* * Copyright (c) 2013, 2014 Christian Couder <[email protected]> */
@@ -25,19 +26,24 @@ struct conf_info {static struct conf_info default_conf_info;struct trailer_item { -struct trailer_item *previous; -struct trailer_item *next; +struct list_head list;char *token;char *value;struct conf_info conf; }; -static struct trailer_item *first_conf_item; +static LIST_HEAD (conf_head);static char *separators =":" ;#define TRAILER_ARG_STRING "$ARG" +/* Iterate over the elements of the list. */ +#define list_for_each_dir (pos, head, is_reverse) \ + for (pos = is_reverse ? (head)->prev : (head)->next; \ + pos != (head); \ + pos = is_reverse ? pos->prev : pos->next) +static int after_or_end (enum action_where where) {return (where ==WHERE_AFTER ) || (where ==WHERE_END );
@@ -120,101 +126,49 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)fprintf (outfile,"%s%c %s\n" , tok, separators[0 ], val); } -static void print_all (FILE *outfile,struct trailer_item *first,int trim_empty) +static void print_all (FILE *outfile,struct list_head *head,int trim_empty) { +struct list_head *pos;struct trailer_item *item; -for (item = first; item; item = item->next) { +list_for_each (pos, head) { + item = list_entry(pos, struct trailer_item, list);if (!trim_empty ||strlen (item->value) >0 )print_tok_val (outfile, item->token, item->value); } } -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 add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok, -struct trailer_item **first, -struct trailer_item **last) -{ -if (after_or_end (arg_tok->conf.where)) { - arg_tok->next = on_tok->next; - on_tok->next = arg_tok; - arg_tok->previous = on_tok; -if (arg_tok->next) - arg_tok->next->previous = arg_tok; -update_last (last); - }else { - arg_tok->previous = on_tok->previous; - on_tok->previous = arg_tok; - arg_tok->next = on_tok; -if (arg_tok->previous) - arg_tok->previous->next = arg_tok; -update_first (first); - } +struct trailer_item *arg_tok) +{ +if (after_or_end (arg_tok->conf.where)) +list_add (&arg_tok->list, &on_tok->list); +else +list_add_tail (&arg_tok->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok,struct trailer_item *arg_tok, -int check_all) +int check_all, +struct list_head *head) {enum action_where where = arg_tok->conf.where; +struct list_head *next_head;do { -if (!in_tok) -return 1 ;if (same_trailer (in_tok, arg_tok))return 0 ;/* * if we want to add a trailer after another one, * we have to check those before this one */ - in_tok =after_or_end (where) ? in_tok->previous : in_tok->next; + next_head =after_or_end (where) ? in_tok->list.prev + : in_tok->list.next; +if (next_head == head) +break ; + in_tok = list_entry(next_head, struct trailer_item, list); }while (check_all);return 1 ; } -static void remove_from_list (struct trailer_item *item, -struct trailer_item **first, -struct trailer_item **last) -{ -struct trailer_item *next = item->next; -struct trailer_item *previous = item->previous; - -if (next) { - item->next->previous = previous; - item->next =NULL ; - }else if (last) - *last = previous; - -if (previous) { - item->previous->next = next; - item->previous =NULL ; - }else if (first) - *first = 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 char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;
@@ -266,8 +220,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_itemstatic void apply_arg_if_exists (struct trailer_item *in_tok,struct trailer_item *arg_tok,struct trailer_item *on_tok, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING :
@@ -275,40 +228,34 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -remove_from_list (in_tok, in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok); +list_del (&in_tok->list);free_trailer_item (in_tok);break ;case EXISTS_ADD :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT :apply_item_command (in_tok, arg_tok); -if (check_if_different (in_tok, arg_tok,1 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (in_tok, arg_tok,1 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok); -if (check_if_different (on_tok, arg_tok,0 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (on_tok, arg_tok,0 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ; } } -static void apply_arg_if_missing (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static void apply_arg_if_missing (struct list_head *head,struct trailer_item *arg_tok) { -struct trailer_item **in_tok;enum action_where where;switch (arg_tok->conf.if_missing) {
@@ -317,68 +264,60 @@ static void apply_arg_if_missing(struct trailer_item **in_tok_first,break ;case MISSING_ADD : where = arg_tok->conf.where; - in_tok =after_or_end (where) ? in_tok_last : in_tok_first;apply_item_command (NULL , arg_tok); -if (*in_tok) { -add_arg_to_input_list (*in_tok, arg_tok, - in_tok_first, in_tok_last); - }else { - *in_tok_first = arg_tok; - *in_tok_last = arg_tok; - } -break ; +if (after_or_end (where)) +list_add_tail (&arg_tok->list, head); +else +list_add (&arg_tok->list, head); } } -static int find_same_and_apply_arg (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static int find_same_and_apply_arg (struct list_head *head,struct trailer_item *arg_tok) { +struct list_head *pos;struct trailer_item *in_tok;struct trailer_item *on_tok; -struct trailer_item *following_tok;enum action_where where = arg_tok->conf.where;int middle = (where ==WHERE_AFTER ) || (where ==WHERE_BEFORE );int backwards =after_or_end (where); -struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first; +struct trailer_item *start_tok; -for (in_tok = start_tok; in_tok; in_tok = following_tok) { - following_tok = backwards ? in_tok->previous : in_tok->next; +if (list_empty (head)) +return 0 ; + + start_tok =list_entry (backwards ? head->prev : head->next, + struct trailer_item, + list); + +list_for_each_dir (pos, head, backwards) { + in_tok = list_entry(pos, struct trailer_item, list);if (!same_token (in_tok, arg_tok))continue ; on_tok = middle ? in_tok : start_tok; -apply_arg_if_exists (in_tok, arg_tok, on_tok, - in_tok_first, in_tok_last); +apply_arg_if_exists (in_tok, arg_tok, on_tok, head);return 1 ; }return 0 ; } -static void process_trailers_lists (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item **arg_tok_first) +static void process_trailers_lists (struct list_head *head, +struct list_head *arg_head) { +struct list_head *pos, *p;struct trailer_item *arg_tok; -struct trailer_item *next_arg; - -if (!*arg_tok_first) -return ; -for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { +list_for_each_safe (pos, p, arg_head) {int applied =0 ; + arg_tok = list_entry(pos, struct trailer_item, list); - next_arg = arg_tok->next; -remove_from_list (arg_tok, arg_tok_first,NULL ); +list_del (pos); - applied =find_same_and_apply_arg (in_tok_first, - in_tok_last, - arg_tok); + applied =find_same_and_apply_arg (head, arg_tok);if (!applied) -apply_arg_if_missing (in_tok_first, - in_tok_last, - arg_tok); +apply_arg_if_missing (head, arg_tok); } }
@@ -438,13 +377,12 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)static struct trailer_item *get_conf_item (const char *name) { +struct list_head *pos;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) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (!strcasecmp (item->conf.name, name))return item; }
@@ -454,12 +392,7 @@ static struct trailer_item *get_conf_item(const char *name)duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); -if (!previous) - first_conf_item = item; -else { - previous->next = item; - item->previous = previous; - } +list_add_tail (&item->list, &conf_head);return item; }
@@ -633,6 +566,7 @@ static struct trailer_item *create_trailer_item(const char *string)struct strbuf val =STRBUF_INIT ;struct trailer_item *item;int tok_len; +struct list_head *pos;if (parse_trailer (&tok, &val, string))return NULL ;
@@ -640,7 +574,8 @@ static struct trailer_item *create_trailer_item(const char *string) tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (token_matches_item (tok.buf, item, tok_len)) return new_trailer_item(item, strbuf_detach(&tok, NULL),
@@ -652,44 +587,34 @@ static struct trailer_item *create_trailer_item(const char *string) strbuf_detach(&val, NULL)); } -static void add_trailer_item (struct trailer_item **first, -struct trailer_item **last, -struct trailer_item *new) +static void add_trailer_item (struct list_head *head,struct trailer_item *new) {if (!new)return ; -if (!*last) { - *first = new; - *last = new; - }else { - (*last)->next = new; - new->previous = *last; - *last = new; - } +list_add_tail (&new->list, head); } -static struct trailer_item *process_command_line_args (struct string_list *trailers) +static void process_command_line_args (struct list_head *arg_head, +struct string_list *trailers) { -struct trailer_item *arg_tok_first =NULL ; -struct trailer_item *arg_tok_last =NULL ;struct string_list_item *tr;struct trailer_item *item; +struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (item->conf.command) {struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_first, &arg_tok_last, new); +add_trailer_item (arg_head, new); } }/* Add a trailer item for each trailer on the command line */ 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_trailer_item (arg_head, new); } - -return arg_tok_first; }static struct strbuf **read_input_file (const char *file)
@@ -805,8 +730,7 @@ static void print_lines(FILE *outfile, struct strbuf **lines, int start, int endstatic int process_input_file (FILE *outfile,struct strbuf **lines, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {int count =0 ;int patch_start, trailer_start, trailer_end, i;
@@ -829,18 +753,19 @@ static int process_input_file(FILE *outfile,for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char) {struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_first, in_tok_last, new); +add_trailer_item (head, new); } }return trailer_end; } -static void free_all (struct trailer_item **first) +static void free_all (struct list_head *head) { -while (*first) { -struct trailer_item *item =remove_first (first); -free_trailer_item (item); +struct list_head *pos, *p; +list_for_each_safe (pos, p, head) { +list_del (pos); +free_trailer_item (list_entry(pos ,struct trailer_item ,list )); } }
@@ -877,9 +802,8 @@ static FILE *create_in_place_tempfile(const char *file)void process_trailers (const char *file,int in_place,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; +LIST_HEAD (head); +LIST_HEAD (arg_head);struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
@@ -894,15 +818,15 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str outfile =create_in_place_tempfile (file);/* Print the lines before the trailers */ - trailer_end =process_input_file (outfile, lines, &in_tok_first, &in_tok_last); + trailer_end =process_input_file (outfile, lines, &head); - arg_tok_first =process_command_line_args (trailers); +process_command_line_args (&arg_head, trailers); -process_trailers_lists (&in_tok_first, &in_tok_last, &arg_tok_first); +process_trailers_lists (&head, &arg_head); -print_all (outfile, in_tok_first, trim_empty); +print_all (outfile, &head, trim_empty); -free_all (&in_tok_first); +free_all (&head);/* Print the lines after the trailers as is */ print_lines (outfile, lines, trailer_end,INT_MAX );
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 3/8 trailer: streamline trailer item create and add
In reply to Jonathan TanCurrently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.
--- trailer.c | 130 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 70 deletions(-)
trailer.c +60 -70
@@ -500,10 +500,31 @@ 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) +static const char *token_from_item (struct trailer_item *item,char *tok) +{ +if (item->conf.key) +return item->conf.key; +if (tok) +return tok; +return item->conf.name; +} + +static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +{ +if (!strncasecmp (tok, item->conf.name, tok_len)) +return 1 ; +return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; +} + +static int parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer) {size_t len;struct strbuf seps =STRBUF_INIT ; +struct trailer_item *item; +int tok_len; +struct list_head *pos; +strbuf_addstr (&seps, separators);strbuf_addch (&seps,'=' ); len =strcspn (trailer, seps.buf);
@@ -523,74 +544,31 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trastrbuf_addstr (tok, trailer);strbuf_trim (tok); } -return 0 ; -} - -static const char *token_from_item (struct trailer_item *item,char *tok) -{ -if (item->conf.key) -return item->conf.key; -if (tok) -return tok; -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 ? val :xstrdup ("" ); - -if (conf_item) { -duplicate_conf (&new->conf, &conf_item->conf); - new->token =xstrdup (token_from_item (conf_item, tok)); -free (tok); - }else { -duplicate_conf (&new->conf, &default_conf_info); - new->token = tok; - } - -return new; -} - -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) -{ -if (!strncasecmp (tok, item->conf.name, tok_len)) -return 1 ; -return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_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_len; -struct list_head *pos; - -if (parse_trailer (&tok, &val, string)) -return NULL ; - - tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ + tok_len =token_len_without_separator (tok->buf, tok->len); + *conf = &default_conf_info;list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (token_matches_item (tok.buf, item, tok_len)) -return new_trailer_item (item, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +if (token_matches_item (tok->buf, item, tok_len)) { +char *tok_buf =strbuf_detach (tok,NULL ); + *conf = &item->conf; +strbuf_addstr (tok,token_from_item (item, tok_buf)); +free (tok_buf); +break ; + } } -return new_trailer_item (NULL , -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +return 0 ; } -static void add_trailer_item (struct list_head *head,struct trailer_item *new) +static void add_trailer_item (struct list_head *head,char *tok,char *val, +const struct conf_info *conf) { -if (!new) -return ; +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); }
@@ -599,21 +577,28 @@ static void process_command_line_args(struct list_head *arg_head, {struct string_list_item *tr;struct trailer_item *item; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (item->conf.command) { -struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (arg_head, new); - } +if (item->conf.command) +add_trailer_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); }/* Add a trailer item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -struct trailer_item *new =create_trailer_item (tr->string); -add_trailer_item (arg_head, new); +if (!parse_trailer (&tok, &val, &conf, tr->string)) +add_trailer_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -734,6 +719,9 @@ static int process_input_file(FILE *outfile, {int count =0 ;int patch_start, trailer_start, trailer_end, i; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -751,10 +739,12 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char) { -struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (head, new); - } +if (lines[i]->buf[0 ] != comment_line_char && + !parse_trailer (&tok, &val, &conf, lines[i]->buf)) +add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 4/8 trailer: make args have their own struct
In reply to Jonathan TanImprove type safety by making arguments (whether from configuration or from the command line) have their own "struct arg_item" type, separate from the "struct trailer_item" type used to represent the trailers in the buffer being manipulated.
This change also prepares "struct trailer_item" to be further differentiated from "struct arg_item" in future patches.
--- trailer.c | 135 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 50 deletions(-)
trailer.c +85 -50
@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;struct conf_info conf; };
@@ -62,7 +68,7 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (struct trailer_item *a,struct trailer_item *b) +static int same_token (struct trailer_item *a,struct arg_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -71,12 +77,12 @@ static int same_token(struct trailer_item *a, struct trailer_item *b)return !strncasecmp (a->token, b->token, min_len); } -static int same_value (struct trailer_item *a,struct trailer_item *b) +static int same_value (struct trailer_item *a,struct arg_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (struct trailer_item *a,struct trailer_item *b) +static int same_trailer (struct trailer_item *a,struct arg_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -98,6 +104,13 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) { +free (item->token); +free (item->value); +free (item); +} + +static void free_arg_item (struct arg_item *item) +{free (item->conf.name);free (item->conf.key);free (item->conf.command);
@@ -137,17 +150,29 @@ static void print_all(FILE *outfile, struct list_head *head, int trim_empty) } } +static struct trailer_item *trailer_from_arg (struct arg_item *arg_tok) +{ +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = arg_tok->token; + new->value = arg_tok->value; + arg_tok->token = arg_tok->value =NULL ; +free_arg_item (arg_tok); +return new; +} +static void add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) { -if (after_or_end (arg_tok->conf.where)) -list_add (&arg_tok->list, &on_tok->list); +int aoe =after_or_end (arg_tok->conf.where); +struct trailer_item *to_add =trailer_from_arg (arg_tok); +if (aoe) +list_add (&to_add->list, &on_tok->list);else -list_add_tail (&arg_tok->list, &on_tok->list); +list_add_tail (&to_add->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,int check_all,struct list_head *head) {
@@ -200,7 +225,7 @@ static char *apply_command(const char *command, const char *arg)return result; } -static void apply_item_command (struct trailer_item *in_tok,struct trailer_item *arg_tok) +static void apply_item_command (struct trailer_item *in_tok,struct arg_item *arg_tok) {if (arg_tok->conf.command) {const char *arg;
@@ -218,13 +243,13 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item }static void apply_arg_if_exists (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,struct trailer_item *on_tok,struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok);
@@ -241,39 +266,41 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,if (check_if_different (in_tok, arg_tok,1 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok);if (check_if_different (on_tok, arg_tok,0 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ; } }static void apply_arg_if_missing (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {enum action_where where; +struct trailer_item *to_add;switch (arg_tok->conf.if_missing) {case MISSING_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case MISSING_ADD : where = arg_tok->conf.where;apply_item_command (NULL , arg_tok); + to_add =trailer_from_arg (arg_tok);if (after_or_end (where)) -list_add_tail (&arg_tok->list, head); +list_add_tail (&to_add->list, head);else -list_add (&arg_tok->list, head); +list_add (&to_add->list, head); } }static int find_same_and_apply_arg (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {struct list_head *pos;struct trailer_item *in_tok;
@@ -306,11 +333,11 @@ static void process_trailers_lists(struct list_head *head, struct list_head *arg_head) {struct list_head *pos, *p; -struct trailer_item *arg_tok; +struct arg_item *arg_tok;list_for_each_safe (pos, p, arg_head) {int applied =0 ; - arg_tok = list_entry(pos, struct trailer_item, list); + arg_tok = list_entry(pos, struct arg_item, list);list_del (pos);
@@ -375,20 +402,20 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src) dst->command =xstrdup (src->command); } -static struct trailer_item *get_conf_item (const char *name) +static struct arg_item *get_conf_item (const char *name) {struct list_head *pos; -struct trailer_item *item; +struct arg_item *item;/* Look up item with same name */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (!strcasecmp (item->conf.name, name))return item; }/* Item does not already exists, create it */ - item =xcalloc (sizeof (struct trailer_item ),1 ); + item =xcalloc (sizeof (*item),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name);
@@ -442,7 +469,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value, vstatic int git_trailer_config (const char *conf_key,const char *value,void *cb) {const char *trailer_item, *variable_name; -struct trailer_item *item; +struct arg_item *item;struct conf_info *conf;char *name =NULL ;enum trailer_info_type type;
@@ -500,7 +527,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)return 0 ; } -static const char *token_from_item (struct trailer_item *item,char *tok) +static const char *token_from_item (struct arg_item *item,char *tok) {if (item->conf.key)return item->conf.key;
@@ -509,7 +536,7 @@ static const char *token_from_item(struct trailer_item *item, char *tok)return item->conf.name; } -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +static int token_matches_item (const char *tok,struct arg_item *item,int tok_len) {if (!strncasecmp (tok, item->conf.name, tok_len))return 1 ;
@@ -521,7 +548,7 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, {size_t len;struct strbuf seps =STRBUF_INIT ; -struct trailer_item *item; +struct arg_item *item;int tok_len;struct list_head *pos;
@@ -547,12 +574,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,/* Lookup if the token matches something in the config */ tok_len =token_len_without_separator (tok->buf, tok->len); - *conf = &default_conf_info; +if (conf) + *conf = &default_conf_info;list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (token_matches_item (tok->buf, item, tok_len)) {char *tok_buf =strbuf_detach (tok,NULL ); - *conf = &item->conf; +if (conf) + *conf = &item->conf;strbuf_addstr (tok,token_from_item (item, tok_buf));free (tok_buf);break ;
@@ -562,43 +591,51 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val, -const struct conf_info *conf) +static void add_trailer_item (struct list_head *head,char *tok,char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val; -duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); } +static void add_arg_item (struct list_head *arg_head,char *tok,char *val, +const struct conf_info *conf) +{ +struct arg_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf); +list_add_tail (&new->list, arg_head); +} +static void process_command_line_args (struct list_head *arg_head,struct string_list *trailers) {struct string_list_item *tr; -struct trailer_item *item; +struct arg_item *item;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ;const struct conf_info *conf;struct list_head *pos; -/* Add a trailer item for each configured trailer with a command */ +/* Add an arg item for each configured trailer with a command */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (item->conf.command) -add_trailer_item (arg_head, -xstrdup (token_from_item (item,NULL )), -xstrdup ("" ), - &item->conf); +add_arg_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); } -/* Add a trailer item for each trailer on the command line */ +/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) {if (!parse_trailer (&tok, &val, &conf, tr->string)) -add_trailer_item (arg_head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +add_arg_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -721,7 +758,6 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; -const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -740,11 +776,10 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val, &conf, lines[i]->buf)) + !parse_trailer (&tok, &val,NULL , lines[i]->buf))add_trailer_item (head,strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +strbuf_detach (&val,NULL )); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 5/8 trailer: clarify failure modes in parse_trailer
In reply to Jonathan TanThe parse_trailer function has a few modes of operation, all depending on whether the separator is present in its input, and if yes, the separator's position. Some of these modes are failure modes, and these failure modes are handled differently depending on whether the trailer line was sourced from a file or from a command-line argument.
Extract a function to find the separator, allowing the invokers of parse_trailer to determine how to handle the failure modes instead of making parse_trailer do it.
--- trailer.c | 70 +++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 22 deletions(-)
trailer.c +48 -22
@@ -543,29 +543,40 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_lereturn item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; } -static int parse_trailer (struct strbuf *tok,struct strbuf *val, -const struct conf_info **conf,const char *trailer) +/* +* Return the location of the first separator or '=' in line, or -1 if either a +* newline or the null terminator is reached first. +*/ +static int find_separator (const char *line) +{ +const char *c; +for (c = line; ; c++) { +if (!*c || *c =='\n' ) +return -1 ; +if (*c =='=' ||strchr (separators, *c)) +return c - line; + } +} + +/* +* Obtain the token, value, and conf from the given trailer. +* +* separator_pos must not be 0, since the token cannot be an empty string. +* +* If separator_pos is -1, interpret the whole trailer as a token. +*/ +static void parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer, +int separator_pos) { -size_t len; -struct strbuf seps =STRBUF_INIT ;struct arg_item *item;int tok_len;struct list_head *pos; -strbuf_addstr (&seps, separators); -strbuf_addch (&seps,'=' ); - len =strcspn (trailer, seps.buf); -strbuf_release (&seps); -if (len ==0 ) { -int l =strlen (trailer); -while (l >0 &&isspace (trailer[l -1 ])) - l--; -return error (_ ("empty trailer token in trailer '%.*s'" ), l, trailer); - } -if (len <strlen (trailer)) { -strbuf_add (tok, trailer, len); +if (separator_pos !=-1 ) { +strbuf_add (tok, trailer, separator_pos);strbuf_trim (tok); -strbuf_addstr (val, trailer + len +1 ); +strbuf_addstr (val, trailer + separator_pos +1 );strbuf_trim (val); }else {strbuf_addstr (tok, trailer);
@@ -587,8 +598,6 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,break ; } } - -return 0 ; }static void add_trailer_item (struct list_head *head,char *tok,char *val)
@@ -631,11 +640,22 @@ static void process_command_line_args(struct list_head *arg_head,/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -if (!parse_trailer (&tok, &val, &conf, tr->string)) +int separator_pos =find_separator (tr->string); +if (separator_pos ==0 ) { +struct strbuf sb =STRBUF_INIT ; +strbuf_addstr (&sb, tr->string); +strbuf_trim (&sb); +error (_ ("empty trailer token in trailer '%.*s'" ), + (int ) sb.len, sb.buf); +strbuf_release (&sb); + }else { +parse_trailer (&tok, &val, &conf, tr->string, + separator_pos);add_arg_item (arg_head,strbuf_detach (&tok,NULL ),strbuf_detach (&val,NULL ), conf); + } } }
@@ -775,11 +795,17 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val,NULL , lines[i]->buf)) +int separator_pos; +if (lines[i]->buf[0 ] == comment_line_char) +continue ; + separator_pos =find_separator (lines[i]->buf); +if (separator_pos >=1 ) { +parse_trailer (&tok, &val,NULL , lines[i]->buf, + separator_pos);add_trailer_item (head,strbuf_detach (&tok,NULL ),strbuf_detach (&val,NULL )); + } }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 28 lines · Jonathan Tan
The
parse_trailerfunction has a few modes of operation, all depending on whether the separator is present in its input, and if yes, the separator's position. Some of these modes are failure modes, and these failure modes are handled differently depending on whether the trailer line was sourced from a file or from a command-line argument.Extract a function to find the separator, allowing the invokers of
parse_trailerto determine how to handle the failure modes instead of makingparse_trailerdo it.Signed-off-by Jonathan Tan <[email protected]>--- trailer.c | 70 +++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 22 deletions(-)trailer.c +3
-2@@ -543,29 +543,40 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_lereturn item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; } -static int parse_trailer (struct strbuf *tok,struct strbuf *val, -const struct conf_info **conf,const char *trailer) +/* +* Return the location of the first separator or '=' in line, or -1 if either a +* newline or the null terminator is reached first.
trailer.c static int token_matches_item(const char *tok, struct arg_item *item, int tok_le · line 558
Quoted 10 lines
trailer.c +10
-0+*/ +static int find_separator (const char *line) +{ +const char *c; +for (c = line; ; c++) { +if (!*c || *c =='\n' ) +return -1 ; +if (*c =='=' ||strchr (separators, *c)) +return c - line; + }
I was about to suggest this function can be simplified and maybe even inlined by the use of strspn or strcspn, but I think manual processing of the string is fine, too, as it would not really be shorter.
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 5 lines · Stefan Beller
Quoted 5 lines
trailer.c +5
-0+static int find_separator (const char *line) +{ +const char *c; +for (c = line; ; c++) { +if (!*c || *c =='\n' )
Quoted 8 lines
trailer.c static int token_matches_item(const char *tok, struct arg_item *item, int tok_le · line 558
Quoted 4 lines
trailer.c +4
-0+return -1 ; +if (*c =='=' ||strchr (separators, *c)) +return c - line; + }I was about to suggest this function can be simplified and maybe even inlined by the use of strspn or strcspn, but I think manual processing of the string is fine, too, as it would not really be shorter.
Hmm, I fear that iterating over a line one-byte-at-a-time and running strchr(separators, *c) on it for each byte has a performance implication over running a single call to strcspn(line, separators).
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 16 lines · Junio C Hamano
Quoted 5 lines · Stefan Beller
Quoted 5 lines
trailer.c +5
-0+static int find_separator (const char *line) +{ +const char *c; +for (c = line; ; c++) { +if (!*c || *c =='\n' )Quoted 8 lines
trailer.c static int token_matches_item(const char *tok, struct arg_item *item, int tok_le · line 558
Quoted 4 lines
trailer.c +4
-0+return -1 ; +if (*c =='=' ||strchr (separators, *c)) +return c - line; + }I was about to suggest this function can be simplified and maybe even inlined by the use of strspn or strcspn, but I think manual processing of the string is fine, too, as it would not really be shorter.
Quoted 3 lines
Hmm, I fear that iterating over a line one-byte-at-a-time and running
strchr(separators, *c)on it for each byte has a performance implication over running a single call tostrcspn(line, separators).
If we do that, there is also the necessity of creating a string that combines the separators and '=' (I guess '\n' is not necessary now, since all the lines are null terminated). I'm OK either way.
(We could cache that string, although I would think that if we did that, we might as well write the loop manually, like in this patch.)
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 6 lines · Jonathan Tan
If we do that, there is also the necessity of creating a string that combines the separators and '=' (I guess '\n' is not necessary now, since all the lines are null terminated). I'm OK either way.
(We could cache that string, although I would think that if we did that, we might as well write the loop manually, like in this patch.)
I wonder if there is a legit reason to look for '=' in the first place. "Signed-off-by= Jonathan Tan <[email protected]>" does not look like a valid trailer line to me.
Isn't that a remnant of lazy coding in the original that tried to share a single parser for contents and command line options or something?
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 9 lines · Junio C Hamano
Quoted 6 lines · Jonathan Tan
If we do that, there is also the necessity of creating a string that combines the separators and '=' (I guess '\n' is not necessary now, since all the lines are null terminated). I'm OK either way.
(We could cache that string, although I would think that if we did that, we might as well write the loop manually, like in this patch.)
Quoted 7 lines
I wonder if there is a legit reason to look for '=' in the first place. "Signed-off-by= Jonathan Tan <[email protected]>" does not look like a valid trailer line to me.
Isn't that a remnant of lazy coding in the original that tried to share a single parser for contents and command line options or something?
That is true - I think we can take the allowed separators as an argument (meaning that we can have different behavior for file parsing and command line parsing), and since we already have that string, we can use strcspn. I'll try this out in the next reroll.
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 4 lines · Jonathan Tan
That is true - I think we can take the allowed separators as an argument (meaning that we can have different behavior for file parsing and command line parsing), and since we already have that string, we can use strcspn. I'll try this out in the next reroll.
Sounds good. Thanks.
The following is a tangent that I think this topic should ignore, but we may want to revisit it sometime later.
I think the design of the "separator" mechanism is one of the things we botched in the current system. If I recall correctly, this was introduced to allow people write "Bug# 538" in the trailer section and get it recognised as a valid trailer.
When I say that this was a botched design, I do not mean to say that we should have instead forced projects to adopt "Bug: 538" format. The design is botched because the users' wish to allow "Bug# 538" or "Bug #538" by setting separators to ":#" from the built-in ":" does not mean that they would want "Signed-off-by# me <[email protected]>" to be accepted.
If I were guiding a topic that introduce this feature from scratch today, I would probably suggest a pattern based approach, e.g. a built-in "[-A-Za-z0-9]+:" [*1*] may be the default prefix that is used to recognize the beginning of a trailer, and a user or a project that wants "Bug #538" would be allowed to add an additional pattern, e.g. "Bug *#", that recognises a custom trailer line that is used by the project.
[Footnote]
*1* Or more lenient "[A-Za-z0-9][- A-Za-z0-9]*:".
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 25 lines · Junio C Hamano
Quoted 4 lines · Jonathan Tan
That is true - I think we can take the allowed separators as an argument (meaning that we can have different behavior for file parsing and command line parsing), and since we already have that string, we can use strcspn. I'll try this out in the next reroll.
Sounds good. Thanks.
The following is a tangent that I think this topic should ignore, but we may want to revisit it sometime later.
I think the design of the "separator" mechanism is one of the things we botched in the current system. If I recall correctly, this was introduced to allow people write "Bug# 538" in the trailer section and get it recognised as a valid trailer.
When I say that this was a botched design, I do not mean to say that we should have instead forced projects to adopt "Bug: 538" format. The design is botched because the users' wish to allow "Bug# 538" or "Bug #538" by setting separators to ":#" from the built-in ":" does not mean that they would want "Signed-off-by# me <[email protected]>" to be accepted.
Quoted 7 lines
If I were guiding a topic that introduce this feature from scratch today, I would probably suggest a pattern based approach, e.g. a built-in "[-A-Za-z0-9]+:" [*1*] may be the default prefix that is used to recognize the beginning of a trailer, and a user or a project that wants "Bug #538" would be allowed to add an additional pattern, e.g. "Bug *#", that recognises a custom trailer line that is used by the project.
When we designed the separator mechanism, we had the following discussions:
https://public-inbox.org/git/[email protected]/
https://public-inbox.org/git/[email protected]/
They made me think that you were against too much flexibility, so I removed functionality that allowed to put separators into the ".key" config options, and now you are saying that we botched the thing and that you would like more flexibility of this kind back.
Anyway I think it is still possible to add back such kind of functionality in a backward compatible way for example by adding ".extendedKey" config options.
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
Quoted 12 lines · Christian Couder
Quoted 8 lines · Junio C Hamano
If I were guiding a topic that introduce this feature from scratch today, I would probably suggest a pattern based approach, e.g. a built-in "[-A-Za-z0-9]+:" [*1*] may be the default prefix that is used to recognize the beginning of a trailer, and a user or a project that wants "Bug #538" would be allowed to add an additional pattern, e.g. "Bug *#", that recognises a custom trailer line that is used by the project.
When we designed the separator mechanism, we had the following discussions:
Quoted 7 lines
https://public-inbox.org/git/[email protected]/
https://public-inbox.org/git/[email protected]/They made me think that you were against too much flexibility, so I removed functionality that allowed to put separators into the ".key" config options, and now you are saying that we botched the thing and that you would like more flexibility of this kind back.
Correct. Pay attention to the fact that I said _we_ botched.
If an initial design made by a topic author is crappy, that may be author's botch. Once a topic goes through a review cycle by getting reviewed, rerolled, re-reviewed, ... to the point that those involved accept the result, and we later realize that it was not good, the botch no longer is author's alone. If it is shipped as part of a release, then it is not just the authors and the reviewers but everybody. We collectively stopped at a place that was not ideal and share the blame ;-).
Quoted 3 lines
Anyway I think it is still possible to add back such kind of functionality in a backward compatible way for example by adding ".extendedKey" config options.
Yup, or with trailer.keyPattern that is multi-values, or with any number of alternatives.
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
In reply to Junio C HamanoQuoted 9 lines · Junio C Hamano
Quoted 6 lines · Jonathan Tan
If we do that, there is also the necessity of creating a string that combines the separators and '=' (I guess '\n' is not necessary now, since all the lines are null terminated). I'm OK either way.
(We could cache that string, although I would think that if we did that, we might as well write the loop manually, like in this patch.)
Quoted 7 lines
I wonder if there is a legit reason to look for '=' in the first place. "Signed-off-by= Jonathan Tan <[email protected]>" does not look like a valid trailer line to me.
Isn't that a remnant of lazy coding in the original that tried to share a single parser for contents and command line options or something?
I think the relevant discussion was this one:
Re: [PATCH v4 5/8] trailer: clarify failure modes in parse_trailer
In reply to Jonathan TanQuoted 22 lines · Jonathan Tan
Quoted 17 lines · Junio C Hamano
Quoted 6 lines · Stefan Beller
Quoted 6 lines
trailer.c +6
-0+static int find_separator (const char *line) +{ +const char *c; +for (c = line; ; c++) { +if (!*c || *c =='\n' ) +return -1 ;Quoted 8 lines
trailer.c static int token_matches_item(const char *tok, struct arg_item *item, int tok_le · line 558
Quoted 3 lines
trailer.c +3
-0+if (*c =='=' ||strchr (separators, *c)) +return c - line; + }I was about to suggest this function can be simplified and maybe even inlined by the use of strspn or strcspn, but I think manual processing of the string is fine, too, as it would not really be shorter.
Quoted 3 lines
Hmm, I fear that iterating over a line one-byte-at-a-time and running
strchr(separators, *c)on it for each byte has a performance implication over running a single call tostrcspn(line, separators).
Quoted 6 lines
If we do that, there is also the necessity of creating a string that combines the separators and '=' (I guess '\n' is not necessary now, since all the lines are null terminated). I'm OK either way.
(We could cache that string, although I would think that if we did that, we might as well write the loop manually, like in this patch.)
Actually I guess we could generate the separators_and_equal string whenever we obtain new separators from the config. I'll do this in the next reroll.
PATCH v4 6/8 trailer: allow non-trailers in trailer block
In reply to Jonathan TanCurrently, interpret-trailers requires all lines of a trailer block to be trailers (or comments) - if not it would not identify that block as a trailer block, and thus create its own trailer block, inserting a blank line. For example:
echo -e "\nSigned-off-by: x\nnot trailer" | git interpret-trailers --trailer "c: d"
would result in:
Signed-off-by: x
not trailer
c: d
Relax the definition of a trailer block to require that the trailers (i) are all trailers, or (ii) contain at least one Git-generated trailer and consists of at least 25% trailers.
Signed-off-by: x
not trailer
c: d
(i) is the existing functionality. (ii) allows arbitrary lines to be included in trailer blocks, like those in [1], and still allow interpret-trailers to be used.
--- Documentation/git-interpret-trailers.txt | 5 +- t/t7513-interpret-trailers.sh | 115 +++++++++++++++++++++++++++++++ trailer.c | 89 ++++++++++++++++++++---- 3 files changed, 194 insertions(+), 15 deletions(-)
Documentation/git-interpret-trailers.txt +3 -2
@@ -48,8 +48,9 @@ with only spaces at the end of the commit message part, one blank line will be added before the new trailer. Existing trailers are extracted from the input message by looking for -a group of one or more lines that contain a colon (by default), where -the group is preceded by one or more empty (or whitespace-only) lines. +a group of one or more lines that (i) are all trailers, or (ii) contains at +least one Git-generated trailer and consists of at least 25% trailers. +The group must be preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three minus signs start the patch part of the message.
t/t7513-interpret-trailers.sh +115 -0
121 lines not on this page · raw message
trailer.c +76 -13
142 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 7/8 trailer: forbid leading whitespace in trailers
In reply to Jonathan TanCurrently, interpret-trailers allows leading whitespace in trailer lines. This leads to false positives, especially for quoted lines or bullet lists.
Forbid leading whitespace in trailers.
--- Documentation/git-interpret-trailers.txt | 2 +- t/t7513-interpret-trailers.sh | 15 +++++++++++++++ trailer.c | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-)
Documentation/git-interpret-trailers.txt +1 -1
@@ -55,7 +55,7 @@ The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three minus signs start the patch part of the message. -When reading trailers, there can be whitespaces before and after the +When reading trailers, there can be whitespaces after the token, the separator and the value. There can also be whitespaces inside the token and the value.
t/t7513-interpret-trailers.sh +15 -0
@@ -241,6 +241,21 @@ test_expect_success 'with non-trailer lines only' ' test_cmp expected actual ' +test_expect_success 'line with leading whitespace is not trailer' ' + q_to_tab >patch <<-\EOF && + + Qtoken: value + EOF + q_to_tab >expected <<-\EOF && + + Qtoken: value + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF &&
trailer.c +1 -1
@@ -770,7 +770,7 @@ static int find_trailer_start(struct strbuf **lines, int count) } separator_pos =find_separator (lines[start]->buf); -if (separator_pos >=1 ) { +if (separator_pos >=1 && !isspace (lines[start]->buf[0 ])) {struct list_head *pos; trailer_lines++;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v4 8/8 trailer: support values folded to multiple lines
In reply to Jonathan TanCurrently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
--- Documentation/git-interpret-trailers.txt | 7 +- t/t7513-interpret-trailers.sh | 169 +++++++++++++++++++++++++++++++ trailer.c | 43 ++++++-- 3 files changed, 210 insertions(+), 9 deletions(-)
Documentation/git-interpret-trailers.txt +4 -3
@@ -57,11 +57,12 @@ minus signs start the patch part of the message. When reading trailers, there can be whitespaces after the token, the separator and the value. There can also be whitespaces -inside the token and the value. +inside the token and the value. The value may be split over multiple lines with +each subsequent line starting with whitespace, like the "folding" in RFC 822. 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. +rules for RFC 822 headers. For example they do not follow +the encoding rules and probably many other rules. OPTIONS -------
t/t7513-interpret-trailers.sh +169 -0
175 lines not on this page · raw message
trailer.c +37 -6
111 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
v5 cover allow non-trailers and multiple-line trailers
In reply to Jonathan TanI've updated patch 5/8 to use strcspn and to pass in the list of separators, meaning that we no longer accept '=' in file input (and also updated its commit message accordingly).
We also discussed inlining find_separator, but after looking at the code, I think that it is more convenient if find_separator returns -1 when there is no separator, because of the 3 times it is used (as of 8/8), it is checked twice with '>= 1' (since both "no separator" and "string begins with separator" are handled in the same way - treating them as a non-trailer line). So I have left it as its own function.
No other updates.
Jonathan Tan (8):
trailer: improve const correctness
trailer: use list.h for doubly-linked list
trailer: streamline trailer item create and add
trailer: make args have their own struct
trailer: clarify failure modes in parse_trailer
trailer: allow non-trailers in trailer block
trailer: forbid leading whitespace in trailers
trailer: support values folded to multiple lines
Documentation/git-interpret-trailers.txt | 14 +- t/t7513-interpret-trailers.sh | 299 +++++++++++++++ trailer.c | 620 +++++++++++++++++-------------- 3 files changed, 654 insertions(+), 279 deletions(-)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 1/8 trailer: improve const correctness
Change "const char *" to "char *" in struct trailer_item and in the return value of apply_command (since those strings are owned strings).
Change "struct conf_info *" to "const struct conf_info *" (since that struct is not modified).
--- trailer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
trailer.c +7 -7
@@ -27,8 +27,8 @@ static struct conf_info default_conf_info;struct trailer_item {struct trailer_item *previous;struct trailer_item *next; -const char *token; -const char *value; +char *token; +char *value;struct conf_info conf; };
@@ -95,8 +95,8 @@ 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->token); +free (item->value);free (item); }
@@ -215,13 +215,13 @@ static struct trailer_item *remove_first(struct trailer_item **first)return item; } -static const char *apply_command (const char *command,const char *arg) +static char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;struct strbuf buf =STRBUF_INIT ;struct child_process cp =CHILD_PROCESS_INIT ;const char *argv[] = {NULL ,NULL }; -const char *result; +char *result;strbuf_addstr (&cmd, command);if (arg)
@@ -425,7 +425,7 @@ static int set_if_missing(struct conf_info *item, const char *value)return 0 ; } -static void duplicate_conf (struct conf_info *dst,struct conf_info *src) +static void duplicate_conf (struct conf_info *dst,const struct conf_info *src) { *dst = *src;if (src->name)
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 2/8 trailer: use list.h for doubly-linked list
In reply to Jonathan TanReplace the existing handwritten implementation of a doubly-linked list in trailer.c with the functions and macros from list.h. This significantly simplifies the code.
--- trailer.c | 258 ++++++++++++++++++++++---------------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-)
trailer.c +91 -167
@@ -4,6 +4,7 @@#include "commit.h" #include "tempfile.h" #include "trailer.h" +#include "list.h" /* * Copyright (c) 2013, 2014 Christian Couder <[email protected]> */
@@ -25,19 +26,24 @@ struct conf_info {static struct conf_info default_conf_info;struct trailer_item { -struct trailer_item *previous; -struct trailer_item *next; +struct list_head list;char *token;char *value;struct conf_info conf; }; -static struct trailer_item *first_conf_item; +static LIST_HEAD (conf_head);static char *separators =":" ;#define TRAILER_ARG_STRING "$ARG" +/* Iterate over the elements of the list. */ +#define list_for_each_dir (pos, head, is_reverse) \ + for (pos = is_reverse ? (head)->prev : (head)->next; \ + pos != (head); \ + pos = is_reverse ? pos->prev : pos->next) +static int after_or_end (enum action_where where) {return (where ==WHERE_AFTER ) || (where ==WHERE_END );
@@ -120,101 +126,49 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)fprintf (outfile,"%s%c %s\n" , tok, separators[0 ], val); } -static void print_all (FILE *outfile,struct trailer_item *first,int trim_empty) +static void print_all (FILE *outfile,struct list_head *head,int trim_empty) { +struct list_head *pos;struct trailer_item *item; -for (item = first; item; item = item->next) { +list_for_each (pos, head) { + item = list_entry(pos, struct trailer_item, list);if (!trim_empty ||strlen (item->value) >0 )print_tok_val (outfile, item->token, item->value); } } -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 add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok, -struct trailer_item **first, -struct trailer_item **last) -{ -if (after_or_end (arg_tok->conf.where)) { - arg_tok->next = on_tok->next; - on_tok->next = arg_tok; - arg_tok->previous = on_tok; -if (arg_tok->next) - arg_tok->next->previous = arg_tok; -update_last (last); - }else { - arg_tok->previous = on_tok->previous; - on_tok->previous = arg_tok; - arg_tok->next = on_tok; -if (arg_tok->previous) - arg_tok->previous->next = arg_tok; -update_first (first); - } +struct trailer_item *arg_tok) +{ +if (after_or_end (arg_tok->conf.where)) +list_add (&arg_tok->list, &on_tok->list); +else +list_add_tail (&arg_tok->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok,struct trailer_item *arg_tok, -int check_all) +int check_all, +struct list_head *head) {enum action_where where = arg_tok->conf.where; +struct list_head *next_head;do { -if (!in_tok) -return 1 ;if (same_trailer (in_tok, arg_tok))return 0 ;/* * if we want to add a trailer after another one, * we have to check those before this one */ - in_tok =after_or_end (where) ? in_tok->previous : in_tok->next; + next_head =after_or_end (where) ? in_tok->list.prev + : in_tok->list.next; +if (next_head == head) +break ; + in_tok = list_entry(next_head, struct trailer_item, list); }while (check_all);return 1 ; } -static void remove_from_list (struct trailer_item *item, -struct trailer_item **first, -struct trailer_item **last) -{ -struct trailer_item *next = item->next; -struct trailer_item *previous = item->previous; - -if (next) { - item->next->previous = previous; - item->next =NULL ; - }else if (last) - *last = previous; - -if (previous) { - item->previous->next = next; - item->previous =NULL ; - }else if (first) - *first = 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 char *apply_command (const char *command,const char *arg) {struct strbuf cmd =STRBUF_INIT ;
@@ -266,8 +220,7 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_itemstatic void apply_arg_if_exists (struct trailer_item *in_tok,struct trailer_item *arg_tok,struct trailer_item *on_tok, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING :
@@ -275,40 +228,34 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); -remove_from_list (in_tok, in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok); +list_del (&in_tok->list);free_trailer_item (in_tok);break ;case EXISTS_ADD :apply_item_command (in_tok, arg_tok); -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +add_arg_to_input_list (on_tok, arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT :apply_item_command (in_tok, arg_tok); -if (check_if_different (in_tok, arg_tok,1 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (in_tok, arg_tok,1 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok); -if (check_if_different (on_tok, arg_tok,0 )) -add_arg_to_input_list (on_tok, arg_tok, - in_tok_first, in_tok_last); +if (check_if_different (on_tok, arg_tok,0 , head)) +add_arg_to_input_list (on_tok, arg_tok);else free_trailer_item (arg_tok);break ; } } -static void apply_arg_if_missing (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static void apply_arg_if_missing (struct list_head *head,struct trailer_item *arg_tok) { -struct trailer_item **in_tok;enum action_where where;switch (arg_tok->conf.if_missing) {
@@ -317,68 +264,60 @@ static void apply_arg_if_missing(struct trailer_item **in_tok_first,break ;case MISSING_ADD : where = arg_tok->conf.where; - in_tok =after_or_end (where) ? in_tok_last : in_tok_first;apply_item_command (NULL , arg_tok); -if (*in_tok) { -add_arg_to_input_list (*in_tok, arg_tok, - in_tok_first, in_tok_last); - }else { - *in_tok_first = arg_tok; - *in_tok_last = arg_tok; - } -break ; +if (after_or_end (where)) +list_add_tail (&arg_tok->list, head); +else +list_add (&arg_tok->list, head); } } -static int find_same_and_apply_arg (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, +static int find_same_and_apply_arg (struct list_head *head,struct trailer_item *arg_tok) { +struct list_head *pos;struct trailer_item *in_tok;struct trailer_item *on_tok; -struct trailer_item *following_tok;enum action_where where = arg_tok->conf.where;int middle = (where ==WHERE_AFTER ) || (where ==WHERE_BEFORE );int backwards =after_or_end (where); -struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first; +struct trailer_item *start_tok; -for (in_tok = start_tok; in_tok; in_tok = following_tok) { - following_tok = backwards ? in_tok->previous : in_tok->next; +if (list_empty (head)) +return 0 ; + + start_tok =list_entry (backwards ? head->prev : head->next, + struct trailer_item, + list); + +list_for_each_dir (pos, head, backwards) { + in_tok = list_entry(pos, struct trailer_item, list);if (!same_token (in_tok, arg_tok))continue ; on_tok = middle ? in_tok : start_tok; -apply_arg_if_exists (in_tok, arg_tok, on_tok, - in_tok_first, in_tok_last); +apply_arg_if_exists (in_tok, arg_tok, on_tok, head);return 1 ; }return 0 ; } -static void process_trailers_lists (struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last, -struct trailer_item **arg_tok_first) +static void process_trailers_lists (struct list_head *head, +struct list_head *arg_head) { +struct list_head *pos, *p;struct trailer_item *arg_tok; -struct trailer_item *next_arg; - -if (!*arg_tok_first) -return ; -for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) { +list_for_each_safe (pos, p, arg_head) {int applied =0 ; + arg_tok = list_entry(pos, struct trailer_item, list); - next_arg = arg_tok->next; -remove_from_list (arg_tok, arg_tok_first,NULL ); +list_del (pos); - applied =find_same_and_apply_arg (in_tok_first, - in_tok_last, - arg_tok); + applied =find_same_and_apply_arg (head, arg_tok);if (!applied) -apply_arg_if_missing (in_tok_first, - in_tok_last, - arg_tok); +apply_arg_if_missing (head, arg_tok); } }
@@ -438,13 +377,12 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)static struct trailer_item *get_conf_item (const char *name) { +struct list_head *pos;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) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (!strcasecmp (item->conf.name, name))return item; }
@@ -454,12 +392,7 @@ static struct trailer_item *get_conf_item(const char *name)duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name); -if (!previous) - first_conf_item = item; -else { - previous->next = item; - item->previous = previous; - } +list_add_tail (&item->list, &conf_head);return item; }
@@ -633,6 +566,7 @@ static struct trailer_item *create_trailer_item(const char *string)struct strbuf val =STRBUF_INIT ;struct trailer_item *item;int tok_len; +struct list_head *pos;if (parse_trailer (&tok, &val, string))return NULL ;
@@ -640,7 +574,8 @@ static struct trailer_item *create_trailer_item(const char *string) tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (token_matches_item (tok.buf, item, tok_len)) return new_trailer_item(item, strbuf_detach(&tok, NULL),
@@ -652,44 +587,34 @@ static struct trailer_item *create_trailer_item(const char *string) strbuf_detach(&val, NULL)); } -static void add_trailer_item (struct trailer_item **first, -struct trailer_item **last, -struct trailer_item *new) +static void add_trailer_item (struct list_head *head,struct trailer_item *new) {if (!new)return ; -if (!*last) { - *first = new; - *last = new; - }else { - (*last)->next = new; - new->previous = *last; - *last = new; - } +list_add_tail (&new->list, head); } -static struct trailer_item *process_command_line_args (struct string_list *trailers) +static void process_command_line_args (struct list_head *arg_head, +struct string_list *trailers) { -struct trailer_item *arg_tok_first =NULL ; -struct trailer_item *arg_tok_last =NULL ;struct string_list_item *tr;struct trailer_item *item; +struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ -for (item = first_conf_item; item; item = item->next) { +list_for_each (pos, &conf_head) { + item = list_entry(pos, struct trailer_item, list);if (item->conf.command) {struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (&arg_tok_first, &arg_tok_last, new); +add_trailer_item (arg_head, new); } }/* Add a trailer item for each trailer on the command line */ 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_trailer_item (arg_head, new); } - -return arg_tok_first; }static struct strbuf **read_input_file (const char *file)
@@ -805,8 +730,7 @@ static void print_lines(FILE *outfile, struct strbuf **lines, int start, int endstatic int process_input_file (FILE *outfile,struct strbuf **lines, -struct trailer_item **in_tok_first, -struct trailer_item **in_tok_last) +struct list_head *head) {int count =0 ;int patch_start, trailer_start, trailer_end, i;
@@ -829,18 +753,19 @@ static int process_input_file(FILE *outfile,for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char) {struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (in_tok_first, in_tok_last, new); +add_trailer_item (head, new); } }return trailer_end; } -static void free_all (struct trailer_item **first) +static void free_all (struct list_head *head) { -while (*first) { -struct trailer_item *item =remove_first (first); -free_trailer_item (item); +struct list_head *pos, *p; +list_for_each_safe (pos, p, head) { +list_del (pos); +free_trailer_item (list_entry(pos ,struct trailer_item ,list )); } }
@@ -877,9 +802,8 @@ static FILE *create_in_place_tempfile(const char *file)void process_trailers (const char *file,int in_place,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; +LIST_HEAD (head); +LIST_HEAD (arg_head);struct strbuf **lines;int trailer_end;FILE *outfile = stdout;
@@ -894,15 +818,15 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str outfile =create_in_place_tempfile (file);/* Print the lines before the trailers */ - trailer_end =process_input_file (outfile, lines, &in_tok_first, &in_tok_last); + trailer_end =process_input_file (outfile, lines, &head); - arg_tok_first =process_command_line_args (trailers); +process_command_line_args (&arg_head, trailers); -process_trailers_lists (&in_tok_first, &in_tok_last, &arg_tok_first); +process_trailers_lists (&head, &arg_head); -print_all (outfile, in_tok_first, trim_empty); +print_all (outfile, &head, trim_empty); -free_all (&in_tok_first); +free_all (&head);/* Print the lines after the trailers as is */ print_lines (outfile, lines, trailer_end,INT_MAX );
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 3/8 trailer: streamline trailer item create and add
In reply to Jonathan TanCurrently, creation and addition (to a list) of trailer items are spread across multiple functions. Streamline this by only having 2 functions:
one to parse the user-supplied string, and one to add the parsed information to a list.
--- trailer.c | 130 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 70 deletions(-)
trailer.c +60 -70
@@ -500,10 +500,31 @@ 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) +static const char *token_from_item (struct trailer_item *item,char *tok) +{ +if (item->conf.key) +return item->conf.key; +if (tok) +return tok; +return item->conf.name; +} + +static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +{ +if (!strncasecmp (tok, item->conf.name, tok_len)) +return 1 ; +return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; +} + +static int parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer) {size_t len;struct strbuf seps =STRBUF_INIT ; +struct trailer_item *item; +int tok_len; +struct list_head *pos; +strbuf_addstr (&seps, separators);strbuf_addch (&seps,'=' ); len =strcspn (trailer, seps.buf);
@@ -523,74 +544,31 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trastrbuf_addstr (tok, trailer);strbuf_trim (tok); } -return 0 ; -} - -static const char *token_from_item (struct trailer_item *item,char *tok) -{ -if (item->conf.key) -return item->conf.key; -if (tok) -return tok; -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 ? val :xstrdup ("" ); - -if (conf_item) { -duplicate_conf (&new->conf, &conf_item->conf); - new->token =xstrdup (token_from_item (conf_item, tok)); -free (tok); - }else { -duplicate_conf (&new->conf, &default_conf_info); - new->token = tok; - } - -return new; -} - -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) -{ -if (!strncasecmp (tok, item->conf.name, tok_len)) -return 1 ; -return item->conf.key ? !strncasecmp (tok, item->conf.key, tok_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_len; -struct list_head *pos; - -if (parse_trailer (&tok, &val, string)) -return NULL ; - - tok_len =token_len_without_separator (tok.buf, tok.len);/* Lookup if the token matches something in the config */ + tok_len =token_len_without_separator (tok->buf, tok->len); + *conf = &default_conf_info;list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (token_matches_item (tok.buf, item, tok_len)) -return new_trailer_item (item, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +if (token_matches_item (tok->buf, item, tok_len)) { +char *tok_buf =strbuf_detach (tok,NULL ); + *conf = &item->conf; +strbuf_addstr (tok,token_from_item (item, tok_buf)); +free (tok_buf); +break ; + } } -return new_trailer_item (NULL , -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL )); +return 0 ; } -static void add_trailer_item (struct list_head *head,struct trailer_item *new) +static void add_trailer_item (struct list_head *head,char *tok,char *val, +const struct conf_info *conf) { -if (!new) -return ; +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); }
@@ -599,21 +577,28 @@ static void process_command_line_args(struct list_head *arg_head, {struct string_list_item *tr;struct trailer_item *item; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;struct list_head *pos;/* Add a trailer item for each configured trailer with a command */ list_for_each (pos, &conf_head) { item = list_entry(pos, struct trailer_item, list); -if (item->conf.command) { -struct trailer_item *new =new_trailer_item (item,NULL ,NULL ); -add_trailer_item (arg_head, new); - } +if (item->conf.command) +add_trailer_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); }/* Add a trailer item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -struct trailer_item *new =create_trailer_item (tr->string); -add_trailer_item (arg_head, new); +if (!parse_trailer (&tok, &val, &conf, tr->string)) +add_trailer_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -734,6 +719,9 @@ static int process_input_file(FILE *outfile, {int count =0 ;int patch_start, trailer_start, trailer_end, i; +struct strbuf tok =STRBUF_INIT ; +struct strbuf val =STRBUF_INIT ; +const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -751,10 +739,12 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char) { -struct trailer_item *new =create_trailer_item (lines[i]->buf); -add_trailer_item (head, new); - } +if (lines[i]->buf[0 ] != comment_line_char && + !parse_trailer (&tok, &val, &conf, lines[i]->buf)) +add_trailer_item (head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 4/8 trailer: make args have their own struct
In reply to Jonathan TanImprove type safety by making arguments (whether from configuration or from the command line) have their own "struct arg_item" type, separate from the "struct trailer_item" type used to represent the trailers in the buffer being manipulated.
This change also prepares "struct trailer_item" to be further differentiated from "struct arg_item" in future patches.
--- trailer.c | 135 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 50 deletions(-)
trailer.c +85 -50
@@ -29,6 +29,12 @@ struct trailer_item {struct list_head list;char *token;char *value; +}; + +struct arg_item { +struct list_head list; +char *token; +char *value;struct conf_info conf; };
@@ -62,7 +68,7 @@ static size_t token_len_without_separator(const char *token, size_t len)return len; } -static int same_token (struct trailer_item *a,struct trailer_item *b) +static int same_token (struct trailer_item *a,struct arg_item *b) {size_t a_len =token_len_without_separator (a->token,strlen (a->token));size_t b_len =token_len_without_separator (b->token,strlen (b->token));
@@ -71,12 +77,12 @@ static int same_token(struct trailer_item *a, struct trailer_item *b)return !strncasecmp (a->token, b->token, min_len); } -static int same_value (struct trailer_item *a,struct trailer_item *b) +static int same_value (struct trailer_item *a,struct arg_item *b) {return !strcasecmp (a->value, b->value); } -static int same_trailer (struct trailer_item *a,struct trailer_item *b) +static int same_trailer (struct trailer_item *a,struct arg_item *b) {return same_token (a, b) &&same_value (a, b); }
@@ -98,6 +104,13 @@ static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *static void free_trailer_item (struct trailer_item *item) { +free (item->token); +free (item->value); +free (item); +} + +static void free_arg_item (struct arg_item *item) +{free (item->conf.name);free (item->conf.key);free (item->conf.command);
@@ -137,17 +150,29 @@ static void print_all(FILE *outfile, struct list_head *head, int trim_empty) } } +static struct trailer_item *trailer_from_arg (struct arg_item *arg_tok) +{ +struct trailer_item *new =xcalloc (sizeof (*new),1 ); + new->token = arg_tok->token; + new->value = arg_tok->value; + arg_tok->token = arg_tok->value =NULL ; +free_arg_item (arg_tok); +return new; +} +static void add_arg_to_input_list (struct trailer_item *on_tok, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) { -if (after_or_end (arg_tok->conf.where)) -list_add (&arg_tok->list, &on_tok->list); +int aoe =after_or_end (arg_tok->conf.where); +struct trailer_item *to_add =trailer_from_arg (arg_tok); +if (aoe) +list_add (&to_add->list, &on_tok->list);else -list_add_tail (&arg_tok->list, &on_tok->list); +list_add_tail (&to_add->list, &on_tok->list); }static int check_if_different (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,int check_all,struct list_head *head) {
@@ -200,7 +225,7 @@ static char *apply_command(const char *command, const char *arg)return result; } -static void apply_item_command (struct trailer_item *in_tok,struct trailer_item *arg_tok) +static void apply_item_command (struct trailer_item *in_tok,struct arg_item *arg_tok) {if (arg_tok->conf.command) {const char *arg;
@@ -218,13 +243,13 @@ static void apply_item_command(struct trailer_item *in_tok, struct trailer_item }static void apply_arg_if_exists (struct trailer_item *in_tok, -struct trailer_item *arg_tok, +struct arg_item *arg_tok,struct trailer_item *on_tok,struct list_head *head) {switch (arg_tok->conf.if_exists) {case EXISTS_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_REPLACE :apply_item_command (in_tok, arg_tok);
@@ -241,39 +266,41 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,if (check_if_different (in_tok, arg_tok,1 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR :apply_item_command (in_tok, arg_tok);if (check_if_different (on_tok, arg_tok,0 , head))add_arg_to_input_list (on_tok, arg_tok);else -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ; } }static void apply_arg_if_missing (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {enum action_where where; +struct trailer_item *to_add;switch (arg_tok->conf.if_missing) {case MISSING_DO_NOTHING : -free_trailer_item (arg_tok); +free_arg_item (arg_tok);break ;case MISSING_ADD : where = arg_tok->conf.where;apply_item_command (NULL , arg_tok); + to_add =trailer_from_arg (arg_tok);if (after_or_end (where)) -list_add_tail (&arg_tok->list, head); +list_add_tail (&to_add->list, head);else -list_add (&arg_tok->list, head); +list_add (&to_add->list, head); } }static int find_same_and_apply_arg (struct list_head *head, -struct trailer_item *arg_tok) +struct arg_item *arg_tok) {struct list_head *pos;struct trailer_item *in_tok;
@@ -306,11 +333,11 @@ static void process_trailers_lists(struct list_head *head, struct list_head *arg_head) {struct list_head *pos, *p; -struct trailer_item *arg_tok; +struct arg_item *arg_tok;list_for_each_safe (pos, p, arg_head) {int applied =0 ; - arg_tok = list_entry(pos, struct trailer_item, list); + arg_tok = list_entry(pos, struct arg_item, list);list_del (pos);
@@ -375,20 +402,20 @@ static void duplicate_conf(struct conf_info *dst, const struct conf_info *src) dst->command =xstrdup (src->command); } -static struct trailer_item *get_conf_item (const char *name) +static struct arg_item *get_conf_item (const char *name) {struct list_head *pos; -struct trailer_item *item; +struct arg_item *item;/* Look up item with same name */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (!strcasecmp (item->conf.name, name))return item; }/* Item does not already exists, create it */ - item =xcalloc (sizeof (struct trailer_item ),1 ); + item =xcalloc (sizeof (*item),1 );duplicate_conf (&item->conf, &default_conf_info); item->conf.name =xstrdup (name);
@@ -442,7 +469,7 @@ static int git_trailer_default_config(const char *conf_key, const char *value, vstatic int git_trailer_config (const char *conf_key,const char *value,void *cb) {const char *trailer_item, *variable_name; -struct trailer_item *item; +struct arg_item *item;struct conf_info *conf;char *name =NULL ;enum trailer_info_type type;
@@ -500,7 +527,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)return 0 ; } -static const char *token_from_item (struct trailer_item *item,char *tok) +static const char *token_from_item (struct arg_item *item,char *tok) {if (item->conf.key)return item->conf.key;
@@ -509,7 +536,7 @@ static const char *token_from_item(struct trailer_item *item, char *tok)return item->conf.name; } -static int token_matches_item (const char *tok,struct trailer_item *item,int tok_len) +static int token_matches_item (const char *tok,struct arg_item *item,int tok_len) {if (!strncasecmp (tok, item->conf.name, tok_len))return 1 ;
@@ -521,7 +548,7 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val, {size_t len;struct strbuf seps =STRBUF_INIT ; -struct trailer_item *item; +struct arg_item *item;int tok_len;struct list_head *pos;
@@ -547,12 +574,14 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,/* Lookup if the token matches something in the config */ tok_len =token_len_without_separator (tok->buf, tok->len); - *conf = &default_conf_info; +if (conf) + *conf = &default_conf_info;list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (token_matches_item (tok->buf, item, tok_len)) {char *tok_buf =strbuf_detach (tok,NULL ); - *conf = &item->conf; +if (conf) + *conf = &item->conf;strbuf_addstr (tok,token_from_item (item, tok_buf));free (tok_buf);break ;
@@ -562,43 +591,51 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,return 0 ; } -static void add_trailer_item (struct list_head *head,char *tok,char *val, -const struct conf_info *conf) +static void add_trailer_item (struct list_head *head,char *tok,char *val) {struct trailer_item *new =xcalloc (sizeof (*new),1 ); new->token = tok; new->value = val; -duplicate_conf (&new->conf, conf);list_add_tail (&new->list, head); } +static void add_arg_item (struct list_head *arg_head,char *tok,char *val, +const struct conf_info *conf) +{ +struct arg_item *new =xcalloc (sizeof (*new),1 ); + new->token = tok; + new->value = val; +duplicate_conf (&new->conf, conf); +list_add_tail (&new->list, arg_head); +} +static void process_command_line_args (struct list_head *arg_head,struct string_list *trailers) {struct string_list_item *tr; -struct trailer_item *item; +struct arg_item *item;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ;const struct conf_info *conf;struct list_head *pos; -/* Add a trailer item for each configured trailer with a command */ +/* Add an arg item for each configured trailer with a command */ list_for_each (pos, &conf_head) { - item = list_entry(pos, struct trailer_item, list); + item = list_entry(pos, struct arg_item, list);if (item->conf.command) -add_trailer_item (arg_head, -xstrdup (token_from_item (item,NULL )), -xstrdup ("" ), - &item->conf); +add_arg_item (arg_head, +xstrdup (token_from_item (item,NULL )), +xstrdup ("" ), + &item->conf); } -/* Add a trailer item for each trailer on the command line */ +/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) {if (!parse_trailer (&tok, &val, &conf, tr->string)) -add_trailer_item (arg_head, -strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +add_arg_item (arg_head, +strbuf_detach (&tok,NULL ), +strbuf_detach (&val,NULL ), + conf); } }
@@ -721,7 +758,6 @@ static int process_input_file(FILE *outfile,int patch_start, trailer_start, trailer_end, i;struct strbuf tok =STRBUF_INIT ;struct strbuf val =STRBUF_INIT ; -const struct conf_info *conf;/* Get the line count */ while (lines[count])
@@ -740,11 +776,10 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) {if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val, &conf, lines[i]->buf)) + !parse_trailer (&tok, &val,NULL , lines[i]->buf))add_trailer_item (head,strbuf_detach (&tok,NULL ), -strbuf_detach (&val,NULL ), - conf); +strbuf_detach (&val,NULL )); }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 5/8 trailer: clarify failure modes in parse_trailer
In reply to Jonathan TanThe parse_trailer function has a few modes of operation, all depending on whether the separator is present in its input, and if yes, the separator's position. Some of these modes are failure modes, and these failure modes are handled differently depending on whether the trailer line was sourced from a file or from a command-line argument.
Extract a function to find the separator, allowing the invokers of parse_trailer to determine how to handle the failure modes instead of making parse_trailer do it. In this function, also take in the list of separators, so that we can distinguish between command line arguments (which allow '=' as separator) and file input (which does not allow '=' as separator).
--- trailer.c | 75 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 22 deletions(-)
trailer.c +53 -22
@@ -543,29 +543,37 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_lereturn item->conf.key ? !strncasecmp (tok, item->conf.key, tok_len) :0 ; } -static int parse_trailer (struct strbuf *tok,struct strbuf *val, -const struct conf_info **conf,const char *trailer) +/* +* Return the location of the first separator in line, or -1 if there is no +* separator. +*/ +static int find_separator (const char *line,const char *separators) +{ +int loc =strcspn (line, separators); +if (!line[loc]) +return -1 ; +return loc; +} + +/* +* Obtain the token, value, and conf from the given trailer. +* +* separator_pos must not be 0, since the token cannot be an empty string. +* +* If separator_pos is -1, interpret the whole trailer as a token. +*/ +static void parse_trailer (struct strbuf *tok,struct strbuf *val, +const struct conf_info **conf,const char *trailer, +int separator_pos) { -size_t len; -struct strbuf seps =STRBUF_INIT ;struct arg_item *item;int tok_len;struct list_head *pos; -strbuf_addstr (&seps, separators); -strbuf_addch (&seps,'=' ); - len =strcspn (trailer, seps.buf); -strbuf_release (&seps); -if (len ==0 ) { -int l =strlen (trailer); -while (l >0 &&isspace (trailer[l -1 ])) - l--; -return error (_ ("empty trailer token in trailer '%.*s'" ), l, trailer); - } -if (len <strlen (trailer)) { -strbuf_add (tok, trailer, len); +if (separator_pos !=-1 ) { +strbuf_add (tok, trailer, separator_pos);strbuf_trim (tok); -strbuf_addstr (val, trailer + len +1 ); +strbuf_addstr (val, trailer + separator_pos +1 );strbuf_trim (val); }else {strbuf_addstr (tok, trailer);
@@ -587,8 +595,6 @@ static int parse_trailer(struct strbuf *tok, struct strbuf *val,break ; } } - -return 0 ; }static void add_trailer_item (struct list_head *head,char *tok,char *val)
@@ -619,6 +625,12 @@ static void process_command_line_args(struct list_head *arg_head,const struct conf_info *conf;struct list_head *pos; +/* +* In command-line arguments, '=' is accepted (in addition to the +* separators that are defined). +*/ +char *cl_separators =xstrfmt ("=%s" , separators); +/* Add an arg item for each configured trailer with a command */ list_for_each (pos, &conf_head) { item = list_entry(pos, struct arg_item, list);
@@ -631,12 +643,25 @@ static void process_command_line_args(struct list_head *arg_head,/* Add an arg item for each trailer on the command line */ for_each_string_list_item (tr, trailers) { -if (!parse_trailer (&tok, &val, &conf, tr->string)) +int separator_pos =find_separator (tr->string, cl_separators); +if (separator_pos ==0 ) { +struct strbuf sb =STRBUF_INIT ; +strbuf_addstr (&sb, tr->string); +strbuf_trim (&sb); +error (_ ("empty trailer token in trailer '%.*s'" ), + (int ) sb.len, sb.buf); +strbuf_release (&sb); + }else { +parse_trailer (&tok, &val, &conf, tr->string, + separator_pos);add_arg_item (arg_head,strbuf_detach (&tok,NULL ),strbuf_detach (&val,NULL ), conf); + } } + +free (cl_separators); }static struct strbuf **read_input_file (const char *file)
@@ -775,11 +800,17 @@ static int process_input_file(FILE *outfile,/* Parse trailer lines */ for (i = trailer_start; i < trailer_end; i++) { -if (lines[i]->buf[0 ] != comment_line_char && - !parse_trailer (&tok, &val,NULL , lines[i]->buf)) +int separator_pos; +if (lines[i]->buf[0 ] == comment_line_char) +continue ; + separator_pos =find_separator (lines[i]->buf, separators); +if (separator_pos >=1 ) { +parse_trailer (&tok, &val,NULL , lines[i]->buf, + separator_pos);add_trailer_item (head,strbuf_detach (&tok,NULL ),strbuf_detach (&val,NULL )); + } }return trailer_end;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 6/8 trailer: allow non-trailers in trailer block
In reply to Jonathan TanCurrently, interpret-trailers requires all lines of a trailer block to be trailers (or comments) - if not it would not identify that block as a trailer block, and thus create its own trailer block, inserting a blank line. For example:
echo -e "\nSigned-off-by: x\nnot trailer" | git interpret-trailers --trailer "c: d"
would result in:
Signed-off-by: x
not trailer
c: d
Relax the definition of a trailer block to require that the trailers (i) are all trailers, or (ii) contain at least one Git-generated trailer and consists of at least 25% trailers.
Signed-off-by: x
not trailer
c: d
(i) is the existing functionality. (ii) allows arbitrary lines to be included in trailer blocks, like those in [1], and still allow interpret-trailers to be used.
--- Documentation/git-interpret-trailers.txt | 5 +- t/t7513-interpret-trailers.sh | 115 +++++++++++++++++++++++++++++++ trailer.c | 89 ++++++++++++++++++++---- 3 files changed, 194 insertions(+), 15 deletions(-)
Documentation/git-interpret-trailers.txt +3 -2
@@ -48,8 +48,9 @@ with only spaces at the end of the commit message part, one blank line will be added before the new trailer. Existing trailers are extracted from the input message by looking for -a group of one or more lines that contain a colon (by default), where -the group is preceded by one or more empty (or whitespace-only) lines. +a group of one or more lines that (i) are all trailers, or (ii) contains at +least one Git-generated trailer and consists of at least 25% trailers. +The group must be preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three minus signs start the patch part of the message.
t/t7513-interpret-trailers.sh +115 -0
121 lines not on this page · raw message
trailer.c +76 -13
142 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 7/8 trailer: forbid leading whitespace in trailers
In reply to Jonathan TanCurrently, interpret-trailers allows leading whitespace in trailer lines. This leads to false positives, especially for quoted lines or bullet lists.
Forbid leading whitespace in trailers.
--- Documentation/git-interpret-trailers.txt | 2 +- t/t7513-interpret-trailers.sh | 15 +++++++++++++++ trailer.c | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-)
Documentation/git-interpret-trailers.txt +1 -1
@@ -55,7 +55,7 @@ The group must either be at the end of the message or be the last non-whitespace lines before a line that starts with '---'. Such three minus signs start the patch part of the message. -When reading trailers, there can be whitespaces before and after the +When reading trailers, there can be whitespaces after the token, the separator and the value. There can also be whitespaces inside the token and the value.
t/t7513-interpret-trailers.sh +15 -0
@@ -241,6 +241,21 @@ test_expect_success 'with non-trailer lines only' ' test_cmp expected actual ' +test_expect_success 'line with leading whitespace is not trailer' ' + q_to_tab >patch <<-\EOF && + + Qtoken: value + EOF + q_to_tab >expected <<-\EOF && + + Qtoken: value + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF &&
trailer.c +1 -1
@@ -775,7 +775,7 @@ static int find_trailer_start(struct strbuf **lines, int count) } separator_pos =find_separator (lines[start]->buf); -if (separator_pos >=1 ) { +if (separator_pos >=1 && !isspace (lines[start]->buf[0 ])) {struct list_head *pos; trailer_lines++;
Signature · 1 line
2.8.0.rc3.226.g39d4020
PATCH v5 8/8 trailer: support values folded to multiple lines
In reply to Jonathan TanCurrently, interpret-trailers requires that a trailer be only on 1 line. For example:
a: first line
second line
would be interpreted as one trailer line followed by one non-trailer line.
Make interpret-trailers support RFC 822-style folding, treating those lines as one logical trailer.
--- Documentation/git-interpret-trailers.txt | 7 +- t/t7513-interpret-trailers.sh | 169 +++++++++++++++++++++++++++++++ trailer.c | 45 ++++++-- 3 files changed, 211 insertions(+), 10 deletions(-)
Documentation/git-interpret-trailers.txt +4 -3
@@ -57,11 +57,12 @@ minus signs start the patch part of the message. When reading trailers, there can be whitespaces after the token, the separator and the value. There can also be whitespaces -inside the token and the value. +inside the token and the value. The value may be split over multiple lines with +each subsequent line starting with whitespace, like the "folding" in RFC 822. 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. +rules for RFC 822 headers. For example they do not follow +the encoding rules and probably many other rules. OPTIONS -------
t/t7513-interpret-trailers.sh +169 -0
175 lines not on this page · raw message
trailer.c +38 -7
116 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3.226.g39d4020
Quoted 3 lines · Jonathan Tan
I've updated patch 5/8 to use strcspn and to pass in the list of separators, meaning that we no longer accept '=' in file input (and also updated its commit message accordingly).
Thanks for a pleasant read. Queued.
Hopefully this is ready for 'next' now.
Quoted 8 lines · Junio C Hamano
Quoted 3 lines · Jonathan Tan
I've updated patch 5/8 to use strcspn and to pass in the list of separators, meaning that we no longer accept '=' in file input (and also updated its commit message accordingly).
Thanks for a pleasant read. Queued.
Hopefully this is ready for 'next' now.
I also just read through and was about to say the same.