From: Nickolai Belakovski <nbelakov...@gmail.com>

Add an atom proving the path of the linked worktree where this ref is
checked out, if it is checked out in any linked worktrees, and empty
string otherwise.
---
 Documentation/git-for-each-ref.txt |  4 +++
 ref-filter.c                       | 70 ++++++++++++++++++++++++++++++++++++++
 t/t6302-for-each-ref-filter.sh     | 15 ++++++++
 3 files changed, 89 insertions(+)

diff --git a/Documentation/git-for-each-ref.txt 
b/Documentation/git-for-each-ref.txt
index 901faef1bf..9590f7beab 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -209,6 +209,10 @@ symref::
        `:lstrip` and `:rstrip` options in the same way as `refname`
        above.
 
+worktreepath::
+       The absolute path to the worktree in which the ref is checked
+       out, if it is checked out in any linked worktree. ' ' otherwise.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
diff --git a/ref-filter.c b/ref-filter.c
index 5de616befe..e8713484a1 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -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;
@@ -34,6 +36,8 @@ static struct ref_msg {
        "ahead %d, behind %d"
 };
 
+static struct worktree ** worktrees;
+
 void setup_ref_filter_porcelain_msg(void)
 {
        msgs.gone = _("gone");
@@ -75,6 +79,12 @@ static struct expand_data {
        struct object_info info;
 } oi, oi_deref;
 
+struct reftoworktreeinfo_entry {
+    struct hashmap_entry ent; // must be the first member!
+    char * ref; // key into map
+    struct worktree * wt;
+};
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -114,6 +124,7 @@ static struct used_atom {
                } objectname;
                struct refname_atom refname;
                char *head;
+               struct hashmap reftoworktreeinfo_map;
        } u;
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
@@ -420,6 +431,31 @@ static int head_atom_parser(const struct ref_format 
*format, struct used_atom *a
        return 0;
 }
 
+static int worktree_atom_parser(const struct ref_format *format,
+                               struct used_atom *atom,
+                               const char *arg,
+                               struct strbuf *unused_err)
+{
+       int i;
+       worktrees = get_worktrees(0);
+
+       hashmap_init(&(atom->u.reftoworktreeinfo_map), NULL, NULL, 0);
+
+       for (i = 0; worktrees[i]; i++) {
+               if (worktrees[i]->head_ref) {
+                       struct reftoworktreeinfo_entry *entry;
+                       FLEXPTR_ALLOC_STR(entry, ref, worktrees[i]->head_ref);
+                       hashmap_entry_init(entry, strhash(entry->ref));
+
+                       entry->wt = worktrees[i];
+
+                       hashmap_add(&(atom->u.reftoworktreeinfo_map), entry);
+               }
+       }
+
+       return 0;
+}
+
 static struct {
        const char *name;
        info_source source;
@@ -461,6 +497,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, FIELD_STR, worktree_atom_parser },
        { "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
        { "end", SOURCE_NONE },
        { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
@@ -1500,6 +1537,28 @@ static int get_object(struct ref_array_item *ref, int 
deref, struct object **obj
        return 0;
 }
 
+static const char * get_worktree_info(const struct used_atom *atom, const 
struct ref_array_item *ref)
+{
+       struct strbuf val = STRBUF_INIT;
+       struct reftoworktreeinfo_entry * entry;
+       struct reftoworktreeinfo_entry * lookup_result;
+
+       FLEXPTR_ALLOC_STR(entry, ref, ref->refname);
+       hashmap_entry_init(entry, strhash(entry->ref));
+       lookup_result = hashmap_get(&(atom->u.reftoworktreeinfo_map), entry, 
NULL);
+       free(entry);
+
+       if (lookup_result)
+       {
+               if (!strncmp(atom->name, "worktreepath", strlen(atom->name)))
+                       strbuf_addstr(&val, lookup_result->wt->path);
+       }
+       else
+               strbuf_addstr(&val, " ");
+
+       return strbuf_detach(&val, NULL);
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -1537,6 +1596,10 @@ static int populate_value(struct ref_array_item *ref, 
struct strbuf *err)
 
                if (starts_with(name, "refname"))
                        refname = get_refname(atom, ref);
+               else if (starts_with(name, "worktreepath")) {
+                       v->s = get_worktree_info(atom, ref);
+                       continue;
+               }
                else if (starts_with(name, "symref"))
                        refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
@@ -2013,7 +2076,14 @@ void ref_array_clear(struct ref_array *array)
        int i;
 
        for (i = 0; i < used_atom_cnt; i++)
+       {
+               if (!strncmp(used_atom[i].name, "worktreepath", 
strlen("worktreepath")))
+               {
+                       hashmap_free(&(used_atom[i].u.reftoworktreeinfo_map), 
1);
+                       free_worktrees(worktrees);
+               }
                free((char *)used_atom[i].name);
+       }
        FREE_AND_NULL(used_atom);
        used_atom_cnt = 0;
        for (i = 0; i < array->nr; i++)
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index fc067ed672..add70a4c3e 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -441,4 +441,19 @@ test_expect_success '--merged is incompatible with 
--no-merged' '
        test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
 '
 
+test_expect_success '"add" a worktree' '
+       mkdir worktree_dir &&
+       git worktree add -b master_worktree worktree_dir master
+'
+
+test_expect_success 'validate worktree atom' '
+       cat >expect <<-\EOF &&
+       master: checked out in a worktree
+       master_worktree: checked out in a worktree
+       side: not checked out in a worktree
+       EOF
+       git for-each-ref --format="%(refname:short): 
%(if)%(worktreepath)%(then)checked out in a worktree%(else)not checked out in a 
worktree%(end)" refs/heads/ >actual &&
+       test_cmp expect actual
+'
+
 test_done
-- 
2.14.2

Reply via email to