When multiple worktrees are used, we need rules to determine if
something belongs to one worktree or all of them. Instead of keeping
adding rules when new stuff comes, have a generic rule:

- Inside $GIT_DIR, which is per-worktree by default, add
  $GIT_DIR/common which is always shared. New features that want to
  share stuff should put stuff under this directory.

- Inside refs/, which is shared by default except refs/bisect, add
  refs/local/ which is per-worktree. We may eventually move
  refs/bisect to this new location and remove the exception in refs
  code.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 Documentation/gitrepository-layout.txt | 11 ++++++--
 path.c                                 |  1 +
 refs.c                                 |  1 +
 refs/files-backend.c                   | 14 +++++++---
 t/t0060-path-utils.sh                  |  2 ++
 t/t1415-worktree-refs.sh               | 36 ++++++++++++++++++++++++++
 6 files changed, 60 insertions(+), 5 deletions(-)
 create mode 100755 t/t1415-worktree-refs.sh

diff --git a/Documentation/gitrepository-layout.txt 
b/Documentation/gitrepository-layout.txt
index e85148f05e..fad404ed7c 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -95,8 +95,10 @@ refs::
        References are stored in subdirectories of this
        directory.  The 'git prune' command knows to preserve
        objects reachable from refs found in this directory and
-       its subdirectories. This directory is ignored if $GIT_COMMON_DIR
-       is set and "$GIT_COMMON_DIR/refs" will be used instead.
+       its subdirectories.
+       This directory is ignored (except refs/bisect and refs/local)
+       if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/refs" will be
+       used instead.
 
 refs/heads/`name`::
        records tip-of-the-tree commit objects of branch `name`
@@ -165,6 +167,11 @@ hooks::
        each hook. This directory is ignored if $GIT_COMMON_DIR is set
        and "$GIT_COMMON_DIR/hooks" will be used instead.
 
+common::
+       When multiple working trees are used, most of files in
+       $GIT_DIR are per-worktree with a few known exceptions. All
+       files under 'common' however will be shared between all
+       working trees.
 
 index::
        The current index file for the repository.  It is
diff --git a/path.c b/path.c
index 34f0f98349..7eb61bf31b 100644
--- a/path.c
+++ b/path.c
@@ -108,6 +108,7 @@ struct common_dir {
 
 static struct common_dir common_list[] = {
        { 0, 1, 0, "branches" },
+       { 0, 1, 0, "common" },
        { 0, 1, 0, "hooks" },
        { 0, 1, 0, "info" },
        { 0, 0, 1, "info/sparse-checkout" },
diff --git a/refs.c b/refs.c
index 9f7268d5fe..a851ef085b 100644
--- a/refs.c
+++ b/refs.c
@@ -624,6 +624,7 @@ int dwim_log(const char *str, int len, struct object_id 
*oid, char **log)
 static int is_per_worktree_ref(const char *refname)
 {
        return !strcmp(refname, "HEAD") ||
+               starts_with(refname, "refs/local/") ||
                starts_with(refname, "refs/bisect/") ||
                starts_with(refname, "refs/rewritten/");
 }
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 16ef9325e0..416eafa453 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -269,9 +269,9 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        closedir(d);
 
        /*
-        * Manually add refs/bisect, which, being per-worktree, might
-        * not appear in the directory listing for refs/ in the main
-        * repo.
+        * Manually add refs/bisect and refs/local, which, being
+        * per-worktree, might not appear in the directory listing for
+        * refs/ in the main repo.
         */
        if (!strcmp(dirname, "refs/")) {
                int pos = search_ref_dir(dir, "refs/bisect/", 12);
@@ -281,6 +281,14 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                                        dir->cache, "refs/bisect/", 12, 1);
                        add_entry_to_dir(dir, child_entry);
                }
+
+               pos = search_ref_dir(dir, "refs/local/", 11);
+
+               if (pos < 0) {
+                       struct ref_entry *child_entry = create_dir_entry(
+                                       dir->cache, "refs/local/", 11, 1);
+                       add_entry_to_dir(dir, child_entry);
+               }
        }
 }
 
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index cd74c0a471..c7b53e494b 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -306,6 +306,8 @@ test_git_path GIT_COMMON_DIR=bar hooks/me                 
bar/hooks/me
 test_git_path GIT_COMMON_DIR=bar config                   bar/config
 test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
 test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
+test_git_path GIT_COMMON_DIR=bar common                   bar/common
+test_git_path GIT_COMMON_DIR=bar common/file              bar/common/file
 
 # In the tests below, $(pwd) must be used because it is a native path on
 # Windows and avoids MSYS's path mangling (which simplifies "foo/../bar" and
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
new file mode 100755
index 0000000000..0c2d5f89a9
--- /dev/null
+++ b/t/t1415-worktree-refs.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='per-worktree refs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit initial &&
+       test_commit wt1 &&
+       test_commit wt2 &&
+       git worktree add wt1 wt1 &&
+       git worktree add wt2 wt2 &&
+       git checkout initial
+'
+
+test_expect_success 'add refs/local' '
+       git update-ref refs/local/foo HEAD &&
+       git -C wt1 update-ref refs/local/foo HEAD &&
+       git -C wt2 update-ref refs/local/foo HEAD
+'
+
+test_expect_success 'refs/local must not be packed' '
+       git pack-refs --all &&
+       test_path_is_missing .git/refs/tags/wt1 &&
+       test_path_is_file .git/refs/local/foo &&
+       test_path_is_file .git/worktrees/wt1/refs/local/foo &&
+       test_path_is_file .git/worktrees/wt2/refs/local/foo
+'
+
+test_expect_success 'refs/local are per-worktree' '
+       test_cmp_rev local/foo initial &&
+       ( cd wt1 && test_cmp_rev local/foo wt1 ) &&
+       ( cd wt2 && test_cmp_rev local/foo wt2 )
+'
+
+test_done
-- 
2.19.0.647.gb9a6049235

Reply via email to