ref-filter: add worktreepath atom
Add an atom providing 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. Signed-off-by: Nickolai Belakovski <nbelakovski@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nickolai Belakovski committed
Apr 28, 2019 at 22:19 UTC
2582083fa1830632e298727e973440907345267f
3 files changed
+96
Documentation/git-for-each-ref.txt
+5
@@ -214,6 +214,11 @@ symref::
214
`:lstrip` and `:rstrip` options in the same way as `refname`
215
above.
216
217
+worktreepath::
218
+ The absolute path to the worktree in which the ref is checked
219
+ out, if it is checked out in any linked worktree. Empty string
220
+ otherwise.
221
+
222
In addition to the above, for commit and tag objects, the header
223
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
224
be used to specify the value in the header field.
ref-filter.c
+78
@@ -20,6 +20,8 @@
20
#include "commit-slab.h"
21
#include "commit-graph.h"
22
#include "commit-reach.h"
23
+#include "worktree.h"
24
+#include "hashmap.h"
25
26
static struct ref_msg {
27
const char *gone;
@@ -75,6 +77,27 @@ static struct expand_data {
77
struct object_info info;
78
} oi, oi_deref;
79
80
+struct ref_to_worktree_entry {
81
+ struct hashmap_entry ent; /* must be the first member! */
82
+ struct worktree *wt; /* key is wt->head_ref */
83
+};
84
+
85
+static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
86
+ const void *existing_hashmap_entry_to_test,
87
+ const void *key,
88
+ const void *keydata_aka_refname)
89
+{
90
+ const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
91
+ const struct ref_to_worktree_entry *k = key;
92
+ return strcmp(e->wt->head_ref,
93
+ keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
94
+}
95
+
96
+static struct ref_to_worktree_map {
97
+ struct hashmap map;
98
+ struct worktree **worktrees;
99
+} ref_to_worktree_map;
100
+
101
/*
102
* An atom is a valid field atom listed below, possibly prefixed with
103
* a "*" to denote deref_tag().
@@ -480,6 +503,7 @@ static struct {
503
{ "flag", SOURCE_NONE },
504
{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
505
{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
506
+ { "worktreepath", SOURCE_NONE },
507
{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
508
{ "end", SOURCE_NONE },
509
{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
@@ -1529,6 +1553,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
1553
return 0;
1554
}
1555
1556
+static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
1557
+{
1558
+ int i;
1559
+
1560
+ for (i = 0; worktrees[i]; i++) {
1561
+ if (worktrees[i]->head_ref) {
1562
+ struct ref_to_worktree_entry *entry;
1563
+ entry = xmalloc(sizeof(*entry));
1564
+ entry->wt = worktrees[i];
1565
+ hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
1566
+
1567
+ hashmap_add(map, entry);
1568
+ }
1569
+ }
1570
+}
1571
+
1572
+static void lazy_init_worktree_map(void)
1573
+{
1574
+ if (ref_to_worktree_map.worktrees)
1575
+ return;
1576
+
1577
+ ref_to_worktree_map.worktrees = get_worktrees(0);
1578
+ hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
1579
+ populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
1580
+}
1581
+
1582
+static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
1583
+{
1584
+ struct hashmap_entry entry;
1585
+ struct ref_to_worktree_entry *lookup_result;
1586
+
1587
+ lazy_init_worktree_map();
1588
+
1589
+ hashmap_entry_init(&entry, strhash(ref->refname));
1590
+ lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
1591
+
1592
+ if (lookup_result)
1593
+ return xstrdup(lookup_result->wt->path);
1594
+ else
1595
+ return xstrdup("");
1596
+}
1597
+
1598
/*
1599
* Parse the object referred by ref, and grab needed value.
1600
*/
@@ -1566,6 +1632,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
1632
1633
if (starts_with(name, "refname"))
1634
refname = get_refname(atom, ref);
1635
+ else if (!strcmp(name, "worktreepath")) {
1636
+ if (ref->kind == FILTER_REFS_BRANCHES)
1637
+ v->s = get_worktree_path(atom, ref);
1638
+ else
1639
+ v->s = xstrdup("");
1640
+ continue;
1641
+ }
1642
else if (starts_with(name, "symref"))
1643
refname = get_symref(atom, ref);
1644
else if (starts_with(name, "upstream")) {
@@ -2049,6 +2122,11 @@ void ref_array_clear(struct ref_array *array)
2122
free_array_item(array->items[i]);
2123
FREE_AND_NULL(array->items);
2124
array->nr = array->alloc = 0;
2125
+ if (ref_to_worktree_map.worktrees) {
2126
+ hashmap_free(&(ref_to_worktree_map.map), 1);
2127
+ free_worktrees(ref_to_worktree_map.worktrees);
2128
+ ref_to_worktree_map.worktrees = NULL;
2129
+ }
2130
}
2131
2132
static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
t/t6302-for-each-ref-filter.sh
+13
@@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' '
441
test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
442
'
443
444
+test_expect_success 'validate worktree atom' '
445
+ cat >expect <<-EOF &&
446
+ master: $(pwd)
447
+ master_worktree: $(pwd)/worktree_dir
448
+ side: not checked out
449
+ EOF
450
+ git worktree add -b master_worktree worktree_dir master &&
451
+ git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
452
+ rm -r worktree_dir &&
453
+ git worktree prune &&
454
+ test_cmp expect actual
455
+'
456
+
457
test_done