Message ID | 20180922180500.4689-4-pclouds@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | fix per-worktree ref iteration in fsck/reflog expire | expand |
On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote: > [...] > 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). Bikeshedding: I wonder if this would be more intuitive if called simply "/HEAD" rather than "main/HEAD". > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> > --- > diff --git a/refs.c b/refs.c > @@ -641,12 +641,32 @@ static int is_pseudoref_syntax(const char *refname) > +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); > +} If the input is "worktrees/refs/" (nothing following the trailing '/'), then an empty string will be passed to is_pseudoref_syntax(), which will return true. Does that result in correct behavior? (Same question about "main/" being passed to is_main_pseudoref_syntax().)
On Sun, Sep 23, 2018 at 10:06 AM Eric Sunshine <sunshine@sunshineco.com> wrote: > > On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote: > > [...] > > 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). > > Bikeshedding: I wonder if this would be more intuitive if called > simply "/HEAD" rather than "main/HEAD". A ref name cannot start with '/'. I'm open to a different name than "main" though, it just felt a better name than "main-worktree".
On Sat, Sep 22, 2018 at 11:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote: > > 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 This has strong similarities to remote refs: Locally I may have a branch master, whose (stale local copy of its distributed) counterpart is named origin/master. It is also possible to have a working tree named origin (just I like to name my worktree "git", when working on git.git), how do we differentiate between the neighbor-worktree "origin/master" and the remote-tracking branch "origin/master" ? As the remote tracking branches are shared between all worktree there is no need to differentiate between a local-worktree remote tracking branch and a neighbor-worktree remote tracking branch. Now that you introduce the magic main working tree, we also need to disallow working trees to be named "main", i.e. $ git worktree add main HEAD produces $ ls .git/worktrees/ main How do we deal with that?
On Tue, Sep 25, 2018 at 4:48 AM Stefan Beller <sbeller@google.com> wrote: > > This patch also makes it possible to specify refs from one worktree in > > another one, e.g. > > > > git log worktrees/foo/HEAD > > This has strong similarities to remote refs: > Locally I may have a branch master, whose (stale local copy of its > distributed) counterpart is named origin/master. If you think of each worktree as independent clones (which is more or less true, the fact that they share ODB is more like an implementation detail) then yes it's almost like remotes. > It is also possible to have a working tree named origin > (just I like to name my worktree "git", when working on git.git), > how do we differentiate between the neighbor-worktree > "origin/master" and the remote-tracking branch "origin/master" ? Hmm.. I think you're thinking that origin/master could either mean refs/worktrees/origin/master or refs/remotes/origin/master. I do not think we're going to support expanding origin/master to refs/worktrees/origin/master. This part about ref resolution did cross my mind but I didn't see a good reason to support it. Even if we do support it, this is not even a new problem. If you have refs/heads/origin/master and refs/remotes/origin/master now, we have ref ambiguity anyway and a solution for this should handle refs/worktrees/origin/master well if it comes into the picture. > As the remote tracking branches are shared between all > worktree there is no need to differentiate between a > local-worktree remote tracking branch and a > neighbor-worktree remote tracking branch. > > Now that you introduce the magic main working tree, > we also need to disallow working trees to be named "main", > i.e. > $ git worktree add main HEAD > > produces > > $ ls .git/worktrees/ > main > > How do we deal with that? main is accessed via worktrees/main/HEAD while the main worktree's HEAD is accessed via main/HEAD (which is _not_ automatically expanded to refs/worktrees/main/HEAD). But if it is, yes we need to detect ambiguity and tell the user to specify full ref name, either refs/main/HEAD or refs/worktrees/main/HEAD.
On Tue, Sep 25, 2018 at 8:49 AM Duy Nguyen <pclouds@gmail.com> wrote: > > On Tue, Sep 25, 2018 at 4:48 AM Stefan Beller <sbeller@google.com> wrote: > > > This patch also makes it possible to specify refs from one worktree in > > > another one, e.g. > > > > > > git log worktrees/foo/HEAD > > > > This has strong similarities to remote refs: > > Locally I may have a branch master, whose (stale local copy of its > > distributed) counterpart is named origin/master. > > If you think of each worktree as independent clones (which is more or > less true, the fact that they share ODB is more like an implementation > detail) then yes it's almost like remotes. Apart from the ODB and the refs subsystem, there is also the config space, which is shared (but you have sent out patches to have local config as well). So I would think worktrees are better than having two clones not just due to the shared ODB, but also due to the common config as then I have to setup my repo only once and can add/remove worktrees cheaply (in terms of "how much time do I need to spend to configure it as I need"). > > It is also possible to have a working tree named origin > > (just I like to name my worktree "git", when working on git.git), > > how do we differentiate between the neighbor-worktree > > "origin/master" and the remote-tracking branch "origin/master" ? > > Hmm.. I think you're thinking that origin/master could either mean > refs/worktrees/origin/master or refs/remotes/origin/master. I do not > think we're going to support expanding origin/master to > refs/worktrees/origin/master. This part about ref resolution did cross > my mind but I didn't see a good reason to support it. > > Even if we do support it, this is not even a new problem. If you have > refs/heads/origin/master and refs/remotes/origin/master now, we have > ref ambiguity anyway and a solution for this should handle > refs/worktrees/origin/master well if it comes into the picture. So once origin/master is overloaded, I would have to spell out refs/worktrees/origin/master and refs/remotes/origin/master to avoid confusing the DWIM machinery. Makes sense. > > How do we deal with that? > > main is accessed via worktrees/main/HEAD while the main worktree's > HEAD is accessed via main/HEAD (which is _not_ automatically expanded > to refs/worktrees/main/HEAD). But if it is, yes we need to detect > ambiguity and tell the user to specify full ref name, either > refs/main/HEAD or refs/worktrees/main/HEAD. Ah, I see. Now I actually understand the last paragraph of the commit message. Thanks for explaining! Stefan
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > 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). I do not quite follow. So with this, both refs/heads/master and main/refs/heads/master are good names for the master branch (even though the local branch names are not per worktree), because in the main worktree, refs/bisect/bad and main/refs/bisect/bad ought to mean the same thing. side note: Or is this only for pseudo-refs (i.e. $GIT_DIR/$name where $name consists of all caps or underscore and typically ends with HEAD)? Even if that were the case, I do not think it essentially changes the issue around disambiguation that much. The disambiguation rule has always been: if you have a confusingly named ref, you can spell it out fully to avoid any ambiguity, e.g. refs/heads/refs/heads/foo can be given to "git rev-parse" and will mean the tip of the branch whose name is "refs/heads/foo", even when another branch whose name is "foo" exists. Would we have a reasonable disambiguation rules that work well with the main/ and worktrees/* prefixes? When somebody has main/HEAD branch and writes "git rev-parse main/HEAD", does it find refs/heads/main/HEAD or $GIT_DIR/HEAD, if the user is in the main worktree? It could be simply that the design is underdocumented in this patch set (in which case I would have appreciated 'RFC' near 'PATCH'), but I have a feeling that the code came way too early before such design issues are fleshed out. > 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
On Tue, Sep 25, 2018 at 11:16 PM Junio C Hamano <gitster@pobox.com> wrote: > > Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > > > 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). > > I do not quite follow. So with this, both refs/heads/master and > main/refs/heads/master are good names for the master branch (even > though the local branch names are not per worktree), because > in the main worktree, refs/bisect/bad and main/refs/bisect/bad ought > to mean the same thing. True. I think the ambiguation here is about the main worktree versus a secondary worktree that is accidentally named "main". Then suddenly we have to worktrees of the same name, and accessing them both via worktrees/<id>/HEAD will not work, and there is no other way to disambiguate them. > side note: Or is this only for pseudo-refs > (i.e. $GIT_DIR/$name where $name consists of all caps or > underscore and typically ends with HEAD)? Right now, due to implementation limitations, only pseudo refs (or loose refs in the case of refs/bisect) are accessible. But I don't see why main/refs/heads/master should not work. > The disambiguation rule has always been: if you have a confusingly > named ref, you can spell it out fully to avoid any ambiguity, e.g. > refs/heads/refs/heads/foo can be given to "git rev-parse" and will > mean the tip of the branch whose name is "refs/heads/foo", even when > another branch whose name is "foo" exists. > > Would we have a reasonable disambiguation rules that work well with > the main/ and worktrees/* prefixes? When somebody has main/HEAD branch > and writes "git rev-parse main/HEAD", does it find refs/heads/main/HEAD > or $GIT_DIR/HEAD, if the user is in the main worktree? The rules are not touched. But it looks like everything still works as expected (I'm adding tests to verify this)
Duy Nguyen <pclouds@gmail.com> writes: >> > 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). >> >> I do not quite follow. So with this, both refs/heads/master and >> main/refs/heads/master are good names for the master branch (even >> though the local branch names are not per worktree), because >> in the main worktree, refs/bisect/bad and main/refs/bisect/bad ought >> to mean the same thing. > > True. I think the ambiguation here is about the main worktree versus a > secondary worktree that is accidentally named "main". Then suddenly we > have to worktrees of the same name, and accessing them both via > worktrees/<id>/HEAD will not work, and there is no other way to > disambiguate them. So those who have happily been referring 'refs/heads/main/foo' as 'main/foo' now suddenly have to say 'refs/heads/main/foo' instead? > The rules are not touched. But it looks like everything still works as > expected (I'm adding tests to verify this) What I am worried about is that _your_ expectation may not coincide with the expectations of users, especially with ones with existing refs that overlap with the namespaces this series suddenly starts carving out and squatting on. As long as that won't be a problem, I think it is OK, even with 'main' not renamed to 'worktree-main' or somesuch.
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
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(-)