Skip to content

Commit

Permalink
ref-filter: add worktreepath atom
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
nbelakovski authored and gitster committed May 7, 2019
1 parent 0e94f7a commit 2582083
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Documentation/git-for-each-ref.txt
Expand Up @@ -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.
Expand Down
78 changes: 78 additions & 0 deletions ref-filter.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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().
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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")) {
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions t/t6302-for-each-ref-filter.sh
Expand Up @@ -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

0 comments on commit 2582083

Please sign in to comment.