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

[PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Sep 22, 2018, 18:04 UTC
Message-ID
<20180922180500.4689-4-pclouds@gmail.com>
In-Reply-To
<20180922180500.4689-1-pclouds@gmail.com>

One of the problems with multiple worktree is accessing per-worktree refs of one worktree from another worktree. This was sort of solved by multiple ref store, where the code can open the ref store of another worktree and has access to the ref space of that worktree.

The problem with this is reporting. "HEAD" in another ref space is also called "HEAD" like in the current ref space. In order to differentiate them, all the code must somehow carry the ref store around and print something like "HEAD from this ref store".

But that is not feasible (or possible with a _lot_ of work). With the current design, we pass a reference around as a string (so called "refname"). Extending this design to pass a string _and_ a ref store is a nightmare, especially when handling extended SHA-1 syntax.

So we do it another way. Instead of entering a separate ref space, we make refs from other worktrees available in the current ref space. So "HEAD" is always HEAD of the current worktree, but then we can have "worktrees/blah/HEAD" to denote HEAD from a worktree named "blah". This syntax coincidentally matches the underlying directory structure which makes implementation a bit easier.

The main worktree has to be treated specially because well.. it's special from the beginning. So HEAD from the main worktree is acccessible via the name "main/HEAD" (we can't use "worktrees/main/HEAD" because "main" under "worktrees" is not reserved).

This patch also makes it possible to specify refs from one worktree in another one, e.g.

    git log worktrees/foo/HEAD
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 refs.c                   | 20 ++++++++++++++++++++
 refs.h                   |  8 +++++---
 refs/files-backend.c     | 28 ++++++++++++++++++++++++++++
 t/t1415-worktree-refs.sh | 30 ++++++++++++++++++++++++++++++
 4 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/refs.c b/refs.c
index a851ef085b..90b73c7334 100644
--- a/refs.c
+++ b/refs.c
@@ -641,12 +641,32 @@ static int is_pseudoref_syntax(const char *refname)
 	return 1;
 }
 
+static int is_main_pseudoref_syntax(const char *refname)
+{
+	return skip_prefix(refname, "main/", &refname) &&
+		is_pseudoref_syntax(refname);
+}
+
+static int is_other_pseudoref_syntax(const char *refname)
+{
+	if (!skip_prefix(refname, "worktrees/", &refname))
+		return 0;
+	refname = strchr(refname, '/');
+	if (!refname)
+		return 0;
+	return is_pseudoref_syntax(refname + 1);
+}
+
 enum ref_type ref_type(const char *refname)
 {
 	if (is_per_worktree_ref(refname))
 		return REF_TYPE_PER_WORKTREE;
 	if (is_pseudoref_syntax(refname))
 		return REF_TYPE_PSEUDOREF;
+	if (is_main_pseudoref_syntax(refname))
+		return REF_TYPE_MAIN_PSEUDOREF;
+	if (is_other_pseudoref_syntax(refname))
+		return REF_TYPE_OTHER_PSEUDOREF;
 	return REF_TYPE_NORMAL;
 }
 
diff --git a/refs.h b/refs.h
index bd52c1bbae..9b53dbeae8 100644
--- a/refs.h
+++ b/refs.h
@@ -704,9 +704,11 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);
 int ref_is_hidden(const char *, const char *);
 
 enum ref_type {
-	REF_TYPE_PER_WORKTREE,
-	REF_TYPE_PSEUDOREF,
-	REF_TYPE_NORMAL,
+	REF_TYPE_PER_WORKTREE,	  /* refs inside refs/ but not shared       */
+	REF_TYPE_PSEUDOREF,	  /* refs outside refs/ in current worktree */
+	REF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */
+	REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */
+	REF_TYPE_NORMAL,	  /* normal/shared refs inside refs/        */
 };
 
 enum ref_type ref_type(const char *refname);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 416eafa453..bf9ed633b1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -149,6 +149,23 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
 	return refs;
 }
 
+static void files_reflog_path_other_worktrees(struct files_ref_store *refs,
+					      struct strbuf *sb,
+					      const char *refname)
+{
+	const char *real_ref;
+
+	if (!skip_prefix(refname, "worktrees/", &real_ref))
+		BUG("refname %s is not a other-worktree ref", refname);
+	real_ref = strchr(real_ref, '/');
+	if (!real_ref)
+		BUG("refname %s is not a other-worktree ref", refname);
+	real_ref++;
+
+	strbuf_addf(sb, "%s/%.*slogs/%s", refs->gitcommondir,
+		    (int)(real_ref - refname), refname, real_ref);
+}
+
 static void files_reflog_path(struct files_ref_store *refs,
 			      struct strbuf *sb,
 			      const char *refname)
