git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 1/3] ref-filter: add worktreepath atom

From
Jeff King <peff@peff.net>
Date
Dec 18, 2018, 17:22 UTC
Message-ID
<20181218172236.GA28455@sigill.intra.peff.net>
In-Reply-To
<20181216215759.24011-2-nbelakovski@gmail.com>
On Sun, Dec 16, 2018 at 01:57:57PM -0800, nbelakovski@gmail.com wrote:
Show 5 quoted lines
> From: Nickolai Belakovski <nbelakovski@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.

I stumbled over the word "proving" here. Maybe "showing" would be more clear?

Show 12 quoted lines
> 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.
> +

Normally single-quotes are used in asciidoc to emphasize text, and the quotes aren't passed through. Asciidoc (and asciidoctor) do seem to render the literal quotes here, which is good. I wonder if it would be more clear to just write it out, though, like:

  ...any linked worktree. Otherwise, replaced with a single space.

Also, why are we replacing it with a single space? Wouldn't the empty string be more customary (and work with the other "if empty, then do this" formatting options)?

Show 5 quoted lines
> @@ -34,6 +36,8 @@ static struct ref_msg {
>  	"ahead %d, behind %d"
>  };
>  
> +static struct worktree ** worktrees;

Minor style nit: we put the "*" in a pointer declaration next to the variable name, without intervening whitespace. Like:

  static struct worktree **worktrees;
Show 9 quoted lines
> @@ -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;
> +};
A few style nits:
  - the "*" space thing from above (it's in other places below, too, but
    I won't point out each)
  - we prefer "/* */" comments, even for single-liners
  - since we do all-lowercase identifiers, use more underscores to break
    things up. E.g., ref_to_worktree_entry.

Here we store the refname as a separate variable, but then point to the worktree itself to access wt->path. Why do we treat these differently? I.e., I'd expect to see either:

  1. Each entry holding a single worktree object, and using its head_ref
     and path fields, like:
       struct ref_to_worktree_entry {
               struct hashmap_entry ent; /* must be first */
	       struct worktree *wt;
       };
       ....
       entry = xmalloc(sizeof(*entry));
       entry->wt = wt;
       hashmap_entry_init(entry, strhash(wt->head_ref));
       ...
       strbuf_addstr(&out, result->wt->path);
  2. Each entry containing just the bits it needs, like:
       struct ref_to_worktree_entry {
               struct hashmap_entry ent; /* must be first */
	       char *ref;
	       char *path;
       };
       ...
       /*
        * We could use FLEXPTR_ALLOC_STR() here, but it doesn't actually
	* support holding _two_ strings. Separate allocations probably
	* aren't a huge deal here, since there are only a handful of
	* worktrees.
	*/
       entry = xmalloc(sizeof(*entry));
       entry->ref = wt->head_ref;
       entry->path = wt->path;
       hashmap_entry_init(entry, strhash(entry->ref));
       ...
       strbuf_addstr(&out, result->path);

I think the first one is strictly preferable unless we're worried about the lifetime of the "struct worktree" going away. I don't think that's an issue, though; they are ours until we call free_worktrees().

Show 7 quoted lines
> @@ -114,6 +124,7 @@ static struct used_atom {
>  		} objectname;
>  		struct refname_atom refname;
>  		char *head;
> +		struct hashmap reftoworktreeinfo_map;
>  	} u;
>  } *used_atom;

This uses one map for each %(worktree) we use. But won't they all be the same? It would ideally be associated with the ref-filter. There's no ref-filter context struct to hold this kind of data, just static globals in ref-filter.c (including this used_atom struct!). That's something we'll probably need to fix in the long run, but I think it would be reasonable to just have:

  static struct hashmap ref_to_worktree_map;

next to the declaration of used_atom_cnt, need_symref, etc. And then those can all eventually get moved into a struct together.

Show 8 quoted lines
> @@ -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 },
Marking as SOURCE_NONE makes sense.
Show 10 quoted lines
> +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);

We shouldn't need to do an allocation just for a lookup. That's what the extra "keydata" parameter is for in the comparison function. And I guess this is what led you to have "char *ref" in the struct, rather than reusing wt->head_ref (because you don't have a "struct worktree" here).

You should be able to do it like this:
  struct hashmap_entry entry;
  struct ref_to_worktree_entry *result;
  hashmap_entry_init(entry, strhash(ref->refname));
  result = hashmap_get(&ref_to_worktree_map, &entry, ref->refname));
  ...
and then your comparison function would look like this:
  int ref_to_worktree_hashcmp(const void *data,
                              const void *entry,
			      const void *entry_or_key,
			      const void *keydata)
  {
          const struct ref_to_worktree_entry *a = entry;
          const struct ref_to_worktree_entry *b = entry;
	  if (keydata)
	          return strcmp(a->wt->head_ref, keydata);
	  else
	          return strcmp(a->wt->head_ref, b->wt->head_ref);
  }

