From c58492d4347fb6ee68fbb59b40e34ffde4dc2389 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:34 +0530 Subject: ref-filter: implement %(if), %(then), and %(else) atoms Implement %(if), %(then) and %(else) atoms. Used as %(if)...%(then)...%(end) or %(if)...%(then)...%(else)...%(end). If the format string between %(if) and %(then) expands to an empty string, or to only whitespaces, then the whole %(if)...%(end) expands to the string following %(then). Otherwise, it expands to the string following %(else), if any. Nesting of this construct is possible. This is in preparation for porting over `git branch -l` to use ref-filter APIs for printing. Add documentation and tests regarding the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 7 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 1a978405e6..0a578722d2 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -22,6 +22,12 @@ struct align { unsigned int width; }; +struct if_then_else { + unsigned int then_atom_seen : 1, + else_atom_seen : 1, + condition_satisfied : 1; +}; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -214,6 +220,9 @@ static struct { { "color", FIELD_STR, color_atom_parser }, { "align", FIELD_STR, align_atom_parser }, { "end" }, + { "if" }, + { "then" }, + { "else" }, }; #define REF_FORMATTING_STATE_INIT { 0, NULL } @@ -221,7 +230,7 @@ static struct { struct ref_formatting_stack { struct ref_formatting_stack *prev; struct strbuf output; - void (*at_end)(struct ref_formatting_stack *stack); + void (*at_end)(struct ref_formatting_stack **stack); void *at_end_data; }; @@ -354,13 +363,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack) *stack = prev; } -static void end_align_handler(struct ref_formatting_stack *stack) +static void end_align_handler(struct ref_formatting_stack **stack) { - struct align *align = (struct align *)stack->at_end_data; + struct ref_formatting_stack *cur = *stack; + struct align *align = (struct align *)cur->at_end_data; struct strbuf s = STRBUF_INIT; - strbuf_utf8_align(&s, align->position, align->width, stack->output.buf); - strbuf_swap(&stack->output, &s); + strbuf_utf8_align(&s, align->position, align->width, cur->output.buf); + strbuf_swap(&cur->output, &s); strbuf_release(&s); } @@ -374,6 +384,104 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s new->at_end_data = &atomv->u.align; } +static void if_then_else_handler(struct ref_formatting_stack **stack) +{ + struct ref_formatting_stack *cur = *stack; + struct ref_formatting_stack *prev = cur->prev; + struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data; + + if (!if_then_else->then_atom_seen) + die(_("format: %%(if) atom used without a %%(then) atom")); + + if (if_then_else->else_atom_seen) { + /* + * There is an %(else) atom: we need to drop one state from the + * stack, either the %(else) branch if the condition is satisfied, or + * the %(then) branch if it isn't. + */ + if (if_then_else->condition_satisfied) { + strbuf_reset(&cur->output); + pop_stack_element(&cur); + } else { + strbuf_swap(&cur->output, &prev->output); + strbuf_reset(&cur->output); + pop_stack_element(&cur); + } + } else if (!if_then_else->condition_satisfied) { + /* + * No %(else) atom: just drop the %(then) branch if the + * condition is not satisfied. + */ + strbuf_reset(&cur->output); + } + + *stack = cur; + free(if_then_else); +} + +static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) +{ + struct ref_formatting_stack *new; + struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1); + + push_stack_element(&state->stack); + new = state->stack; + new->at_end = if_then_else_handler; + new->at_end_data = if_then_else; +} + +static int is_empty(const char *s) +{ + while (*s != '\0') { + if (!isspace(*s)) + return 0; + s++; + } + return 1; +} + +static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) +{ + struct ref_formatting_stack *cur = state->stack; + struct if_then_else *if_then_else = NULL; + + if (cur->at_end == if_then_else_handler) + if_then_else = (struct if_then_else *)cur->at_end_data; + if (!if_then_else) + die(_("format: %%(then) atom used without an %%(if) atom")); + if (if_then_else->then_atom_seen) + die(_("format: %%(then) atom used more than once")); + if (if_then_else->else_atom_seen) + die(_("format: %%(then) atom used after %%(else)")); + if_then_else->then_atom_seen = 1; + /* + * If there exists non-empty string between the 'if' and + * 'then' atom then the 'if' condition is satisfied. + */ + if (cur->output.len && !is_empty(cur->output.buf)) + if_then_else->condition_satisfied = 1; + strbuf_reset(&cur->output); +} + +static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) +{ + struct ref_formatting_stack *prev = state->stack; + struct if_then_else *if_then_else = NULL; + + if (prev->at_end == if_then_else_handler) + if_then_else = (struct if_then_else *)prev->at_end_data; + if (!if_then_else) + die(_("format: %%(else) atom used without an %%(if) atom")); + if (!if_then_else->then_atom_seen) + die(_("format: %%(else) atom used without a %%(then) atom")); + if (if_then_else->else_atom_seen) + die(_("format: %%(else) atom used more than once")); + if_then_else->else_atom_seen = 1; + push_stack_element(&state->stack); + state->stack->at_end_data = prev->at_end_data; + state->stack->at_end = prev->at_end; +} + static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state) { struct ref_formatting_stack *current = state->stack; @@ -381,14 +489,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta if (!current->at_end) die(_("format: %%(end) atom used without corresponding atom")); - current->at_end(current); + current->at_end(&state->stack); + + /* Stack may have been popped within at_end(), hence reset the current pointer */ + current = state->stack; /* * Perform quote formatting when the stack element is that of * a supporting atom. If nested then perform quote formatting * only on the topmost supporting atom. */ - if (!state->stack->prev->prev) { + if (!current->prev->prev) { quote_formatting(&s, current->output.buf, state->quote_style); strbuf_swap(¤t->output, &s); } @@ -1049,6 +1160,15 @@ static void populate_value(struct ref_array_item *ref) } else if (!strcmp(name, "end")) { v->handler = end_atom_handler; continue; + } else if (!strcmp(name, "if")) { + v->handler = if_atom_handler; + continue; + } else if (!strcmp(name, "then")) { + v->handler = then_atom_handler; + continue; + } else if (!strcmp(name, "else")) { + v->handler = else_atom_handler; + continue; } else continue; -- cgit v1.2.3 From c58fc85692d759cb33a59e138a94931044a08284 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:35 +0530 Subject: ref-filter: include reference to 'used_atom' within 'atom_value' Ensure that each 'atom_value' has a reference to its corresponding 'used_atom'. This lets us use values within 'used_atom' in the 'handler' function. Hence we can get the %(align) atom's parameters directly from the 'used_atom' therefore removing the necessity of passing %(align) atom's parameters to 'atom_value'. This also acts as a preparatory patch for the upcoming patch where we introduce %(if:equals=) and %(if:notequals=). Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 0a578722d2..f31c4b68b0 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -241,11 +241,9 @@ struct ref_formatting_state { struct atom_value { const char *s; - union { - struct align align; - } u; void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state); unsigned long ul; /* used for sorting when not FIELD_STR */ + struct used_atom *atom; }; /* @@ -381,7 +379,7 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s push_stack_element(&state->stack); new = state->stack; new->at_end = end_align_handler; - new->at_end_data = &atomv->u.align; + new->at_end_data = &atomv->atom->u.align; } static void if_then_else_handler(struct ref_formatting_stack **stack) @@ -1090,6 +1088,7 @@ static void populate_value(struct ref_array_item *ref) struct branch *branch = NULL; v->handler = append_atom; + v->atom = atom; if (*name == '*') { deref = 1; @@ -1154,7 +1153,6 @@ static void populate_value(struct ref_array_item *ref) v->s = " "; continue; } else if (starts_with(name, "align")) { - v->u.align = atom->u.align; v->handler = align_atom_handler; continue; } else if (!strcmp(name, "end")) { -- cgit v1.2.3 From 4f3e3b37fae818e0e370d14fd56479d3a7d68b6e Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:36 +0530 Subject: ref-filter: implement %(if:equals=) and %(if:notequals=) Implement %(if:equals=) wherein the if condition is only satisfied if the value obtained between the %(if:...) and %(then) atom is the same as the given ''. Similarly, implement (if:notequals=) wherein the if condition is only satisfied if the value obtained between the %(if:...) and %(then) atom is different from the given ''. This is done by introducing 'if_atom_parser()' which parses the given %(if) atom and then stores the data in used_atom which is later passed on to the used_atom of the %(then) atom, so that it can do the required comparisons. Add tests and documentation for the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index f31c4b68b0..302bb2d066 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -16,6 +16,7 @@ #include "trailer.h" typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; +typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status; struct align { align_type position; @@ -23,6 +24,8 @@ struct align { }; struct if_then_else { + cmp_status cmp_status; + const char *str; unsigned int then_atom_seen : 1, else_atom_seen : 1, condition_satisfied : 1; @@ -50,6 +53,10 @@ static struct used_atom { enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; unsigned int nlines; } contents; + struct { + cmp_status cmp_status; + const char *str; + } if_then_else; enum { O_FULL, O_SHORT } objectname; } u; } *used_atom; @@ -179,6 +186,21 @@ static void align_atom_parser(struct used_atom *atom, const char *arg) string_list_clear(¶ms, 0); } +static void if_atom_parser(struct used_atom *atom, const char *arg) +{ + if (!arg) { + atom->u.if_then_else.cmp_status = COMPARE_NONE; + return; + } else if (skip_prefix(arg, "equals=", &atom->u.if_then_else.str)) { + atom->u.if_then_else.cmp_status = COMPARE_EQUAL; + } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) { + atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL; + } else { + die(_("unrecognized %%(if) argument: %s"), arg); + } +} + + static struct { const char *name; cmp_type cmp_type; @@ -220,7 +242,7 @@ static struct { { "color", FIELD_STR, color_atom_parser }, { "align", FIELD_STR, align_atom_parser }, { "end" }, - { "if" }, + { "if", FIELD_STR, if_atom_parser }, { "then" }, { "else" }, }; @@ -422,6 +444,9 @@ static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_stat struct ref_formatting_stack *new; struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1); + if_then_else->str = atomv->atom->u.if_then_else.str; + if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status; + push_stack_element(&state->stack); new = state->stack; new->at_end = if_then_else_handler; @@ -453,10 +478,17 @@ static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_st die(_("format: %%(then) atom used after %%(else)")); if_then_else->then_atom_seen = 1; /* - * If there exists non-empty string between the 'if' and - * 'then' atom then the 'if' condition is satisfied. + * If the 'equals' or 'notequals' attribute is used then + * perform the required comparison. If not, only non-empty + * strings satisfy the 'if' condition. */ - if (cur->output.len && !is_empty(cur->output.buf)) + if (if_then_else->cmp_status == COMPARE_EQUAL) { + if (!strcmp(if_then_else->str, cur->output.buf)) + if_then_else->condition_satisfied = 1; + } else if (if_then_else->cmp_status == COMPARE_UNEQUAL) { + if (strcmp(if_then_else->str, cur->output.buf)) + if_then_else->condition_satisfied = 1; + } else if (cur->output.len && !is_empty(cur->output.buf)) if_then_else->condition_satisfied = 1; strbuf_reset(&cur->output); } @@ -1158,7 +1190,11 @@ static void populate_value(struct ref_array_item *ref) } else if (!strcmp(name, "end")) { v->handler = end_atom_handler; continue; - } else if (!strcmp(name, "if")) { + } else if (starts_with(name, "if")) { + const char *s; + + if (skip_prefix(name, "if:", &s)) + v->s = xstrdup(s); v->handler = if_atom_handler; continue; } else if (!strcmp(name, "then")) { -- cgit v1.2.3 From 42d0eb05eed8e6c66d93091f5b0ece3a1872246c Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:37 +0530 Subject: ref-filter: modify "%(objectname:short)" to take length Add support for %(objectname:short=) which would print the abbreviated unique objectname of given length. When no length is specified, the length is 'DEFAULT_ABBREV'. The minimum length is 'MINIMUM_ABBREV'. The length may be exceeded to ensure that the provided object name is unique. Add tests and documentation for the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Helped-by: Jacob Keller Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 302bb2d066..a88464653b 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -57,7 +57,10 @@ static struct used_atom { cmp_status cmp_status; const char *str; } if_then_else; - enum { O_FULL, O_SHORT } objectname; + struct { + enum { O_FULL, O_LENGTH, O_SHORT } option; + unsigned int length; + } objectname; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -129,10 +132,17 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg) static void objectname_atom_parser(struct used_atom *atom, const char *arg) { if (!arg) - atom->u.objectname = O_FULL; + atom->u.objectname.option = O_FULL; else if (!strcmp(arg, "short")) - atom->u.objectname = O_SHORT; - else + atom->u.objectname.option = O_SHORT; + else if (skip_prefix(arg, "short=", &arg)) { + atom->u.objectname.option = O_LENGTH; + if (strtoul_ui(arg, 10, &atom->u.objectname.length) || + atom->u.objectname.length == 0) + die(_("positive value expected objectname:short=%s"), arg); + if (atom->u.objectname.length < MINIMUM_ABBREV) + atom->u.objectname.length = MINIMUM_ABBREV; + } else die(_("unrecognized %%(objectname) argument: %s"), arg); } @@ -606,12 +616,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1, struct atom_value *v, struct used_atom *atom) { if (starts_with(name, "objectname")) { - if (atom->u.objectname == O_SHORT) { + if (atom->u.objectname.option == O_SHORT) { v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); return 1; - } else if (atom->u.objectname == O_FULL) { + } else if (atom->u.objectname.option == O_FULL) { v->s = xstrdup(sha1_to_hex(sha1)); return 1; + } else if (atom->u.objectname.option == O_LENGTH) { + v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length)); + return 1; } else die("BUG: unknown %%(objectname) option"); } -- cgit v1.2.3 From d4919bb288e46c81b92d6fe02c4f4564b8477fd3 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:38 +0530 Subject: ref-filter: move get_head_description() from branch.c Move the implementation of get_head_description() from branch.c to ref-filter. This gives a description of the HEAD ref if called. This is used as the refname for the HEAD ref whenever the FILTER_REFS_DETACHED_HEAD option is used. Make it public because we need it to calculate the length of the HEAD refs description in branch.c:calc_maxwidth() when we port branch.c to use ref-filter APIs. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index a88464653b..7038703fe7 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -14,6 +14,7 @@ #include "git-compat-util.h" #include "version.h" #include "trailer.h" +#include "wt-status.h" typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status; @@ -1101,6 +1102,37 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, *s = refname; } +char *get_head_description(void) +{ + struct strbuf desc = STRBUF_INIT; + struct wt_status_state state; + memset(&state, 0, sizeof(state)); + wt_status_get_state(&state, 1); + if (state.rebase_in_progress || + state.rebase_interactive_in_progress) + strbuf_addf(&desc, _("(no branch, rebasing %s)"), + state.branch); + else if (state.bisect_in_progress) + strbuf_addf(&desc, _("(no branch, bisect started on %s)"), + state.branch); + else if (state.detached_from) { + /* TRANSLATORS: make sure these match _("HEAD detached at ") + and _("HEAD detached from ") in wt-status.c */ + if (state.detached_at) + strbuf_addf(&desc, _("(HEAD detached at %s)"), + state.detached_from); + else + strbuf_addf(&desc, _("(HEAD detached from %s)"), + state.detached_from); + } + else + strbuf_addstr(&desc, _("(no branch)")); + free(state.branch); + free(state.onto); + free(state.detached_from); + return strbuf_detach(&desc, NULL); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1140,9 +1172,11 @@ static void populate_value(struct ref_array_item *ref) name++; } - if (starts_with(name, "refname")) + if (starts_with(name, "refname")) { refname = ref->refname; - else if (starts_with(name, "symref")) + if (ref->kind & FILTER_REFS_DETACHED_HEAD) + refname = get_head_description(); + } else if (starts_with(name, "symref")) refname = ref->symref ? ref->symref : ""; else if (starts_with(name, "upstream")) { const char *branch_name; -- cgit v1.2.3 From 99c6a71d4f7f4197f5e8600b0edec2a6a9fd9988 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:39 +0530 Subject: ref-filter: introduce format_ref_array_item() To allow column display, we will need to first render the output in a string list to allow print_columns() to compute the proper size of each column before starting the actual output. Introduce the function format_ref_array_item() that does the formatting of a ref_array_item to an strbuf. show_ref_array_item() is kept as a convenience wrapper around it which obtains the strbuf and prints it the standard output. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 7038703fe7..92c2d4fe70 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1833,10 +1833,10 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting } } -void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style) +void format_ref_array_item(struct ref_array_item *info, const char *format, + int quote_style, struct strbuf *final_buf) { const char *cp, *sp, *ep; - struct strbuf *final_buf; struct ref_formatting_state state = REF_FORMATTING_STATE_INIT; state.quote_style = quote_style; @@ -1866,9 +1866,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu } if (state.stack->prev) die(_("format: %%(end) atom missing")); - final_buf = &state.stack->output; - fwrite(final_buf->buf, 1, final_buf->len, stdout); + strbuf_addbuf(final_buf, &state.stack->output); pop_stack_element(&state.stack); +} + +void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style) +{ + struct strbuf final_buf = STRBUF_INIT; + + format_ref_array_item(info, format, quote_style, &final_buf); + fwrite(final_buf.buf, 1, final_buf.len, stdout); + strbuf_release(&final_buf); putchar('\n'); } -- cgit v1.2.3 From ffd921d311c9293ce83e0d191769a962d9197a71 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:40 +0530 Subject: ref-filter: make %(upstream:track) prints "[gone]" for invalid upstreams Borrowing from branch.c's implementation print "[gone]" whenever an unknown upstream ref is encountered instead of just ignoring it. This makes sure that when branch.c is ported over to using ref-filter APIs for printing, this feature is not lost. Make changes to t/t6300-for-each-ref.sh and Documentation/git-for-each-ref.txt to reflect this change. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Helped-by : Jacob Keller Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 92c2d4fe70..4a05150578 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1073,8 +1073,10 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); else if (atom->u.remote_ref == RR_TRACK) { if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) + &num_theirs, NULL)) { + *s = "[gone]"; return; + } if (!num_ours && !num_theirs) *s = ""; -- cgit v1.2.3 From 7743fcca5be6854a1b9a5a0f7e60b79c15fee4b9 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:41 +0530 Subject: ref-filter: add support for %(upstream:track,nobracket) Add support for %(upstream:track,nobracket) which will print the tracking information without the brackets (i.e. "ahead N, behind M"). This is needed when we port branch.c to use ref-filter's printing APIs. Add test and documentation for the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 67 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 23 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 4a05150578..c58765fa7f 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -48,8 +48,10 @@ static struct used_atom { union { char color[COLOR_MAXLEN]; struct align align; - enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } - remote_ref; + struct { + enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option; + unsigned int nobracket : 1; + } remote_ref; struct { enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; unsigned int nlines; @@ -77,16 +79,33 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value) static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) { - if (!arg) - atom->u.remote_ref = RR_NORMAL; - else if (!strcmp(arg, "short")) - atom->u.remote_ref = RR_SHORTEN; - else if (!strcmp(arg, "track")) - atom->u.remote_ref = RR_TRACK; - else if (!strcmp(arg, "trackshort")) - atom->u.remote_ref = RR_TRACKSHORT; - else - die(_("unrecognized format: %%(%s)"), atom->name); + struct string_list params = STRING_LIST_INIT_DUP; + int i; + + if (!arg) { + atom->u.remote_ref.option = RR_NORMAL; + return; + } + + atom->u.remote_ref.nobracket = 0; + string_list_split(¶ms, arg, ',', -1); + + for (i = 0; i < params.nr; i++) { + const char *s = params.items[i].string; + + if (!strcmp(s, "short")) + atom->u.remote_ref.option = RR_SHORTEN; + else if (!strcmp(s, "track")) + atom->u.remote_ref.option = RR_TRACK; + else if (!strcmp(s, "trackshort")) + atom->u.remote_ref.option = RR_TRACKSHORT; + else if (!strcmp(s, "nobracket")) + atom->u.remote_ref.nobracket = 1; + else + die(_("unrecognized format: %%(%s)"), atom->name); + } + + string_list_clear(¶ms, 0); } static void body_atom_parser(struct used_atom *atom, const char *arg) @@ -1069,25 +1088,27 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, struct branch *branch, const char **s) { int num_ours, num_theirs; - if (atom->u.remote_ref == RR_SHORTEN) + if (atom->u.remote_ref.option == RR_SHORTEN) *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); - else if (atom->u.remote_ref == RR_TRACK) { + else if (atom->u.remote_ref.option == RR_TRACK) { if (stat_tracking_info(branch, &num_ours, &num_theirs, NULL)) { - *s = "[gone]"; - return; - } - - if (!num_ours && !num_theirs) + *s = xstrdup("gone"); + } else if (!num_ours && !num_theirs) *s = ""; else if (!num_ours) - *s = xstrfmt("[behind %d]", num_theirs); + *s = xstrfmt("behind %d", num_theirs); else if (!num_theirs) - *s = xstrfmt("[ahead %d]", num_ours); + *s = xstrfmt("ahead %d", num_ours); else - *s = xstrfmt("[ahead %d, behind %d]", + *s = xstrfmt("ahead %d, behind %d", num_ours, num_theirs); - } else if (atom->u.remote_ref == RR_TRACKSHORT) { + if (!atom->u.remote_ref.nobracket && *s[0]) { + const char *to_free = *s; + *s = xstrfmt("[%s]", *s); + free((void *)to_free); + } + } else if (atom->u.remote_ref.option == RR_TRACKSHORT) { if (stat_tracking_info(branch, &num_ours, &num_theirs, NULL)) return; -- cgit v1.2.3 From 01f95825d55b2ca36ee9bc131a5f6899f47621c6 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:42 +0530 Subject: ref-filter: make "%(symref)" atom work with the ':short' modifier The "%(symref)" atom doesn't work when used with the ':short' modifier because we strictly match only 'symref' for setting the 'need_symref' indicator. Fix this by comparing with the valid_atom rather than the used_atom. Add tests for %(symref) and %(symref:short) while we're here. Helped-by: Junio C Hamano Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index c58765fa7f..26116b805e 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -352,7 +352,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep) valid_atom[i].parser(&used_atom[at], arg); if (*atom == '*') need_tagged = 1; - if (!strcmp(used_atom[at].name, "symref")) + if (!strcmp(valid_atom[i].name, "symref")) need_symref = 1; return at; } -- cgit v1.2.3 From b180e6fe19ee4cf701d3a3478025dd7125fb0749 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:43 +0530 Subject: ref-filter: introduce refname_atom_parser_internal() Since there are multiple atoms which print refs ('%(refname)', '%(symref)', '%(push)', '%(upstream)'), it makes sense to have a common ground for parsing them. This would allow us to share implementations of the atom modifiers between these atoms. Introduce refname_atom_parser_internal() to act as a common parsing function for ref printing atoms. This would eventually be used to introduce refname_atom_parser() and symref_atom_parser() and also be internally used in remote_ref_atom_parser(). Helped-by: Jeff King Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 26116b805e..7ff284cdf1 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -32,6 +32,11 @@ struct if_then_else { condition_satisfied : 1; }; +struct refname_atom { + enum { R_NORMAL, R_SHORT, R_STRIP } option; + unsigned int strip; +}; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -64,6 +69,7 @@ static struct used_atom { enum { O_FULL, O_LENGTH, O_SHORT } option; unsigned int length; } objectname; + struct refname_atom refname; } u; } *used_atom; static int used_atom_cnt, need_tagged, need_symref; @@ -77,6 +83,21 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value) die(_("unrecognized color: %%(color:%s)"), color_value); } +static void refname_atom_parser_internal(struct refname_atom *atom, + const char *arg, const char *name) +{ + if (!arg) + atom->option = R_NORMAL; + else if (!strcmp(arg, "short")) + atom->option = R_SHORT; + else if (skip_prefix(arg, "strip=", &arg)) { + atom->option = R_STRIP; + if (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0) + die(_("positive value expected refname:strip=%s"), arg); + } else + die(_("unrecognized %%(%s) argument: %s"), name, arg); +} + static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) { struct string_list params = STRING_LIST_INIT_DUP; -- cgit v1.2.3 From a7984101846ccfb8837526a8d79dda5b8c461d84 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:44 +0530 Subject: ref-filter: introduce refname_atom_parser() Using refname_atom_parser_internal(), introduce refname_atom_parser() which will parse the %(symref) and %(refname) atoms. Store the parsed information into the 'used_atom' structure based on the modifiers used along with the atoms. Now the '%(symref)' atom supports the ':strip' atom modifier. Update the Documentation and tests to reflect this. Helped-by: Jeff King Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 73 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 33 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 7ff284cdf1..6ce7dba2ad 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -187,6 +187,11 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg) die(_("unrecognized %%(objectname) argument: %s"), arg); } +static void refname_atom_parser(struct used_atom *atom, const char *arg) +{ + return refname_atom_parser_internal(&atom->u.refname, arg, atom->name); +} + static align_type parse_align_position(const char *s) { if (!strcmp(s, "right")) @@ -257,7 +262,7 @@ static struct { cmp_type cmp_type; void (*parser)(struct used_atom *atom, const char *arg); } valid_atom[] = { - { "refname" }, + { "refname" , FIELD_STR, refname_atom_parser }, { "objecttype" }, { "objectsize", FIELD_ULONG }, { "objectname", FIELD_STR, objectname_atom_parser }, @@ -287,7 +292,7 @@ static struct { { "contents", FIELD_STR, contents_atom_parser }, { "upstream", FIELD_STR, remote_ref_atom_parser }, { "push", FIELD_STR, remote_ref_atom_parser }, - { "symref" }, + { "symref", FIELD_STR, refname_atom_parser }, { "flag" }, { "HEAD" }, { "color", FIELD_STR, color_atom_parser }, @@ -1082,21 +1087,16 @@ static inline char *copy_advance(char *dst, const char *src) return dst; } -static const char *strip_ref_components(const char *refname, const char *nr_arg) +static const char *strip_ref_components(const char *refname, unsigned int len) { - char *end; - long nr = strtol(nr_arg, &end, 10); - long remaining = nr; + long remaining = len; const char *start = refname; - if (nr < 1 || *end != '\0') - die(_(":strip= requires a positive integer argument")); - while (remaining) { switch (*start++) { case '\0': - die(_("ref '%s' does not have %ld components to :strip"), - refname, nr); + die(_("ref '%s' does not have %ud components to :strip"), + refname, len); case '/': remaining--; break; @@ -1105,6 +1105,16 @@ static const char *strip_ref_components(const char *refname, const char *nr_arg) return start; } +static const char *show_ref(struct refname_atom *atom, const char *refname) +{ + if (atom->option == R_SHORT) + return shorten_unambiguous_ref(refname, warn_ambiguous_refs); + else if (atom->option == R_STRIP) + return strip_ref_components(refname, atom->strip); + else + return refname; +} + static void fill_remote_ref_details(struct used_atom *atom, const char *refname, struct branch *branch, const char **s) { @@ -1177,6 +1187,21 @@ char *get_head_description(void) return strbuf_detach(&desc, NULL); } +static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref) +{ + if (!ref->symref) + return ""; + else + return show_ref(&atom->u.refname, ref->symref); +} + +static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref) +{ + if (ref->kind & FILTER_REFS_DETACHED_HEAD) + return get_head_description(); + return show_ref(&atom->u.refname, ref->refname); +} + /* * Parse the object referred by ref, and grab needed value. */ @@ -1205,7 +1230,6 @@ static void populate_value(struct ref_array_item *ref) struct atom_value *v = &ref->value[i]; int deref = 0; const char *refname; - const char *formatp; struct branch *branch = NULL; v->handler = append_atom; @@ -1216,12 +1240,10 @@ static void populate_value(struct ref_array_item *ref) name++; } - if (starts_with(name, "refname")) { - refname = ref->refname; - if (ref->kind & FILTER_REFS_DETACHED_HEAD) - refname = get_head_description(); - } else if (starts_with(name, "symref")) - refname = ref->symref ? ref->symref : ""; + if (starts_with(name, "refname")) + refname = get_refname(atom, ref); + else if (starts_with(name, "symref")) + refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { const char *branch_name; /* only local branches may have an upstream */ @@ -1297,21 +1319,6 @@ static void populate_value(struct ref_array_item *ref) } else continue; - formatp = strchr(name, ':'); - if (formatp) { - const char *arg; - - formatp++; - if (!strcmp(formatp, "short")) - refname = shorten_unambiguous_ref(refname, - warn_ambiguous_refs); - else if (skip_prefix(formatp, "strip=", &arg)) - refname = strip_ref_components(refname, arg); - else - die(_("unknown %.*s format %s"), - (int)(formatp - name), name, formatp); - } - if (!deref) v->s = refname; else -- cgit v1.2.3 From 3ba308cb4b4ad0d06fa834a42a9380c877b0c16a Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:45 +0530 Subject: ref-filter: make remote_ref_atom_parser() use refname_atom_parser_internal() Use the recently introduced refname_atom_parser_internal() within remote_ref_atom_parser(), this provides a common base for all the ref printing atoms, allowing %(upstream) and %(push) to also use the ':strip' option. The atoms '%(push)' and '%(upstream)' will retain the ':track' and ':trackshort' atom modifiers to themselves as they have no meaning in context to the '%(refname)' and '%(symref)' atoms. Update the documentation and tests to reflect the same. Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 6ce7dba2ad..ffa29446cf 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -54,7 +54,8 @@ static struct used_atom { char color[COLOR_MAXLEN]; struct align align; struct { - enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option; + enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option; + struct refname_atom refname; unsigned int nobracket : 1; } remote_ref; struct { @@ -104,7 +105,9 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) int i; if (!arg) { - atom->u.remote_ref.option = RR_NORMAL; + atom->u.remote_ref.option = RR_REF; + refname_atom_parser_internal(&atom->u.remote_ref.refname, + arg, atom->name); return; } @@ -114,16 +117,17 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg) for (i = 0; i < params.nr; i++) { const char *s = params.items[i].string; - if (!strcmp(s, "short")) - atom->u.remote_ref.option = RR_SHORTEN; - else if (!strcmp(s, "track")) + if (!strcmp(s, "track")) atom->u.remote_ref.option = RR_TRACK; else if (!strcmp(s, "trackshort")) atom->u.remote_ref.option = RR_TRACKSHORT; else if (!strcmp(s, "nobracket")) atom->u.remote_ref.nobracket = 1; - else - die(_("unrecognized format: %%(%s)"), atom->name); + else { + atom->u.remote_ref.option = RR_REF; + refname_atom_parser_internal(&atom->u.remote_ref.refname, + arg, atom->name); + } } string_list_clear(¶ms, 0); @@ -1119,8 +1123,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, struct branch *branch, const char **s) { int num_ours, num_theirs; - if (atom->u.remote_ref.option == RR_SHORTEN) - *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs); + if (atom->u.remote_ref.option == RR_REF) + *s = show_ref(&atom->u.remote_ref.refname, refname); else if (atom->u.remote_ref.option == RR_TRACK) { if (stat_tracking_info(branch, &num_ours, &num_theirs, NULL)) { @@ -1152,8 +1156,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, *s = ">"; else *s = "<>"; - } else /* RR_NORMAL */ - *s = refname; + } else + die("BUG: unhandled RR_* enum"); } char *get_head_description(void) -- cgit v1.2.3 From 17938f171f703c28696c7839a910565f0fb32121 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:46 +0530 Subject: ref-filter: rename the 'strip' option to 'lstrip' In preparation for the upcoming patch, where we introduce the 'rstrip' option. Rename the 'strip' option to 'lstrip' to remove ambiguity. Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index ffa29446cf..cccd86f574 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -33,8 +33,8 @@ struct if_then_else { }; struct refname_atom { - enum { R_NORMAL, R_SHORT, R_STRIP } option; - unsigned int strip; + enum { R_NORMAL, R_SHORT, R_LSTRIP } option; + unsigned int lstrip; }; /* @@ -91,10 +91,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom, atom->option = R_NORMAL; else if (!strcmp(arg, "short")) atom->option = R_SHORT; - else if (skip_prefix(arg, "strip=", &arg)) { - atom->option = R_STRIP; - if (strtoul_ui(arg, 10, &atom->strip) || atom->strip <= 0) - die(_("positive value expected refname:strip=%s"), arg); + else if (skip_prefix(arg, "lstrip=", &arg)) { + atom->option = R_LSTRIP; + if (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0) + die(_("positive value expected refname:lstrip=%s"), arg); } else die(_("unrecognized %%(%s) argument: %s"), name, arg); } @@ -1091,7 +1091,7 @@ static inline char *copy_advance(char *dst, const char *src) return dst; } -static const char *strip_ref_components(const char *refname, unsigned int len) +static const char *lstrip_ref_components(const char *refname, unsigned int len) { long remaining = len; const char *start = refname; @@ -1099,7 +1099,7 @@ static const char *strip_ref_components(const char *refname, unsigned int len) while (remaining) { switch (*start++) { case '\0': - die(_("ref '%s' does not have %ud components to :strip"), + die(_("ref '%s' does not have %ud components to :lstrip"), refname, len); case '/': remaining--; @@ -1113,8 +1113,8 @@ static const char *show_ref(struct refname_atom *atom, const char *refname) { if (atom->option == R_SHORT) return shorten_unambiguous_ref(refname, warn_ambiguous_refs); - else if (atom->option == R_STRIP) - return strip_ref_components(refname, atom->strip); + else if (atom->option == R_LSTRIP) + return lstrip_ref_components(refname, atom->lstrip); else return refname; } -- cgit v1.2.3 From 3a42980f9e5ecc2b74c538109d4007c630881f1c Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:47 +0530 Subject: ref-filter: Do not abruptly die when using the 'lstrip=' option Currently when we use the 'lstrip=' option, if 'N' is greater than the number of components available in the refname, we abruptly end program execution by calling die(). This behavior is undesired since a single refname with few components could end program execution. To avoid this, return an empty string whenever the value 'N' is greater than the number of components available, instead of calling die(). Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index cccd86f574..4fd6ef186c 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1099,8 +1099,7 @@ static const char *lstrip_ref_components(const char *refname, unsigned int len) while (remaining) { switch (*start++) { case '\0': - die(_("ref '%s' does not have %ud components to :lstrip"), - refname, len); + return ""; case '/': remaining--; break; -- cgit v1.2.3 From 1a0ca5e358c1af14160e610e346c706e5dccf535 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:48 +0530 Subject: ref-filter: modify the 'lstrip=' option to work with negative '' Currently the 'lstrip=' option only takes a positive value '' and strips '' slash-separated path components from the left. Modify the 'lstrip' option to also take a negative number '' which would strip from the left as necessary and _leave_ behind only 'N' slash-separated path components from the right-most end. For e.g. %(refname:lstrip=-1) would make 'foo/goo/abc' into 'abc'. Add documentation and tests for the same. Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 4fd6ef186c..2840ca18e3 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -34,7 +34,7 @@ struct if_then_else { struct refname_atom { enum { R_NORMAL, R_SHORT, R_LSTRIP } option; - unsigned int lstrip; + int lstrip; }; /* @@ -93,8 +93,8 @@ static void refname_atom_parser_internal(struct refname_atom *atom, atom->option = R_SHORT; else if (skip_prefix(arg, "lstrip=", &arg)) { atom->option = R_LSTRIP; - if (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0) - die(_("positive value expected refname:lstrip=%s"), arg); + if (strtol_i(arg, 10, &atom->lstrip)) + die(_("Integer value expected refname:lstrip=%s"), arg); } else die(_("unrecognized %%(%s) argument: %s"), name, arg); } @@ -1091,12 +1091,28 @@ static inline char *copy_advance(char *dst, const char *src) return dst; } -static const char *lstrip_ref_components(const char *refname, unsigned int len) +static const char *lstrip_ref_components(const char *refname, int len) { long remaining = len; const char *start = refname; - while (remaining) { + if (len < 0) { + int i; + const char *p = refname; + + /* Find total no of '/' separated path-components */ + for (i = 0; p[i]; p[i] == '/' ? i++ : *p++) + ; + /* + * The number of components we need to strip is now + * the total minus the components to be left (Plus one + * because we count the number of '/', but the number + * of components is one more than the no of '/'). + */ + remaining = i + len + 1; + } + + while (remaining > 0) { switch (*start++) { case '\0': return ""; @@ -1105,6 +1121,7 @@ static const char *lstrip_ref_components(const char *refname, unsigned int len) break; } } + return start; } -- cgit v1.2.3 From 1a34728e6bb469083dd46ff35362fdfc6e489d07 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:49 +0530 Subject: ref-filter: add an 'rstrip=' option to atoms which deal with refnames Complimenting the existing 'lstrip=' option, add an 'rstrip=' option which strips `` slash-separated path components from the end of the refname (e.g., `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`). Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 2840ca18e3..47e292bc46 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -33,8 +33,8 @@ struct if_then_else { }; struct refname_atom { - enum { R_NORMAL, R_SHORT, R_LSTRIP } option; - int lstrip; + enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option; + int lstrip, rstrip; }; /* @@ -95,6 +95,10 @@ static void refname_atom_parser_internal(struct refname_atom *atom, atom->option = R_LSTRIP; if (strtol_i(arg, 10, &atom->lstrip)) die(_("Integer value expected refname:lstrip=%s"), arg); + } else if (skip_prefix(arg, "rstrip=", &arg)) { + atom->option = R_RSTRIP; + if (strtol_i(arg, 10, &atom->rstrip)) + die(_("Integer value expected refname:rstrip=%s"), arg); } else die(_("unrecognized %%(%s) argument: %s"), name, arg); } @@ -1125,12 +1129,45 @@ static const char *lstrip_ref_components(const char *refname, int len) return start; } +static const char *rstrip_ref_components(const char *refname, int len) +{ + long remaining = len; + char *start = xstrdup(refname); + + if (len < 0) { + int i; + const char *p = refname; + + /* Find total no of '/' separated path-components */ + for (i = 0; p[i]; p[i] == '/' ? i++ : *p++) + ; + /* + * The number of components we need to strip is now + * the total minus the components to be left (Plus one + * because we count the number of '/', but the number + * of components is one more than the no of '/'). + */ + remaining = i + len + 1; + } + + while (remaining-- > 0) { + char *p = strrchr(start, '/'); + if (p == NULL) + return ""; + else + p[0] = '\0'; + } + return start; +} + static const char *show_ref(struct refname_atom *atom, const char *refname) { if (atom->option == R_SHORT) return shorten_unambiguous_ref(refname, warn_ambiguous_refs); else if (atom->option == R_LSTRIP) return lstrip_ref_components(refname, atom->lstrip); + else if (atom->option == R_RSTRIP) + return rstrip_ref_components(refname, atom->rstrip); else return refname; } -- cgit v1.2.3 From 6eac70fa6343a31b5b310a465a442adb6731a26b Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Tue, 10 Jan 2017 14:19:50 +0530 Subject: ref-filter: allow porcelain to translate messages in the output Introduce setup_ref_filter_porcelain_msg() so that the messages used in the atom %(upstream:track) can be translated if needed. By default, keep the messages untranslated, which is the right behavior for plumbing commands. This is needed as we port branch.c to use ref-filter's printing API's. Written-by: Matthieu Moy Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- ref-filter.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 47e292bc46..01b5c18ef0 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -16,6 +16,27 @@ #include "trailer.h" #include "wt-status.h" +static struct ref_msg { + const char *gone; + const char *ahead; + const char *behind; + const char *ahead_behind; +} msgs = { + /* Untranslated plumbing messages: */ + "gone", + "ahead %d", + "behind %d", + "ahead %d, behind %d" +}; + +void setup_ref_filter_porcelain_msg(void) +{ + msgs.gone = _("gone"); + msgs.ahead = _("ahead %d"); + msgs.behind = _("behind %d"); + msgs.ahead_behind = _("ahead %d, behind %d"); +} + typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status; @@ -1181,15 +1202,15 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, else if (atom->u.remote_ref.option == RR_TRACK) { if (stat_tracking_info(branch, &num_ours, &num_theirs, NULL)) { - *s = xstrdup("gone"); + *s = xstrdup(msgs.gone); } else if (!num_ours && !num_theirs) *s = ""; else if (!num_ours) - *s = xstrfmt("behind %d", num_theirs); + *s = xstrfmt(msgs.behind, num_theirs); else if (!num_theirs) - *s = xstrfmt("ahead %d", num_ours); + *s = xstrfmt(msgs.ahead, num_ours); else - *s = xstrfmt("ahead %d, behind %d", + *s = xstrfmt(msgs.ahead_behind, num_ours, num_theirs); if (!atom->u.remote_ref.nobracket && *s[0]) { const char *to_free = *s; -- cgit v1.2.3 From 44a6b6ce1777f587c318008fe59b901a296f5326 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 7 Feb 2017 11:50:34 -0800 Subject: ref-filter: resurrect "strip" as a synonym to "lstrip" We forgot that "strip" was introduced at 0571979bd6 ("tag: do not show ambiguous tag names as "tags/foo"", 2016-01-25) as part of Git 2.8 (and 2.7.1) when we started calling this "lstrip" to make it easier to explain the new "rstrip" operation. We shouldn't have renamed the existing one; "lstrip" should have been a new synonym that means the same thing as "strip". Scripts in the wild are surely using the original form already. Signed-off-by: Junio C Hamano --- ref-filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'ref-filter.c') diff --git a/ref-filter.c b/ref-filter.c index 01b5c18ef0..2a94d6da98 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -112,7 +112,8 @@ static void refname_atom_parser_internal(struct refname_atom *atom, atom->option = R_NORMAL; else if (!strcmp(arg, "short")) atom->option = R_SHORT; - else if (skip_prefix(arg, "lstrip=", &arg)) { + else if (skip_prefix(arg, "lstrip=", &arg) || + skip_prefix(arg, "strip=", &arg)) { atom->option = R_LSTRIP; if (strtol_i(arg, 10, &atom->lstrip)) die(_("Integer value expected refname:lstrip=%s"), arg); -- cgit v1.2.3