Merge branch 'js/mingw-use-utf8'
[gitweb.git] / ref-filter.c
index 1d76690c237e19393a35fba2f580380906e402b1..791f0648a6edc4ecc4629865393703d9b1e4a227 100644 (file)
@@ -20,6 +20,8 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
+#include "worktree.h"
+#include "hashmap.h"
 
 static struct ref_msg {
        const char *gone;
@@ -75,6 +77,27 @@ static struct expand_data {
        struct object_info info;
 } oi, oi_deref;
 
+struct ref_to_worktree_entry {
+       struct hashmap_entry ent; /* must be the first member! */
+       struct worktree *wt; /* key is wt->head_ref */
+};
+
+static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
+                                     const void *existing_hashmap_entry_to_test,
+                                     const void *key,
+                                     const void *keydata_aka_refname)
+{
+       const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
+       const struct ref_to_worktree_entry *k = key;
+       return strcmp(e->wt->head_ref,
+               keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
+}
+
+static struct ref_to_worktree_map {
+       struct hashmap map;
+       struct worktree **worktrees;
+} ref_to_worktree_map;
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -480,11 +503,16 @@ static struct {
        { "flag", SOURCE_NONE },
        { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
        { "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+       { "worktreepath", SOURCE_NONE },
        { "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
        { "end", SOURCE_NONE },
        { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
        { "then", SOURCE_NONE },
        { "else", SOURCE_NONE },
+       /*
+        * Please update $__git_ref_fieldlist in git-completion.bash
+        * when you add new atoms
+        */
 };
 
 #define REF_FORMATTING_STATE_INIT  { 0, NULL }
@@ -913,7 +941,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct expand_
 }
 
 /* See grab_values */
-static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
 {
        int i;
        struct tag *tag = (struct tag *) obj;
@@ -935,7 +963,7 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
 }
 
 /* See grab_values */
-static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
 {
        int i;
        struct commit *commit = (struct commit *) obj;
@@ -968,7 +996,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
        }
 }
 
-static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
+static const char *find_wholine(const char *who, int wholen, const char *buf)
 {
        const char *eol;
        while (*buf) {
@@ -1064,7 +1092,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 }
 
 /* See grab_values */
-static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
 {
        int i;
        int wholen = strlen(who);
@@ -1085,7 +1113,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                    !starts_with(name + wholen, "date"))
                        continue;
                if (!wholine)
-                       wholine = find_wholine(who, wholen, buf, sz);
+                       wholine = find_wholine(who, wholen, buf);
                if (!wholine)
                        return; /* no point looking for it */
                if (name[wholen] == 0)
@@ -1105,7 +1133,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
        if (strcmp(who, "tagger") && strcmp(who, "committer"))
                return; /* "author" for commit object is not wanted */
        if (!wholine)
-               wholine = find_wholine(who, wholen, buf, sz);
+               wholine = find_wholine(who, wholen, buf);
        if (!wholine)
                return;
        for (i = 0; i < used_atom_cnt; i++) {
@@ -1123,7 +1151,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
        }
 }
 
-static void find_subpos(const char *buf, unsigned long sz,
+static void find_subpos(const char *buf,
                        const char **sub, unsigned long *sublen,
                        const char **body, unsigned long *bodylen,
                        unsigned long *nonsiglen,
@@ -1192,7 +1220,7 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size
 }
 
 /* See grab_values */
-static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 {
        int i;
        const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
@@ -1212,7 +1240,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
                    !starts_with(name, "contents"))
                        continue;
                if (!subpos)
-                       find_subpos(buf, sz,
+                       find_subpos(buf,
                                    &subpos, &sublen,
                                    &bodypos, &bodylen, &nonsiglen,
                                    &sigpos, &siglen);
@@ -1265,19 +1293,19 @@ static void fill_missing_values(struct atom_value *val)
  * pointed at by the ref itself; otherwise it is the object the
  * ref (which is a tag) refers to.
  */
-static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
 {
        switch (obj->type) {
        case OBJ_TAG:
-               grab_tag_values(val, deref, obj, buf, sz);
-               grab_sub_body_contents(val, deref, obj, buf, sz);
-               grab_person("tagger", val, deref, obj, buf, sz);
+               grab_tag_values(val, deref, obj);
+               grab_sub_body_contents(val, deref, buf);
+               grab_person("tagger", val, deref, buf);
                break;
        case OBJ_COMMIT:
-               grab_commit_values(val, deref, obj, buf, sz);
-               grab_sub_body_contents(val, deref, obj, buf, sz);
-               grab_person("author", val, deref, obj, buf, sz);
-               grab_person("committer", val, deref, obj, buf, sz);
+               grab_commit_values(val, deref, obj);
+               grab_sub_body_contents(val, deref, buf);
+               grab_person("author", val, deref, buf);
+               grab_person("committer", val, deref, buf);
                break;
        case OBJ_TREE:
                /* grab_tree_values(val, deref, obj, buf, sz); */
@@ -1388,7 +1416,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                *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, AHEAD_BEHIND_FULL) < 0) {
+                                      NULL, atom->u.remote_ref.push,
+                                      AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup(msgs.gone);
                } else if (!num_ours && !num_theirs)
                        *s = xstrdup("");
@@ -1406,7 +1435,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                }
        } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
                if (stat_tracking_info(branch, &num_ours, &num_theirs,
-                                      NULL, AHEAD_BEHIND_FULL) < 0) {
+                                      NULL, atom->u.remote_ref.push,
+                                      AHEAD_BEHIND_FULL) < 0) {
                        *s = xstrdup("");
                        return;
                }
@@ -1516,7 +1546,7 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
                        return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
                                               oid_to_hex(&oi->oid), ref->refname);
                }
