Browse Source

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>
maint
Nickolai Belakovski 6 years ago committed by Junio C Hamano
parent
commit
2582083fa1
  1. 5
      Documentation/git-for-each-ref.txt
  2. 78
      ref-filter.c
  3. 13
      t/t6302-for-each-ref-filter.sh

5
Documentation/git-for-each-ref.txt

@ -214,6 +214,11 @@ symref:: @@ -214,6 +214,11 @@ 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. Empty string
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.

78
ref-filter.c

@ -20,6 +20,8 @@ @@ -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 { @@ -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,6 +503,7 @@ static struct { @@ -480,6 +503,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 },
@ -1529,6 +1553,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj @@ -1529,6 +1553,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.
*/
@ -1566,6 +1632,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) @@ -1566,6 +1632,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")) {
@ -2049,6 +2122,11 @@ void ref_array_clear(struct ref_array *array) @@ -2049,6 +2122,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)

13
t/t6302-for-each-ref-filter.sh

@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' ' @@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' '
test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
'

test_expect_success 'validate worktree atom' '
cat >expect <<-EOF &&
master: $(pwd)
master_worktree: $(pwd)/worktree_dir
side: not checked out
EOF
git worktree add -b master_worktree worktree_dir master &&
git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
rm -r worktree_dir &&
git worktree prune &&
test_cmp expect actual
'

test_done

Loading…
Cancel
Save