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

Re: [PATCH 2/2] worktree.c: use submodule interface to access refs from another worktree

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Feb 9, 2017, 06:07 UTC
Message-ID
<37fe2024-0378-a974-a28d-18a89d3e2312@alum.mit.edu>
In-Reply-To
<20170208113144.8201-3-pclouds@gmail.com>
On 02/08/2017 12:31 PM, Nguyễn Thái Ngọc Duy wrote:
Show 34 quoted lines
> The patch itself is relatively simple: manual parsing code is replaced
> with a call to resolve_ref_submodule(). The manual parsing code must die
> because only refs/files-backend.c should do that. Why submodule here is
> a more interesting question.
> 
> From an outside look, any .git/worktrees/foo is seen as a "normal"
> repository. You can set GIT_DIR to it and have access to everything,
> even shared things that are not literally inside that directory, like
> object db or shared refs.
> 
> On top of that, linked worktrees point to those directories with ".git"
> files. These two make a linked worktree's path "X" a "submodule" (*) (**)
> because X/.git is a file that points to a repository somewhere.
> 
> As such, we can just linked worktree's path as a submodule. We just need
> to make sure they are unique because they are used to lookup submodule
> refs store.
> 
> Main worktree is a a bit trickier. If we stand at a linked worktree, we
> may still need to peek into main worktree's HEAD, for example. We can
> treat main worktree's path as submodule as well since git_path_submodule()
> can tolerate ".git" dirs, in addition to ".git" files.
> 
> The constraint is, if main worktree is X, then the git repo must be at
> X/.git. If the user separates .git repo far away and tell git to point
> to it via GIT_DIR or something else, then the "main worktree as submodule"
> trick fails. Within multiple worktree context, I think we can limit
> support to "standard" layout, at least for now.
> 
> (*) The differences in sharing object database and refs between
> submodules and linked worktrees don't really matter in this context.
> 
> (**) At this point, we may want to rename refs *_submodule API to
> something more neutral, maybe s/_submodule/_remote/

It is unquestionably a good goal to avoid parsing references outside of `refs/files-backend.c`. But I'm not a fan of this approach.

There are two meanings of the concept of a "ref store", and I think this change muddles them:

1. The references that happen to be *physically* stored in a particular
   location, for example the `refs/bisect/*` references in a worktree.
2. The references that *logically* should be considered part of a
   particular repository. This might require stitching together
   references from multiple sources, for example `HEAD` and
   `refs/bisect` from a worktree's own directory with other
   references from the main repository.
Either of these concepts can be implemented via the `ref_store` abstraction.

The `ref_store` for a submodule should represent the references logically visible from the submodule. The main program shouldn't care whether the references are stored in a single physical location or spread across multiple locations (for example, if the submodule were itself a linked worktree).

The `ref_store` that you want here for a worktree is not the worktree's *logical* `ref_store`. You want the worktree's *physical* `ref_store`. Mixing logical and physical reference stores together is a bad idea (even if we were willing to ignore the fact that worktrees are not submodules in the accepted sense of the word).

The point of my `submodule-hash` branch [1] was to separate these concepts better by breaking the current 1:1 connection between `ref_store`s and submodules. This would allow `ref_store`s to be created for other purposes, such as to represent worktree refs. If you want the *logical* `ref_store` for a submodule, you access it through the `submodule_ref_stores` table. If you want the *physical* `ref_store` for a worktree, you should access it through a different table.

I think the best solution would be to expose the concept of `ref_store` in the public refs API. Then users of submodules would essentially do

    struct ref_store *refs = get_submodule_refs(submodule_path);
    ... resolve_ref_recursively(refs, refname, 0, sha1, &flags) ...
    ... for_each_ref(refs, fn, cb_data) ...

whereas for a worktree you'd have to look up the `ref_store` instance somewhere else (or maybe keep it as part of some worktree structure, if there is one) but you would use it via the same API.