-               grab_values(ref->value, deref, *obj, oi->content, oi->size);
+               grab_values(ref->value, deref, *obj, oi->content);
        }
 
        grab_common_values(ref->value, deref, oi);
@@ -1525,6 +1555,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
        return 0;
 }
 
+static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
+{
+       int i;
+
+       for (i = 0; worktrees[i]; i++) {
+               if (worktrees[i]->head_ref) {
+                       struct ref_to_worktree_entry *entry;
+                       entry = xmalloc(sizeof(*entry));
+                       entry->wt = worktrees[i];
+                       hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
+
+                       hashmap_add(map, entry);
+               }
+       }
+}
+
+static void lazy_init_worktree_map(void)
+{
+       if (ref_to_worktree_map.worktrees)
+               return;
+
+       ref_to_worktree_map.worktrees = get_worktrees(0);
+       hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
+       populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
+}
+
+static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
+{
+       struct hashmap_entry entry;
+       struct ref_to_worktree_entry *lookup_result;
+
+       lazy_init_worktree_map();
+
+       hashmap_entry_init(&entry, strhash(ref->refname));
+       lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
+
+       if (lookup_result)
+               return xstrdup(lookup_result->wt->path);
+       else
+               return xstrdup("");
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -1562,6 +1634,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 
                if (starts_with(name, "refname"))
                        refname = get_refname(atom, ref);
+               else if (!strcmp(name, "worktreepath")) {
+                       if (ref->kind == FILTER_REFS_BRANCHES)
+                               v->s = get_worktree_path(atom, ref);
+                       else
+                               v->s = xstrdup("");
+                       continue;
+               }
                else if (starts_with(name, "symref"))
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
@@ -2045,6 +2124,11 @@ void ref_array_clear(struct ref_array *array)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
        array->nr = array->alloc = 0;
+       if (ref_to_worktree_map.worktrees) {
+               hashmap_free(&(ref_to_worktree_map.map), 1);
+               free_worktrees(ref_to_worktree_map.worktrees);
+               ref_to_worktree_map.worktrees = NULL;
+       }
 }
 
 static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
@@ -2333,8 +2417,13 @@ void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
 
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
 {
-       if (!arg) /* should --no-sort void the list ? */
-               return -1;
+       /*
+        * NEEDSWORK: We should probably clear the list in this case, but we've
+        * already munged the global used_atoms list, which would need to be
+        * undone.
+        */
+       BUG_ON_OPT_NEG(unset);
+
        parse_ref_sorting(opt->value, arg);
        return 0;
 }