If you're thinking that this API is totally confusing and hard to figure out, I agree. It's optimized to avoid extra allocations. I wish we had a better one for simple cases (especially string->string mappings like this).

Speaking of comparison functions, I didn't see one in your patch. Don't you need to pass one to hashmap_init?

Show 7 quoted lines
> +	if (lookup_result)
> +	{
> +		if (!strncmp(atom->name, "worktreepath", strlen(atom->name)))
> +			strbuf_addstr(&val, lookup_result->wt->path);
> +	}
> +	else
> +		strbuf_addstr(&val, " ");

What's this extra strncmp about? If we're _not_ a worktreepath atom, we'd still do the lookup only to put nothing in the string?

I think we'd only call this function when populate_value() sees a worktreepath atom, though:

Show 8 quoted lines
> @@ -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;
> +		}

So it would be OK to drop the check of atom->name again inside get_worktree_info().

Show 10 quoted lines
> @@ -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);
> +		}

And if we move the mapping out to a static global, then this only has to be done once, not once per atom. In fact, I think this could double-free "worktrees" with your current patch if you have two "%(worktree)" placeholders, since "worktrees" already is a global.

Show 22 quoted lines
> 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
> +'

It's probably worth testing that the path we get is actually sane, too. I.e., expect something more like:

   cat >expect <<-\EOF
   master: $PWD
   master: $PWD/worktree
   side: not checked out
   EOF
   git for-each-ref \
     --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked %out%(end)

