Merge branch 'dl/complete-cherry-pick-revert-skip'
[gitweb.git] / ref-filter.c
index 3e10fd647b516d45dd03b596bc9162739af7262d..220e9bd74a507cb5e73fcdd64e88ab3ff384baff 100644 (file)
@@ -20,6 +20,8 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
+#include "worktree.h"
+#include "hashmap.h"
 #include "argv-array.h"
 
 static struct ref_msg {
@@ -76,6 +78,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().
@@ -481,6 +504,7 @@ 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 },
@@ -1004,7 +1028,7 @@ static const char *copy_name(const char *buf)
                if (!strncmp(cp, " <", 2))
                        return xmemdupz(buf, cp - buf);
        }
-       return "";
+       return xstrdup("");
 }
 
 static const char *copy_email(const char *buf)
@@ -1012,10 +1036,10 @@ static const char *copy_email(const char *buf)
        const char *email = strchr(buf, '<');
        const char *eoemail;
        if (!email)
-               return "";
+               return xstrdup("");
        eoemail = strchr(email, '>');
        if (!eoemail)
-               return "";
+               return xstrdup("");
        return xmemdupz(email, eoemail + 1 - email);
 }
 
@@ -1448,35 +1472,35 @@ char *get_head_description(void)
        struct wt_status_state state;
        memset(&state, 0, sizeof(state));
        wt_status_get_state(the_repository, &state, 1);
+
+       /*
+        * The ( character must be hard-coded and not part of a localizable
+        * string, since the description is used as a sort key and compared
+        * with ref names.
+        */
+       strbuf_addch(&desc, '(');
        if (state.rebase_in_progress ||
            state.rebase_interactive_in_progress) {
                if (state.branch)
-                       strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+                       strbuf_addf(&desc, _("no branch, rebasing %s"),
                                    state.branch);
                else
-                       strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
+                       strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
                                    state.detached_from);
        } else if (state.bisect_in_progress)
-               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+               strbuf_addf(&desc, _("no branch, bisect started on %s"),
                            state.branch);
        else if (state.detached_from) {
                if (state.detached_at)
-                       /*
-                        * TRANSLATORS: make sure this matches "HEAD
-                        * detached at " in wt-status.c
-                        */
-                       strbuf_addf(&desc, _("(HEAD detached at %s)"),
-                               state.detached_from);
+                       strbuf_addstr(&desc, HEAD_DETACHED_AT);
                else
-                       /*
-                        * TRANSLATORS: make sure this matches "HEAD
-                        * detached from " in wt-status.c
-                        */
-                       strbuf_addf(&desc, _("(HEAD detached from %s)"),
-                               state.detached_from);
+                       strbuf_addstr(&desc, HEAD_DETACHED_FROM);
+               strbuf_addstr(&desc, state.detached_from);
        }
        else
-               strbuf_addstr(&desc, _("(no branch)"));
+               strbuf_addstr(&desc, _("no branch"));
+       strbuf_addch(&desc, ')');
+
        free(state.branch);
        free(state.onto);
        free(state.detached_from);
@@ -1532,6 +1556,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.
  */
@@ -1569,6 +1635,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")) {
@@ -1693,7 +1766,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
         * If it is a tag object, see if we use a value that derefs
         * the object, and if we do grab the object it refers to.
         */
-       oi_deref.oid = ((struct tag *)obj)->tagged->oid;
+       oi_deref.oid = *get_tagged_oid((struct tag *)obj);
 
        /*
         * NEEDSWORK: This derefs tag only once, which
@@ -1924,7 +1997,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at,
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
-               tagged_oid = &((struct tag *)obj)->tagged->oid;
+               tagged_oid = get_tagged_oid((struct tag *)obj);
        if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
                return tagged_oid;
        return NULL;
@@ -2069,7 +2142,9 @@ static void free_array_item(struct ref_array_item *item)
 {
        free((char *)item->symref);
        if (item->value) {
-               free((char *)item->value->s);
+               int i;
+               for (i = 0; i < used_atom_cnt; i++)
+                       free((char *)item->value[i].s);
                free(item->value);
        }
        free(item);
@@ -2080,14 +2155,21 @@ void ref_array_clear(struct ref_array *array)
 {
        int i;
 
-       for (i = 0; i < used_atom_cnt; i++)
-               free((char *)used_atom[i].name);
-       FREE_AND_NULL(used_atom);
-       used_atom_cnt = 0;
        for (i = 0; i < array->nr; i++)
                free_array_item(array->items[i]);
        FREE_AND_NULL(array->items);
        array->nr = array->alloc = 0;
+
+       for (i = 0; i < used_atom_cnt; i++)
+               free((char *)used_atom[i].name);
+       FREE_AND_NULL(used_atom);
+       used_atom_cnt = 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)