ref-filter: introduce format_ref_array_item()
[gitweb.git] / ref-filter.c
index 0a578722d247aba5a17b97ed4df3c1d187edfa94..92c2d4fe70794501c5c112e4c0008a4583d22370 100644 (file)
 #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;
 
 struct align {
        align_type position;
@@ -23,6 +25,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,7 +54,14 @@ 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;
-               enum { O_FULL, O_SHORT } objectname;
+               struct {
+                       cmp_status cmp_status;
+                       const char *str;
+               } if_then_else;
+               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;
@@ -122,10 +133,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);
 }
 
@@ -179,6 +197,21 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
        string_list_clear(&params, 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 +253,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" },
 };
@@ -241,11 +274,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 +412,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)
@@ -424,6 +455,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;
@@ -455,10 +489,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);
 }
@@ -576,12 +617,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");
        }
@@ -1058,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.
  */
@@ -1090,15 +1165,18 @@ static void populate_value(struct ref_array_item *ref)
                struct branch *branch = NULL;
 
                v->handler = append_atom;
+               v->atom = atom;
 
                if (*name == '*') {
                        deref = 1;
                        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;
@@ -1154,13 +1232,16 @@ 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")) {
                        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")) {
@@ -1752,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;
@@ -1785,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');
 }