Subject: [PATCH v2 1/2] ref-filter: add worktree atom

From: [email-masked]
Date: Sun, 11 Nov 2018 15:58:30 -0800

From: Nickolai Belakovski <[email-masked]>

Add an atom expressing whether the particular ref is checked out in a
linked worktree.

Signed-off-by: Nickolai Belakovski <[email-masked]>
---
 ref-filter.c                   | 31 +++++++++++++++++++++++++++++++
 t/t6302-for-each-ref-filter.sh | 15 +++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/ref-filter.c b/ref-filter.c
index 0c45ed9d94..53e2504f5d 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -20,6 +20,7 @@
 #include "commit-slab.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
+#include "worktree.h"
 
 static struct ref_msg {
 	const char *gone;
@@ -114,6 +115,7 @@ static struct used_atom {
 		} objectname;
 		struct refname_atom refname;
 		char *head;
+		struct string_list worktree_heads;
 	} u;
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
@@ -420,6 +422,28 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a
 	return 0;
 }
 
+static int worktree_head_atom_parser(const struct ref_format *format,
+									 struct used_atom *atom,
+									 const char *arg,
+									 struct strbuf *unused_err)
+{
+	struct worktree **worktrees = get_worktrees(0);
+	int i;
+
+	string_list_init(&atom->u.worktree_heads, 1);
+
+	for (i = 0; worktrees[i]; i++) {
+		if (worktrees[i]->head_ref)
+			string_list_append(&atom->u.worktree_heads,
+							   worktrees[i]->head_ref);
+	}
+
+	string_list_sort(&atom->u.worktree_heads);
+
+	free_worktrees(worktrees);
+	return 0;
+}
+
 static struct {
 	const char *name;
 	info_source source;
@@ -461,6 +485,7 @@ static struct {
 	{ "flag", SOURCE_NONE },
 	{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
 	{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
+	{ "worktree", SOURCE_NONE, FIELD_STR, worktree_head_atom_parser },
 	{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
 	{ "end", SOURCE_NONE },
 	{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
@@ -1594,6 +1619,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
 			else
 				v->s = xstrdup(" ");
 			continue;
+		} else if (!strcmp(name, "worktree")) {
+			if (string_list_has_string(&atom->u.worktree_heads, ref->refname))
+				v->s = xstrdup("+");
+			else
+				v->s = xstrdup(" ");
+			continue;
 		} else if (starts_with(name, "align")) {
 			v->handler = align_atom_handler;
 			v->s = xstrdup("");
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index fc067ed672..5e6d249d4c 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)%(worktree)%(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