(I wish there was a way to avoid that really long line, but I don't think there is).

-Peff
Previous: nbelakovski@gmail.comNext: Nickolai Belakovski
Message 36 of 125 in “branch: colorize branches checked out in a linked working tree the same way as the current branch is colorized”
  1. branch: colorize branches checked out in a linked working tree the same way as the current branch is colorizedNickolai Belakovski, Sep 27, 2018
  2. Ævar Arnfjörð BjarmasonSep 27, 2018
  3. Nickolai BelakovskiSep 27, 2018
  4. Duy NguyenSep 27, 2018
  5. Jeff KingSep 27, 2018
  6. Nickolai BelakovskiSep 27, 2018
  7. Jeff KingSep 27, 2018
  8. Rafael AscensãoSep 27, 2018
  9. Jeff KingSep 27, 2018
  10. Jeff KingSep 27, 2018
  11. Junio C HamanoSep 27, 2018
  12. Jeff KingSep 28, 2018
  13. Junio C HamanoSep 28, 2018
  14. Rafael AscensãoSep 27, 2018
  15. Jeff KingSep 28, 2018
  16. Ævar Arnfjörð BjarmasonSep 27, 2018
  17. Nickolai BelakovskiSep 27, 2018
  18. Rafael AscensãoSep 27, 2018
  19. 0/2 refactoring branch colorization to ref-filternbelakovski@gmail.com, Nov 11, 2018
  20. 1/2 ref-filter: add worktree atomnbelakovski@gmail.com, Nov 11, 2018
  21. Junio C HamanoNov 12, 2018
  22. Jeff KingNov 12, 2018
  23. Junio C HamanoNov 13, 2018
  24. Nickolai BelakovskiNov 21, 2018
  25. Jeff KingNov 21, 2018
  26. Jeff KingNov 12, 2018
  27. 2/2 branch: Mark and colorize a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Nov 11, 2018
  28. Junio C HamanoNov 12, 2018
  29. Jeff KingNov 12, 2018
  30. Rafael AscensãoNov 12, 2018
  31. Junio C HamanoNov 13, 2018
  32. Jeff KingNov 13, 2018
  33. Nickolai BelakovskiNov 21, 2018
  34. 0/3 nbelakovski@gmail.com, Dec 16, 2018
  35. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Dec 16, 2018
  36. Jeff KingDec 18, 2018
  37. Nickolai BelakovskiDec 20, 2018
  38. Jeff KingDec 20, 2018
  39. 0/3 nbelakovski@gmail.com, Dec 24, 2018
  40. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Dec 24, 2018
  41. Jeff KingJan 3, 2019
  42. Eric SunshineJan 3, 2019
  43. 2/3 branch: Mark and color a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Dec 24, 2018
  44. 3/3 branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktreenbelakovski@gmail.com, Dec 24, 2018
  45. Jeff KingJan 3, 2019
  46. Jeff KingJan 3, 2019
  47. 2/3 branch: Mark and color a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Dec 16, 2018
  48. 3/3 branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktreenbelakovski@gmail.com, Dec 16, 2018
  49. Jeff KingDec 18, 2018
  50. 0/3 nbelakovski@gmail.com, Jan 6, 2019
  51. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Jan 6, 2019
  52. Junio C HamanoJan 7, 2019
  53. Nickolai BelakovskiJan 18, 2019
  54. Nickolai BelakovskiJan 18, 2019
  55. 2/3 branch: Mark and color a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Jan 6, 2019
  56. Junio C HamanoJan 7, 2019
  57. Philip OakleyJan 10, 2019
  58. Nickolai BelakovskiJan 13, 2019
  59. Junio C HamanoJan 14, 2019
  60. Nickolai BelakovskiJan 18, 2019
  61. 3/3 branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktreenbelakovski@gmail.com, Jan 6, 2019
  62. Junio C HamanoJan 7, 2019
  63. 0/3 nbelakovski@gmail.com, Jan 22, 2019
  64. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Jan 22, 2019
  65. Junio C HamanoJan 23, 2019
  66. Nickolai BelakovskiJan 23, 2019
  67. Junio C HamanoJan 24, 2019
  68. Jeff KingJan 24, 2019
  69. Junio C HamanoJan 24, 2019
  70. Jeff KingJan 24, 2019
  71. Junio C HamanoJan 24, 2019
  72. Jeff KingJan 24, 2019
  73. Nickolai BelakovskiJan 31, 2019
  74. Jeff KingJan 31, 2019
  75. Junio C HamanoJan 31, 2019
  76. Jeff KingJan 31, 2019
  77. 2/3 branch: Mark and color a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Jan 22, 2019
  78. 3/3 branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktreenbelakovski@gmail.com, Jan 22, 2019
  79. 0/3 nbelakovski@gmail.com, Feb 1, 2019
  80. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Feb 1, 2019
  81. Eric SunshineFeb 1, 2019
  82. Nickolai BelakovskiFeb 1, 2019
  83. Junio C HamanoFeb 4, 2019
  84. Nickolai BelakovskiFeb 18, 2019
  85. Junio C HamanoFeb 1, 2019
  86. 2/3 branch: Mark and color a branch differently if it is checked out in a linked worktreenbelakovski@gmail.com, Feb 1, 2019
  87. Junio C HamanoFeb 1, 2019
  88. Nickolai BelakovskiFeb 1, 2019
  89. Junio C HamanoFeb 4, 2019
  90. 3/3 branch: Add an extra verbose output displaying worktree path for refs checked out in a linked worktreenbelakovski@gmail.com, Feb 1, 2019
  91. Eric SunshineFeb 1, 2019
  92. Nickolai BelakovskiFeb 1, 2019
  93. Junio C HamanoFeb 1, 2019
  94. Nickolai BelakovskiFeb 1, 2019
  95. [RFC] Sample of test for git branch -vvnbelakovski@gmail.com, Feb 2, 2019
  96. Junio C HamanoFeb 1, 2019
  97. Nickolai BelakovskiFeb 1, 2019
  98. 0/3 nbelakovski@gmail.com, Feb 19, 2019
  99. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Feb 19, 2019
  100. 2/3 branch: update output to include worktree infonbelakovski@gmail.com, Feb 19, 2019
  101. Jeff KingFeb 21, 2019
  102. Nickolai BelakovskiMar 14, 2019
  103. 3/3 branch: add worktree info on verbose outputnbelakovski@gmail.com, Feb 19, 2019
  104. Jeff KingFeb 21, 2019
  105. Nickolai BelakovskiMar 14, 2019
  106. Junio C HamanoMar 18, 2019
  107. Jeff KingFeb 21, 2019
  108. Nickolai BelakovskiMar 14, 2019
  109. 0/3 nbelakovski@gmail.com, Mar 16, 2019
  110. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Mar 16, 2019
  111. 2/3 branch: update output to include worktree infonbelakovski@gmail.com, Mar 16, 2019
  112. 3/3 branch: add worktree info on verbose outputnbelakovski@gmail.com, Mar 16, 2019
  113. SZEDER GáborMar 18, 2019
  114. Junio C HamanoMar 18, 2019
  115. 0/3 nbelakovski@gmail.com, Apr 29, 2019
  116. 1/3 ref-filter: add worktreepath atomnbelakovski@gmail.com, Apr 29, 2019
  117. 2/3 branch: update output to include worktree infonbelakovski@gmail.com, Apr 29, 2019
  118. 3/3 branch: add worktree info on verbose outputnbelakovski@gmail.com, Apr 29, 2019
  119. SZEDER GáborApr 29, 2019
  120. Nickolai BelakovskiApr 29, 2019
  121. Johannes SchindelinApr 29, 2019
  122. Nickolai BelakovskiApr 29, 2019
  123. Ævar Arnfjörð BjarmasonSep 27, 2018
  124. Johannes SchindelinOct 2, 2018
  125. Johannes SchindelinApr 30, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.