{"thread":{"id":"49405","subject":"[PATCH 0/8] fix per-worktree ref iteration in fsck/reflog expire","startedAt":"2018-09-22T18:05:10Z","lastAt":"2018-11-26T04:45:00Z","messageCount":60,"participants":["Nguyễn Thái Ngọc Duy","Eric Sunshine","Duy Nguyen","Stefan Beller","Junio C Hamano","SZEDER Gábor","Ævar Arnfjörð Bjarmason","Carlo Arenas"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"358702","messageId":"20180922180500.4689-1-pclouds@gmail.com","threadId":"49405","inReplyTo":null,"subject":"[PATCH 0/8] fix per-worktree ref iteration in fsck/reflog expire","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:52Z","receivedAt":"2018-09-22T18:05:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Sorry it took me so long to revisit these bugs, even though the first\none was reported nearly a year ago. I guess I slept on it way longer\nthan I should have.\n\nThis series opens up the currrent worktree's ref space, allowing one\nworktree to see refs from other worktrees. With this, it's possible to\nhave less confusing error reports (e.g. \"heh.. what HEAD are you\ntalking about?\"). And it's possible to peek one worktree from another,\ne.g. now I could do\n\n    git checkout main/HEAD\n    make test -j8\n\nin one terminal and go back to the original terminal and continue\nhacking while tests are being run in the background.\n\nWith this issue out of the way, I could now continue to fix the\n\"forget to look at all HEADs/reflogs\" problem in fsck and \"reflog\nexpire\". The fsck patches are mostly from Elijah with some code/test\nadaptation from me.\n\nI also take this opportunity to try to standardize a common/private\nspace in $GIT_DIR or ref hierarchy so we don't have to add more rules\nin the future.\n\nOne heads up. One remaining problem with \"refs and worktrees\" is the\nability to completely separate ref space between worktrees (i.e.\nrefs/heads/master on worktree A is completely different than one in\nworktree B). This is needed to make use worktrees in submodules. But\nthe way I see it, per-worktree refs may have to be moved back to\n$GIT_COMMON_DIR/refs to be efficient.\n\nThis may be backward incompatible change. Haven't thought it through\nyet (and didn't see it coming because I largely ignored refs/bisect,\nwhich should have made me think about this much earlier)\n\nElijah Newren (2):\n  fsck: Move fsck_head_link() to get_default_heads() to avoid some globals\n  fsck: check HEAD and reflog from other worktrees\n\nNguyễn Thái Ngọc Duy (6):\n  refs.c: indent with tabs, not spaces\n  Add a place for (not) sharing stuff between worktrees\n  refs: new ref types to make per-worktree refs visible to all worktrees\n  revision.c: correct a parameter name\n  revision.c: better error reporting on ref from different worktrees\n  reflog expire: cover reflog from all worktrees\n\n Documentation/git-reflog.txt           |  7 ++-\n Documentation/gitrepository-layout.txt | 11 ++++-\n builtin/fsck.c                         | 68 ++++++++++++++++++--------\n builtin/reflog.c                       | 22 +++++++--\n path.c                                 |  1 +\n refs.c                                 | 23 ++++++++-\n refs.h                                 |  8 +--\n refs/files-backend.c                   | 42 ++++++++++++++--\n revision.c                             | 22 ++++++---\n t/t0060-path-utils.sh                  |  2 +\n t/t1415-worktree-refs.sh               | 66 +++++++++++++++++++++++++\n t/t1450-fsck.sh                        | 39 +++++++++++++++\n worktree.c                             | 32 ++++++++++--\n worktree.h                             | 14 ++++++\n 14 files changed, 312 insertions(+), 45 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358703","messageId":"20180922180500.4689-2-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 1/8] refs.c: indent with tabs, not spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:53Z","receivedAt":"2018-09-22T18:05:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex a7a75b4cc0..9f7268d5fe 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -646,7 +646,7 @@ enum ref_type ref_type(const char *refname)\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n-       return REF_TYPE_NORMAL;\n+\treturn REF_TYPE_NORMAL;\n }\n \n long get_files_ref_lock_timeout_ms(void)\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358704","messageId":"20180922180500.4689-3-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:54Z","receivedAt":"2018-09-22T18:05:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When multiple worktrees are used, we need rules to determine if\nsomething belongs to one worktree or all of them. Instead of keeping\nadding rules when new stuff comes, have a generic rule:\n\n- Inside $GIT_DIR, which is per-worktree by default, add\n  $GIT_DIR/common which is always shared. New features that want to\n  share stuff should put stuff under this directory.\n\n- Inside refs/, which is shared by default except refs/bisect, add\n  refs/local/ which is per-worktree. We may eventually move\n  refs/bisect to this new location and remove the exception in refs\n  code.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitrepository-layout.txt | 11 ++++++--\n path.c                                 |  1 +\n refs.c                                 |  1 +\n refs/files-backend.c                   | 14 +++++++---\n t/t0060-path-utils.sh                  |  2 ++\n t/t1415-worktree-refs.sh               | 36 ++++++++++++++++++++++++++\n 6 files changed, 60 insertions(+), 5 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..fad404ed7c 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -95,8 +95,10 @@ refs::\n \tReferences are stored in subdirectories of this\n \tdirectory.  The 'git prune' command knows to preserve\n \tobjects reachable from refs found in this directory and\n-\tits subdirectories. This directory is ignored if $GIT_COMMON_DIR\n-\tis set and \"$GIT_COMMON_DIR/refs\" will be used instead.\n+\tits subdirectories.\n+\tThis directory is ignored (except refs/bisect and refs/local)\n+\tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/refs\" will be\n+\tused instead.\n \n refs/heads/`name`::\n \trecords tip-of-the-tree commit objects of branch `name`\n@@ -165,6 +167,11 @@ hooks::\n \teach hook. This directory is ignored if $GIT_COMMON_DIR is set\n \tand \"$GIT_COMMON_DIR/hooks\" will be used instead.\n \n+common::\n+\tWhen multiple working trees are used, most of files in\n+\t$GIT_DIR are per-worktree with a few known exceptions. All\n+\tfiles under 'common' however will be shared between all\n+\tworking trees.\n \n index::\n \tThe current index file for the repository.  It is\ndiff --git a/path.c b/path.c\nindex 34f0f98349..7eb61bf31b 100644\n--- a/path.c\n+++ b/path.c\n@@ -108,6 +108,7 @@ struct common_dir {\n \n static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"branches\" },\n+\t{ 0, 1, 0, \"common\" },\n \t{ 0, 1, 0, \"hooks\" },\n \t{ 0, 1, 0, \"info\" },\n \t{ 0, 0, 1, \"info/sparse-checkout\" },\ndiff --git a/refs.c b/refs.c\nindex 9f7268d5fe..a851ef085b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -624,6 +624,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)\n static int is_per_worktree_ref(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/local/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\") ||\n \t\tstarts_with(refname, \"refs/rewritten/\");\n }\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 16ef9325e0..416eafa453 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -269,9 +269,9 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \tclosedir(d);\n \n \t/*\n-\t * Manually add refs/bisect, which, being per-worktree, might\n-\t * not appear in the directory listing for refs/ in the main\n-\t * repo.\n+\t * Manually add refs/bisect and refs/local, which, being\n+\t * per-worktree, might not appear in the directory listing for\n+\t * refs/ in the main repo.\n \t */\n \tif (!strcmp(dirname, \"refs/\")) {\n \t\tint pos = search_ref_dir(dir, \"refs/bisect/\", 12);\n@@ -281,6 +281,14 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \t\t\t\t\tdir->cache, \"refs/bisect/\", 12, 1);\n \t\t\tadd_entry_to_dir(dir, child_entry);\n \t\t}\n+\n+\t\tpos = search_ref_dir(dir, \"refs/local/\", 11);\n+\n+\t\tif (pos < 0) {\n+\t\t\tstruct ref_entry *child_entry = create_dir_entry(\n+\t\t\t\t\tdir->cache, \"refs/local/\", 11, 1);\n+\t\t\tadd_entry_to_dir(dir, child_entry);\n+\t\t}\n \t}\n }\n \ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex cd74c0a471..c7b53e494b 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -306,6 +306,8 @@ test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me\n test_git_path GIT_COMMON_DIR=bar config                   bar/config\n test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n+test_git_path GIT_COMMON_DIR=bar common                   bar/common\n+test_git_path GIT_COMMON_DIR=bar common/file              bar/common/file\n \n # In the tests below, $(pwd) must be used because it is a native path on\n # Windows and avoids MSYS's path mangling (which simplifies \"foo/../bar\" and\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nnew file mode 100755\nindex 0000000000..0c2d5f89a9\n--- /dev/null\n+++ b/t/t1415-worktree-refs.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+\n+test_description='per-worktree refs'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial &&\n+\ttest_commit wt1 &&\n+\ttest_commit wt2 &&\n+\tgit worktree add wt1 wt1 &&\n+\tgit worktree add wt2 wt2 &&\n+\tgit checkout initial\n+'\n+\n+test_expect_success 'add refs/local' '\n+\tgit update-ref refs/local/foo HEAD &&\n+\tgit -C wt1 update-ref refs/local/foo HEAD &&\n+\tgit -C wt2 update-ref refs/local/foo HEAD\n+'\n+\n+test_expect_success 'refs/local must not be packed' '\n+\tgit pack-refs --all &&\n+\ttest_path_is_missing .git/refs/tags/wt1 &&\n+\ttest_path_is_file .git/refs/local/foo &&\n+\ttest_path_is_file .git/worktrees/wt1/refs/local/foo &&\n+\ttest_path_is_file .git/worktrees/wt2/refs/local/foo\n+'\n+\n+test_expect_success 'refs/local are per-worktree' '\n+\ttest_cmp_rev local/foo initial &&\n+\t( cd wt1 && test_cmp_rev local/foo wt1 ) &&\n+\t( cd wt2 && test_cmp_rev local/foo wt2 )\n+'\n+\n+test_done\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358705","messageId":"20180922180500.4689-4-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:55Z","receivedAt":"2018-09-22T18:05:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One of the problems with multiple worktree is accessing per-worktree\nrefs of one worktree from another worktree. This was sort of solved by\nmultiple ref store, where the code can open the ref store of another\nworktree and has access to the ref space of that worktree.\n\nThe problem with this is reporting. \"HEAD\" in another ref space is\nalso called \"HEAD\" like in the current ref space. In order to\ndifferentiate them, all the code must somehow carry the ref store\naround and print something like \"HEAD from this ref store\".\n\nBut that is not feasible (or possible with a _lot_ of work). With the\ncurrent design, we pass a reference around as a string (so called\n\"refname\"). Extending this design to pass a string _and_ a ref store\nis a nightmare, especially when handling extended SHA-1 syntax.\n\nSo we do it another way. Instead of entering a separate ref space, we\nmake refs from other worktrees available in the current ref space. So\n\"HEAD\" is always HEAD of the current worktree, but then we can have\n\"worktrees/blah/HEAD\" to denote HEAD from a worktree named\n\"blah\". This syntax coincidentally matches the underlying directory\nstructure which makes implementation a bit easier.\n\nThe main worktree has to be treated specially because well.. it's\nspecial from the beginning. So HEAD from the main worktree is\nacccessible via the name \"main/HEAD\" (we can't use\n\"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\nreserved).\n\nThis patch also makes it possible to specify refs from one worktree in\nanother one, e.g.\n\n    git log worktrees/foo/HEAD\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c                   | 20 ++++++++++++++++++++\n refs.h                   |  8 +++++---\n refs/files-backend.c     | 28 ++++++++++++++++++++++++++++\n t/t1415-worktree-refs.sh | 30 ++++++++++++++++++++++++++++++\n 4 files changed, 83 insertions(+), 3 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex a851ef085b..90b73c7334 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -641,12 +641,32 @@ static int is_pseudoref_syntax(const char *refname)\n \treturn 1;\n }\n \n+static int is_main_pseudoref_syntax(const char *refname)\n+{\n+\treturn skip_prefix(refname, \"main/\", &refname) &&\n+\t\tis_pseudoref_syntax(refname);\n+}\n+\n+static int is_other_pseudoref_syntax(const char *refname)\n+{\n+\tif (!skip_prefix(refname, \"worktrees/\", &refname))\n+\t\treturn 0;\n+\trefname = strchr(refname, '/');\n+\tif (!refname)\n+\t\treturn 0;\n+\treturn is_pseudoref_syntax(refname + 1);\n+}\n+\n enum ref_type ref_type(const char *refname)\n {\n \tif (is_per_worktree_ref(refname))\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n+\tif (is_main_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_MAIN_PSEUDOREF;\n+\tif (is_other_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_OTHER_PSEUDOREF;\n \treturn REF_TYPE_NORMAL;\n }\n \ndiff --git a/refs.h b/refs.h\nindex bd52c1bbae..9b53dbeae8 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -704,9 +704,11 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);\n int ref_is_hidden(const char *, const char *);\n \n enum ref_type {\n-\tREF_TYPE_PER_WORKTREE,\n-\tREF_TYPE_PSEUDOREF,\n-\tREF_TYPE_NORMAL,\n+\tREF_TYPE_PER_WORKTREE,\t  /* refs inside refs/ but not shared       */\n+\tREF_TYPE_PSEUDOREF,\t  /* refs outside refs/ in current worktree */\n+\tREF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */\n+\tREF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */\n+\tREF_TYPE_NORMAL,\t  /* normal/shared refs inside refs/        */\n };\n \n enum ref_type ref_type(const char *refname);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 416eafa453..bf9ed633b1 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -149,6 +149,23 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n \treturn refs;\n }\n \n+static void files_reflog_path_other_worktrees(struct files_ref_store *refs,\n+\t\t\t\t\t      struct strbuf *sb,\n+\t\t\t\t\t      const char *refname)\n+{\n+\tconst char *real_ref;\n+\n+\tif (!skip_prefix(refname, \"worktrees/\", &real_ref))\n+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n+\treal_ref = strchr(real_ref, '/');\n+\tif (!real_ref)\n+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n+\treal_ref++;\n+\n+\tstrbuf_addf(sb, \"%s/%.*slogs/%s\", refs->gitcommondir,\n+\t\t    (int)(real_ref - refname), refname, real_ref);\n+}\n+\n static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      struct strbuf *sb,\n \t\t\t      const char *refname)\n@@ -158,6 +175,12 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n+\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (!skip_prefix(refname, \"main/\", &refname))\n+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n+\t\t/* passthru */\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n \t\tbreak;\n@@ -176,6 +199,11 @@ static void files_ref_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (!skip_prefix(refname, \"main/\", &refname))\n+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n+\t\t/* passthru */\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n \t\tbreak;\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex 0c2d5f89a9..46ca7bfc19 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -33,4 +33,34 @@ test_expect_success 'refs/local are per-worktree' '\n \t( cd wt2 && test_cmp_rev local/foo wt2 )\n '\n \n+test_expect_success 'resolve main/HEAD' '\n+\ttest_cmp_rev main/HEAD initial &&\n+\t( cd wt1 && test_cmp_rev main/HEAD initial ) &&\n+\t( cd wt2 && test_cmp_rev main/HEAD initial )\n+'\n+\n+test_expect_success 'resolve worktrees/xx/HEAD' '\n+\ttest_cmp_rev worktrees/wt1/HEAD wt1 &&\n+\t( cd wt1 && test_cmp_rev worktrees/wt1/HEAD wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )\n+'\n+\n+test_expect_success 'reflog of main/HEAD' '\n+\tgit reflog HEAD | sed \"s/HEAD/main\\/HEAD/\" >expected &&\n+\tgit reflog main/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog main/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1\n+'\n+\n+test_expect_success 'reflog of worktrees/xx/HEAD' '\n+\tgit -C wt2 reflog HEAD | sed \"s/HEAD/worktrees\\/wt2\\/HEAD/\" >expected &&\n+\tgit reflog worktrees/wt2/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1 &&\n+\tgit -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&\n+\ttest_cmp expected actual.wt2\n+'\n+\n test_done\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358706","messageId":"20180922180500.4689-5-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 4/8] revision.c: correct a parameter name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:56Z","receivedAt":"2018-09-22T18:05:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is a callback of for_each_reflog() which will pass a ref\nname as the first argument, not a path (to a reflog file).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex e18bd530e4..63aae722c1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1277,13 +1277,14 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *path, const struct object_id *oid,\n+static int handle_one_reflog(const char *refname,\n+\t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = path;\n-\trefs_for_each_reflog_ent(cb->refs, path,\n+\tcb->name_for_errormsg = refname;\n+\trefs_for_each_reflog_ent(cb->refs, refname,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n \treturn 0;\n }\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358707","messageId":"20180922180500.4689-6-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:57Z","receivedAt":"2018-09-22T18:05:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Make use of the new ref aliases to pass refs from another worktree\naround and access them from the current ref store instead. This does\nnot change any functionality, but when a problem shows up, we would\nreport something like\n\n    fatal: bad object worktrees/ztemp/HEAD\n    warning: reflog of 'main/HEAD' references pruned commits\n\ninstead of\n\n    fatal: bad object HEAD\n    warning: reflog of 'HEAD' references pruned commits\n\nwhich does not really tell where the refs are from.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 21 +++++++++++++--------\n worktree.c | 32 +++++++++++++++++++++++++++++---\n worktree.h | 14 ++++++++++++++\n 3 files changed, 56 insertions(+), 11 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 63aae722c1..8ce660e3b1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1177,7 +1177,7 @@ struct all_refs_cb {\n \tint warned_bad_reflog;\n \tstruct rev_info *all_revs;\n \tconst char *name_for_errormsg;\n-\tstruct ref_store *refs;\n+\tstruct worktree *wt;\n };\n \n int ref_excluded(struct string_list *ref_excludes, const char *path)\n@@ -1214,7 +1214,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,\n \tcb->all_revs = revs;\n \tcb->all_flags = flags;\n \trevs->rev_input_given = 1;\n-\tcb->refs = NULL;\n+\tcb->wt = NULL;\n }\n \n void clear_ref_exclusion(struct string_list **ref_excludes_p)\n@@ -1277,15 +1277,20 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *refname,\n+static int handle_one_reflog(const char *refname_in_wt,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = refname;\n-\trefs_for_each_reflog_ent(cb->refs, refname,\n+\tstrbuf_worktree_ref(cb->wt, &refname, refname_in_wt);\n+\tcb->name_for_errormsg = refname.buf;\n+\trefs_for_each_reflog_ent(get_main_ref_store(the_repository),\n+\t\t\t\t refname.buf,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -1300,8 +1305,8 @@ static void add_other_reflogs_to_pending(struct all_refs_cb *cb)\n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\tcb->refs = get_worktree_ref_store(wt);\n-\t\trefs_for_each_reflog(cb->refs,\n+\t\tcb->wt = wt;\n+\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n \t\t\t\t     handle_one_reflog,\n \t\t\t\t     cb);\n \t}\n@@ -1314,7 +1319,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)\n \n \tcb.all_revs = revs;\n \tcb.all_flags = flags;\n-\tcb.refs = get_main_ref_store(the_repository);\n+\tcb.wt = NULL;\n \tfor_each_reflog(handle_one_reflog, &cb);\n \n \tif (!revs->single_worktree)\ndiff --git a/worktree.c b/worktree.c\nindex b0d0b5426d..ec1a5bc511 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -487,6 +487,28 @@ int submodule_uses_worktrees(const char *path)\n \treturn ret;\n }\n \n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname)\n+{\n+\tif (wt && !wt->is_current) {\n+\t\tif (is_main_worktree(wt))\n+\t\t\tstrbuf_addstr(sb, \"main/\");\n+\t\telse\n+\t\t\tstrbuf_addf(sb, \"worktrees/%s/\", wt->id);\n+\t}\n+\tstrbuf_addstr(sb, refname);\n+}\n+\n+const char *worktree_ref(const struct worktree *wt, const char *refname)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_worktree_ref(wt, &sb, refname);\n+\treturn sb.buf;\n+}\n+\n int other_head_refs(each_ref_fn fn, void *cb_data)\n {\n \tstruct worktree **worktrees, **p;\n@@ -495,13 +517,17 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \tworktrees = get_worktrees(0);\n \tfor (p = worktrees; *p; p++) {\n \t\tstruct worktree *wt = *p;\n-\t\tstruct ref_store *refs;\n+\t\tstruct object_id oid;\n+\t\tint flag;\n \n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\trefs = get_worktree_ref_store(wt);\n-\t\tret = refs_head_ref(refs, fn, cb_data);\n+\t\tif (!refs_read_ref_full(get_main_ref_store(the_repository),\n+\t\t\t\t\tworktree_ref(wt, \"HEAD\"),\n+\t\t\t\t\tRESOLVE_REF_READING,\n+\t\t\t\t\t&oid, &flag))\n+\t\t\tret = fn(worktree_ref(wt, \"HEAD\"), &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\ndiff --git a/worktree.h b/worktree.h\nindex df3fc30f73..0016eb9e88 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -108,4 +108,18 @@ extern const char *worktree_git_path(const struct worktree *wt,\n \t\t\t\t     const char *fmt, ...)\n \t__attribute__((format (printf, 2, 3)));\n \n+/*\n+ * Return a refname suitable for access from the current ref store.\n+ */\n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname);\n+\n+/*\n+ * Return a refname suitable for access from the current ref\n+ * store. The result may be destroyed at the next call.\n+ */\n+const char *worktree_ref(const struct worktree *wt,\n+\t\t\t const char *refname);\n+\n #endif\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358708","messageId":"20180922180500.4689-8-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 7/8] fsck: check HEAD and reflog from other worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:59Z","receivedAt":"2018-09-22T18:05:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nfsck is a repo-wide operation and should check all references no\nmatter which worktree they are associated to.\n\nReported-by: Jeff King <peff@peff.net>\nHelped-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c  | 55 ++++++++++++++++++++++++++++++++++---------------\n t/t1450-fsck.sh | 39 +++++++++++++++++++++++++++++++++++\n 2 files changed, 77 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 24f8a09a3c..71492c158d 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -19,6 +19,7 @@\n #include \"packfile.h\"\n #include \"object-store.h\"\n #include \"run-command.h\"\n+#include \"worktree.h\"\n \n #define REACHABLE 0x0001\n #define SEEN      0x0002\n@@ -444,7 +445,11 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid\n static int fsck_handle_reflog(const char *logname, const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\n-\tfor_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n+\tstrbuf_worktree_ref(cb_data, &refname, logname);\n+\tfor_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -482,20 +487,34 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid);\n \n static void get_default_heads(void)\n {\n+\tstruct worktree **worktrees, **p;\n \tconst char *head_points_at;\n \tstruct object_id head_oid;\n \n-\tfsck_head_link(&head_points_at, &head_oid);\n-\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n-\tif (include_reflogs)\n-\t\tfor_each_reflog(fsck_handle_reflog, NULL);\n+\n+\tworktrees = get_worktrees(0);\n+\tfor (p = worktrees; *p; p++) {\n+\t\tstruct worktree *wt = *p;\n+\t\tstruct strbuf ref = STRBUF_INIT;\n+\n+\t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n+\t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n+\t\tif (head_points_at && !is_null_oid(&head_oid))\n+\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\tstrbuf_release(&ref);\n+\n+\t\tif (include_reflogs)\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n+\t\t\t\t\t     fsck_handle_reflog, wt);\n+\t}\n+\tfree_worktrees(worktrees);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\n@@ -584,34 +603,36 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n-\t\tfprintf(stderr, \"Checking HEAD link\\n\");\n+\t\tfprintf(stderr, \"Checking %s link\\n\", head_ref_name);\n \n-\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\t*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);\n \tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"Invalid HEAD\");\n+\t\treturn error(\"Invalid %s\", head_ref_name);\n \t}\n-\tif (!strcmp(*head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, head_ref_name))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n \telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     *head_points_at);\n+\t\treturn error(\"%s points to something strange (%s)\",\n+\t\t\t     head_ref_name, *head_points_at);\n \t}\n \tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n-\t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n+\t\t\treturn error(\"%s: detached HEAD points at nothing\",\n+\t\t\t\t     head_ref_name);\n \t\t}\n-\t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\t*head_points_at + 11);\n+\t\tfprintf(stderr, \"notice: %s points to an unborn branch (%s)\\n\",\n+\t\t\thead_ref_name, *head_points_at + 11);\n \t}\n \treturn 0;\n }\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 0f2dd26f74..444e8c1ad9 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -101,6 +101,45 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \tgrep \"HEAD points to something strange\" out\n '\n \n+test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\tgit worktree add wt &&\n+\tmv .git/HEAD .git/SAVED_HEAD &&\n+\techo 0000000000000000000000000000000000000000 >.git/HEAD &&\n+\t# avoid corrupt/broken HEAD from interfering with repo discovery\n+\ttest_must_fail git -C wt fsck 2>out &&\n+\tcat out &&\n+\tgrep \"main/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo 0000000000000000000000000000000000000000 >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tcat out &&\n+\tgrep \"worktrees/other/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at missing object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tcat out &&\n+\tgrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny place' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tcat out &&\n+\tgrep \"worktrees/other/HEAD points to something strange\" out\n+'\n+\n test_expect_success 'email without @ is okay' '\n \tgit cat-file commit HEAD >basis &&\n \tsed \"s/@/AT/\" basis >okay &&\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358709","messageId":"20180922180500.4689-9-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 8/8] reflog expire: cover reflog from all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:05:00Z","receivedAt":"2018-09-22T18:05:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reported-by: Jeff King <peff@peff.net>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-reflog.txt |  7 ++++++-\n builtin/reflog.c             | 22 +++++++++++++++++++---\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt\nindex 472a6808cd..ff487ff77d 100644\n--- a/Documentation/git-reflog.txt\n+++ b/Documentation/git-reflog.txt\n@@ -20,7 +20,7 @@ depending on the subcommand:\n 'git reflog' ['show'] [log-options] [<ref>]\n 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]\n \t[--rewrite] [--updateref] [--stale-fix]\n-\t[--dry-run | -n] [--verbose] [--all | <refs>...]\n+\t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] ref@\\{specifier\\}...\n 'git reflog exists' <ref>\n@@ -72,6 +72,11 @@ Options for `expire`\n --all::\n \tProcess the reflogs of all references.\n \n+--single-worktree::\n+\tBy default when `--all` is specified, reflogs from all working\n+\ttrees are processed. This option limits the processing to reflogs\n+\tfrom the current working tree only.\n+\n --expire=<time>::\n \tPrune entries older than the specified time. If this option is\n \tnot specified, the expiration time is taken from the\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 3acef5a0ab..eed956851e 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -10,6 +10,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"reachable.h\"\n+#include \"worktree.h\"\n \n /* NEEDSWORK: switch to using parse_options */\n static const char reflog_expire_usage[] =\n@@ -52,6 +53,7 @@ struct collect_reflog_cb {\n \tstruct collected_reflog **e;\n \tint alloc;\n \tint nr;\n+\tstruct worktree *wt;\n };\n \n /* Remember to update object flag allocation in object.h */\n@@ -388,8 +390,12 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus\n {\n \tstruct collected_reflog *e;\n \tstruct collect_reflog_cb *cb = cb_data;\n+\tstruct strbuf newref = STRBUF_INIT;\n+\n+\tstrbuf_worktree_ref(cb->wt, &newref, ref);\n+\tFLEX_ALLOC_STR(e, reflog, newref.buf);\n+\tstrbuf_release(&newref);\n \n-\tFLEX_ALLOC_STR(e, reflog, ref);\n \toidcpy(&e->oid, oid);\n \tALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);\n \tcb->e[cb->nr++] = e;\n@@ -512,7 +518,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n {\n \tstruct expire_reflog_policy_cb cb;\n \ttimestamp_t now = time(NULL);\n-\tint i, status, do_all;\n+\tint i, status, do_all, all_worktrees = 1;\n \tint explicit_expiry = 0;\n \tunsigned int flags = 0;\n \n@@ -549,6 +555,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\t\tflags |= EXPIRE_REFLOGS_UPDATE_REF;\n \t\telse if (!strcmp(arg, \"--all\"))\n \t\t\tdo_all = 1;\n+\t\telse if (!strcmp(arg, \"--single-worktree\"))\n+\t\t\tall_worktrees = 0;\n \t\telse if (!strcmp(arg, \"--verbose\"))\n \t\t\tflags |= EXPIRE_REFLOGS_VERBOSE;\n \t\telse if (!strcmp(arg, \"--\")) {\n@@ -577,10 +585,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \n \tif (do_all) {\n \t\tstruct collect_reflog_cb collected;\n+\t\tstruct worktree **worktrees, **p;\n \t\tint i;\n \n \t\tmemset(&collected, 0, sizeof(collected));\n-\t\tfor_each_reflog(collect_reflog, &collected);\n+\t\tworktrees = get_worktrees(0);\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tif (!all_worktrees && !(*p)->is_current)\n+\t\t\t\tcontinue;\n+\t\t\tcollected.wt = *p;\n+\t\t\tfor_each_reflog(collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n \t\tfor (i = 0; i < collected.nr; i++) {\n \t\t\tstruct collected_reflog *e = collected.e[i];\n \t\t\tset_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358710","messageId":"20180922180500.4689-7-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-22T18:04:58Z","receivedAt":"2018-09-22T18:05:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThis will make it easier to check the HEAD of other worktrees from fsck.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c | 27 ++++++++++++++++-----------\n 1 file changed, 16 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 63c8578cc1..24f8a09a3c 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -36,8 +36,6 @@ static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n-static struct object_id head_oid;\n-static const char *head_points_at;\n static int errors_found;\n static int write_lost_and_found;\n static int verbose;\n@@ -484,8 +482,15 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid);\n+\n static void get_default_heads(void)\n {\n+\tconst char *head_points_at;\n+\tstruct object_id head_oid;\n+\n+\tfsck_head_link(&head_points_at, &head_oid);\n \tif (head_points_at && !is_null_oid(&head_oid))\n \t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n@@ -579,33 +584,34 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(void)\n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n \t\tfprintf(stderr, \"Checking HEAD link\\n\");\n \n-\thead_points_at = resolve_ref_unsafe(\"HEAD\", 0, &head_oid, NULL);\n-\tif (!head_points_at) {\n+\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"Invalid HEAD\");\n \t}\n-\tif (!strcmp(head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, \"HEAD\"))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n-\telse if (!starts_with(head_points_at, \"refs/heads/\")) {\n+\telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     head_points_at);\n+\t\t\t     *head_points_at);\n \t}\n-\tif (is_null_oid(&head_oid)) {\n+\tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n \t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n \t\t}\n \t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\thead_points_at + 11);\n+\t\t\t*head_points_at + 11);\n \t}\n \treturn 0;\n }\n@@ -720,7 +726,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \tgit_config(fsck_config, NULL);\n \n-\tfsck_head_link();\n \tif (connectivity_only) {\n \t\tfor_each_loose_object(mark_loose_for_connectivity, NULL, 0);\n \t\tfor_each_packed_object(mark_packed_for_connectivity, NULL, 0);\n-- \n2.19.0.647.gb9a6049235\n\n"},{"id":"358715","messageId":"CAPig+cS-S8cnDnZfXLyz=W3fVM=Bp9ntNVab6uZqeHA9_CSUCw@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-3-pclouds@gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-23T07:51:22Z","receivedAt":"2018-09-23T07:51:35Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> When multiple worktrees are used, we need rules to determine if\n> something belongs to one worktree or all of them. Instead of keeping\n> adding rules when new stuff comes, have a generic rule:\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\n> @@ -0,0 +1,36 @@\n> +test_expect_success 'setup' '\n> +       test_commit initial &&\n> +       test_commit wt1 &&\n> +       test_commit wt2 &&\n> +       git worktree add wt1 wt1 &&\n> +       git worktree add wt2 wt2 &&\n> +       git checkout initial\n> +'\n> +\n> +test_expect_success 'add refs/local' '\n> +       git update-ref refs/local/foo HEAD &&\n> +       git -C wt1 update-ref refs/local/foo HEAD &&\n> +       git -C wt2 update-ref refs/local/foo HEAD\n> +'\n\nNot at all worth a re-roll, but the \"add refs/local\" test seems like\njust more setup, thus could be rolled into the \"setup\" test (unless it\nwill be growing in some non-setup way in later patches).\n"},{"id":"358716","messageId":"CAPig+cTtjwCMRMx2xS1WPyM30ipxx5QJK_Aueu50i-jLQdpdBA@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-4-pclouds@gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-23T08:06:03Z","receivedAt":"2018-09-23T08:06:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> [...]\n> The main worktree has to be treated specially because well.. it's\n> special from the beginning. So HEAD from the main worktree is\n> acccessible via the name \"main/HEAD\" (we can't use\n> \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n> reserved).\n\nBikeshedding: I wonder if this would be more intuitive if called\nsimply \"/HEAD\" rather than \"main/HEAD\".\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/refs.c b/refs.c\n> @@ -641,12 +641,32 @@ static int is_pseudoref_syntax(const char *refname)\n> +static int is_main_pseudoref_syntax(const char *refname)\n> +{\n> +       return skip_prefix(refname, \"main/\", &refname) &&\n> +               is_pseudoref_syntax(refname);\n> +}\n> +\n> +static int is_other_pseudoref_syntax(const char *refname)\n> +{\n> +       if (!skip_prefix(refname, \"worktrees/\", &refname))\n> +               return 0;\n> +       refname = strchr(refname, '/');\n> +       if (!refname)\n> +               return 0;\n> +       return is_pseudoref_syntax(refname + 1);\n> +}\n\nIf the input is \"worktrees/refs/\" (nothing following the trailing\n'/'), then an empty string will be passed to is_pseudoref_syntax(),\nwhich will return true. Does that result in correct behavior? (Same\nquestion about \"main/\" being passed to is_main_pseudoref_syntax().)\n"},{"id":"358717","messageId":"CAPig+cTZpgU_n78wG4W0PDvM7Xdr3-1DoHz4d25XBFUmUy7a+A@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-6-pclouds@gmail.com","subject":"Re: [PATCH 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-23T08:25:39Z","receivedAt":"2018-09-23T08:38:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Make use of the new ref aliases to pass refs from another worktree\n> around and access them from the current ref store instead. This does\n> not change any functionality, but when a problem shows up, we would\n> report something like\n\nFrom this description, I had a hard time grasping that the first\nexample output is the desired one. Not necessarily worth a re-roll,\nbut had it said instead something like this:\n\n    ...but when a problem arises, we would like the reported\n    messages to mention full ref aliases, like this:\n\nit would have been more obvious.\n\nMore below...\n\n>     fatal: bad object worktrees/ztemp/HEAD\n>     warning: reflog of 'main/HEAD' references pruned commits\n>\n> instead of\n>\n>     fatal: bad object HEAD\n>     warning: reflog of 'HEAD' references pruned commits\n>\n> which does not really tell where the refs are from.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/worktree.c b/worktree.c\n> @@ -487,6 +487,28 @@ int submodule_uses_worktrees(const char *path)\n> +void strbuf_worktree_ref(const struct worktree *wt,\n> +                        struct strbuf *sb,\n> +                        const char *refname)\n> +{\n> +       if (wt && !wt->is_current) {\n> +               if (is_main_worktree(wt))\n> +                       strbuf_addstr(sb, \"main/\");\n> +               else\n> +                       strbuf_addf(sb, \"worktrees/%s/\", wt->id);\n> +       }\n> +       strbuf_addstr(sb, refname);\n> +}\n\nSeeing this use worktree->id to construct \"worktrees/<id>/<refname>\"\nmakes me wonder how the user is going to know the ID of a worktree in\norder to specify such refs in the first place. For example, how is the\nuser going to know the <id> in \"git rev-parse worktrees/<id>/HEAD\"? I\ndon't think we print the worktree ID anywhere, do we? Certainly, \"git\nworktree list\" doesn't show it, and \"git worktree add\" stopped showing\nit as of 2c27002a0a (worktree: improve message when creating a new\nworktree, 2018-04-24).\n\n> diff --git a/worktree.h b/worktree.h\n> @@ -108,4 +108,18 @@ extern const char *worktree_git_path(const struct worktree *wt,\n> +/*\n> + * Return a refname suitable for access from the current ref\n> + * store. The result may be destroyed at the next call.\n> + */\n\nIf you re-roll, perhaps: s/may/will/\n"},{"id":"358718","messageId":"CAPig+cTcaS-J5CwNo5RqeZiJYQ9OTt+Qzo08tdDjvZNaVSK2OQ@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-8-pclouds@gmail.com","subject":"Re: [PATCH 7/8] fsck: check HEAD and reflog from other worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-23T08:41:32Z","receivedAt":"2018-09-23T08:41:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> fsck is a repo-wide operation and should check all references no\n> matter which worktree they are associated to.\n>\n> Reported-by: Jeff King <peff@peff.net>\n> Helped-by: Elijah Newren <newren@gmail.com>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> @@ -101,6 +101,45 @@ test_expect_success 'HEAD link pointing at a funny place' '\n> +test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n> +       test_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n> +       test_when_finished \"rm -rf .git/worktrees wt\" &&\n> +       git worktree add wt &&\n> +       mv .git/HEAD .git/SAVED_HEAD &&\n> +       echo 0000000000000000000000000000000000000000 >.git/HEAD &&\n\nPerhaps use $ZERO_OID here instead of hardcoded \"0...\" string to play\nfriendly with brian's bc/hash-independent-tests (in 'next'). Same for\nfollowing test.\n\n> +       # avoid corrupt/broken HEAD from interfering with repo discovery\n> +       test_must_fail git -C wt fsck 2>out &&\n> +       cat out &&\n\nUnneeded 'cat'. Debugging cruft? Same for other tests.\n\n> +       grep \"main/HEAD: detached HEAD points\" out\n\nThis message doesn't get localized, so no need for test_i18ngrep(). Okay.\n\n> +'\n"},{"id":"358721","messageId":"CACsJy8BM=JH+z2yaMjqJpfXvSxjkagVNJAs7SPsiFJYw-eCYDg@mail.gmail.com","threadId":"49405","inReplyTo":"CAPig+cTtjwCMRMx2xS1WPyM30ipxx5QJK_Aueu50i-jLQdpdBA@mail.gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-23T13:10:21Z","receivedAt":"2018-09-23T13:10:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 23, 2018 at 10:06 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Sat, Sep 22, 2018 at 2:05 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > [...]\n> > The main worktree has to be treated specially because well.. it's\n> > special from the beginning. So HEAD from the main worktree is\n> > acccessible via the name \"main/HEAD\" (we can't use\n> > \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n> > reserved).\n>\n> Bikeshedding: I wonder if this would be more intuitive if called\n> simply \"/HEAD\" rather than \"main/HEAD\".\n\nA ref name cannot start with '/'. I'm open to a different name than\n\"main\" though, it just felt a better name than \"main-worktree\".\n-- \nDuy\n"},{"id":"358722","messageId":"CACsJy8CN-2vBhNrsunBoZ3WMSPK9ULoBRGULCeDbNjLTp5J+ig@mail.gmail.com","threadId":"49405","inReplyTo":"CAPig+cTZpgU_n78wG4W0PDvM7Xdr3-1DoHz4d25XBFUmUy7a+A@mail.gmail.com","subject":"Re: [PATCH 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-23T13:15:04Z","receivedAt":"2018-09-23T13:15:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 23, 2018 at 10:25 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > @@ -487,6 +487,28 @@ int submodule_uses_worktrees(const char *path)\n> > +void strbuf_worktree_ref(const struct worktree *wt,\n> > +                        struct strbuf *sb,\n> > +                        const char *refname)\n> > +{\n> > +       if (wt && !wt->is_current) {\n> > +               if (is_main_worktree(wt))\n> > +                       strbuf_addstr(sb, \"main/\");\n> > +               else\n> > +                       strbuf_addf(sb, \"worktrees/%s/\", wt->id);\n> > +       }\n> > +       strbuf_addstr(sb, refname);\n> > +}\n>\n> Seeing this use worktree->id to construct \"worktrees/<id>/<refname>\"\n> makes me wonder how the user is going to know the ID of a worktree in\n> order to specify such refs in the first place. For example, how is the\n> user going to know the <id> in \"git rev-parse worktrees/<id>/HEAD\"? I\n> don't think we print the worktree ID anywhere, do we? Certainly, \"git\n> worktree list\" doesn't show it, and \"git worktree add\" stopped showing\n> it as of 2c27002a0a (worktree: improve message when creating a new\n> worktree, 2018-04-24).\n\nOh yes I forgot to point this out. With this approach we have to have\nan id, and the directory name inside $GIT_DIR/worktrees seems a\nnatural choice. I was hoping to go with extended ref syntax instead\n[1] which gives us more flexibility in identifying a worktree. But I\ndon't think that's going to happen. \"git worktree\" would need some\nimprovements to show this id, specify it at \"git worktree add\" and\npotentially rename it even.\n\n[1] https://public-inbox.org/git/CACsJy8BOvU_z-uLrFmzFyryMXHNDfc_FUN_4i4NnVXWoShaBLQ@mail.gmail.com/\n-- \nDuy\n"},{"id":"358815","messageId":"CAGZ79kZF1+0PTEgF_NwM_AwttJ0sedAP8CT834L5ZGJpxZ+G_Q@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-3-pclouds@gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-09-25T02:35:29Z","receivedAt":"2018-09-25T02:35:42Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Sat, Sep 22, 2018 at 11:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> When multiple worktrees are used, we need rules to determine if\n> something belongs to one worktree or all of them. Instead of keeping\n> adding rules when new stuff comes, have a generic rule:\n>\n> - Inside $GIT_DIR, which is per-worktree by default, add\n>   $GIT_DIR/common which is always shared. New features that want to\n>   share stuff should put stuff under this directory.\n\nSo that /common is a directory and you have to use it specifically\nin new code? That would be easy to overlook when coming up\nwith $GIT_DIR/foo for implementing the git-foo.\n\n>\n> - Inside refs/, which is shared by default except refs/bisect, add\n>   refs/local/ which is per-worktree. We may eventually move\n>   refs/bisect to this new location and remove the exception in refs\n>   code.\n\nThat sounds dangerous to me. There is already a concept of\nlocal and remote-tracking branches. So I would think that local\nmay soon become an overused word, (just like \"index\" today or\n\"recursive\" to a lesser extend).\n\nCould this special area be more explicit?\n(refs/worktree-local/ ? or after peeking at the docs below\nrefs/un-common/ ?)\n"},{"id":"358816","messageId":"CAGZ79kYTzs=Xx-K-ghUa=as7Q0tiw-z1h7_D2=6zaO3fpWMrWg@mail.gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-4-pclouds@gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-09-25T02:48:18Z","receivedAt":"2018-09-25T02:48:32Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Sat, Sep 22, 2018 at 11:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>\n> One of the problems with multiple worktree is accessing per-worktree\n> refs of one worktree from another worktree. This was sort of solved by\n> multiple ref store, where the code can open the ref store of another\n> worktree and has access to the ref space of that worktree.\n>\n> The problem with this is reporting. \"HEAD\" in another ref space is\n> also called \"HEAD\" like in the current ref space. In order to\n> differentiate them, all the code must somehow carry the ref store\n> around and print something like \"HEAD from this ref store\".\n>\n> But that is not feasible (or possible with a _lot_ of work). With the\n> current design, we pass a reference around as a string (so called\n> \"refname\"). Extending this design to pass a string _and_ a ref store\n> is a nightmare, especially when handling extended SHA-1 syntax.\n>\n> So we do it another way. Instead of entering a separate ref space, we\n> make refs from other worktrees available in the current ref space. So\n> \"HEAD\" is always HEAD of the current worktree, but then we can have\n> \"worktrees/blah/HEAD\" to denote HEAD from a worktree named\n> \"blah\". This syntax coincidentally matches the underlying directory\n> structure which makes implementation a bit easier.\n>\n> The main worktree has to be treated specially because well.. it's\n> special from the beginning. So HEAD from the main worktree is\n> acccessible via the name \"main/HEAD\" (we can't use\n> \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n> reserved).\n>\n> This patch also makes it possible to specify refs from one worktree in\n> another one, e.g.\n>\n>     git log worktrees/foo/HEAD\n\nThis has strong similarities to remote refs:\nLocally I may have a branch master, whose (stale local copy of its\ndistributed) counterpart is named origin/master.\n\nIt is also possible to have a working tree named origin\n(just I like to name my worktree \"git\", when working on git.git),\nhow do we differentiate between the neighbor-worktree\n\"origin/master\" and the remote-tracking branch \"origin/master\" ?\n\nAs the remote tracking branches are shared between all\nworktree there is no need to differentiate between a\nlocal-worktree remote tracking branch and a\nneighbor-worktree remote tracking branch.\n\nNow that you introduce the magic main working tree,\nwe also need to disallow working trees to be named \"main\",\ni.e.\n    $ git worktree add main HEAD\n\nproduces\n\n  $ ls .git/worktrees/\n  main\n\nHow do we deal with that?\n"},{"id":"358837","messageId":"CACsJy8BKTkbc=ZgMnO7Yuk0eaqzZnifo80tnR872_T8b02biqg@mail.gmail.com","threadId":"49405","inReplyTo":"CAGZ79kZF1+0PTEgF_NwM_AwttJ0sedAP8CT834L5ZGJpxZ+G_Q@mail.gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-25T15:36:02Z","receivedAt":"2018-09-25T15:36:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 25, 2018 at 4:35 AM Stefan Beller <sbeller@google.com> wrote:\n>\n> On Sat, Sep 22, 2018 at 11:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> >\n> > When multiple worktrees are used, we need rules to determine if\n> > something belongs to one worktree or all of them. Instead of keeping\n> > adding rules when new stuff comes, have a generic rule:\n> >\n> > - Inside $GIT_DIR, which is per-worktree by default, add\n> >   $GIT_DIR/common which is always shared. New features that want to\n> >   share stuff should put stuff under this directory.\n>\n> So that /common is a directory and you have to use it specifically\n> in new code? That would be easy to overlook when coming up\n> with $GIT_DIR/foo for implementing the git-foo.\n\nThere's no easy way out. I have to do _something_ if you want to share\n$GIT_DIR/foo to all worktrees. Either we have to update path.c and add\n\"foo\" which is not even an option for external commands, or we put\n\"foo\" in a common place, e.g. $GIT_DIR/common/foo.\n\n> > - Inside refs/, which is shared by default except refs/bisect, add\n> >   refs/local/ which is per-worktree. We may eventually move\n> >   refs/bisect to this new location and remove the exception in refs\n> >   code.\n>\n> That sounds dangerous to me. There is already a concept of\n> local and remote-tracking branches. So I would think that local\n> may soon become an overused word, (just like \"index\" today or\n> \"recursive\" to a lesser extend).\n>\n> Could this special area be more explicit?\n> (refs/worktree-local/ ? or after peeking at the docs below\n> refs/un-common/ ?)\n\nrefs/un-common sounds really \"uncommon\" :D. If refs/local is bad, I\nguess we could go with either refs/worktree-local, refs/worktree,\nrefs/private, refs/per-worktree... My vote is on refs/worktree. I\nthink as long as the word \"worktree\" is in there, people would notice\nthe difference.\n-- \nDuy\n"},{"id":"358839","messageId":"CACsJy8DsykQzUzx7iTqPeuGBSJ9gcEHJRw_UCXJCrsXxf+hntQ@mail.gmail.com","threadId":"49405","inReplyTo":"CAGZ79kYTzs=Xx-K-ghUa=as7Q0tiw-z1h7_D2=6zaO3fpWMrWg@mail.gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-25T15:49:05Z","receivedAt":"2018-09-25T15:49:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 25, 2018 at 4:48 AM Stefan Beller <sbeller@google.com> wrote:\n> > This patch also makes it possible to specify refs from one worktree in\n> > another one, e.g.\n> >\n> >     git log worktrees/foo/HEAD\n>\n> This has strong similarities to remote refs:\n> Locally I may have a branch master, whose (stale local copy of its\n> distributed) counterpart is named origin/master.\n\nIf you think of each worktree as independent clones (which is more or\nless true, the fact that they share ODB is more like an implementation\ndetail) then yes it's almost like remotes.\n\n> It is also possible to have a working tree named origin\n> (just I like to name my worktree \"git\", when working on git.git),\n> how do we differentiate between the neighbor-worktree\n> \"origin/master\" and the remote-tracking branch \"origin/master\" ?\n\nHmm.. I think you're thinking that origin/master could either mean\nrefs/worktrees/origin/master or refs/remotes/origin/master. I do not\nthink we're going to support expanding origin/master to\nrefs/worktrees/origin/master. This part about ref resolution did cross\nmy mind but I didn't see a good reason to support it.\n\nEven if we do support it, this is not even a new problem. If you have\nrefs/heads/origin/master and refs/remotes/origin/master now, we have\nref ambiguity anyway and a solution for this should handle\nrefs/worktrees/origin/master well if it comes into the picture.\n\n> As the remote tracking branches are shared between all\n> worktree there is no need to differentiate between a\n> local-worktree remote tracking branch and a\n> neighbor-worktree remote tracking branch.\n>\n> Now that you introduce the magic main working tree,\n> we also need to disallow working trees to be named \"main\",\n> i.e.\n>     $ git worktree add main HEAD\n>\n> produces\n>\n>   $ ls .git/worktrees/\n>   main\n>\n> How do we deal with that?\n\nmain is accessed via worktrees/main/HEAD while the main worktree's\nHEAD is accessed via main/HEAD (which is _not_ automatically expanded\nto refs/worktrees/main/HEAD). But if it is, yes we need to detect\nambiguity and tell the user to specify full ref name, either\nrefs/main/HEAD or refs/worktrees/main/HEAD.\n-- \nDuy\n"},{"id":"358843","messageId":"CAGZ79kZw8-BiW5VE_YN5X2E07FeMA=XtHpjcUoSFtWNRu44fAQ@mail.gmail.com","threadId":"49405","inReplyTo":"CACsJy8BKTkbc=ZgMnO7Yuk0eaqzZnifo80tnR872_T8b02biqg@mail.gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-09-25T16:24:29Z","receivedAt":"2018-09-25T16:24:44Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Sep 25, 2018 at 8:36 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Sep 25, 2018 at 4:35 AM Stefan Beller <sbeller@google.com> wrote:\n> >\n> > On Sat, Sep 22, 2018 at 11:05 AM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > >\n> > > When multiple worktrees are used, we need rules to determine if\n> > > something belongs to one worktree or all of them. Instead of keeping\n> > > adding rules when new stuff comes, have a generic rule:\n> > >\n> > > - Inside $GIT_DIR, which is per-worktree by default, add\n> > >   $GIT_DIR/common which is always shared. New features that want to\n> > >   share stuff should put stuff under this directory.\n> >\n> > So that /common is a directory and you have to use it specifically\n> > in new code? That would be easy to overlook when coming up\n> > with $GIT_DIR/foo for implementing the git-foo.\n>\n> There's no easy way out. I have to do _something_ if you want to share\n> $GIT_DIR/foo to all worktrees. Either we have to update path.c and add\n> \"foo\" which is not even an option for external commands, or we put\n> \"foo\" in a common place, e.g. $GIT_DIR/common/foo.\n>\n> > > - Inside refs/, which is shared by default except refs/bisect, add\n> > >   refs/local/ which is per-worktree. We may eventually move\n> > >   refs/bisect to this new location and remove the exception in refs\n> > >   code.\n> >\n> > That sounds dangerous to me. There is already a concept of\n> > local and remote-tracking branches. So I would think that local\n> > may soon become an overused word, (just like \"index\" today or\n> > \"recursive\" to a lesser extend).\n> >\n> > Could this special area be more explicit?\n> > (refs/worktree-local/ ? or after peeking at the docs below\n> > refs/un-common/ ?)\n>\n> refs/un-common sounds really \"uncommon\" :D. If refs/local is bad, I\n> guess we could go with either refs/worktree-local, refs/worktree,\n> refs/private, refs/per-worktree... My vote is on refs/worktree. I\n\nrefs/worktree sounds good to me (I do not object), but I am not\noverly enthused either, as when I think further worktrees and\nsubmodules are both features with a very similar nature in that\nthey touch a lot of core concepts in Git, but seem to be a niche\nfeature for the masses for now.\n\nFor example I could think of submodules following this addressing\nmode as well: submodule/<path>/master sounds similar to the\noriginally proposed worktree/<name>/<branch> convention.\nFor now it is not quite clear to me why you would want to have\naccess to the submodule refs in the superproject, but maybe\nthe use case will come later.\n\nAnd with that said, I wonder if the \"local\" part should be feature agnostic,\nor if we want to be \"local\" for worktrees, \"local\" for remotes, \"local\"\nfor submodules (i.e. our own refs vs submodule refs).\n\n> think as long as the word \"worktree\" is in there, people would notice\n> the difference.\n\nThat makes sense. But is refs/worktree shared or local? It's not quite\nobvious to me, as I could have refs/worktree/<worktree-name>/master\ninstead when it is shared, so I tend to favor refs/local-worktree/ a bit\nmore, but that is more typing. :/\n\n==\nAs we grow the worktree feature, do we ever expect the need to\nreference the current worktree?\n\nFor example when there is a ref \"test\" that could be unique per\nrepo and in the common area, so refs/heads/test would describe\nit and \"test\" would get there in DWIM mode.\n\nBut then I could also delete the common ref and recreate a \"test\"\nref in worktree A, in worktree B however DWIMming \"test\" could still\nrefer to A's \"test\" as it is unique (so far) in the repository.\nAnd maybe I would want to check if test exists locally, so I'd\nwant to ask for \"self/test\" (with \"self\" == \"B\" as that is my cwd).\n\nStefan\n"},{"id":"358844","messageId":"CAGZ79kaDoV-1rtOzXasQ+-sfcoWV7FSBhDrm_kueTt_+a95gUw@mail.gmail.com","threadId":"49405","inReplyTo":"CACsJy8DsykQzUzx7iTqPeuGBSJ9gcEHJRw_UCXJCrsXxf+hntQ@mail.gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-09-25T16:53:21Z","receivedAt":"2018-09-25T16:53:36Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Sep 25, 2018 at 8:49 AM Duy Nguyen <pclouds@gmail.com> wrote:\n>\n> On Tue, Sep 25, 2018 at 4:48 AM Stefan Beller <sbeller@google.com> wrote:\n> > > This patch also makes it possible to specify refs from one worktree in\n> > > another one, e.g.\n> > >\n> > >     git log worktrees/foo/HEAD\n> >\n> > This has strong similarities to remote refs:\n> > Locally I may have a branch master, whose (stale local copy of its\n> > distributed) counterpart is named origin/master.\n>\n> If you think of each worktree as independent clones (which is more or\n> less true, the fact that they share ODB is more like an implementation\n> detail) then yes it's almost like remotes.\n\nApart from the ODB and the refs subsystem, there is also the config\nspace, which is shared (but you have sent out patches to have local\nconfig as well).\n\nSo I would think worktrees are better than having two clones not just\ndue to the shared ODB, but also due to the common config as then I\nhave to setup my repo only once and can add/remove worktrees\ncheaply (in terms of \"how much time do I need to spend to configure\nit as I need\").\n\n> > It is also possible to have a working tree named origin\n> > (just I like to name my worktree \"git\", when working on git.git),\n> > how do we differentiate between the neighbor-worktree\n> > \"origin/master\" and the remote-tracking branch \"origin/master\" ?\n>\n> Hmm.. I think you're thinking that origin/master could either mean\n> refs/worktrees/origin/master or refs/remotes/origin/master. I do not\n> think we're going to support expanding origin/master to\n> refs/worktrees/origin/master. This part about ref resolution did cross\n> my mind but I didn't see a good reason to support it.\n>\n> Even if we do support it, this is not even a new problem. If you have\n> refs/heads/origin/master and refs/remotes/origin/master now, we have\n> ref ambiguity anyway and a solution for this should handle\n> refs/worktrees/origin/master well if it comes into the picture.\n\nSo once origin/master is overloaded, I would have to spell out\nrefs/worktrees/origin/master and refs/remotes/origin/master to\navoid confusing the DWIM machinery. Makes sense.\n\n> > How do we deal with that?\n>\n> main is accessed via worktrees/main/HEAD while the main worktree's\n> HEAD is accessed via main/HEAD (which is _not_ automatically expanded\n> to refs/worktrees/main/HEAD). But if it is, yes we need to detect\n> ambiguity and tell the user to specify full ref name, either\n> refs/main/HEAD or refs/worktrees/main/HEAD.\n\nAh, I see. Now I actually understand the last paragraph of the\ncommit message. Thanks for explaining!\n\nStefan\n"},{"id":"358845","messageId":"CACsJy8BRv1wb7urzriaj9AceZh-Ot1Tsb2w9rExRXkoHFn7_hw@mail.gmail.com","threadId":"49405","inReplyTo":"CAGZ79kZw8-BiW5VE_YN5X2E07FeMA=XtHpjcUoSFtWNRu44fAQ@mail.gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-25T16:55:31Z","receivedAt":"2018-09-25T16:56:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 25, 2018 at 6:24 PM Stefan Beller <sbeller@google.com> wrote:\n> > > That sounds dangerous to me. There is already a concept of\n> > > local and remote-tracking branches. So I would think that local\n> > > may soon become an overused word, (just like \"index\" today or\n> > > \"recursive\" to a lesser extend).\n> > >\n> > > Could this special area be more explicit?\n> > > (refs/worktree-local/ ? or after peeking at the docs below\n> > > refs/un-common/ ?)\n> >\n> > refs/un-common sounds really \"uncommon\" :D. If refs/local is bad, I\n> > guess we could go with either refs/worktree-local, refs/worktree,\n> > refs/private, refs/per-worktree... My vote is on refs/worktree. I\n>\n> refs/worktree sounds good to me (I do not object), but I am not\n> overly enthused either, as when I think further worktrees and\n> submodules are both features with a very similar nature in that\n> they touch a lot of core concepts in Git, but seem to be a niche\n> feature for the masses for now.\n\nI think the similarity is partly because submodule feature also has to\nmanage worktrees. My view is at some point, this \"git worktree\" would\nbe good enough that it can handle submodules as well (for the worktree\npart only of course)\n\n> For example I could think of submodules following this addressing\n> mode as well: submodule/<path>/master sounds similar to the\n> originally proposed worktree/<name>/<branch> convention.\n> For now it is not quite clear to me why you would want to have\n> access to the submodule refs in the superproject, but maybe\n> the use case will come later.\n\nYeah. In theory we could \"mount\" the submodule ref store to a\nsuperproject's ref store. I think it may be needed just for the same\nreason it's needed for worktree: error reporting. If you peek into a\nsubmodule and say \"HEAD has an error\", the user will get confused\nwhether it's superproject's HEAD or a submodule's HEAD.\n\n> And with that said, I wonder if the \"local\" part should be feature agnostic,\n> or if we want to be \"local\" for worktrees, \"local\" for remotes, \"local\"\n> for submodules (i.e. our own refs vs submodule refs).\n\nYou lost me here.\n\n>\n> > think as long as the word \"worktree\" is in there, people would notice\n> > the difference.\n>\n> That makes sense. But is refs/worktree shared or local? It's not quite\n> obvious to me, as I could have refs/worktree/<worktree-name>/master\n> instead when it is shared, so I tend to favor refs/local-worktree/ a bit\n> more, but that is more typing. :/\n\nOK I think mixing the two patches will different purposes messes you\n(or me) up ;-)\n\nrefs/worktrees/xxx (and refs/main/xxx) are about visibility from other\nworktrees. Or like Eric put it, they are simply aliases. These refs\nare not shared because if they are, you can already see them without\nnew \"ref mount points\" like this.\n\nrefs/worktree (previously refs/local) is also per-worktree but it's\nspecifically because you can't have per-worktree inside \"refs/\" (the\nonly exception so far is refs/bisect which is hard coded). You can\nhave refs outside \"refs/\" (like HEAD or FETCH_HEAD) and they will not\nbe shared, but they cannot be iterated while those inside refs/ can\nbe. This is more about deciding what to share and I believe is really\nworktree-specific and only matters to _current_ worktree.\n\nSince refs/worktree is per-worktree, you can also view them from a\ndifferent worktree via refs/worktrees/. E.g. if you have\nrefs/worktree/foo then another worktree can see it via\nrefs/worktrees/xxx/refs/worktree/foo (besides pseudo refs like\nrefs/worktrees/xxx/HEAD)\n\n> As we grow the worktree feature, do we ever expect the need to\n> reference the current worktree?\n>\n> For example when there is a ref \"test\" that could be unique per\n> repo and in the common area, so refs/heads/test would describe\n> it and \"test\" would get there in DWIM mode.\n>\n> But then I could also delete the common ref and recreate a \"test\"\n> ref in worktree A, in worktree B however DWIMming \"test\" could still\n> refer to A's \"test\" as it is unique (so far) in the repository.\n> And maybe I would want to check if test exists locally, so I'd\n> want to ask for \"self/test\" (with \"self\" == \"B\" as that is my cwd).\n\nYou probably lost me again. In theory we must be able to detect\nambiguity and stop DWIMing. If you want to be ambiguity-free, you\nspecify full ref name, starting with \"refs/\" which should function\nlike \"self/\" because worktree design so far is always about the\ncurrent worktree's view.\n-- \nDuy\n"},{"id":"358848","messageId":"CAGZ79kZqQVZJpiAkS0xy1rN9vLa-udaPDwWd03OC8TGiayOTcQ@mail.gmail.com","threadId":"49405","inReplyTo":"CACsJy8BRv1wb7urzriaj9AceZh-Ot1Tsb2w9rExRXkoHFn7_hw@mail.gmail.com","subject":"Re: [PATCH 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-09-25T17:56:24Z","receivedAt":"2018-09-25T17:56:38Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Sep 25, 2018 at 9:55 AM Duy Nguyen <pclouds@gmail.com> wrote:\n\n> > And with that said, I wonder if the \"local\" part should be feature agnostic,\n> > or if we want to be \"local\" for worktrees, \"local\" for remotes, \"local\"\n> > for submodules (i.e. our own refs vs submodule refs).\n>\n> You lost me here.\n\nYeah, me too after rereading. :P\n\nI think the \"local\" part always implies that there is a part that is\nnot local and depending on the feature you call it remote or other\nworktree.\n\nWhen writing this comment I briefly wondered if we want to combine\nthe local aspects of the various features.\nHowever the \"local\" part really depends on the feature\n(e.g. a ref on a different worktree is still local from the here/remote\nperspective or from the superproject/submodule perspective),\nso I think I was misguided.\n\n> > > think as long as the word \"worktree\" is in there, people would notice\n> > > the difference.\n> >\n> > That makes sense. But is refs/worktree shared or local? It's not quite\n> > obvious to me, as I could have refs/worktree/<worktree-name>/master\n> > instead when it is shared, so I tend to favor refs/local-worktree/ a bit\n> > more, but that is more typing. :/\n>\n> OK I think mixing the two patches will different purposes messes you\n> (or me) up ;-)\n\npossible.\n\n>\n> refs/worktrees/xxx (and refs/main/xxx) are about visibility from other\n> worktrees. Or like Eric put it, they are simply aliases. These refs\n> are not shared because if they are, you can already see them without\n> new \"ref mount points\" like this.\n>\n> refs/worktree (previously refs/local) is also per-worktree but it's\n> specifically because you can't have per-worktree inside \"refs/\" (the\n> only exception so far is refs/bisect which is hard coded). You can\n> have refs outside \"refs/\" (like HEAD or FETCH_HEAD) and they will not\n> be shared, but they cannot be iterated while those inside refs/ can\n> be. This is more about deciding what to share and I believe is really\n> worktree-specific and only matters to _current_ worktree.\n>\n> Since refs/worktree is per-worktree, you can also view them from a\n> different worktree via refs/worktrees/. E.g. if you have\n> refs/worktree/foo then another worktree can see it via\n> refs/worktrees/xxx/refs/worktree/foo (besides pseudo refs like\n> refs/worktrees/xxx/HEAD)\n\nAh. now I seem to understand, thanks for explaining.\n"},{"id":"358873","messageId":"xmqqy3bpnupm.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"20180922180500.4689-4-pclouds@gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-09-25T21:16:53Z","receivedAt":"2018-09-25T21:16:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> The main worktree has to be treated specially because well.. it's\n> special from the beginning. So HEAD from the main worktree is\n> acccessible via the name \"main/HEAD\" (we can't use\n> \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n> reserved).\n\nI do not quite follow.  So with this, both refs/heads/master and\nmain/refs/heads/master are good names for the master branch (even\nthough the local branch names are not per worktree), because\nin the main worktree, refs/bisect/bad and main/refs/bisect/bad ought\nto mean the same thing.\n\n\tside note: Or is this only for pseudo-refs\n\t(i.e. $GIT_DIR/$name where $name consists of all caps or\n\tunderscore and typically ends with HEAD)?  Even if that were\n\tthe case, I do not think it essentially changes the issue\n\taround disambiguation that much.\n\nThe disambiguation rule has always been: if you have a confusingly\nnamed ref, you can spell it out fully to avoid any ambiguity, e.g.\nrefs/heads/refs/heads/foo can be given to \"git rev-parse\" and will\nmean the tip of the branch whose name is \"refs/heads/foo\", even when\nanother branch whose name is \"foo\" exists.\n\nWould we have a reasonable disambiguation rules that work well with\nthe main/ and worktrees/* prefixes?  When somebody has main/HEAD branch\nand writes \"git rev-parse main/HEAD\", does it find refs/heads/main/HEAD\nor $GIT_DIR/HEAD, if the user is in the main worktree?\n\nIt could be simply that the design is underdocumented in this patch\nset (in which case I would have appreciated 'RFC' near 'PATCH'), but\nI have a feeling that the code came way too early before such design\nissues are fleshed out.\n\n> diff --git a/refs.h b/refs.h\n> index bd52c1bbae..9b53dbeae8 100644\n> --- a/refs.h\n> +++ b/refs.h\n> @@ -704,9 +704,11 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);\n>  int ref_is_hidden(const char *, const char *);\n>  \n>  enum ref_type {\n> -\tREF_TYPE_PER_WORKTREE,\n> -\tREF_TYPE_PSEUDOREF,\n> -\tREF_TYPE_NORMAL,\n> +\tREF_TYPE_PER_WORKTREE,\t  /* refs inside refs/ but not shared       */\n> +\tREF_TYPE_PSEUDOREF,\t  /* refs outside refs/ in current worktree */\n> +\tREF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */\n> +\tREF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */\n> +\tREF_TYPE_NORMAL,\t  /* normal/shared refs inside refs/        */\n>  };\n>  \n>  enum ref_type ref_type(const char *refname);\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 416eafa453..bf9ed633b1 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> @@ -149,6 +149,23 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n>  \treturn refs;\n>  }\n>  \n> +static void files_reflog_path_other_worktrees(struct files_ref_store *refs,\n> +\t\t\t\t\t      struct strbuf *sb,\n> +\t\t\t\t\t      const char *refname)\n> +{\n> +\tconst char *real_ref;\n> +\n> +\tif (!skip_prefix(refname, \"worktrees/\", &real_ref))\n> +\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n> +\treal_ref = strchr(real_ref, '/');\n> +\tif (!real_ref)\n> +\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n> +\treal_ref++;\n> +\n> +\tstrbuf_addf(sb, \"%s/%.*slogs/%s\", refs->gitcommondir,\n> +\t\t    (int)(real_ref - refname), refname, real_ref);\n> +}\n> +\n>  static void files_reflog_path(struct files_ref_store *refs,\n>  \t\t\t      struct strbuf *sb,\n>  \t\t\t      const char *refname)\n> @@ -158,6 +175,12 @@ static void files_reflog_path(struct files_ref_store *refs,\n>  \tcase REF_TYPE_PSEUDOREF:\n>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n>  \t\tbreak;\n> +\tcase REF_TYPE_OTHER_PSEUDOREF:\n> +\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n> +\tcase REF_TYPE_MAIN_PSEUDOREF:\n> +\t\tif (!skip_prefix(refname, \"main/\", &refname))\n> +\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n> +\t\t/* passthru */\n>  \tcase REF_TYPE_NORMAL:\n>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n>  \t\tbreak;\n> @@ -176,6 +199,11 @@ static void files_ref_path(struct files_ref_store *refs,\n>  \tcase REF_TYPE_PSEUDOREF:\n>  \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n>  \t\tbreak;\n> +\tcase REF_TYPE_MAIN_PSEUDOREF:\n> +\t\tif (!skip_prefix(refname, \"main/\", &refname))\n> +\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n> +\t\t/* passthru */\n> +\tcase REF_TYPE_OTHER_PSEUDOREF:\n>  \tcase REF_TYPE_NORMAL:\n>  \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n>  \t\tbreak;\n> diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\n> index 0c2d5f89a9..46ca7bfc19 100755\n> --- a/t/t1415-worktree-refs.sh\n> +++ b/t/t1415-worktree-refs.sh\n> @@ -33,4 +33,34 @@ test_expect_success 'refs/local are per-worktree' '\n>  \t( cd wt2 && test_cmp_rev local/foo wt2 )\n>  '\n>  \n> +test_expect_success 'resolve main/HEAD' '\n> +\ttest_cmp_rev main/HEAD initial &&\n> +\t( cd wt1 && test_cmp_rev main/HEAD initial ) &&\n> +\t( cd wt2 && test_cmp_rev main/HEAD initial )\n> +'\n> +\n> +test_expect_success 'resolve worktrees/xx/HEAD' '\n> +\ttest_cmp_rev worktrees/wt1/HEAD wt1 &&\n> +\t( cd wt1 && test_cmp_rev worktrees/wt1/HEAD wt1 ) &&\n> +\t( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )\n> +'\n> +\n> +test_expect_success 'reflog of main/HEAD' '\n> +\tgit reflog HEAD | sed \"s/HEAD/main\\/HEAD/\" >expected &&\n> +\tgit reflog main/HEAD >actual &&\n> +\ttest_cmp expected actual &&\n> +\tgit -C wt1 reflog main/HEAD >actual.wt1 &&\n> +\ttest_cmp expected actual.wt1\n> +'\n> +\n> +test_expect_success 'reflog of worktrees/xx/HEAD' '\n> +\tgit -C wt2 reflog HEAD | sed \"s/HEAD/worktrees\\/wt2\\/HEAD/\" >expected &&\n> +\tgit reflog worktrees/wt2/HEAD >actual &&\n> +\ttest_cmp expected actual &&\n> +\tgit -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&\n> +\ttest_cmp expected actual.wt1 &&\n> +\tgit -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&\n> +\ttest_cmp expected actual.wt2\n> +'\n> +\n>  test_done\n"},{"id":"359303","messageId":"CACsJy8AiVdXHNz362rixeUWj0Y4HknU93qqcT2EFMWVbsjwpJg@mail.gmail.com","threadId":"49405","inReplyTo":"xmqqy3bpnupm.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T18:26:59Z","receivedAt":"2018-09-29T18:27:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Sep 25, 2018 at 11:16 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > The main worktree has to be treated specially because well.. it's\n> > special from the beginning. So HEAD from the main worktree is\n> > acccessible via the name \"main/HEAD\" (we can't use\n> > \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n> > reserved).\n>\n> I do not quite follow.  So with this, both refs/heads/master and\n> main/refs/heads/master are good names for the master branch (even\n> though the local branch names are not per worktree), because\n> in the main worktree, refs/bisect/bad and main/refs/bisect/bad ought\n> to mean the same thing.\n\nTrue. I think the ambiguation here is about the main worktree versus a\nsecondary worktree that is accidentally named \"main\". Then suddenly we\nhave to worktrees of the same name, and accessing them both via\nworktrees/<id>/HEAD will not work, and there is no other way to\ndisambiguate them.\n\n>         side note: Or is this only for pseudo-refs\n>         (i.e. $GIT_DIR/$name where $name consists of all caps or\n>         underscore and typically ends with HEAD)?\n\nRight now, due to implementation limitations, only pseudo refs (or\nloose refs in the case of refs/bisect) are accessible. But I don't see\nwhy main/refs/heads/master should not work.\n\n> The disambiguation rule has always been: if you have a confusingly\n> named ref, you can spell it out fully to avoid any ambiguity, e.g.\n> refs/heads/refs/heads/foo can be given to \"git rev-parse\" and will\n> mean the tip of the branch whose name is \"refs/heads/foo\", even when\n> another branch whose name is \"foo\" exists.\n>\n> Would we have a reasonable disambiguation rules that work well with\n> the main/ and worktrees/* prefixes?  When somebody has main/HEAD branch\n> and writes \"git rev-parse main/HEAD\", does it find refs/heads/main/HEAD\n> or $GIT_DIR/HEAD, if the user is in the main worktree?\n\nThe rules are not touched. But it looks like everything still works as\nexpected (I'm adding tests to verify this)\n-- \nDuy\n"},{"id":"359306","messageId":"20180929184003.GA20362@duynguyen.home","threadId":"49405","inReplyTo":"CAPig+cTcaS-J5CwNo5RqeZiJYQ9OTt+Qzo08tdDjvZNaVSK2OQ@mail.gmail.com","subject":"Re: [PATCH 7/8] fsck: check HEAD and reflog from other worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T18:40:03Z","receivedAt":"2018-09-29T18:40:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 23, 2018 at 04:41:32AM -0400, Eric Sunshine wrote:\n> > +       grep \"main/HEAD: detached HEAD points\" out\n> \n> This message doesn't get localized, so no need for\n> test_i18ngrep(). Okay.\n\nDon't remind me. I started to mark all strings in fsck for translation\nand faced the reality that I would need to change a lot to\ntest_i18ngrep :(\n--\nDuy\n"},{"id":"359310","messageId":"20180929191029.13994-1-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180922180500.4689-1-pclouds@gmail.com","subject":"[PATCH v2 0/8] fix per-worktree ref iteration in fsck/reflog expire","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:21Z","receivedAt":"2018-09-29T19:10:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v2 changes\n\n- more documentation\n- main/ prefix is renamed to main-worktree/ to reduce ambiguation\n  chances and make it clearer\n- refs/local is renamed to refs/worktree\n- bug fix in is_main_pseudoref_syntax() and\n  is_other_pseudoref_syntax()\n\nInterdiff\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..58415f9207 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,35 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+REFS\n+----\n+In multiple working trees, some refs may be shared between all working\n+trees, some refs are local. One example is HEAD is different for all\n+working trees. This section is about the sharing rules and how to access\n+refs of one working tree from another.\n+\n+In general, all pseudo refs are per working tree and all refs starting\n+with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n+directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n+exception to this: refs inside refs/bisect and refs/worktree is not\n+shared.\n+\n+Refs that are per working tree can still be accessed from another\n+working tree via two special paths main-worktree and worktrees. The\n+former gives access to per-worktree refs of the main working tree,\n+while the former to all linked working trees.\n+\n+For example, main-worktree/HEAD or main-worktree/refs/bisect/good\n+resolve to the same value as the main working tree's HEAD and\n+refs/bisect/good respectively. Similarly, worktrees/foo/HEAD or\n+worktrees/bar/refs/bisect/bad are the same as\n+GIT_COMMON_DIR/worktrees/foo/HEAD and\n+GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad.\n+\n+To access refs, it's best not to look inside GIT_DIR directly. Instead\n+use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n+which will handle refs correctly.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n@@ -228,7 +257,8 @@ linked working tree `git rev-parse --git-path HEAD` returns\n `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git\n rev-parse --git-path refs/heads/master` uses\n $GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,\n-since refs are shared across all working trees.\n+since refs are shared across all working trees, except refs/bisect and\n+refs/worktree.\n \n See linkgit:gitrepository-layout[5] for more information. The rule of\n thumb is do not make any assumption about whether a path belongs to\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex fad404ed7c..89b616e049 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -96,9 +96,9 @@ refs::\n \tdirectory.  The 'git prune' command knows to preserve\n \tobjects reachable from refs found in this directory and\n \tits subdirectories.\n-\tThis directory is ignored (except refs/bisect and refs/local)\n-\tif $GIT_COMMON_DIR is set and \"$GIT_COMMON_DIR/refs\" will be\n-\tused instead.\n+\tThis directory is ignored (except refs/bisect and\n+\trefs/worktree) if $GIT_COMMON_DIR is set and\n+\t\"$GIT_COMMON_DIR/refs\" will be used instead.\n \n refs/heads/`name`::\n \trecords tip-of-the-tree commit objects of branch `name`\ndiff --git a/path.c b/path.c\nindex 7eb61bf31b..bf4bb02a27 100644\n--- a/path.c\n+++ b/path.c\n@@ -119,6 +119,7 @@ static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"objects\" },\n \t{ 0, 1, 0, \"refs\" },\n \t{ 0, 1, 1, \"refs/bisect\" },\n+\t{ 0, 1, 1, \"refs/worktree\" },\n \t{ 0, 1, 0, \"remotes\" },\n \t{ 0, 1, 0, \"worktrees\" },\n \t{ 0, 1, 0, \"rr-cache\" },\ndiff --git a/refs.c b/refs.c\nindex 90b73c7334..2378b2e7fc 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -624,7 +624,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)\n static int is_per_worktree_ref(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") ||\n-\t\tstarts_with(refname, \"refs/local/\") ||\n+\t\tstarts_with(refname, \"refs/worktree/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\") ||\n \t\tstarts_with(refname, \"refs/rewritten/\");\n }\n@@ -643,7 +643,8 @@ static int is_pseudoref_syntax(const char *refname)\n \n static int is_main_pseudoref_syntax(const char *refname)\n {\n-\treturn skip_prefix(refname, \"main/\", &refname) &&\n+\treturn skip_prefix(refname, \"main-worktree/\", &refname) &&\n+\t\t*refname &&\n \t\tis_pseudoref_syntax(refname);\n }\n \n@@ -652,7 +653,7 @@ static int is_other_pseudoref_syntax(const char *refname)\n \tif (!skip_prefix(refname, \"worktrees/\", &refname))\n \t\treturn 0;\n \trefname = strchr(refname, '/');\n-\tif (!refname)\n+\tif (!refname || !refname[1])\n \t\treturn 0;\n \treturn is_pseudoref_syntax(refname + 1);\n }\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex bf9ed633b1..9ca2a3706c 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -178,7 +178,7 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tcase REF_TYPE_OTHER_PSEUDOREF:\n \t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n \tcase REF_TYPE_MAIN_PSEUDOREF:\n-\t\tif (!skip_prefix(refname, \"main/\", &refname))\n+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n \t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n \t\t/* passthru */\n \tcase REF_TYPE_NORMAL:\n@@ -200,7 +200,7 @@ static void files_ref_path(struct files_ref_store *refs,\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n \t\tbreak;\n \tcase REF_TYPE_MAIN_PSEUDOREF:\n-\t\tif (!skip_prefix(refname, \"main/\", &refname))\n+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n \t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n \t\t/* passthru */\n \tcase REF_TYPE_OTHER_PSEUDOREF:\n@@ -297,7 +297,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \tclosedir(d);\n \n \t/*\n-\t * Manually add refs/bisect and refs/local, which, being\n+\t * Manually add refs/bisect and refs/worktree, which, being\n \t * per-worktree, might not appear in the directory listing for\n \t * refs/ in the main repo.\n \t */\n@@ -310,11 +310,11 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \t\t\tadd_entry_to_dir(dir, child_entry);\n \t\t}\n \n-\t\tpos = search_ref_dir(dir, \"refs/local/\", 11);\n+\t\tpos = search_ref_dir(dir, \"refs/worktree/\", 11);\n \n \t\tif (pos < 0) {\n \t\t\tstruct ref_entry *child_entry = create_dir_entry(\n-\t\t\t\t\tdir->cache, \"refs/local/\", 11, 1);\n+\t\t\t\t\tdir->cache, \"refs/worktree/\", 11, 1);\n \t\t\tadd_entry_to_dir(dir, child_entry);\n \t\t}\n \t}\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex 46ca7bfc19..8b701d07af 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -10,33 +10,38 @@ test_expect_success 'setup' '\n \ttest_commit wt2 &&\n \tgit worktree add wt1 wt1 &&\n \tgit worktree add wt2 wt2 &&\n-\tgit checkout initial\n+\tgit checkout initial &&\n+\tgit update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt1 update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt2 update-ref refs/worktree/foo HEAD\n '\n \n-test_expect_success 'add refs/local' '\n-\tgit update-ref refs/local/foo HEAD &&\n-\tgit -C wt1 update-ref refs/local/foo HEAD &&\n-\tgit -C wt2 update-ref refs/local/foo HEAD\n-'\n-\n-test_expect_success 'refs/local must not be packed' '\n+test_expect_success 'refs/worktree must not be packed' '\n \tgit pack-refs --all &&\n \ttest_path_is_missing .git/refs/tags/wt1 &&\n-\ttest_path_is_file .git/refs/local/foo &&\n-\ttest_path_is_file .git/worktrees/wt1/refs/local/foo &&\n-\ttest_path_is_file .git/worktrees/wt2/refs/local/foo\n+\ttest_path_is_file .git/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt1/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt2/refs/worktree/foo\n '\n \n-test_expect_success 'refs/local are per-worktree' '\n-\ttest_cmp_rev local/foo initial &&\n-\t( cd wt1 && test_cmp_rev local/foo wt1 ) &&\n-\t( cd wt2 && test_cmp_rev local/foo wt2 )\n+test_expect_success 'refs/worktree are per-worktree' '\n+\ttest_cmp_rev worktree/foo initial &&\n+\t( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktree/foo wt2 )\n '\n \n-test_expect_success 'resolve main/HEAD' '\n-\ttest_cmp_rev main/HEAD initial &&\n-\t( cd wt1 && test_cmp_rev main/HEAD initial ) &&\n-\t( cd wt2 && test_cmp_rev main/HEAD initial )\n+test_expect_success 'resolve main-worktree/HEAD' '\n+\ttest_cmp_rev main-worktree/HEAD initial &&\n+\t( cd wt1 && test_cmp_rev main-worktree/HEAD initial ) &&\n+\t( cd wt2 && test_cmp_rev main-worktree/HEAD initial )\n+'\n+\n+test_expect_success 'ambiguous main-worktree/HEAD' '\n+\tmkdir -p .git/refs/heads/main-worktree &&\n+\ttest_when_finished rm .git/refs/heads/main-worktree/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/main-worktree/HEAD &&\n+\tgit rev-parse main-worktree/HEAD 2>warn >/dev/null &&\n+\tgrep \"main-worktree/HEAD.*ambiguous\" warn\n '\n \n test_expect_success 'resolve worktrees/xx/HEAD' '\n@@ -45,11 +50,19 @@ test_expect_success 'resolve worktrees/xx/HEAD' '\n \t( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )\n '\n \n-test_expect_success 'reflog of main/HEAD' '\n-\tgit reflog HEAD | sed \"s/HEAD/main\\/HEAD/\" >expected &&\n-\tgit reflog main/HEAD >actual &&\n+test_expect_success 'ambiguous worktrees/xx/HEAD' '\n+\tmkdir -p .git/refs/heads/worktrees/wt1 &&\n+\ttest_when_finished rm .git/refs/heads/worktrees/wt1/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&\n+\tgit rev-parse worktrees/wt1/HEAD 2>warn >/dev/null &&\n+\tgrep \"worktrees/wt1/HEAD.*ambiguous\" warn\n+'\n+\n+test_expect_success 'reflog of main-worktree/HEAD' '\n+\tgit reflog HEAD | sed \"s/HEAD/main-worktree\\/HEAD/\" >expected &&\n+\tgit reflog main-worktree/HEAD >actual &&\n \ttest_cmp expected actual &&\n-\tgit -C wt1 reflog main/HEAD >actual.wt1 &&\n+\tgit -C wt1 reflog main-worktree/HEAD >actual.wt1 &&\n \ttest_cmp expected actual.wt1\n '\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 444e8c1ad9..e97e6a7c6d 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -106,19 +106,17 @@ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n \ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n \tgit worktree add wt &&\n \tmv .git/HEAD .git/SAVED_HEAD &&\n-\techo 0000000000000000000000000000000000000000 >.git/HEAD &&\n+\techo $ZERO_OID >.git/HEAD &&\n \t# avoid corrupt/broken HEAD from interfering with repo discovery\n \ttest_must_fail git -C wt fsck 2>out &&\n-\tcat out &&\n \tgrep \"main/HEAD: detached HEAD points\" out\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny object' '\n \ttest_when_finished \"rm -rf .git/worktrees other\" &&\n \tgit worktree add other &&\n-\techo 0000000000000000000000000000000000000000 >.git/worktrees/other/HEAD &&\n+\techo $ZERO_OID >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n-\tcat out &&\n \tgrep \"worktrees/other/HEAD: detached HEAD points\" out\n '\n \n@@ -127,7 +125,6 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n \tgit worktree add other &&\n \techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n-\tcat out &&\n \tgrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n '\n \n@@ -136,7 +133,6 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '\n \tgit worktree add other &&\n \techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n-\tcat out &&\n \tgrep \"worktrees/other/HEAD points to something strange\" out\n '\n \n\nElijah Newren (1):\n  fsck: Move fsck_head_link() to get_default_heads() to avoid some\n    globals\n\nNguyễn Thái Ngọc Duy (7):\n  refs.c: indent with tabs, not spaces\n  Add a place for (not) sharing stuff between worktrees\n  refs: new ref types to make per-worktree refs visible to all worktrees\n  revision.c: correct a parameter name\n  revision.c: better error reporting on ref from different worktrees\n  fsck: check HEAD and reflog from other worktrees\n  reflog expire: cover reflog from all worktrees\n\n Documentation/git-reflog.txt           |  7 ++-\n Documentation/git-worktree.txt         | 32 ++++++++++-\n Documentation/gitrepository-layout.txt | 11 +++-\n builtin/fsck.c                         | 68 +++++++++++++++-------\n builtin/reflog.c                       | 22 ++++++-\n path.c                                 |  2 +\n refs.c                                 | 24 +++++++-\n refs.h                                 |  8 ++-\n refs/files-backend.c                   | 42 +++++++++++++-\n revision.c                             | 22 ++++---\n t/t0060-path-utils.sh                  |  2 +\n t/t1415-worktree-refs.sh               | 79 ++++++++++++++++++++++++++\n t/t1450-fsck.sh                        | 35 ++++++++++++\n worktree.c                             | 32 ++++++++++-\n worktree.h                             | 14 +++++\n 15 files changed, 354 insertions(+), 46 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359313","messageId":"20180929191029.13994-3-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:23Z","receivedAt":"2018-09-29T19:10:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When multiple worktrees are used, we need rules to determine if\nsomething belongs to one worktree or all of them. Instead of keeping\nadding rules when new stuff comes (*), have a generic rule:\n\n- Inside $GIT_DIR, which is per-worktree by default, add\n  $GIT_DIR/common which is always shared. New features that want to\n  share stuff should put stuff under this directory.\n\n- Inside refs/, which is shared by default except refs/bisect, add\n  refs/worktree/ which is per-worktree. We may eventually move\n  refs/bisect to this new location and remove the exception in refs\n  code.\n\n(*) And it may also include stuff from external commands which will\n    have no way to modify common/per-worktree rules.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-worktree.txt         | 19 ++++++++++++++-\n Documentation/gitrepository-layout.txt | 11 +++++++--\n path.c                                 |  2 ++\n refs.c                                 |  1 +\n refs/files-backend.c                   | 14 ++++++++---\n t/t0060-path-utils.sh                  |  2 ++\n t/t1415-worktree-refs.sh               | 33 ++++++++++++++++++++++++++\n 7 files changed, 76 insertions(+), 6 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..a50fbf8094 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,22 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+REFS\n+----\n+In multiple working trees, some refs may be shared between all working\n+trees, some refs are local. One example is HEAD is different for all\n+working trees. This section is about the sharing rules.\n+\n+In general, all pseudo refs are per working tree and all refs starting\n+with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n+directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n+exception to this: refs inside refs/bisect and refs/worktree is not\n+shared.\n+\n+To access refs, it's best not to look inside GIT_DIR directly. Instead\n+use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n+which will handle refs correctly.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n@@ -228,7 +244,8 @@ linked working tree `git rev-parse --git-path HEAD` returns\n `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git\n rev-parse --git-path refs/heads/master` uses\n $GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,\n-since refs are shared across all working trees.\n+since refs are shared across all working trees, except refs/bisect and\n+refs/worktree.\n \n See linkgit:gitrepository-layout[5] for more information. The rule of\n thumb is do not make any assumption about whether a path belongs to\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..89b616e049 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -95,8 +95,10 @@ refs::\n \tReferences are stored in subdirectories of this\n \tdirectory.  The 'git prune' command knows to preserve\n \tobjects reachable from refs found in this directory and\n-\tits subdirectories. This directory is ignored if $GIT_COMMON_DIR\n-\tis set and \"$GIT_COMMON_DIR/refs\" will be used instead.\n+\tits subdirectories.\n+\tThis directory is ignored (except refs/bisect and\n+\trefs/worktree) if $GIT_COMMON_DIR is set and\n+\t\"$GIT_COMMON_DIR/refs\" will be used instead.\n \n refs/heads/`name`::\n \trecords tip-of-the-tree commit objects of branch `name`\n@@ -165,6 +167,11 @@ hooks::\n \teach hook. This directory is ignored if $GIT_COMMON_DIR is set\n \tand \"$GIT_COMMON_DIR/hooks\" will be used instead.\n \n+common::\n+\tWhen multiple working trees are used, most of files in\n+\t$GIT_DIR are per-worktree with a few known exceptions. All\n+\tfiles under 'common' however will be shared between all\n+\tworking trees.\n \n index::\n \tThe current index file for the repository.  It is\ndiff --git a/path.c b/path.c\nindex 34f0f98349..bf4bb02a27 100644\n--- a/path.c\n+++ b/path.c\n@@ -108,6 +108,7 @@ struct common_dir {\n \n static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"branches\" },\n+\t{ 0, 1, 0, \"common\" },\n \t{ 0, 1, 0, \"hooks\" },\n \t{ 0, 1, 0, \"info\" },\n \t{ 0, 0, 1, \"info/sparse-checkout\" },\n@@ -118,6 +119,7 @@ static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"objects\" },\n \t{ 0, 1, 0, \"refs\" },\n \t{ 0, 1, 1, \"refs/bisect\" },\n+\t{ 0, 1, 1, \"refs/worktree\" },\n \t{ 0, 1, 0, \"remotes\" },\n \t{ 0, 1, 0, \"worktrees\" },\n \t{ 0, 1, 0, \"rr-cache\" },\ndiff --git a/refs.c b/refs.c\nindex 9f7268d5fe..1bc4ed301b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -624,6 +624,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)\n static int is_per_worktree_ref(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/worktree/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\") ||\n \t\tstarts_with(refname, \"refs/rewritten/\");\n }\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 16ef9325e0..2dd77f9485 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -269,9 +269,9 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \tclosedir(d);\n \n \t/*\n-\t * Manually add refs/bisect, which, being per-worktree, might\n-\t * not appear in the directory listing for refs/ in the main\n-\t * repo.\n+\t * Manually add refs/bisect and refs/worktree, which, being\n+\t * per-worktree, might not appear in the directory listing for\n+\t * refs/ in the main repo.\n \t */\n \tif (!strcmp(dirname, \"refs/\")) {\n \t\tint pos = search_ref_dir(dir, \"refs/bisect/\", 12);\n@@ -281,6 +281,14 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \t\t\t\t\tdir->cache, \"refs/bisect/\", 12, 1);\n \t\t\tadd_entry_to_dir(dir, child_entry);\n \t\t}\n+\n+\t\tpos = search_ref_dir(dir, \"refs/worktree/\", 11);\n+\n+\t\tif (pos < 0) {\n+\t\t\tstruct ref_entry *child_entry = create_dir_entry(\n+\t\t\t\t\tdir->cache, \"refs/worktree/\", 11, 1);\n+\t\t\tadd_entry_to_dir(dir, child_entry);\n+\t\t}\n \t}\n }\n \ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex cd74c0a471..c7b53e494b 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -306,6 +306,8 @@ test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me\n test_git_path GIT_COMMON_DIR=bar config                   bar/config\n test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n+test_git_path GIT_COMMON_DIR=bar common                   bar/common\n+test_git_path GIT_COMMON_DIR=bar common/file              bar/common/file\n \n # In the tests below, $(pwd) must be used because it is a native path on\n # Windows and avoids MSYS's path mangling (which simplifies \"foo/../bar\" and\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nnew file mode 100755\nindex 0000000000..16c91bef57\n--- /dev/null\n+++ b/t/t1415-worktree-refs.sh\n@@ -0,0 +1,33 @@\n+#!/bin/sh\n+\n+test_description='per-worktree refs'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial &&\n+\ttest_commit wt1 &&\n+\ttest_commit wt2 &&\n+\tgit worktree add wt1 wt1 &&\n+\tgit worktree add wt2 wt2 &&\n+\tgit checkout initial &&\n+\tgit update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt1 update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt2 update-ref refs/worktree/foo HEAD\n+'\n+\n+test_expect_success 'refs/worktree must not be packed' '\n+\tgit pack-refs --all &&\n+\ttest_path_is_missing .git/refs/tags/wt1 &&\n+\ttest_path_is_file .git/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt1/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt2/refs/worktree/foo\n+'\n+\n+test_expect_success 'refs/worktree are per-worktree' '\n+\ttest_cmp_rev worktree/foo initial &&\n+\t( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktree/foo wt2 )\n+'\n+\n+test_done\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359314","messageId":"20180929191029.13994-2-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 1/8] refs.c: indent with tabs, not spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:22Z","receivedAt":"2018-09-29T19:10:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex a7a75b4cc0..9f7268d5fe 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -646,7 +646,7 @@ enum ref_type ref_type(const char *refname)\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n-       return REF_TYPE_NORMAL;\n+\treturn REF_TYPE_NORMAL;\n }\n \n long get_files_ref_lock_timeout_ms(void)\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359311","messageId":"20180929191029.13994-4-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:24Z","receivedAt":"2018-09-29T19:10:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One of the problems with multiple worktree is accessing per-worktree\nrefs of one worktree from another worktree. This was sort of solved by\nmultiple ref store, where the code can open the ref store of another\nworktree and has access to the ref space of that worktree.\n\nThe problem with this is reporting. \"HEAD\" in another ref space is\nalso called \"HEAD\" like in the current ref space. In order to\ndifferentiate them, all the code must somehow carry the ref store\naround and print something like \"HEAD from this ref store\".\n\nBut that is not feasible (or possible with a _lot_ of work). With the\ncurrent design, we pass a reference around as a string (so called\n\"refname\"). Extending this design to pass a string _and_ a ref store\nis a nightmare, especially when handling extended SHA-1 syntax.\n\nSo we do it another way. Instead of entering a separate ref space, we\nmake refs from other worktrees available in the current ref space. So\n\"HEAD\" is always HEAD of the current worktree, but then we can have\n\"worktrees/blah/HEAD\" to denote HEAD from a worktree named\n\"blah\". This syntax coincidentally matches the underlying directory\nstructure which makes implementation a bit easier.\n\nThe main worktree has to be treated specially because well.. it's\nspecial from the beginning. So HEAD from the main worktree is\nacccessible via the name \"main-worktree/HEAD\" instead of\n\"worktrees/main/HEAD\" because \"main\" could be just another secondary\nworktree.\n\nThis patch also makes it possible to specify refs from one worktree in\nanother one, e.g.\n\n    git log worktrees/foo/HEAD\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-worktree.txt | 15 ++++++++++-\n refs.c                         | 21 ++++++++++++++++\n refs.h                         |  8 +++---\n refs/files-backend.c           | 28 +++++++++++++++++++++\n t/t1415-worktree-refs.sh       | 46 ++++++++++++++++++++++++++++++++++\n 5 files changed, 114 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex a50fbf8094..58415f9207 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -208,7 +208,8 @@ REFS\n ----\n In multiple working trees, some refs may be shared between all working\n trees, some refs are local. One example is HEAD is different for all\n-working trees. This section is about the sharing rules.\n+working trees. This section is about the sharing rules and how to access\n+refs of one working tree from another.\n \n In general, all pseudo refs are per working tree and all refs starting\n with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n@@ -216,6 +217,18 @@ directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n exception to this: refs inside refs/bisect and refs/worktree is not\n shared.\n \n+Refs that are per working tree can still be accessed from another\n+working tree via two special paths main-worktree and worktrees. The\n+former gives access to per-worktree refs of the main working tree,\n+while the former to all linked working trees.\n+\n+For example, main-worktree/HEAD or main-worktree/refs/bisect/good\n+resolve to the same value as the main working tree's HEAD and\n+refs/bisect/good respectively. Similarly, worktrees/foo/HEAD or\n+worktrees/bar/refs/bisect/bad are the same as\n+GIT_COMMON_DIR/worktrees/foo/HEAD and\n+GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad.\n+\n To access refs, it's best not to look inside GIT_DIR directly. Instead\n use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n which will handle refs correctly.\ndiff --git a/refs.c b/refs.c\nindex 1bc4ed301b..2378b2e7fc 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -641,12 +641,33 @@ static int is_pseudoref_syntax(const char *refname)\n \treturn 1;\n }\n \n+static int is_main_pseudoref_syntax(const char *refname)\n+{\n+\treturn skip_prefix(refname, \"main-worktree/\", &refname) &&\n+\t\t*refname &&\n+\t\tis_pseudoref_syntax(refname);\n+}\n+\n+static int is_other_pseudoref_syntax(const char *refname)\n+{\n+\tif (!skip_prefix(refname, \"worktrees/\", &refname))\n+\t\treturn 0;\n+\trefname = strchr(refname, '/');\n+\tif (!refname || !refname[1])\n+\t\treturn 0;\n+\treturn is_pseudoref_syntax(refname + 1);\n+}\n+\n enum ref_type ref_type(const char *refname)\n {\n \tif (is_per_worktree_ref(refname))\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n+\tif (is_main_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_MAIN_PSEUDOREF;\n+\tif (is_other_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_OTHER_PSEUDOREF;\n \treturn REF_TYPE_NORMAL;\n }\n \ndiff --git a/refs.h b/refs.h\nindex bd52c1bbae..9b53dbeae8 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -704,9 +704,11 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);\n int ref_is_hidden(const char *, const char *);\n \n enum ref_type {\n-\tREF_TYPE_PER_WORKTREE,\n-\tREF_TYPE_PSEUDOREF,\n-\tREF_TYPE_NORMAL,\n+\tREF_TYPE_PER_WORKTREE,\t  /* refs inside refs/ but not shared       */\n+\tREF_TYPE_PSEUDOREF,\t  /* refs outside refs/ in current worktree */\n+\tREF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */\n+\tREF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */\n+\tREF_TYPE_NORMAL,\t  /* normal/shared refs inside refs/        */\n };\n \n enum ref_type ref_type(const char *refname);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 2dd77f9485..9ca2a3706c 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -149,6 +149,23 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n \treturn refs;\n }\n \n+static void files_reflog_path_other_worktrees(struct files_ref_store *refs,\n+\t\t\t\t\t      struct strbuf *sb,\n+\t\t\t\t\t      const char *refname)\n+{\n+\tconst char *real_ref;\n+\n+\tif (!skip_prefix(refname, \"worktrees/\", &real_ref))\n+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n+\treal_ref = strchr(real_ref, '/');\n+\tif (!real_ref)\n+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n+\treal_ref++;\n+\n+\tstrbuf_addf(sb, \"%s/%.*slogs/%s\", refs->gitcommondir,\n+\t\t    (int)(real_ref - refname), refname, real_ref);\n+}\n+\n static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      struct strbuf *sb,\n \t\t\t      const char *refname)\n@@ -158,6 +175,12 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n+\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n+\t\t/* passthru */\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n \t\tbreak;\n@@ -176,6 +199,11 @@ static void files_ref_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n+\t\t/* passthru */\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n \t\tbreak;\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex 16c91bef57..8b701d07af 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -30,4 +30,50 @@ test_expect_success 'refs/worktree are per-worktree' '\n \t( cd wt2 && test_cmp_rev worktree/foo wt2 )\n '\n \n+test_expect_success 'resolve main-worktree/HEAD' '\n+\ttest_cmp_rev main-worktree/HEAD initial &&\n+\t( cd wt1 && test_cmp_rev main-worktree/HEAD initial ) &&\n+\t( cd wt2 && test_cmp_rev main-worktree/HEAD initial )\n+'\n+\n+test_expect_success 'ambiguous main-worktree/HEAD' '\n+\tmkdir -p .git/refs/heads/main-worktree &&\n+\ttest_when_finished rm .git/refs/heads/main-worktree/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/main-worktree/HEAD &&\n+\tgit rev-parse main-worktree/HEAD 2>warn >/dev/null &&\n+\tgrep \"main-worktree/HEAD.*ambiguous\" warn\n+'\n+\n+test_expect_success 'resolve worktrees/xx/HEAD' '\n+\ttest_cmp_rev worktrees/wt1/HEAD wt1 &&\n+\t( cd wt1 && test_cmp_rev worktrees/wt1/HEAD wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )\n+'\n+\n+test_expect_success 'ambiguous worktrees/xx/HEAD' '\n+\tmkdir -p .git/refs/heads/worktrees/wt1 &&\n+\ttest_when_finished rm .git/refs/heads/worktrees/wt1/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&\n+\tgit rev-parse worktrees/wt1/HEAD 2>warn >/dev/null &&\n+\tgrep \"worktrees/wt1/HEAD.*ambiguous\" warn\n+'\n+\n+test_expect_success 'reflog of main-worktree/HEAD' '\n+\tgit reflog HEAD | sed \"s/HEAD/main-worktree\\/HEAD/\" >expected &&\n+\tgit reflog main-worktree/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog main-worktree/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1\n+'\n+\n+test_expect_success 'reflog of worktrees/xx/HEAD' '\n+\tgit -C wt2 reflog HEAD | sed \"s/HEAD/worktrees\\/wt2\\/HEAD/\" >expected &&\n+\tgit reflog worktrees/wt2/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1 &&\n+\tgit -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&\n+\ttest_cmp expected actual.wt2\n+'\n+\n test_done\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359312","messageId":"20180929191029.13994-5-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 4/8] revision.c: correct a parameter name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:25Z","receivedAt":"2018-09-29T19:10:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is a callback of for_each_reflog() which will pass a ref\nname as the first argument, not a path (to a reflog file).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex e18bd530e4..63aae722c1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1277,13 +1277,14 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *path, const struct object_id *oid,\n+static int handle_one_reflog(const char *refname,\n+\t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = path;\n-\trefs_for_each_reflog_ent(cb->refs, path,\n+\tcb->name_for_errormsg = refname;\n+\trefs_for_each_reflog_ent(cb->refs, refname,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n \treturn 0;\n }\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359316","messageId":"20180929191029.13994-6-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:26Z","receivedAt":"2018-09-29T19:10:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Make use of the new ref aliases to pass refs from another worktree\naround and access them from the current ref store instead. This does\nnot change any functionality, but when a problem arises, we would like\nthe reported messages to mention full ref aliases, like this:\n\n    fatal: bad object worktrees/ztemp/HEAD\n    warning: reflog of 'main-worktree/HEAD' references pruned commits\n\ninstead of\n\n    fatal: bad object HEAD\n    warning: reflog of 'HEAD' references pruned commits\n\nwhich does not really tell where the refs are from.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 21 +++++++++++++--------\n worktree.c | 32 +++++++++++++++++++++++++++++---\n worktree.h | 14 ++++++++++++++\n 3 files changed, 56 insertions(+), 11 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 63aae722c1..8ce660e3b1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1177,7 +1177,7 @@ struct all_refs_cb {\n \tint warned_bad_reflog;\n \tstruct rev_info *all_revs;\n \tconst char *name_for_errormsg;\n-\tstruct ref_store *refs;\n+\tstruct worktree *wt;\n };\n \n int ref_excluded(struct string_list *ref_excludes, const char *path)\n@@ -1214,7 +1214,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,\n \tcb->all_revs = revs;\n \tcb->all_flags = flags;\n \trevs->rev_input_given = 1;\n-\tcb->refs = NULL;\n+\tcb->wt = NULL;\n }\n \n void clear_ref_exclusion(struct string_list **ref_excludes_p)\n@@ -1277,15 +1277,20 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *refname,\n+static int handle_one_reflog(const char *refname_in_wt,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = refname;\n-\trefs_for_each_reflog_ent(cb->refs, refname,\n+\tstrbuf_worktree_ref(cb->wt, &refname, refname_in_wt);\n+\tcb->name_for_errormsg = refname.buf;\n+\trefs_for_each_reflog_ent(get_main_ref_store(the_repository),\n+\t\t\t\t refname.buf,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -1300,8 +1305,8 @@ static void add_other_reflogs_to_pending(struct all_refs_cb *cb)\n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\tcb->refs = get_worktree_ref_store(wt);\n-\t\trefs_for_each_reflog(cb->refs,\n+\t\tcb->wt = wt;\n+\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n \t\t\t\t     handle_one_reflog,\n \t\t\t\t     cb);\n \t}\n@@ -1314,7 +1319,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)\n \n \tcb.all_revs = revs;\n \tcb.all_flags = flags;\n-\tcb.refs = get_main_ref_store(the_repository);\n+\tcb.wt = NULL;\n \tfor_each_reflog(handle_one_reflog, &cb);\n \n \tif (!revs->single_worktree)\ndiff --git a/worktree.c b/worktree.c\nindex b0d0b5426d..ec1a5bc511 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -487,6 +487,28 @@ int submodule_uses_worktrees(const char *path)\n \treturn ret;\n }\n \n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname)\n+{\n+\tif (wt && !wt->is_current) {\n+\t\tif (is_main_worktree(wt))\n+\t\t\tstrbuf_addstr(sb, \"main/\");\n+\t\telse\n+\t\t\tstrbuf_addf(sb, \"worktrees/%s/\", wt->id);\n+\t}\n+\tstrbuf_addstr(sb, refname);\n+}\n+\n+const char *worktree_ref(const struct worktree *wt, const char *refname)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_worktree_ref(wt, &sb, refname);\n+\treturn sb.buf;\n+}\n+\n int other_head_refs(each_ref_fn fn, void *cb_data)\n {\n \tstruct worktree **worktrees, **p;\n@@ -495,13 +517,17 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \tworktrees = get_worktrees(0);\n \tfor (p = worktrees; *p; p++) {\n \t\tstruct worktree *wt = *p;\n-\t\tstruct ref_store *refs;\n+\t\tstruct object_id oid;\n+\t\tint flag;\n \n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\trefs = get_worktree_ref_store(wt);\n-\t\tret = refs_head_ref(refs, fn, cb_data);\n+\t\tif (!refs_read_ref_full(get_main_ref_store(the_repository),\n+\t\t\t\t\tworktree_ref(wt, \"HEAD\"),\n+\t\t\t\t\tRESOLVE_REF_READING,\n+\t\t\t\t\t&oid, &flag))\n+\t\t\tret = fn(worktree_ref(wt, \"HEAD\"), &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\ndiff --git a/worktree.h b/worktree.h\nindex df3fc30f73..0016eb9e88 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -108,4 +108,18 @@ extern const char *worktree_git_path(const struct worktree *wt,\n \t\t\t\t     const char *fmt, ...)\n \t__attribute__((format (printf, 2, 3)));\n \n+/*\n+ * Return a refname suitable for access from the current ref store.\n+ */\n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname);\n+\n+/*\n+ * Return a refname suitable for access from the current ref\n+ * store. The result may be destroyed at the next call.\n+ */\n+const char *worktree_ref(const struct worktree *wt,\n+\t\t\t const char *refname);\n+\n #endif\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359315","messageId":"20180929191029.13994-8-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 7/8] fsck: check HEAD and reflog from other worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:28Z","receivedAt":"2018-09-29T19:10:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"fsck is a repo-wide operation and should check all references no\nmatter which worktree they are associated to.\n\nReported-by: Jeff King <peff@peff.net>\nHelped-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c  | 55 ++++++++++++++++++++++++++++++++++---------------\n t/t1450-fsck.sh | 35 +++++++++++++++++++++++++++++++\n 2 files changed, 73 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 24f8a09a3c..71492c158d 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -19,6 +19,7 @@\n #include \"packfile.h\"\n #include \"object-store.h\"\n #include \"run-command.h\"\n+#include \"worktree.h\"\n \n #define REACHABLE 0x0001\n #define SEEN      0x0002\n@@ -444,7 +445,11 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid\n static int fsck_handle_reflog(const char *logname, const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\n-\tfor_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n+\tstrbuf_worktree_ref(cb_data, &refname, logname);\n+\tfor_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -482,20 +487,34 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid);\n \n static void get_default_heads(void)\n {\n+\tstruct worktree **worktrees, **p;\n \tconst char *head_points_at;\n \tstruct object_id head_oid;\n \n-\tfsck_head_link(&head_points_at, &head_oid);\n-\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n-\tif (include_reflogs)\n-\t\tfor_each_reflog(fsck_handle_reflog, NULL);\n+\n+\tworktrees = get_worktrees(0);\n+\tfor (p = worktrees; *p; p++) {\n+\t\tstruct worktree *wt = *p;\n+\t\tstruct strbuf ref = STRBUF_INIT;\n+\n+\t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n+\t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n+\t\tif (head_points_at && !is_null_oid(&head_oid))\n+\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\tstrbuf_release(&ref);\n+\n+\t\tif (include_reflogs)\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n+\t\t\t\t\t     fsck_handle_reflog, wt);\n+\t}\n+\tfree_worktrees(worktrees);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\n@@ -584,34 +603,36 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n-\t\tfprintf(stderr, \"Checking HEAD link\\n\");\n+\t\tfprintf(stderr, \"Checking %s link\\n\", head_ref_name);\n \n-\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\t*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);\n \tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"Invalid HEAD\");\n+\t\treturn error(\"Invalid %s\", head_ref_name);\n \t}\n-\tif (!strcmp(*head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, head_ref_name))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n \telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     *head_points_at);\n+\t\treturn error(\"%s points to something strange (%s)\",\n+\t\t\t     head_ref_name, *head_points_at);\n \t}\n \tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n-\t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n+\t\t\treturn error(\"%s: detached HEAD points at nothing\",\n+\t\t\t\t     head_ref_name);\n \t\t}\n-\t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\t*head_points_at + 11);\n+\t\tfprintf(stderr, \"notice: %s points to an unborn branch (%s)\\n\",\n+\t\t\thead_ref_name, *head_points_at + 11);\n \t}\n \treturn 0;\n }\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 0f2dd26f74..e97e6a7c6d 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -101,6 +101,41 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \tgrep \"HEAD points to something strange\" out\n '\n \n+test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\tgit worktree add wt &&\n+\tmv .git/HEAD .git/SAVED_HEAD &&\n+\techo $ZERO_OID >.git/HEAD &&\n+\t# avoid corrupt/broken HEAD from interfering with repo discovery\n+\ttest_must_fail git -C wt fsck 2>out &&\n+\tgrep \"main/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo $ZERO_OID >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at missing object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny place' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD points to something strange\" out\n+'\n+\n test_expect_success 'email without @ is okay' '\n \tgit cat-file commit HEAD >basis &&\n \tsed \"s/@/AT/\" basis >okay &&\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359317","messageId":"20180929191029.13994-7-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:27Z","receivedAt":"2018-09-29T19:10:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThis will make it easier to check the HEAD of other worktrees from fsck.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c | 27 ++++++++++++++++-----------\n 1 file changed, 16 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 63c8578cc1..24f8a09a3c 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -36,8 +36,6 @@ static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n-static struct object_id head_oid;\n-static const char *head_points_at;\n static int errors_found;\n static int write_lost_and_found;\n static int verbose;\n@@ -484,8 +482,15 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid);\n+\n static void get_default_heads(void)\n {\n+\tconst char *head_points_at;\n+\tstruct object_id head_oid;\n+\n+\tfsck_head_link(&head_points_at, &head_oid);\n \tif (head_points_at && !is_null_oid(&head_oid))\n \t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n@@ -579,33 +584,34 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(void)\n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n \t\tfprintf(stderr, \"Checking HEAD link\\n\");\n \n-\thead_points_at = resolve_ref_unsafe(\"HEAD\", 0, &head_oid, NULL);\n-\tif (!head_points_at) {\n+\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"Invalid HEAD\");\n \t}\n-\tif (!strcmp(head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, \"HEAD\"))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n-\telse if (!starts_with(head_points_at, \"refs/heads/\")) {\n+\telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     head_points_at);\n+\t\t\t     *head_points_at);\n \t}\n-\tif (is_null_oid(&head_oid)) {\n+\tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n \t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n \t\t}\n \t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\thead_points_at + 11);\n+\t\t\t*head_points_at + 11);\n \t}\n \treturn 0;\n }\n@@ -720,7 +726,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \tgit_config(fsck_config, NULL);\n \n-\tfsck_head_link();\n \tif (connectivity_only) {\n \t\tfor_each_loose_object(mark_loose_for_connectivity, NULL, 0);\n \t\tfor_each_packed_object(mark_packed_for_connectivity, NULL, 0);\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359318","messageId":"20180929191029.13994-9-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v2 8/8] reflog expire: cover reflog from all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-09-29T19:10:29Z","receivedAt":"2018-09-29T19:11:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reported-by: Jeff King <peff@peff.net>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-reflog.txt |  7 ++++++-\n builtin/reflog.c             | 22 +++++++++++++++++++---\n 2 files changed, 25 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt\nindex 472a6808cd..ff487ff77d 100644\n--- a/Documentation/git-reflog.txt\n+++ b/Documentation/git-reflog.txt\n@@ -20,7 +20,7 @@ depending on the subcommand:\n 'git reflog' ['show'] [log-options] [<ref>]\n 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]\n \t[--rewrite] [--updateref] [--stale-fix]\n-\t[--dry-run | -n] [--verbose] [--all | <refs>...]\n+\t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] ref@\\{specifier\\}...\n 'git reflog exists' <ref>\n@@ -72,6 +72,11 @@ Options for `expire`\n --all::\n \tProcess the reflogs of all references.\n \n+--single-worktree::\n+\tBy default when `--all` is specified, reflogs from all working\n+\ttrees are processed. This option limits the processing to reflogs\n+\tfrom the current working tree only.\n+\n --expire=<time>::\n \tPrune entries older than the specified time. If this option is\n \tnot specified, the expiration time is taken from the\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 3acef5a0ab..eed956851e 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -10,6 +10,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"reachable.h\"\n+#include \"worktree.h\"\n \n /* NEEDSWORK: switch to using parse_options */\n static const char reflog_expire_usage[] =\n@@ -52,6 +53,7 @@ struct collect_reflog_cb {\n \tstruct collected_reflog **e;\n \tint alloc;\n \tint nr;\n+\tstruct worktree *wt;\n };\n \n /* Remember to update object flag allocation in object.h */\n@@ -388,8 +390,12 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus\n {\n \tstruct collected_reflog *e;\n \tstruct collect_reflog_cb *cb = cb_data;\n+\tstruct strbuf newref = STRBUF_INIT;\n+\n+\tstrbuf_worktree_ref(cb->wt, &newref, ref);\n+\tFLEX_ALLOC_STR(e, reflog, newref.buf);\n+\tstrbuf_release(&newref);\n \n-\tFLEX_ALLOC_STR(e, reflog, ref);\n \toidcpy(&e->oid, oid);\n \tALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);\n \tcb->e[cb->nr++] = e;\n@@ -512,7 +518,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n {\n \tstruct expire_reflog_policy_cb cb;\n \ttimestamp_t now = time(NULL);\n-\tint i, status, do_all;\n+\tint i, status, do_all, all_worktrees = 1;\n \tint explicit_expiry = 0;\n \tunsigned int flags = 0;\n \n@@ -549,6 +555,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\t\tflags |= EXPIRE_REFLOGS_UPDATE_REF;\n \t\telse if (!strcmp(arg, \"--all\"))\n \t\t\tdo_all = 1;\n+\t\telse if (!strcmp(arg, \"--single-worktree\"))\n+\t\t\tall_worktrees = 0;\n \t\telse if (!strcmp(arg, \"--verbose\"))\n \t\t\tflags |= EXPIRE_REFLOGS_VERBOSE;\n \t\telse if (!strcmp(arg, \"--\")) {\n@@ -577,10 +585,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \n \tif (do_all) {\n \t\tstruct collect_reflog_cb collected;\n+\t\tstruct worktree **worktrees, **p;\n \t\tint i;\n \n \t\tmemset(&collected, 0, sizeof(collected));\n-\t\tfor_each_reflog(collect_reflog, &collected);\n+\t\tworktrees = get_worktrees(0);\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tif (!all_worktrees && !(*p)->is_current)\n+\t\t\t\tcontinue;\n+\t\t\tcollected.wt = *p;\n+\t\t\tfor_each_reflog(collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n \t\tfor (i = 0; i < collected.nr; i++) {\n \t\t\tstruct collected_reflog *e = collected.e[i];\n \t\t\tset_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);\n-- \n2.19.0.341.g3acb95d729\n\n"},{"id":"359330","messageId":"CAPig+cTRNzhKhcmt7hnLHP=8Oc86EnokSDzDD4z2CGYFGmuz4Q@mail.gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T05:13:28Z","receivedAt":"2018-09-30T05:13:42Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 29, 2018 at 3:10 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The main worktree has to be treated specially because well.. it's\n\nNit: s/well../well.../\n\n> special from the beginning. So HEAD from the main worktree is\n> acccessible via the name \"main-worktree/HEAD\" instead of\n> \"worktrees/main/HEAD\" because \"main\" could be just another secondary\n> worktree.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> @@ -216,6 +217,18 @@ directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n> +Refs that are per working tree can still be accessed from another\n> +working tree via two special paths main-worktree and worktrees. The\n\ns/paths/paths,/\n\n> +former gives access to per-worktree refs of the main working tree,\n> +while the former to all linked working trees.\n\ns/former/latter/\n\n> diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\n> @@ -30,4 +30,50 @@ test_expect_success 'refs/worktree are per-worktree' '\n> +test_expect_success 'ambiguous main-worktree/HEAD' '\n> +       mkdir -p .git/refs/heads/main-worktree &&\n> +       test_when_finished rm .git/refs/heads/main-worktree/HEAD &&\n> +       cp .git/HEAD .git/refs/heads/main-worktree/HEAD &&\n\nBetter to use \"rm -f\" for cleanup in case this 'cp' fails for some reason.\n\n> +       git rev-parse main-worktree/HEAD 2>warn >/dev/null &&\n\nYou could probably omit the /dev/null redirect.\n\n> +       grep \"main-worktree/HEAD.*ambiguous\" warn\n> +'\n> +\n> +test_expect_success 'ambiguous worktrees/xx/HEAD' '\n> +       mkdir -p .git/refs/heads/worktrees/wt1 &&\n> +       test_when_finished rm .git/refs/heads/worktrees/wt1/HEAD &&\n\nDitto \"rm -f\".\n\n> +       cp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&\n> +       git rev-parse worktrees/wt1/HEAD 2>warn >/dev/null &&\n\nDitto /dev/null.\n\n> +       grep \"worktrees/wt1/HEAD.*ambiguous\" warn\n> +'\n"},{"id":"359332","messageId":"CAPig+cRb=xC09z8AS=4uV9J_YDzGB3ni0RX1FT3AD+VoAu=hGg@mail.gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-6-pclouds@gmail.com","subject":"Re: [PATCH v2 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T05:25:54Z","receivedAt":"2018-09-30T05:26:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 29, 2018 at 3:11 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Make use of the new ref aliases to pass refs from another worktree\n> around and access them from the current ref store instead. This does\n> not change any functionality, but when a problem arises, we would like\n> the reported messages to mention full ref aliases, like this:\n> [...]\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/worktree.c b/worktree.c\n> @@ -487,6 +487,28 @@ int submodule_uses_worktrees(const char *path)\n> +void strbuf_worktree_ref(const struct worktree *wt,\n> +                        struct strbuf *sb,\n> +                        const char *refname)\n> +{\n> +       if (wt && !wt->is_current) {\n> +               if (is_main_worktree(wt))\n> +                       strbuf_addstr(sb, \"main/\");\n\nI think this needs to be \"main-worktree/\", doesn't it?\n\n> +               else\n> +                       strbuf_addf(sb, \"worktrees/%s/\", wt->id);\n> +       }\n> +       strbuf_addstr(sb, refname);\n> +}\n> diff --git a/worktree.h b/worktree.h\n> @@ -108,4 +108,18 @@ extern const char *worktree_git_path(const struct worktree *wt,\n> +/*\n> + * Return a refname suitable for access from the current ref\n> + * store. The result may be destroyed at the next call.\n\ns/may/will/\n"},{"id":"359334","messageId":"CAPig+cQBiXRvzf6skmmyVap-6fLEbmVRGNBQqD4H-HfAPampjA@mail.gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-9-pclouds@gmail.com","subject":"Re: [PATCH v2 8/8] reflog expire: cover reflog from all worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-09-30T05:36:21Z","receivedAt":"2018-09-30T05:36:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Sep 29, 2018 at 3:11 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt\n> @@ -72,6 +72,11 @@ Options for `expire`\n> +--single-worktree::\n> +       By default when `--all` is specified, reflogs from all working\n> +       trees are processed. This option limits the processing to reflogs\n> +       from the current working tree only.\n\nBikeshedding: I wonder if this should be named \"--this-worktree\" or\n\"--this-worktree-only\" or if it should somehow be orthogonal to --all\nrather than modifying it. (Genuine questions. I don't have the\nanswers.)\n\n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> @@ -577,10 +585,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n>         if (do_all) {\n>                 struct collect_reflog_cb collected;\n> +               struct worktree **worktrees, **p;\n>                 int i;\n>\n>                 memset(&collected, 0, sizeof(collected));\n> -               for_each_reflog(collect_reflog, &collected);\n> +               worktrees = get_worktrees(0);\n> +               for (p = worktrees; *p; p++) {\n> +                       if (!all_worktrees && !(*p)->is_current)\n> +                               continue;\n> +                       collected.wt = *p;\n> +                       for_each_reflog(collect_reflog, &collected);\n> +               }\n> +               free_worktrees(worktrees);\n\nShould this have a test in the test suite?\n"},{"id":"359433","messageId":"CACsJy8C1niWzC8GPiKkXmyBwxbctb-vmM51CVts12TMzLFnJVA@mail.gmail.com","threadId":"49405","inReplyTo":"CAPig+cQBiXRvzf6skmmyVap-6fLEbmVRGNBQqD4H-HfAPampjA@mail.gmail.com","subject":"Re: [PATCH v2 8/8] reflog expire: cover reflog from all worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-02T16:16:12Z","receivedAt":"2018-10-02T16:16:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 30, 2018 at 7:36 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Sat, Sep 29, 2018 at 3:11 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> > ---\n> > diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt\n> > @@ -72,6 +72,11 @@ Options for `expire`\n> > +--single-worktree::\n> > +       By default when `--all` is specified, reflogs from all working\n> > +       trees are processed. This option limits the processing to reflogs\n> > +       from the current working tree only.\n>\n> Bikeshedding: I wonder if this should be named \"--this-worktree\" or\n> \"--this-worktree-only\" or if it should somehow be orthogonal to --all\n> rather than modifying it. (Genuine questions. I don't have the\n> answers.)\n\nIt follows a precedent (made by me :p) which is rev-list\n--single-worktree. I doubt that option is widely used though so we\ncould still rename it if there's a better name. I made\n--single-worktree to contrast \"all worktrees\" by default. Even if it's\n\"this/current worktree\" it still has to somehow say \"everything in\nthis worktree\" so I felt modifying --all was a good idea.\n\n> > diff --git a/builtin/reflog.c b/builtin/reflog.c\n> > @@ -577,10 +585,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n> >         if (do_all) {\n> >                 struct collect_reflog_cb collected;\n> > +               struct worktree **worktrees, **p;\n> >                 int i;\n> >\n> >                 memset(&collected, 0, sizeof(collected));\n> > -               for_each_reflog(collect_reflog, &collected);\n> > +               worktrees = get_worktrees(0);\n> > +               for (p = worktrees; *p; p++) {\n> > +                       if (!all_worktrees && !(*p)->is_current)\n> > +                               continue;\n> > +                       collected.wt = *p;\n> > +                       for_each_reflog(collect_reflog, &collected);\n> > +               }\n> > +               free_worktrees(worktrees);\n>\n> Should this have a test in the test suite?\n\nOf course. I was partly lazy/tired near the end, and anticipated more\ncomments anyway so I did not do it :D\n-- \nDuy\n"},{"id":"359500","messageId":"CAPig+cSHRus=zTd0reWoFMi+ERbjQO92tNhJBT9jnmUP8vvm3Q@mail.gmail.com","threadId":"49405","inReplyTo":"CACsJy8C1niWzC8GPiKkXmyBwxbctb-vmM51CVts12TMzLFnJVA@mail.gmail.com","subject":"Re: [PATCH v2 8/8] reflog expire: cover reflog from all worktrees","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-10-03T07:49:20Z","receivedAt":"2018-10-03T07:49:33Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Oct 2, 2018 at 12:16 PM Duy Nguyen <pclouds@gmail.com> wrote:\n> On Sun, Sep 30, 2018 at 7:36 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > On Sat, Sep 29, 2018 at 3:11 PM Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> > > +--single-worktree::\n> > > +       By default when `--all` is specified, reflogs from all working\n> > > +       trees are processed. This option limits the processing to reflogs\n> > > +       from the current working tree only.\n> >\n> > Bikeshedding: I wonder if this should be named \"--this-worktree\" or\n> > \"--this-worktree-only\" or if it should somehow be orthogonal to --all\n> > rather than modifying it. (Genuine questions. I don't have the\n> > answers.)\n>\n> It follows a precedent (made by me :p) which is rev-list\n> --single-worktree. I doubt that option is widely used though so we\n> could still rename it if there's a better name. I made\n> --single-worktree to contrast \"all worktrees\" by default. Even if it's\n> \"this/current worktree\" it still has to somehow say \"everything in\n> this worktree\" so I felt modifying --all was a good idea.\n\nPrecedent overrides bikeshedding, so leaving it as-is is fine.\n"},{"id":"359775","messageId":"xmqqbm86wtks.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"CACsJy8AiVdXHNz362rixeUWj0Y4HknU93qqcT2EFMWVbsjwpJg@mail.gmail.com","subject":"Re: [PATCH 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-06T23:20:35Z","receivedAt":"2018-10-06T23:20:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> > The main worktree has to be treated specially because well.. it's\n>> > special from the beginning. So HEAD from the main worktree is\n>> > acccessible via the name \"main/HEAD\" (we can't use\n>> > \"worktrees/main/HEAD\" because \"main\" under \"worktrees\" is not\n>> > reserved).\n>>\n>> I do not quite follow.  So with this, both refs/heads/master and\n>> main/refs/heads/master are good names for the master branch (even\n>> though the local branch names are not per worktree), because\n>> in the main worktree, refs/bisect/bad and main/refs/bisect/bad ought\n>> to mean the same thing.\n>\n> True. I think the ambiguation here is about the main worktree versus a\n> secondary worktree that is accidentally named \"main\". Then suddenly we\n> have to worktrees of the same name, and accessing them both via\n> worktrees/<id>/HEAD will not work, and there is no other way to\n> disambiguate them.\n\nSo those who have happily been referring 'refs/heads/main/foo' as\n'main/foo' now suddenly have to say 'refs/heads/main/foo' instead?\n\n> The rules are not touched. But it looks like everything still works as\n> expected (I'm adding tests to verify this)\n\nWhat I am worried about is that _your_ expectation may not coincide\nwith the expectations of users, especially with ones with existing\nrefs that overlap with the namespaces this series suddenly starts\ncarving out and squatting on.  As long as that won't be a problem, I\nthink it is OK, even with 'main' not renamed to 'worktree-main' or\nsomesuch.\n\n\n"},{"id":"359786","messageId":"xmqqva6etu3n.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"20180929191029.13994-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-07T01:37:32Z","receivedAt":"2018-10-07T01:37:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> diff --git a/refs/files-backend.c b/refs/files-backend.c\n> index 2dd77f9485..9ca2a3706c 100644\n> --- a/refs/files-backend.c\n> +++ b/refs/files-backend.c\n> ...\n>  \tcase REF_TYPE_PSEUDOREF:\n>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n>  \t\tbreak;\n> +\tcase REF_TYPE_OTHER_PSEUDOREF:\n> +\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n> +\tcase REF_TYPE_MAIN_PSEUDOREF:\n> +\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n> +\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n> +\t\t/* passthru */\n\nCorrect spelling of the colloquial phrase is fallthru, but see\n1cf01a34 (\"consistently use \"fallthrough\" comments in switches\",\n2017-09-21) that encourages use of \"fallthrough\" fully spelled out.\n\nOtherwise you'd see something like this.\n\n    CC refs/files-backend.o\nrefs/files-backend.c: In function 'files_ref_path':\nrefs/files-backend.c:203:6: error: this statement may fall through [-Werror=implicit-fallthrough=]\n   if (!skip_prefix(refname, \"main-worktree/\", &refname))\n      ^\nrefs/files-backend.c:206:2: note: here\n  case REF_TYPE_OTHER_PSEUDOREF:\n  ^~~~\nrefs/files-backend.c: In function 'files_reflog_path':\nrefs/files-backend.c:181:6: error: this statement may fall through [-Werror=implicit-fallthrough=]\n   if (!skip_prefix(refname, \"main-worktree/\", &refname))\n      ^\nrefs/files-backend.c:184:2: note: here\n  case REF_TYPE_NORMAL:\n  ^~~~\ncc1: all warnings being treated as errors\nMakefile:2289: recipe for target 'refs/files-backend.o' failed\nmake: *** [refs/files-backend.o] Error 1\n"},{"id":"361076","messageId":"20181021080859.3203-1-pclouds@gmail.com","threadId":"49405","inReplyTo":"20180929191029.13994-1-pclouds@gmail.com","subject":"[PATCH v3 0/8] fix per-worktree ref iteration in fsck/reflog expire","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:51Z","receivedAt":"2018-10-21T08:09:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v3 changes\n\n- fix incorrect ref reporting (it reported main/HEAD instead of\n  main-worktree/HEAD)\n- other document typos\n- fix strbuf_worktree_ref() producing refs that cannot be handled by\n  files-backend.c, e.g. worktrees/foo/refs/heads/master (one day the\n  ref store will, but not now)\n- make sure the \"HEAD\" special case in reflog expire still works\n  with main-worktree/HEAD and worktrees/xxx/HEAD\n- tests added for reflog\n\nElijah Newren (1):\n  fsck: Move fsck_head_link() to get_default_heads() to avoid some\n    globals\n\nNguyễn Thái Ngọc Duy (7):\n  refs.c: indent with tabs, not spaces\n  Add a place for (not) sharing stuff between worktrees\n  refs: new ref types to make per-worktree refs visible to all worktrees\n  revision.c: correct a parameter name\n  revision.c: better error reporting on ref from different worktrees\n  fsck: check HEAD and reflog from other worktrees\n  reflog expire: cover reflog from all worktrees\n\n Documentation/git-reflog.txt           |  7 ++-\n Documentation/git-worktree.txt         | 32 ++++++++++-\n Documentation/gitrepository-layout.txt | 11 +++-\n builtin/fsck.c                         | 68 +++++++++++++++-------\n builtin/reflog.c                       | 46 +++++++++++++--\n path.c                                 |  2 +\n refs.c                                 | 24 +++++++-\n refs.h                                 |  8 ++-\n refs/files-backend.c                   | 42 +++++++++++++-\n revision.c                             | 22 ++++---\n t/t0060-path-utils.sh                  |  2 +\n t/t1410-reflog.sh                      | 15 +++++\n t/t1415-worktree-refs.sh               | 79 ++++++++++++++++++++++++++\n t/t1450-fsck.sh                        | 35 ++++++++++++\n worktree.c                             | 79 +++++++++++++++++++++++++-\n worktree.h                             | 24 ++++++++\n 16 files changed, 449 insertions(+), 47 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\nRange-diff against v2:\n1:  328a4d1263 ! 1:  fff4cfcc93 refs: new ref types to make per-worktree refs visible to all worktrees\n    @@ -24,7 +24,7 @@\n         \"blah\". This syntax coincidentally matches the underlying directory\n         structure which makes implementation a bit easier.\n     \n    -    The main worktree has to be treated specially because well.. it's\n    +    The main worktree has to be treated specially because well... it's\n         special from the beginning. So HEAD from the main worktree is\n         acccessible via the name \"main-worktree/HEAD\" instead of\n         \"worktrees/main/HEAD\" because \"main\" could be just another secondary\n    @@ -53,9 +53,9 @@\n      shared.\n      \n     +Refs that are per working tree can still be accessed from another\n    -+working tree via two special paths main-worktree and worktrees. The\n    ++working tree via two special paths, main-worktree and worktrees. The\n     +former gives access to per-worktree refs of the main working tree,\n    -+while the former to all linked working trees.\n    ++while the latter to all linked working trees.\n     +\n     +For example, main-worktree/HEAD or main-worktree/refs/bisect/good\n     +resolve to the same value as the main working tree's HEAD and\n    @@ -128,6 +128,14 @@\n      diff --git a/refs/files-backend.c b/refs/files-backend.c\n      --- a/refs/files-backend.c\n      +++ b/refs/files-backend.c\n    +@@\n    + #include \"../object.h\"\n    + #include \"../dir.h\"\n    + #include \"../chdir-notify.h\"\n    ++#include \"worktree.h\"\n    + \n    + /*\n    +  * This backend uses the following flags in `ref_update::flags` for\n     @@\n      \treturn refs;\n      }\n    @@ -137,16 +145,18 @@\n     +\t\t\t\t\t      const char *refname)\n     +{\n     +\tconst char *real_ref;\n    ++\tconst char *worktree_name;\n    ++\tint length;\n     +\n    -+\tif (!skip_prefix(refname, \"worktrees/\", &real_ref))\n    -+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n    -+\treal_ref = strchr(real_ref, '/');\n    -+\tif (!real_ref)\n    ++\tif (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))\n     +\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n    -+\treal_ref++;\n     +\n    -+\tstrbuf_addf(sb, \"%s/%.*slogs/%s\", refs->gitcommondir,\n    -+\t\t    (int)(real_ref - refname), refname, real_ref);\n    ++\tif (worktree_name)\n    ++\t\tstrbuf_addf(sb, \"%s/worktrees/%.*s/logs/%s\", refs->gitcommondir,\n    ++\t\t\t    length, worktree_name, real_ref);\n    ++\telse\n    ++\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir,\n    ++\t\t\t    real_ref);\n     +}\n     +\n      static void files_reflog_path(struct files_ref_store *refs,\n    @@ -157,11 +167,8 @@\n      \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n      \t\tbreak;\n     +\tcase REF_TYPE_OTHER_PSEUDOREF:\n    -+\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n     +\tcase REF_TYPE_MAIN_PSEUDOREF:\n    -+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n    -+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n    -+\t\t/* passthru */\n    ++\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n      \tcase REF_TYPE_NORMAL:\n      \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n      \t\tbreak;\n    @@ -172,7 +179,7 @@\n     +\tcase REF_TYPE_MAIN_PSEUDOREF:\n     +\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n     +\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n    -+\t\t/* passthru */\n    ++\t\t/* fallthrough */\n     +\tcase REF_TYPE_OTHER_PSEUDOREF:\n      \tcase REF_TYPE_NORMAL:\n      \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n    @@ -193,9 +200,9 @@\n     +\n     +test_expect_success 'ambiguous main-worktree/HEAD' '\n     +\tmkdir -p .git/refs/heads/main-worktree &&\n    -+\ttest_when_finished rm .git/refs/heads/main-worktree/HEAD &&\n    ++\ttest_when_finished rm -f .git/refs/heads/main-worktree/HEAD &&\n     +\tcp .git/HEAD .git/refs/heads/main-worktree/HEAD &&\n    -+\tgit rev-parse main-worktree/HEAD 2>warn >/dev/null &&\n    ++\tgit rev-parse main-worktree/HEAD 2>warn &&\n     +\tgrep \"main-worktree/HEAD.*ambiguous\" warn\n     +'\n     +\n    @@ -207,9 +214,9 @@\n     +\n     +test_expect_success 'ambiguous worktrees/xx/HEAD' '\n     +\tmkdir -p .git/refs/heads/worktrees/wt1 &&\n    -+\ttest_when_finished rm .git/refs/heads/worktrees/wt1/HEAD &&\n    ++\ttest_when_finished rm -f .git/refs/heads/worktrees/wt1/HEAD &&\n     +\tcp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&\n    -+\tgit rev-parse worktrees/wt1/HEAD 2>warn >/dev/null &&\n    ++\tgit rev-parse worktrees/wt1/HEAD 2>warn &&\n     +\tgrep \"worktrees/wt1/HEAD.*ambiguous\" warn\n     +'\n     +\n    @@ -232,3 +239,62 @@\n     +'\n     +\n      test_done\n    +\n    + diff --git a/worktree.c b/worktree.c\n    + --- a/worktree.c\n    + +++ b/worktree.c\n    +@@\n    + \treturn ret;\n    + }\n    + \n    ++int parse_worktree_ref(const char *worktree_ref, const char **name,\n    ++\t\t       int *name_length, const char **ref)\n    ++{\n    ++\tif (skip_prefix(worktree_ref, \"main-worktree/\", &worktree_ref)) {\n    ++\t\tif (!*worktree_ref)\n    ++\t\t\treturn -1;\n    ++\t\tif (name)\n    ++\t\t\t*name = NULL;\n    ++\t\tif (name_length)\n    ++\t\t\t*name_length = 0;\n    ++\t\tif (ref)\n    ++\t\t\t*ref = worktree_ref;\n    ++\t\treturn 0;\n    ++\t}\n    ++\tif (skip_prefix(worktree_ref, \"worktrees/\", &worktree_ref)) {\n    ++\t\tconst char *slash = strchr(worktree_ref, '/');\n    ++\n    ++\t\tif (!slash || slash == worktree_ref || !slash[1])\n    ++\t\t\treturn -1;\n    ++\t\tif (name)\n    ++\t\t\t*name = worktree_ref;\n    ++\t\tif (name_length)\n    ++\t\t\t*name_length = slash - worktree_ref;\n    ++\t\tif (ref)\n    ++\t\t\t*ref = slash + 1;\n    ++\t\treturn 0;\n    ++\t}\n    ++\treturn -1;\n    ++}\n    ++\n    + int other_head_refs(each_ref_fn fn, void *cb_data)\n    + {\n    + \tstruct worktree **worktrees, **p;\n    +\n    + diff --git a/worktree.h b/worktree.h\n    + --- a/worktree.h\n    + +++ b/worktree.h\n    +@@\n    + \t\t\t\t     const char *fmt, ...)\n    + \t__attribute__((format (printf, 2, 3)));\n    + \n    ++/*\n    ++ * Parse a worktree ref (i.e. with prefix main-worktree/ or\n    ++ * worktrees/) and return the position of the worktree's name and\n    ++ * length (or NULL and zero if it's main worktree), and ref.\n    ++ *\n    ++ * All name, name_length and ref arguments could be NULL.\n    ++ */\n    ++int parse_worktree_ref(const char *worktree_ref, const char **name,\n    ++\t\t       int *name_length, const char **ref);\n    + #endif\n2:  ffdd30f7fc = 2:  e936a0af1e revision.c: correct a parameter name\n3:  6809bab191 ! 3:  382c645d73 revision.c: better error reporting on ref from different worktrees\n    @@ -87,18 +87,35 @@\n      --- a/worktree.c\n      +++ b/worktree.c\n     @@\n    - \treturn ret;\n    + \treturn -1;\n      }\n      \n     +void strbuf_worktree_ref(const struct worktree *wt,\n     +\t\t\t struct strbuf *sb,\n     +\t\t\t const char *refname)\n     +{\n    -+\tif (wt && !wt->is_current) {\n    -+\t\tif (is_main_worktree(wt))\n    -+\t\t\tstrbuf_addstr(sb, \"main/\");\n    -+\t\telse\n    -+\t\t\tstrbuf_addf(sb, \"worktrees/%s/\", wt->id);\n    ++\tswitch (ref_type(refname)) {\n    ++\tcase REF_TYPE_PSEUDOREF:\n    ++\tcase REF_TYPE_PER_WORKTREE:\n    ++\t\tif (wt && !wt->is_current) {\n    ++\t\t\tif (is_main_worktree(wt))\n    ++\t\t\t\tstrbuf_addstr(sb, \"main-worktree/\");\n    ++\t\t\telse\n    ++\t\t\t\tstrbuf_addf(sb, \"worktrees/%s/\", wt->id);\n    ++\t\t}\n    ++\t\tbreak;\n    ++\n    ++\tcase REF_TYPE_MAIN_PSEUDOREF:\n    ++\tcase REF_TYPE_OTHER_PSEUDOREF:\n    ++\t\tbreak;\n    ++\n    ++\tcase REF_TYPE_NORMAL:\n    ++\t\t/*\n    ++\t\t * For shared refs, don't prefix worktrees/ or\n    ++\t\t * main-worktree/. It's not necessary and\n    ++\t\t * files-backend.c can't handle it anyway.\n    ++\t\t */\n    ++\t\tbreak;\n     +\t}\n     +\tstrbuf_addstr(sb, refname);\n     +}\n    @@ -141,9 +158,10 @@\n      --- a/worktree.h\n      +++ b/worktree.h\n     @@\n    - \t\t\t\t     const char *fmt, ...)\n    - \t__attribute__((format (printf, 2, 3)));\n    - \n    +  */\n    + int parse_worktree_ref(const char *worktree_ref, const char **name,\n    + \t\t       int *name_length, const char **ref);\n    ++\n     +/*\n     + * Return a refname suitable for access from the current ref store.\n     + */\n    @@ -153,7 +171,7 @@\n     +\n     +/*\n     + * Return a refname suitable for access from the current ref\n    -+ * store. The result may be destroyed at the next call.\n    ++ * store. The result will be destroyed at the next call.\n     + */\n     +const char *worktree_ref(const struct worktree *wt,\n     +\t\t\t const char *refname);\n4:  2e13fa8361 = 4:  e2b99ef955 fsck: Move fsck_head_link() to get_default_heads() to avoid some globals\n5:  65f1547df3 ! 5:  8fbff370c3 fsck: check HEAD and reflog from other worktrees\n    @@ -136,7 +136,7 @@\n     +\techo $ZERO_OID >.git/HEAD &&\n     +\t# avoid corrupt/broken HEAD from interfering with repo discovery\n     +\ttest_must_fail git -C wt fsck 2>out &&\n    -+\tgrep \"main/HEAD: detached HEAD points\" out\n    ++\tgrep \"main-worktree/HEAD: detached HEAD points\" out\n     +'\n     +\n     +test_expect_success 'other worktree HEAD link pointing at a funny object' '\n6:  86326b44b5 ! 6:  2cd501f2ce reflog expire: cover reflog from all worktrees\n    @@ -48,12 +48,48 @@\n      };\n      \n      /* Remember to update object flag allocation in object.h */\n    +@@\n    + \treturn 0;\n    + }\n    + \n    ++static int is_head(const char *refname)\n    ++{\n    ++\tswitch (ref_type(refname)) {\n    ++\tcase REF_TYPE_OTHER_PSEUDOREF:\n    ++\tcase REF_TYPE_MAIN_PSEUDOREF:\n    ++\t\tif (parse_worktree_ref(refname, NULL, NULL, &refname))\n    ++\t\t\tBUG(\"not a worktree ref: %s\", refname);\n    ++\t\tbreak;\n    ++\tdefault:\n    ++\t\tbreak;\n    ++\t}\n    ++\treturn !strcmp(refname, \"HEAD\");\n    ++}\n    ++\n    + static void reflog_expiry_prepare(const char *refname,\n    + \t\t\t\t  const struct object_id *oid,\n    + \t\t\t\t  void *cb_data)\n    + {\n    + \tstruct expire_reflog_policy_cb *cb = cb_data;\n    + \n    +-\tif (!cb->cmd.expire_unreachable || !strcmp(refname, \"HEAD\")) {\n    ++\tif (!cb->cmd.expire_unreachable || is_head(refname)) {\n    + \t\tcb->tip_commit = NULL;\n    + \t\tcb->unreachable_expire_kind = UE_HEAD;\n    + \t} else {\n     @@\n      {\n      \tstruct collected_reflog *e;\n      \tstruct collect_reflog_cb *cb = cb_data;\n     +\tstruct strbuf newref = STRBUF_INIT;\n     +\n    ++\t/*\n    ++\t * Avoid collecting the same shared ref multiple times because\n    ++\t * they are available via all worktrees.\n    ++\t */\n    ++\tif (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)\n    ++\t\treturn 0;\n    ++\n     +\tstrbuf_worktree_ref(cb->wt, &newref, ref);\n     +\tFLEX_ALLOC_STR(e, reflog, newref.buf);\n     +\tstrbuf_release(&newref);\n    @@ -94,9 +130,34 @@\n     +\t\t\tif (!all_worktrees && !(*p)->is_current)\n     +\t\t\t\tcontinue;\n     +\t\t\tcollected.wt = *p;\n    -+\t\t\tfor_each_reflog(collect_reflog, &collected);\n    ++\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n    ++\t\t\t\t\t     collect_reflog, &collected);\n     +\t\t}\n     +\t\tfree_worktrees(worktrees);\n      \t\tfor (i = 0; i < collected.nr; i++) {\n      \t\t\tstruct collected_reflog *e = collected.e[i];\n      \t\t\tset_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);\n    +\n    + diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n    + --- a/t/t1410-reflog.sh\n    + +++ b/t/t1410-reflog.sh\n    +@@\n    + \t)\n    + '\n    + \n    ++test_expect_success 'expire with multiple worktrees' '\n    ++\tgit init main-wt &&\n    ++\t(\n    ++\t\tcd main-wt &&\n    ++\t\ttest_tick &&\n    ++\t\ttest_commit foo &&\n    ++\t\tgit  worktree add link-wt &&\n    ++\t\ttest_tick &&\n    ++\t\ttest_commit -C link-wt foobar &&\n    ++\t\ttest_tick &&\n    ++\t\tgit reflog expire --verbose --all --expire=$test_tick &&\n    ++\t\ttest_must_be_empty .git/worktrees/link-wt/logs/HEAD\n    ++\t)\n    ++'\n    ++\n    + test_done\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361077","messageId":"20181021080859.3203-2-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 1/8] refs.c: indent with tabs, not spaces","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:52Z","receivedAt":"2018-10-21T08:09:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex bbcac921b6..f07c775b50 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -646,7 +646,7 @@ enum ref_type ref_type(const char *refname)\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n-       return REF_TYPE_NORMAL;\n+\treturn REF_TYPE_NORMAL;\n }\n \n long get_files_ref_lock_timeout_ms(void)\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361078","messageId":"20181021080859.3203-3-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:53Z","receivedAt":"2018-10-21T08:09:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When multiple worktrees are used, we need rules to determine if\nsomething belongs to one worktree or all of them. Instead of keeping\nadding rules when new stuff comes (*), have a generic rule:\n\n- Inside $GIT_DIR, which is per-worktree by default, add\n  $GIT_DIR/common which is always shared. New features that want to\n  share stuff should put stuff under this directory.\n\n- Inside refs/, which is shared by default except refs/bisect, add\n  refs/worktree/ which is per-worktree. We may eventually move\n  refs/bisect to this new location and remove the exception in refs\n  code.\n\n(*) And it may also include stuff from external commands which will\n    have no way to modify common/per-worktree rules.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-worktree.txt         | 19 ++++++++++++++-\n Documentation/gitrepository-layout.txt | 11 +++++++--\n path.c                                 |  2 ++\n refs.c                                 |  1 +\n refs/files-backend.c                   | 14 ++++++++---\n t/t0060-path-utils.sh                  |  2 ++\n t/t1415-worktree-refs.sh               | 33 ++++++++++++++++++++++++++\n 7 files changed, 76 insertions(+), 6 deletions(-)\n create mode 100755 t/t1415-worktree-refs.sh\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex e2ee9fc21b..a50fbf8094 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -204,6 +204,22 @@ working trees, it can be used to identify worktrees. For example if\n you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n \n+REFS\n+----\n+In multiple working trees, some refs may be shared between all working\n+trees, some refs are local. One example is HEAD is different for all\n+working trees. This section is about the sharing rules.\n+\n+In general, all pseudo refs are per working tree and all refs starting\n+with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n+directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n+exception to this: refs inside refs/bisect and refs/worktree is not\n+shared.\n+\n+To access refs, it's best not to look inside GIT_DIR directly. Instead\n+use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n+which will handle refs correctly.\n+\n DETAILS\n -------\n Each linked working tree has a private sub-directory in the repository's\n@@ -228,7 +244,8 @@ linked working tree `git rev-parse --git-path HEAD` returns\n `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git\n rev-parse --git-path refs/heads/master` uses\n $GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,\n-since refs are shared across all working trees.\n+since refs are shared across all working trees, except refs/bisect and\n+refs/worktree.\n \n See linkgit:gitrepository-layout[5] for more information. The rule of\n thumb is do not make any assumption about whether a path belongs to\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex e85148f05e..89b616e049 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -95,8 +95,10 @@ refs::\n \tReferences are stored in subdirectories of this\n \tdirectory.  The 'git prune' command knows to preserve\n \tobjects reachable from refs found in this directory and\n-\tits subdirectories. This directory is ignored if $GIT_COMMON_DIR\n-\tis set and \"$GIT_COMMON_DIR/refs\" will be used instead.\n+\tits subdirectories.\n+\tThis directory is ignored (except refs/bisect and\n+\trefs/worktree) if $GIT_COMMON_DIR is set and\n+\t\"$GIT_COMMON_DIR/refs\" will be used instead.\n \n refs/heads/`name`::\n \trecords tip-of-the-tree commit objects of branch `name`\n@@ -165,6 +167,11 @@ hooks::\n \teach hook. This directory is ignored if $GIT_COMMON_DIR is set\n \tand \"$GIT_COMMON_DIR/hooks\" will be used instead.\n \n+common::\n+\tWhen multiple working trees are used, most of files in\n+\t$GIT_DIR are per-worktree with a few known exceptions. All\n+\tfiles under 'common' however will be shared between all\n+\tworking trees.\n \n index::\n \tThe current index file for the repository.  It is\ndiff --git a/path.c b/path.c\nindex 34f0f98349..bf4bb02a27 100644\n--- a/path.c\n+++ b/path.c\n@@ -108,6 +108,7 @@ struct common_dir {\n \n static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"branches\" },\n+\t{ 0, 1, 0, \"common\" },\n \t{ 0, 1, 0, \"hooks\" },\n \t{ 0, 1, 0, \"info\" },\n \t{ 0, 0, 1, \"info/sparse-checkout\" },\n@@ -118,6 +119,7 @@ static struct common_dir common_list[] = {\n \t{ 0, 1, 0, \"objects\" },\n \t{ 0, 1, 0, \"refs\" },\n \t{ 0, 1, 1, \"refs/bisect\" },\n+\t{ 0, 1, 1, \"refs/worktree\" },\n \t{ 0, 1, 0, \"remotes\" },\n \t{ 0, 1, 0, \"worktrees\" },\n \t{ 0, 1, 0, \"rr-cache\" },\ndiff --git a/refs.c b/refs.c\nindex f07c775b50..67daf0918e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -624,6 +624,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)\n static int is_per_worktree_ref(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") ||\n+\t\tstarts_with(refname, \"refs/worktree/\") ||\n \t\tstarts_with(refname, \"refs/bisect/\") ||\n \t\tstarts_with(refname, \"refs/rewritten/\");\n }\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 16ef9325e0..2dd77f9485 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -269,9 +269,9 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \tclosedir(d);\n \n \t/*\n-\t * Manually add refs/bisect, which, being per-worktree, might\n-\t * not appear in the directory listing for refs/ in the main\n-\t * repo.\n+\t * Manually add refs/bisect and refs/worktree, which, being\n+\t * per-worktree, might not appear in the directory listing for\n+\t * refs/ in the main repo.\n \t */\n \tif (!strcmp(dirname, \"refs/\")) {\n \t\tint pos = search_ref_dir(dir, \"refs/bisect/\", 12);\n@@ -281,6 +281,14 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,\n \t\t\t\t\tdir->cache, \"refs/bisect/\", 12, 1);\n \t\t\tadd_entry_to_dir(dir, child_entry);\n \t\t}\n+\n+\t\tpos = search_ref_dir(dir, \"refs/worktree/\", 11);\n+\n+\t\tif (pos < 0) {\n+\t\t\tstruct ref_entry *child_entry = create_dir_entry(\n+\t\t\t\t\tdir->cache, \"refs/worktree/\", 11, 1);\n+\t\t\tadd_entry_to_dir(dir, child_entry);\n+\t\t}\n \t}\n }\n \ndiff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh\nindex cd74c0a471..c7b53e494b 100755\n--- a/t/t0060-path-utils.sh\n+++ b/t/t0060-path-utils.sh\n@@ -306,6 +306,8 @@ test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me\n test_git_path GIT_COMMON_DIR=bar config                   bar/config\n test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs\n test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow\n+test_git_path GIT_COMMON_DIR=bar common                   bar/common\n+test_git_path GIT_COMMON_DIR=bar common/file              bar/common/file\n \n # In the tests below, $(pwd) must be used because it is a native path on\n # Windows and avoids MSYS's path mangling (which simplifies \"foo/../bar\" and\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nnew file mode 100755\nindex 0000000000..16c91bef57\n--- /dev/null\n+++ b/t/t1415-worktree-refs.sh\n@@ -0,0 +1,33 @@\n+#!/bin/sh\n+\n+test_description='per-worktree refs'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial &&\n+\ttest_commit wt1 &&\n+\ttest_commit wt2 &&\n+\tgit worktree add wt1 wt1 &&\n+\tgit worktree add wt2 wt2 &&\n+\tgit checkout initial &&\n+\tgit update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt1 update-ref refs/worktree/foo HEAD &&\n+\tgit -C wt2 update-ref refs/worktree/foo HEAD\n+'\n+\n+test_expect_success 'refs/worktree must not be packed' '\n+\tgit pack-refs --all &&\n+\ttest_path_is_missing .git/refs/tags/wt1 &&\n+\ttest_path_is_file .git/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt1/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt2/refs/worktree/foo\n+'\n+\n+test_expect_success 'refs/worktree are per-worktree' '\n+\ttest_cmp_rev worktree/foo initial &&\n+\t( cd wt1 && test_cmp_rev worktree/foo wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktree/foo wt2 )\n+'\n+\n+test_done\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361079","messageId":"20181021080859.3203-4-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:54Z","receivedAt":"2018-10-21T08:09:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One of the problems with multiple worktree is accessing per-worktree\nrefs of one worktree from another worktree. This was sort of solved by\nmultiple ref store, where the code can open the ref store of another\nworktree and has access to the ref space of that worktree.\n\nThe problem with this is reporting. \"HEAD\" in another ref space is\nalso called \"HEAD\" like in the current ref space. In order to\ndifferentiate them, all the code must somehow carry the ref store\naround and print something like \"HEAD from this ref store\".\n\nBut that is not feasible (or possible with a _lot_ of work). With the\ncurrent design, we pass a reference around as a string (so called\n\"refname\"). Extending this design to pass a string _and_ a ref store\nis a nightmare, especially when handling extended SHA-1 syntax.\n\nSo we do it another way. Instead of entering a separate ref space, we\nmake refs from other worktrees available in the current ref space. So\n\"HEAD\" is always HEAD of the current worktree, but then we can have\n\"worktrees/blah/HEAD\" to denote HEAD from a worktree named\n\"blah\". This syntax coincidentally matches the underlying directory\nstructure which makes implementation a bit easier.\n\nThe main worktree has to be treated specially because well... it's\nspecial from the beginning. So HEAD from the main worktree is\nacccessible via the name \"main-worktree/HEAD\" instead of\n\"worktrees/main/HEAD\" because \"main\" could be just another secondary\nworktree.\n\nThis patch also makes it possible to specify refs from one worktree in\nanother one, e.g.\n\n    git log worktrees/foo/HEAD\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-worktree.txt | 15 ++++++++++-\n refs.c                         | 21 ++++++++++++++++\n refs.h                         |  8 +++---\n refs/files-backend.c           | 28 +++++++++++++++++++++\n t/t1415-worktree-refs.sh       | 46 ++++++++++++++++++++++++++++++++++\n worktree.c                     | 30 ++++++++++++++++++++++\n worktree.h                     |  9 +++++++\n 7 files changed, 153 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex a50fbf8094..9117e4fb50 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -208,7 +208,8 @@ REFS\n ----\n In multiple working trees, some refs may be shared between all working\n trees, some refs are local. One example is HEAD is different for all\n-working trees. This section is about the sharing rules.\n+working trees. This section is about the sharing rules and how to access\n+refs of one working tree from another.\n \n In general, all pseudo refs are per working tree and all refs starting\n with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n@@ -216,6 +217,18 @@ directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n exception to this: refs inside refs/bisect and refs/worktree is not\n shared.\n \n+Refs that are per working tree can still be accessed from another\n+working tree via two special paths, main-worktree and worktrees. The\n+former gives access to per-worktree refs of the main working tree,\n+while the latter to all linked working trees.\n+\n+For example, main-worktree/HEAD or main-worktree/refs/bisect/good\n+resolve to the same value as the main working tree's HEAD and\n+refs/bisect/good respectively. Similarly, worktrees/foo/HEAD or\n+worktrees/bar/refs/bisect/bad are the same as\n+GIT_COMMON_DIR/worktrees/foo/HEAD and\n+GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad.\n+\n To access refs, it's best not to look inside GIT_DIR directly. Instead\n use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n which will handle refs correctly.\ndiff --git a/refs.c b/refs.c\nindex 67daf0918e..17e4307f31 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -641,12 +641,33 @@ static int is_pseudoref_syntax(const char *refname)\n \treturn 1;\n }\n \n+static int is_main_pseudoref_syntax(const char *refname)\n+{\n+\treturn skip_prefix(refname, \"main-worktree/\", &refname) &&\n+\t\t*refname &&\n+\t\tis_pseudoref_syntax(refname);\n+}\n+\n+static int is_other_pseudoref_syntax(const char *refname)\n+{\n+\tif (!skip_prefix(refname, \"worktrees/\", &refname))\n+\t\treturn 0;\n+\trefname = strchr(refname, '/');\n+\tif (!refname || !refname[1])\n+\t\treturn 0;\n+\treturn is_pseudoref_syntax(refname + 1);\n+}\n+\n enum ref_type ref_type(const char *refname)\n {\n \tif (is_per_worktree_ref(refname))\n \t\treturn REF_TYPE_PER_WORKTREE;\n \tif (is_pseudoref_syntax(refname))\n \t\treturn REF_TYPE_PSEUDOREF;\n+\tif (is_main_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_MAIN_PSEUDOREF;\n+\tif (is_other_pseudoref_syntax(refname))\n+\t\treturn REF_TYPE_OTHER_PSEUDOREF;\n \treturn REF_TYPE_NORMAL;\n }\n \ndiff --git a/refs.h b/refs.h\nindex 6cc0397679..308fa1f03b 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -714,9 +714,11 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);\n int ref_is_hidden(const char *, const char *);\n \n enum ref_type {\n-\tREF_TYPE_PER_WORKTREE,\n-\tREF_TYPE_PSEUDOREF,\n-\tREF_TYPE_NORMAL,\n+\tREF_TYPE_PER_WORKTREE,\t  /* refs inside refs/ but not shared       */\n+\tREF_TYPE_PSEUDOREF,\t  /* refs outside refs/ in current worktree */\n+\tREF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */\n+\tREF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */\n+\tREF_TYPE_NORMAL,\t  /* normal/shared refs inside refs/        */\n };\n \n enum ref_type ref_type(const char *refname);\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 2dd77f9485..9183875dad 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -10,6 +10,7 @@\n #include \"../object.h\"\n #include \"../dir.h\"\n #include \"../chdir-notify.h\"\n+#include \"worktree.h\"\n \n /*\n  * This backend uses the following flags in `ref_update::flags` for\n@@ -149,6 +150,25 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,\n \treturn refs;\n }\n \n+static void files_reflog_path_other_worktrees(struct files_ref_store *refs,\n+\t\t\t\t\t      struct strbuf *sb,\n+\t\t\t\t\t      const char *refname)\n+{\n+\tconst char *real_ref;\n+\tconst char *worktree_name;\n+\tint length;\n+\n+\tif (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))\n+\t\tBUG(\"refname %s is not a other-worktree ref\", refname);\n+\n+\tif (worktree_name)\n+\t\tstrbuf_addf(sb, \"%s/worktrees/%.*s/logs/%s\", refs->gitcommondir,\n+\t\t\t    length, worktree_name, real_ref);\n+\telse\n+\t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir,\n+\t\t\t    real_ref);\n+}\n+\n static void files_reflog_path(struct files_ref_store *refs,\n \t\t\t      struct strbuf *sb,\n \t\t\t      const char *refname)\n@@ -158,6 +178,9 @@ static void files_reflog_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n \t\tbreak;\n@@ -176,6 +199,11 @@ static void files_ref_path(struct files_ref_store *refs,\n \tcase REF_TYPE_PSEUDOREF:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitdir, refname);\n \t\tbreak;\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (!skip_prefix(refname, \"main-worktree/\", &refname))\n+\t\t\tBUG(\"ref %s is not a main pseudoref\", refname);\n+\t\t/* fallthrough */\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/%s\", refs->gitcommondir, refname);\n \t\tbreak;\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex 16c91bef57..b664e51250 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -30,4 +30,50 @@ test_expect_success 'refs/worktree are per-worktree' '\n \t( cd wt2 && test_cmp_rev worktree/foo wt2 )\n '\n \n+test_expect_success 'resolve main-worktree/HEAD' '\n+\ttest_cmp_rev main-worktree/HEAD initial &&\n+\t( cd wt1 && test_cmp_rev main-worktree/HEAD initial ) &&\n+\t( cd wt2 && test_cmp_rev main-worktree/HEAD initial )\n+'\n+\n+test_expect_success 'ambiguous main-worktree/HEAD' '\n+\tmkdir -p .git/refs/heads/main-worktree &&\n+\ttest_when_finished rm -f .git/refs/heads/main-worktree/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/main-worktree/HEAD &&\n+\tgit rev-parse main-worktree/HEAD 2>warn &&\n+\tgrep \"main-worktree/HEAD.*ambiguous\" warn\n+'\n+\n+test_expect_success 'resolve worktrees/xx/HEAD' '\n+\ttest_cmp_rev worktrees/wt1/HEAD wt1 &&\n+\t( cd wt1 && test_cmp_rev worktrees/wt1/HEAD wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktrees/wt1/HEAD wt1 )\n+'\n+\n+test_expect_success 'ambiguous worktrees/xx/HEAD' '\n+\tmkdir -p .git/refs/heads/worktrees/wt1 &&\n+\ttest_when_finished rm -f .git/refs/heads/worktrees/wt1/HEAD &&\n+\tcp .git/HEAD .git/refs/heads/worktrees/wt1/HEAD &&\n+\tgit rev-parse worktrees/wt1/HEAD 2>warn &&\n+\tgrep \"worktrees/wt1/HEAD.*ambiguous\" warn\n+'\n+\n+test_expect_success 'reflog of main-worktree/HEAD' '\n+\tgit reflog HEAD | sed \"s/HEAD/main-worktree\\/HEAD/\" >expected &&\n+\tgit reflog main-worktree/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog main-worktree/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1\n+'\n+\n+test_expect_success 'reflog of worktrees/xx/HEAD' '\n+\tgit -C wt2 reflog HEAD | sed \"s/HEAD/worktrees\\/wt2\\/HEAD/\" >expected &&\n+\tgit reflog worktrees/wt2/HEAD >actual &&\n+\ttest_cmp expected actual &&\n+\tgit -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&\n+\ttest_cmp expected actual.wt1 &&\n+\tgit -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&\n+\ttest_cmp expected actual.wt2\n+'\n+\n test_done\ndiff --git a/worktree.c b/worktree.c\nindex b0d0b5426d..4193496f2a 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -487,6 +487,36 @@ int submodule_uses_worktrees(const char *path)\n \treturn ret;\n }\n \n+int parse_worktree_ref(const char *worktree_ref, const char **name,\n+\t\t       int *name_length, const char **ref)\n+{\n+\tif (skip_prefix(worktree_ref, \"main-worktree/\", &worktree_ref)) {\n+\t\tif (!*worktree_ref)\n+\t\t\treturn -1;\n+\t\tif (name)\n+\t\t\t*name = NULL;\n+\t\tif (name_length)\n+\t\t\t*name_length = 0;\n+\t\tif (ref)\n+\t\t\t*ref = worktree_ref;\n+\t\treturn 0;\n+\t}\n+\tif (skip_prefix(worktree_ref, \"worktrees/\", &worktree_ref)) {\n+\t\tconst char *slash = strchr(worktree_ref, '/');\n+\n+\t\tif (!slash || slash == worktree_ref || !slash[1])\n+\t\t\treturn -1;\n+\t\tif (name)\n+\t\t\t*name = worktree_ref;\n+\t\tif (name_length)\n+\t\t\t*name_length = slash - worktree_ref;\n+\t\tif (ref)\n+\t\t\t*ref = slash + 1;\n+\t\treturn 0;\n+\t}\n+\treturn -1;\n+}\n+\n int other_head_refs(each_ref_fn fn, void *cb_data)\n {\n \tstruct worktree **worktrees, **p;\ndiff --git a/worktree.h b/worktree.h\nindex df3fc30f73..440bb219dd 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -108,4 +108,13 @@ extern const char *worktree_git_path(const struct worktree *wt,\n \t\t\t\t     const char *fmt, ...)\n \t__attribute__((format (printf, 2, 3)));\n \n+/*\n+ * Parse a worktree ref (i.e. with prefix main-worktree/ or\n+ * worktrees/) and return the position of the worktree's name and\n+ * length (or NULL and zero if it's main worktree), and ref.\n+ *\n+ * All name, name_length and ref arguments could be NULL.\n+ */\n+int parse_worktree_ref(const char *worktree_ref, const char **name,\n+\t\t       int *name_length, const char **ref);\n #endif\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361080","messageId":"20181021080859.3203-5-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 4/8] revision.c: correct a parameter name","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:55Z","receivedAt":"2018-10-21T08:09:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function is a callback of for_each_reflog() which will pass a ref\nname as the first argument, not a path (to a reflog file).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 7 ++++---\n 1 file changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex b5108b75ab..ea1d695e23 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1278,13 +1278,14 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *path, const struct object_id *oid,\n+static int handle_one_reflog(const char *refname,\n+\t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = path;\n-\trefs_for_each_reflog_ent(cb->refs, path,\n+\tcb->name_for_errormsg = refname;\n+\trefs_for_each_reflog_ent(cb->refs, refname,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n \treturn 0;\n }\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361081","messageId":"20181021080859.3203-6-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 5/8] revision.c: better error reporting on ref from different worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:56Z","receivedAt":"2018-10-21T08:09:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Make use of the new ref aliases to pass refs from another worktree\naround and access them from the current ref store instead. This does\nnot change any functionality, but when a problem arises, we would like\nthe reported messages to mention full ref aliases, like this:\n\n    fatal: bad object worktrees/ztemp/HEAD\n    warning: reflog of 'main-worktree/HEAD' references pruned commits\n\ninstead of\n\n    fatal: bad object HEAD\n    warning: reflog of 'HEAD' references pruned commits\n\nwhich does not really tell where the refs are from.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n revision.c | 21 +++++++++++++--------\n worktree.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++---\n worktree.h | 15 +++++++++++++++\n 3 files changed, 74 insertions(+), 11 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex ea1d695e23..1737aad3ec 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1178,7 +1178,7 @@ struct all_refs_cb {\n \tint warned_bad_reflog;\n \tstruct rev_info *all_revs;\n \tconst char *name_for_errormsg;\n-\tstruct ref_store *refs;\n+\tstruct worktree *wt;\n };\n \n int ref_excluded(struct string_list *ref_excludes, const char *path)\n@@ -1215,7 +1215,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,\n \tcb->all_revs = revs;\n \tcb->all_flags = flags;\n \trevs->rev_input_given = 1;\n-\tcb->refs = NULL;\n+\tcb->wt = NULL;\n }\n \n void clear_ref_exclusion(struct string_list **ref_excludes_p)\n@@ -1278,15 +1278,20 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,\n \treturn 0;\n }\n \n-static int handle_one_reflog(const char *refname,\n+static int handle_one_reflog(const char *refname_in_wt,\n \t\t\t     const struct object_id *oid,\n \t\t\t     int flag, void *cb_data)\n {\n \tstruct all_refs_cb *cb = cb_data;\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n \tcb->warned_bad_reflog = 0;\n-\tcb->name_for_errormsg = refname;\n-\trefs_for_each_reflog_ent(cb->refs, refname,\n+\tstrbuf_worktree_ref(cb->wt, &refname, refname_in_wt);\n+\tcb->name_for_errormsg = refname.buf;\n+\trefs_for_each_reflog_ent(get_main_ref_store(the_repository),\n+\t\t\t\t refname.buf,\n \t\t\t\t handle_one_reflog_ent, cb_data);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -1301,8 +1306,8 @@ static void add_other_reflogs_to_pending(struct all_refs_cb *cb)\n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\tcb->refs = get_worktree_ref_store(wt);\n-\t\trefs_for_each_reflog(cb->refs,\n+\t\tcb->wt = wt;\n+\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n \t\t\t\t     handle_one_reflog,\n \t\t\t\t     cb);\n \t}\n@@ -1315,7 +1320,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)\n \n \tcb.all_revs = revs;\n \tcb.all_flags = flags;\n-\tcb.refs = get_main_ref_store(revs->repo);\n+\tcb.wt = NULL;\n \tfor_each_reflog(handle_one_reflog, &cb);\n \n \tif (!revs->single_worktree)\ndiff --git a/worktree.c b/worktree.c\nindex 4193496f2a..e6a65ec684 100644\n--- a/worktree.c\n+++ b/worktree.c\n@@ -517,6 +517,45 @@ int parse_worktree_ref(const char *worktree_ref, const char **name,\n \treturn -1;\n }\n \n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname)\n+{\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_PSEUDOREF:\n+\tcase REF_TYPE_PER_WORKTREE:\n+\t\tif (wt && !wt->is_current) {\n+\t\t\tif (is_main_worktree(wt))\n+\t\t\t\tstrbuf_addstr(sb, \"main-worktree/\");\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(sb, \"worktrees/%s/\", wt->id);\n+\t\t}\n+\t\tbreak;\n+\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n+\t\tbreak;\n+\n+\tcase REF_TYPE_NORMAL:\n+\t\t/*\n+\t\t * For shared refs, don't prefix worktrees/ or\n+\t\t * main-worktree/. It's not necessary and\n+\t\t * files-backend.c can't handle it anyway.\n+\t\t */\n+\t\tbreak;\n+\t}\n+\tstrbuf_addstr(sb, refname);\n+}\n+\n+const char *worktree_ref(const struct worktree *wt, const char *refname)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\n+\tstrbuf_reset(&sb);\n+\tstrbuf_worktree_ref(wt, &sb, refname);\n+\treturn sb.buf;\n+}\n+\n int other_head_refs(each_ref_fn fn, void *cb_data)\n {\n \tstruct worktree **worktrees, **p;\n@@ -525,13 +564,17 @@ int other_head_refs(each_ref_fn fn, void *cb_data)\n \tworktrees = get_worktrees(0);\n \tfor (p = worktrees; *p; p++) {\n \t\tstruct worktree *wt = *p;\n-\t\tstruct ref_store *refs;\n+\t\tstruct object_id oid;\n+\t\tint flag;\n \n \t\tif (wt->is_current)\n \t\t\tcontinue;\n \n-\t\trefs = get_worktree_ref_store(wt);\n-\t\tret = refs_head_ref(refs, fn, cb_data);\n+\t\tif (!refs_read_ref_full(get_main_ref_store(the_repository),\n+\t\t\t\t\tworktree_ref(wt, \"HEAD\"),\n+\t\t\t\t\tRESOLVE_REF_READING,\n+\t\t\t\t\t&oid, &flag))\n+\t\t\tret = fn(worktree_ref(wt, \"HEAD\"), &oid, flag, cb_data);\n \t\tif (ret)\n \t\t\tbreak;\n \t}\ndiff --git a/worktree.h b/worktree.h\nindex 440bb219dd..1164ca396f 100644\n--- a/worktree.h\n+++ b/worktree.h\n@@ -117,4 +117,19 @@ extern const char *worktree_git_path(const struct worktree *wt,\n  */\n int parse_worktree_ref(const char *worktree_ref, const char **name,\n \t\t       int *name_length, const char **ref);\n+\n+/*\n+ * Return a refname suitable for access from the current ref store.\n+ */\n+void strbuf_worktree_ref(const struct worktree *wt,\n+\t\t\t struct strbuf *sb,\n+\t\t\t const char *refname);\n+\n+/*\n+ * Return a refname suitable for access from the current ref\n+ * store. The result will be destroyed at the next call.\n+ */\n+const char *worktree_ref(const struct worktree *wt,\n+\t\t\t const char *refname);\n+\n #endif\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361082","messageId":"20181021080859.3203-7-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 6/8] fsck: Move fsck_head_link() to get_default_heads() to avoid some globals","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:57Z","receivedAt":"2018-10-21T08:09:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nThis will make it easier to check the HEAD of other worktrees from fsck.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c | 27 ++++++++++++++++-----------\n 1 file changed, 16 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 06eb421720..0d39462058 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -36,8 +36,6 @@ static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n-static struct object_id head_oid;\n-static const char *head_points_at;\n static int errors_found;\n static int write_lost_and_found;\n static int verbose;\n@@ -484,8 +482,15 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid);\n+\n static void get_default_heads(void)\n {\n+\tconst char *head_points_at;\n+\tstruct object_id head_oid;\n+\n+\tfsck_head_link(&head_points_at, &head_oid);\n \tif (head_points_at && !is_null_oid(&head_oid))\n \t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n@@ -579,33 +584,34 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(void)\n+static int fsck_head_link(const char **head_points_at,\n+\t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n \t\tfprintf(stderr, \"Checking HEAD link\\n\");\n \n-\thead_points_at = resolve_ref_unsafe(\"HEAD\", 0, &head_oid, NULL);\n-\tif (!head_points_at) {\n+\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"Invalid HEAD\");\n \t}\n-\tif (!strcmp(head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, \"HEAD\"))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n-\telse if (!starts_with(head_points_at, \"refs/heads/\")) {\n+\telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n \t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     head_points_at);\n+\t\t\t     *head_points_at);\n \t}\n-\tif (is_null_oid(&head_oid)) {\n+\tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n \t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n \t\t}\n \t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\thead_points_at + 11);\n+\t\t\t*head_points_at + 11);\n \t}\n \treturn 0;\n }\n@@ -720,7 +726,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \tgit_config(fsck_config, NULL);\n \n-\tfsck_head_link();\n \tif (connectivity_only) {\n \t\tfor_each_loose_object(mark_loose_for_connectivity, NULL, 0);\n \t\tfor_each_packed_object(mark_packed_for_connectivity, NULL, 0);\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361083","messageId":"20181021080859.3203-8-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 7/8] fsck: check HEAD and reflog from other worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:58Z","receivedAt":"2018-10-21T08:09:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"fsck is a repo-wide operation and should check all references no\nmatter which worktree they are associated to.\n\nReported-by: Jeff King <peff@peff.net>\nHelped-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fsck.c  | 55 ++++++++++++++++++++++++++++++++++---------------\n t/t1450-fsck.sh | 35 +++++++++++++++++++++++++++++++\n 2 files changed, 73 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 0d39462058..3c3e0f06e7 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -19,6 +19,7 @@\n #include \"packfile.h\"\n #include \"object-store.h\"\n #include \"run-command.h\"\n+#include \"worktree.h\"\n \n #define REACHABLE 0x0001\n #define SEEN      0x0002\n@@ -444,7 +445,11 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid\n static int fsck_handle_reflog(const char *logname, const struct object_id *oid,\n \t\t\t      int flag, void *cb_data)\n {\n-\tfor_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);\n+\tstruct strbuf refname = STRBUF_INIT;\n+\n+\tstrbuf_worktree_ref(cb_data, &refname, logname);\n+\tfor_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);\n+\tstrbuf_release(&refname);\n \treturn 0;\n }\n \n@@ -482,20 +487,34 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid);\n \n static void get_default_heads(void)\n {\n+\tstruct worktree **worktrees, **p;\n \tconst char *head_points_at;\n \tstruct object_id head_oid;\n \n-\tfsck_head_link(&head_points_at, &head_oid);\n-\tif (head_points_at && !is_null_oid(&head_oid))\n-\t\tfsck_handle_ref(\"HEAD\", &head_oid, 0, NULL);\n \tfor_each_rawref(fsck_handle_ref, NULL);\n-\tif (include_reflogs)\n-\t\tfor_each_reflog(fsck_handle_reflog, NULL);\n+\n+\tworktrees = get_worktrees(0);\n+\tfor (p = worktrees; *p; p++) {\n+\t\tstruct worktree *wt = *p;\n+\t\tstruct strbuf ref = STRBUF_INIT;\n+\n+\t\tstrbuf_worktree_ref(wt, &ref, \"HEAD\");\n+\t\tfsck_head_link(ref.buf, &head_points_at, &head_oid);\n+\t\tif (head_points_at && !is_null_oid(&head_oid))\n+\t\t\tfsck_handle_ref(ref.buf, &head_oid, 0, NULL);\n+\t\tstrbuf_release(&ref);\n+\n+\t\tif (include_reflogs)\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(wt),\n+\t\t\t\t\t     fsck_handle_reflog, wt);\n+\t}\n+\tfree_worktrees(worktrees);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\n@@ -584,34 +603,36 @@ static void fsck_object_dir(const char *path)\n \tstop_progress(&progress);\n }\n \n-static int fsck_head_link(const char **head_points_at,\n+static int fsck_head_link(const char *head_ref_name,\n+\t\t\t  const char **head_points_at,\n \t\t\t  struct object_id *head_oid)\n {\n \tint null_is_error = 0;\n \n \tif (verbose)\n-\t\tfprintf(stderr, \"Checking HEAD link\\n\");\n+\t\tfprintf(stderr, \"Checking %s link\\n\", head_ref_name);\n \n-\t*head_points_at = resolve_ref_unsafe(\"HEAD\", 0, head_oid, NULL);\n+\t*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);\n \tif (!*head_points_at) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"Invalid HEAD\");\n+\t\treturn error(\"Invalid %s\", head_ref_name);\n \t}\n-\tif (!strcmp(*head_points_at, \"HEAD\"))\n+\tif (!strcmp(*head_points_at, head_ref_name))\n \t\t/* detached HEAD */\n \t\tnull_is_error = 1;\n \telse if (!starts_with(*head_points_at, \"refs/heads/\")) {\n \t\terrors_found |= ERROR_REFS;\n-\t\treturn error(\"HEAD points to something strange (%s)\",\n-\t\t\t     *head_points_at);\n+\t\treturn error(\"%s points to something strange (%s)\",\n+\t\t\t     head_ref_name, *head_points_at);\n \t}\n \tif (is_null_oid(head_oid)) {\n \t\tif (null_is_error) {\n \t\t\terrors_found |= ERROR_REFS;\n-\t\t\treturn error(\"HEAD: detached HEAD points at nothing\");\n+\t\t\treturn error(\"%s: detached HEAD points at nothing\",\n+\t\t\t\t     head_ref_name);\n \t\t}\n-\t\tfprintf(stderr, \"notice: HEAD points to an unborn branch (%s)\\n\",\n-\t\t\t*head_points_at + 11);\n+\t\tfprintf(stderr, \"notice: %s points to an unborn branch (%s)\\n\",\n+\t\t\thead_ref_name, *head_points_at + 11);\n \t}\n \treturn 0;\n }\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 0f2dd26f74..28201677d5 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -101,6 +101,41 @@ test_expect_success 'HEAD link pointing at a funny place' '\n \tgrep \"HEAD points to something strange\" out\n '\n \n+test_expect_success 'HEAD link pointing at a funny object (from different wt)' '\n+\ttest_when_finished \"mv .git/SAVED_HEAD .git/HEAD\" &&\n+\ttest_when_finished \"rm -rf .git/worktrees wt\" &&\n+\tgit worktree add wt &&\n+\tmv .git/HEAD .git/SAVED_HEAD &&\n+\techo $ZERO_OID >.git/HEAD &&\n+\t# avoid corrupt/broken HEAD from interfering with repo discovery\n+\ttest_must_fail git -C wt fsck 2>out &&\n+\tgrep \"main-worktree/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo $ZERO_OID >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD: detached HEAD points\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at missing object' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"Contents missing from repo\" | git hash-object --stdin >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n+'\n+\n+test_expect_success 'other worktree HEAD link pointing at a funny place' '\n+\ttest_when_finished \"rm -rf .git/worktrees other\" &&\n+\tgit worktree add other &&\n+\techo \"ref: refs/funny/place\" >.git/worktrees/other/HEAD &&\n+\ttest_must_fail git fsck 2>out &&\n+\tgrep \"worktrees/other/HEAD points to something strange\" out\n+'\n+\n test_expect_success 'email without @ is okay' '\n \tgit cat-file commit HEAD >basis &&\n \tsed \"s/@/AT/\" basis >okay &&\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361084","messageId":"20181021080859.3203-9-pclouds@gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-1-pclouds@gmail.com","subject":"[PATCH v3 8/8] reflog expire: cover reflog from all worktrees","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-21T08:08:59Z","receivedAt":"2018-10-21T08:09:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Reported-by: Jeff King <peff@peff.net>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-reflog.txt |  7 +++++-\n builtin/reflog.c             | 46 ++++++++++++++++++++++++++++++++----\n t/t1410-reflog.sh            | 15 ++++++++++++\n 3 files changed, 63 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt\nindex 472a6808cd..ff487ff77d 100644\n--- a/Documentation/git-reflog.txt\n+++ b/Documentation/git-reflog.txt\n@@ -20,7 +20,7 @@ depending on the subcommand:\n 'git reflog' ['show'] [log-options] [<ref>]\n 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]\n \t[--rewrite] [--updateref] [--stale-fix]\n-\t[--dry-run | -n] [--verbose] [--all | <refs>...]\n+\t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] ref@\\{specifier\\}...\n 'git reflog exists' <ref>\n@@ -72,6 +72,11 @@ Options for `expire`\n --all::\n \tProcess the reflogs of all references.\n \n+--single-worktree::\n+\tBy default when `--all` is specified, reflogs from all working\n+\ttrees are processed. This option limits the processing to reflogs\n+\tfrom the current working tree only.\n+\n --expire=<time>::\n \tPrune entries older than the specified time. If this option is\n \tnot specified, the expiration time is taken from the\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex b5941c1ff3..7a85e4b164 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -10,6 +10,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"reachable.h\"\n+#include \"worktree.h\"\n \n /* NEEDSWORK: switch to using parse_options */\n static const char reflog_expire_usage[] =\n@@ -52,6 +53,7 @@ struct collect_reflog_cb {\n \tstruct collected_reflog **e;\n \tint alloc;\n \tint nr;\n+\tstruct worktree *wt;\n };\n \n /* Remember to update object flag allocation in object.h */\n@@ -330,13 +332,27 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n+static int is_head(const char *refname)\n+{\n+\tswitch (ref_type(refname)) {\n+\tcase REF_TYPE_OTHER_PSEUDOREF:\n+\tcase REF_TYPE_MAIN_PSEUDOREF:\n+\t\tif (parse_worktree_ref(refname, NULL, NULL, &refname))\n+\t\t\tBUG(\"not a worktree ref: %s\", refname);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\treturn !strcmp(refname, \"HEAD\");\n+}\n+\n static void reflog_expiry_prepare(const char *refname,\n \t\t\t\t  const struct object_id *oid,\n \t\t\t\t  void *cb_data)\n {\n \tstruct expire_reflog_policy_cb *cb = cb_data;\n \n-\tif (!cb->cmd.expire_unreachable || !strcmp(refname, \"HEAD\")) {\n+\tif (!cb->cmd.expire_unreachable || is_head(refname)) {\n \t\tcb->tip_commit = NULL;\n \t\tcb->unreachable_expire_kind = UE_HEAD;\n \t} else {\n@@ -388,8 +404,19 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus\n {\n \tstruct collected_reflog *e;\n \tstruct collect_reflog_cb *cb = cb_data;\n+\tstruct strbuf newref = STRBUF_INIT;\n+\n+\t/*\n+\t * Avoid collecting the same shared ref multiple times because\n+\t * they are available via all worktrees.\n+\t */\n+\tif (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)\n+\t\treturn 0;\n+\n+\tstrbuf_worktree_ref(cb->wt, &newref, ref);\n+\tFLEX_ALLOC_STR(e, reflog, newref.buf);\n+\tstrbuf_release(&newref);\n \n-\tFLEX_ALLOC_STR(e, reflog, ref);\n \toidcpy(&e->oid, oid);\n \tALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);\n \tcb->e[cb->nr++] = e;\n@@ -512,7 +539,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n {\n \tstruct expire_reflog_policy_cb cb;\n \ttimestamp_t now = time(NULL);\n-\tint i, status, do_all;\n+\tint i, status, do_all, all_worktrees = 1;\n \tint explicit_expiry = 0;\n \tunsigned int flags = 0;\n \n@@ -549,6 +576,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\t\tflags |= EXPIRE_REFLOGS_UPDATE_REF;\n \t\telse if (!strcmp(arg, \"--all\"))\n \t\t\tdo_all = 1;\n+\t\telse if (!strcmp(arg, \"--single-worktree\"))\n+\t\t\tall_worktrees = 0;\n \t\telse if (!strcmp(arg, \"--verbose\"))\n \t\t\tflags |= EXPIRE_REFLOGS_VERBOSE;\n \t\telse if (!strcmp(arg, \"--\")) {\n@@ -577,10 +606,19 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \n \tif (do_all) {\n \t\tstruct collect_reflog_cb collected;\n+\t\tstruct worktree **worktrees, **p;\n \t\tint i;\n \n \t\tmemset(&collected, 0, sizeof(collected));\n-\t\tfor_each_reflog(collect_reflog, &collected);\n+\t\tworktrees = get_worktrees(0);\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tif (!all_worktrees && !(*p)->is_current)\n+\t\t\t\tcontinue;\n+\t\t\tcollected.wt = *p;\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n+\t\t\t\t\t     collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n \t\tfor (i = 0; i < collected.nr; i++) {\n \t\t\tstruct collected_reflog *e = collected.e[i];\n \t\t\tset_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);\ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 388b0611d8..3e053532eb 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -368,4 +368,19 @@ test_expect_success 'continue walking past root commits' '\n \t)\n '\n \n+test_expect_success 'expire with multiple worktrees' '\n+\tgit init main-wt &&\n+\t(\n+\t\tcd main-wt &&\n+\t\ttest_tick &&\n+\t\ttest_commit foo &&\n+\t\tgit  worktree add link-wt &&\n+\t\ttest_tick &&\n+\t\ttest_commit -C link-wt foobar &&\n+\t\ttest_tick &&\n+\t\tgit reflog expire --verbose --all --expire=$test_tick &&\n+\t\ttest_must_be_empty .git/worktrees/link-wt/logs/HEAD\n+\t)\n+'\n+\n test_done\n-- \n2.19.1.647.g708186aaf9\n\n"},{"id":"361131","messageId":"xmqqin1uwqo6.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"20181021080859.3203-3-pclouds@gmail.com","subject":"Re: [PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-22T04:28:41Z","receivedAt":"2018-10-22T04:28:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Subject: Re: [PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees\n\n\"a place\"?  Missing \"in $GIR_DIR\" in the descrition made me read the\nabove three times before getting what it wanted to say.\n\nMy attempt to improve it, which admittedly is not great, came up with:\n\nworktree: convention to make per-worktree things identifiable in $GIT_DIR\n\n> When multiple worktrees are used, we need rules to determine if\n> something belongs to one worktree or all of them. Instead of keeping\n> adding rules when new stuff comes (*), have a generic rule:\n>\n> - Inside $GIT_DIR, which is per-worktree by default, add\n>   $GIT_DIR/common which is always shared. New features that want to\n>   share stuff should put stuff under this directory.\n>\n> - Inside refs/, which is shared by default except refs/bisect, add\n>   refs/worktree/ which is per-worktree. We may eventually move\n>   refs/bisect to this new location and remove the exception in refs\n>   code.\n>\n> (*) And it may also include stuff from external commands which will\n>     have no way to modify common/per-worktree rules.\n\nOK.  Establishing such a convention is a good role for the core-git\nshould play to help third-party tools.\n\nShould this play well with the per-worktree configuration as well?\nIs it better to carve out a configuration variable namespace so that\ncertain keys are never read from common ones (or per-worktree ones),\nso that people can tell which ones are what?  I know your current\ndesign says \"this is just another new layer, and the users can hang\nthemselves with this new rope\".  I am wondering if there is a need\nto do something a bit more structured.\n\n\n"},{"id":"361152","messageId":"20181022102529.GF30222@szeder.dev","threadId":"49405","inReplyTo":"20181021080859.3203-3-pclouds@gmail.com","subject":"Re: [PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-10-22T10:25:29Z","receivedAt":"2018-10-22T10:25:36Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Oct 21, 2018 at 10:08:53AM +0200, Nguyễn Thái Ngọc Duy wrote:\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> index e2ee9fc21b..a50fbf8094 100644\n> --- a/Documentation/git-worktree.txt\n> +++ b/Documentation/git-worktree.txt\n> @@ -204,6 +204,22 @@ working trees, it can be used to identify worktrees. For example if\n>  you only have two working trees, at \"/abc/def/ghi\" and \"/abc/def/ggg\",\n>  then \"ghi\" or \"def/ghi\" is enough to point to the former working tree.\n>  \n> +REFS\n> +----\n> +In multiple working trees, some refs may be shared between all working\n> +trees, some refs are local. One example is HEAD is different for all\n> +working trees. This section is about the sharing rules.\n> +\n> +In general, all pseudo refs are per working tree and all refs starting\n> +with \"refs/\" are shared. Pseudo refs are ones like HEAD which are\n> +directly under GIT_DIR instead of inside GIT_DIR/refs. There are one\n> +exception to this: refs inside refs/bisect and refs/worktree is not\n> +shared.\n> +\n> +To access refs, it's best not to look inside GIT_DIR directly. Instead\n> +use commands such as linkgit:git-revparse[1] or linkgit:git-update-ref[1]\n\ns/revparse/rev-parse/\n\n> +which will handle refs correctly.\n> +\n"},{"id":"361926","messageId":"CACsJy8Bkjw-T4Aeb8WfdOcjr5OB4HdG1SFYJ9Sokt2JNc2fnOQ@mail.gmail.com","threadId":"49405","inReplyTo":"xmqqin1uwqo6.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 2/8] Add a place for (not) sharing stuff between worktrees","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-10-29T17:18:47Z","receivedAt":"2018-10-29T17:19:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Oct 22, 2018 at 6:28 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > When multiple worktrees are used, we need rules to determine if\n> > something belongs to one worktree or all of them. Instead of keeping\n> > adding rules when new stuff comes (*), have a generic rule:\n> >\n> > - Inside $GIT_DIR, which is per-worktree by default, add\n> >   $GIT_DIR/common which is always shared. New features that want to\n> >   share stuff should put stuff under this directory.\n> >\n> > - Inside refs/, which is shared by default except refs/bisect, add\n> >   refs/worktree/ which is per-worktree. We may eventually move\n> >   refs/bisect to this new location and remove the exception in refs\n> >   code.\n> >\n> > (*) And it may also include stuff from external commands which will\n> >     have no way to modify common/per-worktree rules.\n>\n> OK.  Establishing such a convention is a good role for the core-git\n> should play to help third-party tools.\n>\n> Should this play well with the per-worktree configuration as well?\n> Is it better to carve out a configuration variable namespace so that\n> certain keys are never read from common ones (or per-worktree ones),\n> so that people can tell which ones are what?  I know your current\n> design says \"this is just another new layer, and the users can hang\n> themselves with this new rope\".  I am wondering if there is a need\n> to do something a bit more structured.\n\nI actually find the layered design of config files more elegant. Even\nbefore config.worktree is added, the user has system/per-user/per-repo\nplaces to put stuff in. \"git config\" gives control to read or write\nfrom a specific layer. We have to add layers to $GIT_DIR/refs to\nhandle multiple worktrees, but since the \"API\" for those don't have\nthe concept of layers at all, we need a single view where items from\ndifferent worktrees are accessible via the same path/ref name.\n\nAdding variable namespace in config files does not look easy either.\nSuppose we have perWorktree.* section just for per-workree stuff, what\ndo we do when the user just do \"git config -f not-per-worktree-file\nperWorktree.something foo\" (or --global instead of -f)? What we could\ndo (and already do for some config keys) is ignore perWorktree.*\nunless they come from config.worktree. Which does help a bit, but not\nreally perfect.\n\nAnd a namespace like this means the user cannot have subsections\nanymore (e.g. perWorktree.group.<name>.var is not supported if I\nremember correctly). So yeah... perhaps we should wait a bit and see\nwhat the user (or third party tool makers) comes up with.\n-- \nDuy\n"},{"id":"364010","messageId":"87a7ly1djb.fsf@evledraar.gmail.com","threadId":"49405","inReplyTo":"20181021080859.3203-4-pclouds@gmail.com","subject":"Re: [PATCH v3 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-11-24T19:27:52Z","receivedAt":"2018-11-24T19:27:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Oct 21 2018, Nguyễn Thái Ngọc Duy wrote:\n\nThis change has a regression in 2.20:\n\n> [...]\n>  static void files_reflog_path(struct files_ref_store *refs,\n>  \t\t\t      struct strbuf *sb,\n>  \t\t\t      const char *refname)\n> @@ -158,6 +178,9 @@ static void files_reflog_path(struct files_ref_store *refs,\n>  \tcase REF_TYPE_PSEUDOREF:\n>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n>  \t\tbreak;\n> +\tcase REF_TYPE_OTHER_PSEUDOREF:\n> +\tcase REF_TYPE_MAIN_PSEUDOREF:\n> +\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n>  \tcase REF_TYPE_NORMAL:\n>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n>  \t\tbreak;\n\nSunCC on Solaris hard errors on this:\n\n    \"refs/files-backend.c\", line 183: void function cannot return value\n\nNeeds to be files...(); break; instead.\n"},{"id":"364018","messageId":"xmqqtvk60xa3.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"87a7ly1djb.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3 3/8] refs: new ref types to make per-worktree refs visible to all worktrees","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-25T01:19:00Z","receivedAt":"2018-11-25T01:19:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> On Sun, Oct 21 2018, Nguyễn Thái Ngọc Duy wrote:\n>\n> This change has a regression in 2.20:\n>\n>> [...]\n>>  static void files_reflog_path(struct files_ref_store *refs,\n>>  \t\t\t      struct strbuf *sb,\n>>  \t\t\t      const char *refname)\n>> @@ -158,6 +178,9 @@ static void files_reflog_path(struct files_ref_store *refs,\n>>  \tcase REF_TYPE_PSEUDOREF:\n>>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitdir, refname);\n>>  \t\tbreak;\n>> +\tcase REF_TYPE_OTHER_PSEUDOREF:\n>> +\tcase REF_TYPE_MAIN_PSEUDOREF:\n>> +\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n>>  \tcase REF_TYPE_NORMAL:\n>>  \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n>>  \t\tbreak;\n>\n> SunCC on Solaris hard errors on this:\n>\n>     \"refs/files-backend.c\", line 183: void function cannot return value\n>\n> Needs to be files...(); break; instead.\n\nTrue.\n\nThe caller itself returns \"void\", so it would be nice if this were a\nmere warning() from practical usabliity's point of view, though ;-)\n"},{"id":"364022","messageId":"20181125045816.12185-1-pclouds@gmail.com","threadId":"49405","inReplyTo":"87a7ly1djb.fsf@evledraar.gmail.com","subject":"[PATCH] files-backend.c: fix build error on Solaris","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-25T04:58:16Z","receivedAt":"2018-11-25T04:58:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This function files_reflog_path returns void, which usually means\n\"return;\" not returning \"void value\" from another function.\n\nReported-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs/files-backend.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 9183875dad..dd8abe9185 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -180,7 +180,8 @@ static void files_reflog_path(struct files_ref_store *refs,\n \t\tbreak;\n \tcase REF_TYPE_OTHER_PSEUDOREF:\n \tcase REF_TYPE_MAIN_PSEUDOREF:\n-\t\treturn files_reflog_path_other_worktrees(refs, sb, refname);\n+\t\tfiles_reflog_path_other_worktrees(refs, sb, refname);\n+\t\tbreak;\n \tcase REF_TYPE_NORMAL:\n \t\tstrbuf_addf(sb, \"%s/logs/%s\", refs->gitcommondir, refname);\n \t\tbreak;\n-- \n2.19.1.1327.g328c130451.dirty\n\n"},{"id":"364024","messageId":"CAPUEsphe9eXk8zLE35iRq-M9a-tukj8APQ0gHzR6PHfNZNLxLA@mail.gmail.com","threadId":"49405","inReplyTo":"20181125045816.12185-1-pclouds@gmail.com","subject":"Re: [PATCH] files-backend.c: fix build error on Solaris","fromName":"Carlo Arenas","fromEmail":"carenas@gmail.com","sentAt":"2018-11-25T10:19:20Z","receivedAt":"2018-11-25T10:19:35Z","isPatch":true,"sender":{"key":"carenas@gmail.com","avatar":"https://avatars.githubusercontent.com/u/76036?v=4"},"body":"Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>\n\nclang with -Wpedantic also catch this (at least with Apple LLVM\nversion 10.0.0); recent versions of gcc also include that flag and at\nleast 8.2.0 shows a warning for it, so it might be worth adding it to\ndeveloper mode (maybe under the pedantic DEVOPTS), would this be\nsomething I should be adding?, what are the expectations around\nwarnings and compilers?\n\nCarlo\n"},{"id":"364025","messageId":"CACsJy8AJZ52XuWoCiMT_2=RT_a3OaAhSuekc1YY2-B9zc6y64Q@mail.gmail.com","threadId":"49405","inReplyTo":"CAPUEsphe9eXk8zLE35iRq-M9a-tukj8APQ0gHzR6PHfNZNLxLA@mail.gmail.com","subject":"Re: [PATCH] files-backend.c: fix build error on Solaris","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-11-25T10:40:21Z","receivedAt":"2018-11-25T10:42:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Nov 25, 2018 at 11:19 AM Carlo Arenas <carenas@gmail.com> wrote:\n>\n> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>\n>\n> clang with -Wpedantic also catch this (at least with Apple LLVM\n> version 10.0.0); recent versions of gcc also include that flag and at\n> least 8.2.0 shows a warning for it, so it might be worth adding it to\n> developer mode (maybe under the pedantic DEVOPTS), would this be\n> something I should be adding?, what are the expectations around\n> warnings and compilers?\n\nmake DEVELOPER=1 DEVOPTS=pedantic (with either gcc 7.3.0 or 8.2.0) can\nalready catch this. I have no comment if this pedantic should be part\nof default DEVELOPER=1. But at least in controlled environments like\nTravis, we could turn it on to catch more.\n-- \nDuy\n"},{"id":"364052","messageId":"xmqqin0kzbug.fsf@gitster-ct.c.googlers.com","threadId":"49405","inReplyTo":"CAPUEsphe9eXk8zLE35iRq-M9a-tukj8APQ0gHzR6PHfNZNLxLA@mail.gmail.com","subject":"Re: [PATCH] files-backend.c: fix build error on Solaris","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-26T04:44:55Z","receivedAt":"2018-11-26T04:45:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Carlo Arenas <carenas@gmail.com> writes:\n\n> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>\n\nDo you mean Tested-by: (meaning, you actually saw the breakage with\nSunCC without the patch and also saw the patch fixed the breakage)?\n\n> clang with -Wpedantic also catch this (at least with Apple LLVM\n> version 10.0.0); recent versions of gcc also include that flag and at\n> least 8.2.0 shows a warning for it, so it might be worth adding it to\n> developer mode (maybe under the pedantic DEVOPTS).\n\nNice to know.\n\n> ..., would this be something I should be adding?, what are the expectations around\n> warnings and compilers?\n\nIt may be a worthwhile thing to discuss, and as a discussion\nstarter, a patch would help ;-).\n"}]}