@@ -158,6 +175,12 @@ static void files_reflog_path(struct files_ref_store *refs,
 	case REF_TYPE_PSEUDOREF:
 		strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
 		break;
+	case REF_TYPE_OTHER_PSEUDOREF:
+		return files_reflog_path_other_worktrees(refs, sb, refname);
+	case REF_TYPE_MAIN_PSEUDOREF:
+		if (!skip_prefix(refname, "main/", &refname))
+			BUG("ref %s is not a main pseudoref", refname);
+		/* passthru */
 	case REF_TYPE_NORMAL:
 		strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
 		break;
@@ -176,6 +199,11 @@ static void files_ref_path(struct files_ref_store *refs,
 	case REF_TYPE_PSEUDOREF:
 		strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
 		break;
+	case REF_TYPE_MAIN_PSEUDOREF:
+		if (!skip_prefix(refname, "main/", &refname))
+			BUG("ref %s is not a main pseudoref", refname);
+		/* passthru */
+	case REF_TYPE_OTHER_PSEUDOREF:
 	case REF_TYPE_NORMAL:
 		strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
 		break;
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index 0c2d5f89a9..46ca7bfc19 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -33,4 +33,34 @@ test_expect_success 'refs/local are per-worktree' '
 	( cd wt2 && test_cmp_rev local/foo wt2 )
 '
 
+test_expect_success 'resolve main/HEAD' '
+	test_cmp_rev main/HEAD initial &&
+	( cd wt1 && test_cmp_rev main/HEAD initial ) &&
+	( cd wt2 && test_cmp_rev main/HEAD initial )
+'
+
+test_expect_success 'resolve worktrees/xx/HEAD' '
+	test_cmp_rev worktrees/wt1/HEAD wt1 &&
+	( cd wt1 && test_cmp_rev worktrees/wt1/HEAD wt1 ) &&
+	( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )
+'
+
+test_expect_success 'reflog of main/HEAD' '
+	git reflog HEAD | sed "s/HEAD/main\/HEAD/" >expected &&
+	git reflog main/HEAD >actual &&
+	test_cmp expected actual &&
+	git -C wt1 reflog main/HEAD >actual.wt1 &&
+	test_cmp expected actual.wt1
+'
+
+test_expect_success 'reflog of worktrees/xx/HEAD' '
+	git -C wt2 reflog HEAD | sed "s/HEAD/worktrees\/wt2\/HEAD/" >expected &&
+	git reflog worktrees/wt2/HEAD >actual &&
+	test_cmp expected actual &&
+	git -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&
+	test_cmp expected actual.wt1 &&
+	git -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&
+	test_cmp expected actual.wt2
+'
+
 test_done
-- 
2.19.0.647.gb9a6049235
Previous: Stefan BellerNext: Eric Sunshine
Message 10 of 60 in “fix per-worktree ref iteration in fsck/reflog expire”
  1. 0/8 fix per-worktree ref iteration in fsck/reflog expireNguyễn Thái Ngọc Duy, Sep 22, 2018
  2. 1/8 refs.c: indent with tabs, not spacesNguyễn Thái Ngọc Duy, Sep 22, 2018
  3. 2/8 Add a place for (not) sharing stuff between worktreesNguyễn Thái Ngọc Duy, Sep 22, 2018
  4. Eric SunshineSep 23, 2018
  5. Stefan BellerSep 25, 2018
  6. Duy NguyenSep 25, 2018
  7. Stefan BellerSep 25, 2018
  8. Duy NguyenSep 25, 2018
  9. Stefan BellerSep 25, 2018
  10. 3/8 refs: new ref types to make per-worktree refs visible to all worktreesNguyễn Thái Ngọc Duy, Sep 22, 2018
  11. Eric SunshineSep 23, 2018
  12. Duy NguyenSep 23, 2018
  13. Stefan BellerSep 25, 2018
  14. Duy NguyenSep 25, 2018
  15. Stefan BellerSep 25, 2018
  16. Junio C HamanoSep 25, 2018
  17. Duy NguyenSep 29, 2018
  18. Junio C HamanoOct 6, 2018
  19. 4/8 revision.c: correct a parameter nameNguyễn Thái Ngọc Duy, Sep 22, 2018
  20. 5/8 revision.c: better error reporting on ref from different worktreesNguyễn Thái Ngọc Duy, Sep 22, 2018
  21. Eric SunshineSep 23, 2018
  22. Duy NguyenSep 23, 2018
  23. 7/8 fsck: check HEAD and reflog from other worktreesNguyễn Thái Ngọc Duy, Sep 22, 2018
  24. Eric SunshineSep 23, 2018
  25. Duy NguyenSep 29, 2018
  26. 8/8 reflog expire: cover reflog from all worktreesNguyễn Thái Ngọc Duy, Sep 22, 2018
  27. 6/8 fsck: Move fsck_head_link() to get_default_heads() to avoid some globalsNguyễn Thái Ngọc Duy, Sep 22, 2018
  28. 0/8 fix per-worktree ref iteration in fsck/reflog expireNguyễn Thái Ngọc Duy, Sep 29, 2018
  29. 2/8 Add a place for (not) sharing stuff between worktreesNguyễn Thái Ngọc Duy, Sep 29, 2018
  30. 1/8 refs.c: indent with tabs, not spacesNguyễn Thái Ngọc Duy, Sep 29, 2018
  31. 3/8 refs: new ref types to make per-worktree refs visible to all worktreesNguyễn Thái Ngọc Duy, Sep 29, 2018
  32. Eric SunshineSep 30, 2018
  33. Junio C HamanoOct 7, 2018
  34. 4/8 revision.c: correct a parameter nameNguyễn Thái Ngọc Duy, Sep 29, 2018
  35. 5/8 revision.c: better error reporting on ref from different worktreesNguyễn Thái Ngọc Duy, Sep 29, 2018
  36. Eric SunshineSep 30, 2018
  37. 7/8 fsck: check HEAD and reflog from other worktreesNguyễn Thái Ngọc Duy, Sep 29, 2018
  38. 6/8 fsck: Move fsck_head_link() to get_default_heads() to avoid some globalsNguyễn Thái Ngọc Duy, Sep 29, 2018
  39. 8/8 reflog expire: cover reflog from all worktreesNguyễn Thái Ngọc Duy, Sep 29, 2018
  40. Eric SunshineSep 30, 2018
  41. Duy NguyenOct 2, 2018
  42. Eric SunshineOct 3, 2018
  43. 0/8 fix per-worktree ref iteration in fsck/reflog expireNguyễn Thái Ngọc Duy, Oct 21, 2018
  44. 1/8 refs.c: indent with tabs, not spacesNguyễn Thái Ngọc Duy, Oct 21, 2018
  45. 2/8 Add a place for (not) sharing stuff between worktreesNguyễn Thái Ngọc Duy, Oct 21, 2018
  46. Junio C HamanoOct 22, 2018
  47. Duy NguyenOct 29, 2018
  48. SZEDER GáborOct 22, 2018
  49. 3/8 refs: new ref types to make per-worktree refs visible to all worktreesNguyễn Thái Ngọc Duy, Oct 21, 2018
  50. Ævar Arnfjörð BjarmasonNov 24, 2018
  51. Junio C HamanoNov 25, 2018
  52. files-backend.c: fix build error on SolarisNguyễn Thái Ngọc Duy, Nov 25, 2018
  53. Carlo ArenasNov 25, 2018
  54. Duy NguyenNov 25, 2018
  55. Junio C HamanoNov 26, 2018
  56. 4/8 revision.c: correct a parameter nameNguyễn Thái Ngọc Duy, Oct 21, 2018
  57. 5/8 revision.c: better error reporting on ref from different worktreesNguyễn Thái Ngọc Duy, Oct 21, 2018
  58. 6/8 fsck: Move fsck_head_link() to get_default_heads() to avoid some globalsNguyễn Thái Ngọc Duy, Oct 21, 2018
  59. 7/8 fsck: check HEAD and reflog from other worktreesNguyễn Thái Ngọc Duy, Oct 21, 2018
  60. 8/8 reflog expire: cover reflog from all worktreesNguyễn Thái Ngọc Duy, Oct 21, 2018

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.