Michael
[1] https://github.com/mhagger/git
Show 213 quoted lines
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  branch.c   |  3 +-
>  worktree.c | 99 +++++++++++++++-----------------------------------------------
>  worktree.h |  2 +-
>  3 files changed, 27 insertions(+), 77 deletions(-)
> 
> diff --git a/branch.c b/branch.c
> index b955d4f316..db5843718f 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -354,7 +354,8 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
>  	for (i = 0; worktrees[i]; i++) {
>  		if (worktrees[i]->is_detached)
>  			continue;
> -		if (strcmp(oldref, worktrees[i]->head_ref))
> +		if (worktrees[i]->head_ref &&
> +		    strcmp(oldref, worktrees[i]->head_ref))
>  			continue;
>  
>  		if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
> diff --git a/worktree.c b/worktree.c
> index d633761575..25e5bc9a3e 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -19,54 +19,24 @@ void free_worktrees(struct worktree **worktrees)
>  	free (worktrees);
>  }
>  
> -/*
> - * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,
> - * set is_detached to 1 (0) if the ref is detached (is not detached).
> - *
> - * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
> - * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
> - * git_path). Parse the ref ourselves.
> - *
> - * return -1 if the ref is not a proper ref, 0 otherwise (success)
> - */
> -static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
> -{
> -	if (is_detached)
> -		*is_detached = 0;
> -	if (!strbuf_readlink(ref, path_to_ref, 0)) {
> -		/* HEAD is symbolic link */
> -		if (!starts_with(ref->buf, "refs/") ||
> -				check_refname_format(ref->buf, 0))
> -			return -1;
> -	} else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
> -		/* textual symref or detached */
> -		if (!starts_with(ref->buf, "ref:")) {
> -			if (is_detached)
> -				*is_detached = 1;
> -		} else {
> -			strbuf_remove(ref, 0, strlen("ref:"));
> -			strbuf_trim(ref);
> -			if (check_refname_format(ref->buf, 0))
> -				return -1;
> -		}
> -	} else
> -		return -1;
> -	return 0;
> -}
> -
>  /**
> - * Add the head_sha1 and head_ref (if not detached) to the given worktree
> + * Update head_sha1, head_ref and is_detached of the given worktree
>   */
> -static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
> +static void add_head_info(struct worktree *wt)
>  {
> -	if (head_ref->len) {
> -		if (worktree->is_detached) {
> -			get_sha1_hex(head_ref->buf, worktree->head_sha1);
> -		} else {
> -			resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
> -			worktree->head_ref = strbuf_detach(head_ref, NULL);
> -		}
> -	}
> +	int flags;
> +	const char *target;
> +
> +	target = resolve_ref_submodule(wt->path, "HEAD",
> +				       RESOLVE_REF_READING,
> +				       wt->head_sha1, &flags);
> +	if (!target)
> +		return;
> +
> +	if (flags & REF_ISSYMREF)
> +		wt->head_ref = xstrdup(target);
> +	else
> +		wt->is_detached = 1;
>  }
>  
>  /**
> @@ -77,9 +47,7 @@ static struct worktree *get_main_worktree(void)
>  	struct worktree *worktree = NULL;
>  	struct strbuf path = STRBUF_INIT;
>  	struct strbuf worktree_path = STRBUF_INIT;
> -	struct strbuf head_ref = STRBUF_INIT;
>  	int is_bare = 0;
> -	int is_detached = 0;
>  
>  	strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
>  	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
> @@ -91,13 +59,10 @@ static struct worktree *get_main_worktree(void)
>  	worktree = xcalloc(1, sizeof(*worktree));
>  	worktree->path = strbuf_detach(&worktree_path, NULL);
>  	worktree->is_bare = is_bare;
> -	worktree->is_detached = is_detached;
> -	if (!parse_ref(path.buf, &head_ref, &is_detached))
> -		add_head_info(&head_ref, worktree);
> +	add_head_info(worktree);
>  
>  	strbuf_release(&path);
>  	strbuf_release(&worktree_path);
> -	strbuf_release(&head_ref);
>  	return worktree;
>  }
>  
> @@ -106,8 +71,6 @@ static struct worktree *get_linked_worktree(const char *id)
>  	struct worktree *worktree = NULL;
>  	struct strbuf path = STRBUF_INIT;
>  	struct strbuf worktree_path = STRBUF_INIT;
> -	struct strbuf head_ref = STRBUF_INIT;
> -	int is_detached = 0;
>  
>  	if (!id)
>  		die("Missing linked worktree name");
> @@ -127,19 +90,14 @@ static struct worktree *get_linked_worktree(const char *id)
>  	strbuf_reset(&path);
>  	strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
>  
> -	if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
> -		goto done;
> -
>  	worktree = xcalloc(1, sizeof(*worktree));
>  	worktree->path = strbuf_detach(&worktree_path, NULL);
>  	worktree->id = xstrdup(id);
> -	worktree->is_detached = is_detached;
> -	add_head_info(&head_ref, worktree);
> +	add_head_info(worktree);
>  
>  done:
>  	strbuf_release(&path);
>  	strbuf_release(&worktree_path);
> -	strbuf_release(&head_ref);
>  	return worktree;
>  }
>  
> @@ -334,8 +292,6 @@ const struct worktree *find_shared_symref(const char *symref,
>  					  const char *target)
>  {
>  	const struct worktree *existing = NULL;
> -	struct strbuf path = STRBUF_INIT;
> -	struct strbuf sb = STRBUF_INIT;
>  	static struct worktree **worktrees;
>  	int i = 0;
>  
> @@ -345,6 +301,10 @@ const struct worktree *find_shared_symref(const char *symref,
>  
>  	for (i = 0; worktrees[i]; i++) {
>  		struct worktree *wt = worktrees[i];
> +		const char *symref_target;
> +		unsigned char sha1[20];
> +		int flags;
> +
>  		if (wt->is_bare)
>  			continue;
>  
> @@ -359,25 +319,14 @@ const struct worktree *find_shared_symref(const char *symref,
>  			}
>  		}
>  
> -		strbuf_reset(&path);
> -		strbuf_reset(&sb);
> -		strbuf_addf(&path, "%s/%s",
> -			    get_worktree_git_dir(wt),
> -			    symref);
> -
> -		if (parse_ref(path.buf, &sb, NULL)) {
> -			continue;
> -		}
> -
> -		if (!strcmp(sb.buf, target)) {
> +		symref_target = resolve_ref_submodule(wt->path, symref, 0,
> +						      sha1, &flags);
> +		if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
>  			existing = wt;
>  			break;
>  		}
>  	}
>  
> -	strbuf_release(&path);
> -	strbuf_release(&sb);
> -
>  	return existing;
>  }
>  
> diff --git a/worktree.h b/worktree.h
> index 6bfb985203..5ea5e503fb 100644
> --- a/worktree.h
> +++ b/worktree.h
> @@ -4,7 +4,7 @@
>  struct worktree {
>  	char *path;
>  	char *id;
> -	char *head_ref;
> +	char *head_ref;		/* NULL if HEAD is broken or detached */
>  	char *lock_reason;	/* internal use */
>  	unsigned char head_sha1[20];
>  	int is_detached;
> 
Previous: Junio C HamanoNext: Junio C Hamano
Message 8 of 51 in “Kill manual ref parsing code in worktree.c”
  1. 0/2 Kill manual ref parsing code in worktree.cNguyễn Thái Ngọc Duy, Feb 8, 2017
  2. 1/2 refs.c: add resolve_ref_submodule()Nguyễn Thái Ngọc Duy, Feb 8, 2017
  3. Michael HaggertyFeb 9, 2017
  4. 2/2 worktree.c: use submodule interface to access refs from another worktreeNguyễn Thái Ngọc Duy, Feb 8, 2017
  5. Stefan BellerFeb 8, 2017
  6. Junio C HamanoFeb 8, 2017
  7. Junio C HamanoFeb 8, 2017
  8. Michael HaggertyFeb 9, 2017
  9. Junio C HamanoFeb 9, 2017
  10. Michael HaggertyFeb 9, 2017
  11. Duy NguyenFeb 9, 2017
  12. Michael HaggertyFeb 9, 2017
  13. Junio C HamanoFeb 8, 2017
  14. 0/5 Kill manual ref parsing code in worktree.cNguyễn Thái Ngọc Duy, Feb 16, 2017
  15. 1/5 refs: introduce get_worktree_ref_store()Nguyễn Thái Ngọc Duy, Feb 16, 2017
  16. 2/5 refs.c: add refs_resolve_ref_unsafe()Nguyễn Thái Ngọc Duy, Feb 16, 2017
  17. 3/5 worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe()Nguyễn Thái Ngọc Duy, Feb 16, 2017
  18. 4/5 refs: add refs_create_symref()Nguyễn Thái Ngọc Duy, Feb 16, 2017
  19. 5/5 refs: kill set_worktree_head_symref()Nguyễn Thái Ngọc Duy, Feb 16, 2017
  20. 0/4 Kill manual ref parsing code in worktree.cNguyễn Thái Ngọc Duy, Mar 18, 2017
  21. 4/4 refs: kill set_worktree_head_symref()Nguyễn Thái Ngọc Duy, Mar 18, 2017
  22. 3/4 worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe()Nguyễn Thái Ngọc Duy, Mar 18, 2017
  23. 2/4 refs: introduce get_worktree_ref_store()Nguyễn Thái Ngọc Duy, Mar 18, 2017
  24. Michael HaggertyMar 20, 2017
  25. Duy NguyenMar 20, 2017
  26. Michael HaggertyMar 20, 2017
  27. Duy NguyenMar 26, 2017
  28. 1/4 environment.c: fix potential segfault by get_git_common_dir()Nguyễn Thái Ngọc Duy, Mar 18, 2017
  29. Junio C HamanoMar 18, 2017
  30. Duy NguyenMar 19, 2017
  31. Junio C HamanoMar 18, 2017
  32. 0/5 Kill manual ref parsing code in worktree.cNguyễn Thái Ngọc Duy, Apr 4, 2017
  33. 1/5 environment.c: fix potential segfault by get_git_common_dir()Nguyễn Thái Ngọc Duy, Apr 4, 2017
  34. 2/5 refs.c: make submodule ref store hashmap genericNguyễn Thái Ngọc Duy, Apr 4, 2017
  35. 3/5 refs: introduce get_worktree_ref_store()Nguyễn Thái Ngọc Duy, Apr 4, 2017
  36. Michael HaggertyApr 22, 2017
  37. 4/5 worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe()Nguyễn Thái Ngọc Duy, Apr 4, 2017
  38. 5/5 refs: kill set_worktree_head_symref()Nguyễn Thái Ngọc Duy, Apr 4, 2017
  39. Junio C HamanoApr 14, 2017
  40. Junio C HamanoApr 14, 2017
  41. Duy NguyenApr 14, 2017
  42. Junio C HamanoApr 17, 2017
  43. Michael HaggertyApr 22, 2017
  44. 0/6 Kill manual ref parsing code in worktree.cNguyễn Thái Ngọc Duy, Apr 24, 2017
  45. 1/6 environment.c: fix potential segfault by get_git_common_dir()Nguyễn Thái Ngọc Duy, Apr 24, 2017
  46. 2/6 refs.c: make submodule ref store hashmap genericNguyễn Thái Ngọc Duy, Apr 24, 2017
  47. 4/6 refs: introduce get_worktree_ref_store()Nguyễn Thái Ngọc Duy, Apr 24, 2017
  48. 5/6 worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe()Nguyễn Thái Ngọc Duy, Apr 24, 2017
  49. 3/6 refs: add REFS_STORE_ALL_CAPSNguyễn Thái Ngọc Duy, Apr 24, 2017
  50. 6/6 refs: kill set_worktree_head_symref()Nguyễn Thái Ngọc Duy, Apr 24, 2017
  51. Junio C HamanoApr 25, 2017

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.