{"thread":{"id":"62561","subject":"[PATCH 0/2] Ensure unique worktree ids across repositories","startedAt":"2024-11-29T02:44:40Z","lastAt":"2024-12-01T04:38:13Z","messageCount":11,"participants":["Caleb White","Junio C Hamano","shejialuo"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"508314","messageId":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","threadId":"62561","inReplyTo":null,"subject":"[PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T02:44:24Z","receivedAt":"2024-11-29T02:44:40Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"The `es/worktree-repair-copied` topic added support for repairing a\nworktree from a copy scenario. I noted[1,2] that the topic added the\nability for a repository to \"take over\" a worktree from another\nrepository if the worktree_id matched a worktree inside the current\nrepository which can happen if two repositories use the same worktree name.\n\nThis series teaches Git to create worktrees with a unique suffix so\nthat the worktree_id is unique across all repositories even if they have\nthe same name. For example creating a worktree `develop` would look like:\n\n    foo/\n    ├── .git/worktrees/develop-5445874156/\n    └── develop/\n    bar/\n    ├── .git/worktrees/develop-1549518426/\n    └── develop/\n\nThe actual worktree directory name is still `develop`, but the\nworktree_id is unique and prevents the \"take over\" scenario. The suffix\nis given by the `git_rand()` function, but I'm open to suggestions if\nthere's a better random or hashing function to use.\n\n[1]: https://lore.kernel.org/git/20241008153035.71178-1-cdwhite3@pm.me/\n[2]: https://lore.kernel.org/git/r4zmcET41Skr_FMop47AKd7cms9E8bKPSvHuAUpnYavzKEY6JybJta0_7GfuYB0q-gD-XNcvh5VDTfiT3qthGKjqhS1sbT4M2lUABynOz2Q=@pm.me/\n\nSigned-off-by: Caleb White <cdwhite3@pm.me>\n---\nCaleb White (2):\n      worktree: add worktree with unique suffix\n      worktree: rename worktree id during worktree move\n\n Documentation/git-worktree.txt     |  5 +-\n builtin/worktree.c                 | 30 ++++++++++++\n t/t0035-safe-bare-repository.sh    |  4 +-\n t/t0600-reffiles-backend.sh        | 10 ++--\n t/t0601-reffiles-pack-refs.sh      |  4 +-\n t/t0610-reftable-basics.sh         | 54 +++++++++++-----------\n t/t1407-worktree-ref-store.sh      |  4 +-\n t/t1410-reflog.sh                  | 10 ++--\n t/t1415-worktree-refs.sh           | 26 +++++------\n t/t1450-fsck.sh                    | 14 +++---\n t/t1500-rev-parse.sh               |  6 +--\n t/t2400-worktree-add.sh            | 51 +++++++++++----------\n t/t2401-worktree-prune.sh          | 20 ++++----\n t/t2403-worktree-move.sh           | 38 ++++++++--------\n t/t2405-worktree-submodule.sh      | 10 ++--\n t/t2406-worktree-repair.sh         | 93 ++++++++++++++++++++++++--------------\n t/t2407-worktree-heads.sh          | 27 +++++------\n t/t3200-branch.sh                  | 10 ++--\n t/t5304-prune.sh                   |  2 +-\n t/t7412-submodule-absorbgitdirs.sh |  4 +-\n 20 files changed, 239 insertions(+), 183 deletions(-)\n---\nbase-commit: 090d24e9af6e9f59c3f7bee97c42bb1ae3c7f559\nchange-id: 20241127-wt_unique_ids-1ffd7ea0bb19\nprerequisite-change-id: 20241025-wt_relative_options-afa41987bc32:v5\nprerequisite-patch-id: 179410e257e8eedf100f4f9faa9467cbbba4d61b\nprerequisite-patch-id: 56ffe0afeadd511c9eef5f548a371659b040acab\nprerequisite-patch-id: 809c1314e5dfa966f4f3d73b52f286f8aa89370f\nprerequisite-patch-id: cf5f9491c8f8e58d1e0e103a5f8c64c55f2896e3\nprerequisite-patch-id: 3d3bb3cc81d3030b1d27c39fdb4cf0e383937f89\nprerequisite-patch-id: 62a09496d98d78a6bd1f9150ba887ee72359c7ee\nprerequisite-patch-id: 5527e4b745963dd4fa08029491fcbfe3d91d5104\nprerequisite-patch-id: bf433443e90939a493fa586de30938f78cb77020\n\nBest regards,\n-- \nCaleb White <cdwhite3@pm.me>\n\n\n"},{"id":"508315","messageId":"20241128-wt_unique_ids-v1-1-30345d010e43@pm.me","threadId":"62561","inReplyTo":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","subject":"[PATCH 1/2] worktree: add worktree with unique suffix","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T02:44:33Z","receivedAt":"2024-11-29T02:44:51Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"The `es/worktree-repair-copied` topic added support for repairing a\nworktree from a copy scenario. However, the topic added the\nability for a repository to \"take over\" a worktree from another\nrepository if the worktree_id matched a worktree inside the current\nrepository which can happen if two repositories use the same worktree name.\n\nThis teaches Git to create worktrees with a unique suffix so the\nworktree_id is unique across all repositories even if they have the\nsame name. For example creating a worktree `develop` would look like:\n\n    foo/\n    ├── .git/worktrees/develop-5445874156/\n    └── develop/\n    bar/\n    ├── .git/worktrees/develop-1549518426/\n    └── develop/\n\nThe actual worktree directory name is still `develop`, but the\nworktree_id is unique and prevents the \"take over\" scenario. The suffix\nis given by the `git_rand()` function. Worktree ids can already differ\nfrom the actual directory name (appended with a number like `develop1`)\nif the worktree name was already taken, so this should not be a\ndrastic change.\n\nSigned-off-by: Caleb White <cdwhite3@pm.me>\n---\n Documentation/git-worktree.txt     |  5 +-\n builtin/worktree.c                 |  6 +++\n t/t0035-safe-bare-repository.sh    |  4 +-\n t/t0600-reffiles-backend.sh        | 10 ++--\n t/t0601-reffiles-pack-refs.sh      |  4 +-\n t/t0610-reftable-basics.sh         | 54 +++++++++++-----------\n t/t1407-worktree-ref-store.sh      |  4 +-\n t/t1410-reflog.sh                  | 10 ++--\n t/t1415-worktree-refs.sh           | 26 +++++------\n t/t1450-fsck.sh                    | 14 +++---\n t/t1500-rev-parse.sh               |  6 +--\n t/t2400-worktree-add.sh            | 51 +++++++++++----------\n t/t2401-worktree-prune.sh          | 20 ++++----\n t/t2403-worktree-move.sh           | 32 ++++++-------\n t/t2405-worktree-submodule.sh      | 10 ++--\n t/t2406-worktree-repair.sh         | 93 ++++++++++++++++++++++++--------------\n t/t2407-worktree-heads.sh          | 27 +++++------\n t/t3200-branch.sh                  | 10 ++--\n t/t5304-prune.sh                   |  2 +-\n t/t7412-submodule-absorbgitdirs.sh |  4 +-\n 20 files changed, 212 insertions(+), 180 deletions(-)\n\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 8340b7f028e6c1c3bae3de0879e9754098466d14..e0604b043361828f94b58f676a5ed4f15b116348 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -352,12 +352,11 @@ DETAILS\n -------\n Each linked worktree has a private sub-directory in the repository's\n `$GIT_DIR/worktrees` directory.  The private sub-directory's name is usually\n-the base name of the linked worktree's path, possibly appended with a\n+the base name of the linked worktree's path, appended with a random\n number to make it unique.  For example, when `$GIT_DIR=/path/main/.git` the\n command `git worktree add /path/other/test-next next` creates the linked\n worktree in `/path/other/test-next` and also creates a\n-`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1`\n-if `test-next` is already taken).\n+`$GIT_DIR/worktrees/test-next-#######` directory.\n \n Within a linked worktree, `$GIT_DIR` is set to point to this private\n directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex fde9ff4dc9a734c655e95ccd62774282950cbba6..3ad355ca762729401fc0c8625f4fd05b154a84ec 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -421,6 +421,7 @@ static int add_worktree(const char *path, const char *refname,\n \tstruct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;\n \tstruct strbuf sb = STRBUF_INIT;\n \tconst char *name;\n+\tconst char *suffix;\n \tstruct strvec child_env = STRVEC_INIT;\n \tunsigned int counter = 0;\n \tint len, ret;\n@@ -455,6 +456,11 @@ static int add_worktree(const char *path, const char *refname,\n \tstrbuf_reset(&sb);\n \tname = sb_name.buf;\n \tgit_path_buf(&sb_repo, \"worktrees/%s\", name);\n+\tsuffix = getenv(\"GIT_TEST_WORKTREE_SUFFIX\");\n+\tif (suffix)\n+\t\tstrbuf_addf(&sb_repo, \"-%s\", suffix);\n+\telse\n+\t\tstrbuf_addf(&sb_repo, \"-%u\", git_rand());\n \tlen = sb_repo.len;\n \tif (safe_create_leading_directories_const(sb_repo.buf))\n \t\tdie_errno(_(\"could not create leading directories of '%s'\"),\ndiff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh\nindex d3cb2a1cb9edb8f9ad7480be6ec3e02b464046bd..30cbf7fd32e2e4afd6d680e0328ee18b791778da 100755\n--- a/t/t0035-safe-bare-repository.sh\n+++ b/t/t0035-safe-bare-repository.sh\n@@ -41,7 +41,7 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu\n \t\tgit -c protocol.file.allow=always \\\n \t\t\tsubmodule add --name subn -- ./bare-repo subd\n \t) &&\n-\ttest_path_is_dir outer-repo/.git/worktrees/outer-secondary &&\n+\ttest_path_is_dir outer-repo/.git/worktrees/outer-secondary-* &&\n \ttest_path_is_dir outer-repo/.git/modules/subn\n '\n \n@@ -97,7 +97,7 @@ test_expect_success 'no trace when \"bare repository\" is a subdir of .git' '\n '\n \n test_expect_success 'no trace in $GIT_DIR of secondary worktree' '\n-\texpect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary\n+\texpect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary-*\n '\n \n test_expect_success 'no trace in $GIT_DIR of a submodule' '\ndiff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh\nindex bef2b70871364931ab8b3ead950f59d11b6fe216..8da81b1ae34f5f095ba9406e8f031fd5e43ac789 100755\n--- a/t/t0600-reffiles-backend.sh\n+++ b/t/t0600-reffiles-backend.sh\n@@ -256,12 +256,12 @@ test_expect_success 'delete fails cleanly if packed-refs.new write fails' '\n \ttest_cmp unchanged actual\n '\n \n-RWT=\"test-tool ref-store worktree:wt\"\n+RWT=\"test-tool ref-store worktree:wt-123\"\n RMAIN=\"test-tool ref-store worktree:main\"\n \n test_expect_success 'setup worktree' '\n \ttest_commit first &&\n-\tgit worktree add -b wt-main wt &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add -b wt-main wt &&\n \t(\n \t\tcd wt &&\n \t\ttest_commit second\n@@ -279,9 +279,9 @@ test_expect_success 'for_each_reflog()' '\n \tmkdir -p     .git/logs/refs/bisect &&\n \techo $ZERO_OID >.git/logs/refs/bisect/random &&\n \n-\techo $ZERO_OID >.git/worktrees/wt/logs/PSEUDO_WT_HEAD &&\n-\tmkdir -p     .git/worktrees/wt/logs/refs/bisect &&\n-\techo $ZERO_OID >.git/worktrees/wt/logs/refs/bisect/wt-random &&\n+\techo $ZERO_OID >.git/worktrees/wt-123/logs/PSEUDO_WT_HEAD &&\n+\tmkdir -p     .git/worktrees/wt-123/logs/refs/bisect &&\n+\techo $ZERO_OID >.git/worktrees/wt-123/logs/refs/bisect/wt-random &&\n \n \t$RWT for-each-reflog >actual &&\n \tcat >expected <<-\\EOF &&\ndiff --git a/t/t0601-reffiles-pack-refs.sh b/t/t0601-reffiles-pack-refs.sh\nindex d8cbd3f202b5f00c9a94c5a2ea2dced6606b6f4d..d6597938caca5e6c3a6e86b860f3c0d309aa2140 100755\n--- a/t/t0601-reffiles-pack-refs.sh\n+++ b/t/t0601-reffiles-pack-refs.sh\n@@ -326,8 +326,8 @@ 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+\ttest_path_is_file .git/worktrees/wt1-*/refs/worktree/foo &&\n+\ttest_path_is_file .git/worktrees/wt2-*/refs/worktree/foo\n '\n \n # we do not want to count on running pack-refs to\ndiff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh\nindex eaf6fab6d29f01430abae3c7abf5a750d4271a36..f21fd9dbba2b77636045b297073e340209d61a2d 100755\n--- a/t/t0610-reftable-basics.sh\n+++ b/t/t0610-reftable-basics.sh\n@@ -965,11 +965,11 @@ test_expect_success 'worktree: adding worktree creates separate stack' '\n \ttest_commit -C repo A &&\n \n \tgit -C repo worktree add ../worktree &&\n-\ttest_path_is_file repo/.git/worktrees/worktree/refs/heads &&\n+\ttest_path_is_file repo/.git/worktrees/worktree-*/refs/heads &&\n \techo \"ref: refs/heads/.invalid\" >expect &&\n-\ttest_cmp expect repo/.git/worktrees/worktree/HEAD &&\n-\ttest_path_is_dir repo/.git/worktrees/worktree/reftable &&\n-\ttest_path_is_file repo/.git/worktrees/worktree/reftable/tables.list\n+\ttest_cmp expect repo/.git/worktrees/worktree-*/HEAD &&\n+\ttest_path_is_dir repo/.git/worktrees/worktree-*/reftable &&\n+\ttest_path_is_file repo/.git/worktrees/worktree-*/reftable/tables.list\n '\n \n test_expect_success 'worktree: pack-refs in main repo packs main refs' '\n@@ -982,10 +982,10 @@ test_expect_success 'worktree: pack-refs in main repo packs main refs' '\n \tGIT_TEST_REFTABLE_AUTOCOMPACTION=false \\\n \tgit -C worktree update-ref refs/worktree/per-worktree HEAD &&\n \n-\ttest_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 4 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 3 repo/.git/reftable/tables.list &&\n \tgit -C repo pack-refs &&\n-\ttest_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 4 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list\n '\n \n@@ -999,10 +999,10 @@ test_expect_success 'worktree: pack-refs in worktree packs worktree refs' '\n \tGIT_TEST_REFTABLE_AUTOCOMPACTION=false \\\n \tgit -C worktree update-ref refs/worktree/per-worktree HEAD &&\n \n-\ttest_line_count = 4 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 4 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 3 repo/.git/reftable/tables.list &&\n \tgit -C worktree pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 3 repo/.git/reftable/tables.list\n '\n \n@@ -1014,12 +1014,12 @@ test_expect_success 'worktree: creating shared ref updates main stack' '\n \tgit -C repo worktree add ../worktree &&\n \tgit -C repo pack-refs &&\n \tgit -C worktree pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list &&\n \n \tGIT_TEST_REFTABLE_AUTOCOMPACTION=false \\\n \tgit -C worktree update-ref refs/heads/shared HEAD &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 2 repo/.git/reftable/tables.list\n '\n \n@@ -1031,11 +1031,11 @@ test_expect_success 'worktree: creating per-worktree ref updates worktree stack'\n \tgit -C repo worktree add ../worktree &&\n \tgit -C repo pack-refs &&\n \tgit -C worktree pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list &&\n \n \tgit -C worktree update-ref refs/bisect/per-worktree HEAD &&\n-\ttest_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 2 repo/.git/worktrees/worktree-*/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list\n '\n \n@@ -1044,14 +1044,14 @@ test_expect_success 'worktree: creating per-worktree ref from main repo' '\n \tgit init repo &&\n \ttest_commit -C repo A &&\n \n-\tgit -C repo worktree add ../worktree &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git -C repo worktree add ../worktree &&\n \tgit -C repo pack-refs &&\n \tgit -C worktree pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-456/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list &&\n \n-\tgit -C repo update-ref worktrees/worktree/refs/bisect/per-worktree HEAD &&\n-\ttest_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\tgit -C repo update-ref worktrees/worktree-456/refs/bisect/per-worktree HEAD &&\n+\ttest_line_count = 2 repo/.git/worktrees/worktree-456/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list\n '\n \n@@ -1060,18 +1060,18 @@ test_expect_success 'worktree: creating per-worktree ref from second worktree' '\n \tgit init repo &&\n \ttest_commit -C repo A &&\n \n-\tgit -C repo worktree add ../wt1 &&\n-\tgit -C repo worktree add ../wt2 &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git -C repo worktree add ../wt1 &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git -C repo worktree add ../wt2 &&\n \tgit -C repo pack-refs &&\n \tgit -C wt1 pack-refs &&\n \tgit -C wt2 pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&\n-\ttest_line_count = 1 repo/.git/worktrees/wt2/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/wt1-123/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/wt2-456/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list &&\n \n-\tgit -C wt1 update-ref worktrees/wt2/refs/bisect/per-worktree HEAD &&\n-\ttest_line_count = 1 repo/.git/worktrees/wt1/reftable/tables.list &&\n-\ttest_line_count = 2 repo/.git/worktrees/wt2/reftable/tables.list &&\n+\tgit -C wt1 update-ref worktrees/wt2-456/refs/bisect/per-worktree HEAD &&\n+\ttest_line_count = 1 repo/.git/worktrees/wt1-123/reftable/tables.list &&\n+\ttest_line_count = 2 repo/.git/worktrees/wt2-456/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list\n '\n \n@@ -1080,18 +1080,18 @@ test_expect_success 'worktree: can create shared and per-worktree ref in one tra\n \tgit init repo &&\n \ttest_commit -C repo A &&\n \n-\tgit -C repo worktree add ../worktree &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git -C repo worktree add ../worktree &&\n \tgit -C repo pack-refs &&\n \tgit -C worktree pack-refs &&\n-\ttest_line_count = 1 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 1 repo/.git/worktrees/worktree-123/reftable/tables.list &&\n \ttest_line_count = 1 repo/.git/reftable/tables.list &&\n \n \tcat >stdin <<-EOF &&\n-\tcreate worktrees/worktree/refs/bisect/per-worktree HEAD\n+\tcreate worktrees/worktree-123/refs/bisect/per-worktree HEAD\n \tcreate refs/branches/shared HEAD\n \tEOF\n \tgit -C repo update-ref --stdin <stdin &&\n-\ttest_line_count = 2 repo/.git/worktrees/worktree/reftable/tables.list &&\n+\ttest_line_count = 2 repo/.git/worktrees/worktree-123/reftable/tables.list &&\n \ttest_line_count = 2 repo/.git/reftable/tables.list\n '\n \ndiff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh\nindex 48b1c92a41450b25645d6cd782aa86c8e630164b..4d081627a1e6b55936cd4051ed760fa92e7cdc02 100755\n--- a/t/t1407-worktree-ref-store.sh\n+++ b/t/t1407-worktree-ref-store.sh\n@@ -8,12 +8,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n-RWT=\"test-tool ref-store worktree:wt\"\n+RWT=\"test-tool ref-store worktree:wt-456\"\n RMAIN=\"test-tool ref-store worktree:main\"\n \n test_expect_success 'setup' '\n \ttest_commit first &&\n-\tgit worktree add -b wt-main wt &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git worktree add -b wt-main wt &&\n \t(\n \t\tcd wt &&\n \t\ttest_commit second\ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 246a3f46abafdf0e24528be59b33a4987ff791c1..c1b6b4d8fab8d261713a192478af12179f9bc917 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -402,12 +402,12 @@ test_expect_success 'expire with multiple worktrees' '\n \t\tcd main-wt &&\n \t\ttest_tick &&\n \t\ttest_commit foo &&\n-\t\tgit  worktree add link-wt &&\n+\t\tGIT_TEST_WORKTREE_SUFFIX=123 git  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-tool ref-store worktree:link-wt for-each-reflog-ent HEAD >actual &&\n+\t\ttest-tool ref-store worktree:link-wt-123 for-each-reflog-ent HEAD >actual &&\n \t\ttest_must_be_empty actual\n \t)\n '\n@@ -418,17 +418,17 @@ test_expect_success 'expire one of multiple worktrees' '\n \t\tcd main-wt2 &&\n \t\ttest_tick &&\n \t\ttest_commit foo &&\n-\t\tgit worktree add link-wt &&\n+\t\tGIT_TEST_WORKTREE_SUFFIX=456 git worktree add link-wt &&\n \t\ttest_tick &&\n \t\ttest_commit -C link-wt foobar &&\n \t\ttest_tick &&\n-\t\ttest-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \\\n+\t\ttest-tool ref-store worktree:link-wt-456 for-each-reflog-ent HEAD \\\n \t\t\t>expect-link-wt &&\n \t\tgit reflog expire --verbose --all --expire=$test_tick \\\n \t\t\t--single-worktree &&\n \t\ttest-tool ref-store worktree:main for-each-reflog-ent HEAD \\\n \t\t\t>actual-main &&\n-\t\ttest-tool ref-store worktree:link-wt for-each-reflog-ent HEAD \\\n+\t\ttest-tool ref-store worktree:link-wt-456 for-each-reflog-ent HEAD \\\n \t\t\t>actual-link-wt &&\n \t\ttest_must_be_empty actual-main &&\n \t\ttest_cmp expect-link-wt actual-link-wt\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex eb4eec8becbfa64efcde4e866334363a866c01a2..c46bf29aa4d9ceac85147d685a1d23144ec23b2b 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -9,8 +9,8 @@ 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_TEST_WORKTREE_SUFFIX=123 git worktree add wt1 wt1 &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git 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@@ -37,16 +37,16 @@ test_expect_success 'ambiguous main-worktree/HEAD' '\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+\ttest_cmp_rev worktrees/wt1-123/HEAD wt1 &&\n+\t( cd wt1 && test_cmp_rev worktrees/wt1-123/HEAD wt1 ) &&\n+\t( cd wt2 && test_cmp_rev worktrees/wt1-123/HEAD wt1 )\n '\n \n test_expect_success 'ambiguous worktrees/xx/HEAD' '\n-\tgit update-ref refs/heads/worktrees/wt1/HEAD $(git rev-parse HEAD) &&\n-\ttest_when_finished git update-ref -d refs/heads/worktrees/wt1/HEAD &&\n-\tgit rev-parse worktrees/wt1/HEAD 2>warn &&\n-\tgrep \"worktrees/wt1/HEAD.*ambiguous\" warn\n+\tgit update-ref refs/heads/worktrees/wt1-123/HEAD $(git rev-parse HEAD) &&\n+\ttest_when_finished git update-ref -d refs/heads/worktrees/wt1-123/HEAD &&\n+\tgit rev-parse worktrees/wt1-123/HEAD 2>warn &&\n+\tgrep \"worktrees/wt1-123/HEAD.*ambiguous\" warn\n '\n \n test_expect_success 'reflog of main-worktree/HEAD' '\n@@ -58,12 +58,12 @@ test_expect_success 'reflog of main-worktree/HEAD' '\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+\tgit -C wt2 reflog HEAD | sed \"s/HEAD/worktrees\\/wt2-456\\/HEAD/\" >expected &&\n+\tgit reflog worktrees/wt2-456/HEAD >actual &&\n \ttest_cmp expected actual &&\n-\tgit -C wt1 reflog worktrees/wt2/HEAD >actual.wt1 &&\n+\tgit -C wt1 reflog worktrees/wt2-456/HEAD >actual.wt1 &&\n \ttest_cmp expected actual.wt1 &&\n-\tgit -C wt2 reflog worktrees/wt2/HEAD >actual.wt2 &&\n+\tgit -C wt2 reflog worktrees/wt2-456/HEAD >actual.wt2 &&\n \ttest_cmp expected actual.wt2\n '\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 280cbf3e031e1ab67ed28aa2af4c3b105b7d254e..de25ab24fd8924e288969650a658908a5e9afd22 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -152,10 +152,10 @@ test_expect_success REFFILES 'HEAD link pointing at a funny object (from differe\n \n test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '\n \ttest_when_finished \"git worktree remove -f other\" &&\n-\tgit worktree add other &&\n-\techo $ZERO_OID >.git/worktrees/other/HEAD &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add other &&\n+\techo $ZERO_OID >.git/worktrees/other-123/HEAD &&\n \ttest_must_fail git fsck 2>out &&\n-\ttest_grep \"worktrees/other/HEAD: detached HEAD points\" out\n+\ttest_grep \"worktrees/other-123/HEAD: detached HEAD points\" out\n '\n \n test_expect_success 'other worktree HEAD link pointing at missing object' '\n@@ -164,7 +164,7 @@ test_expect_success 'other worktree HEAD link pointing at missing object' '\n \tobject_id=$(echo \"Contents missing from repo\" | git hash-object --stdin) &&\n \ttest-tool -C other ref-store main update-ref msg HEAD $object_id \"\" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&\n \ttest_must_fail git fsck 2>out &&\n-\ttest_grep \"worktrees/other/HEAD: invalid sha1 pointer\" out\n+\ttest_grep \"worktrees/other-.*/HEAD: invalid sha1 pointer\" out\n '\n \n test_expect_success 'other worktree HEAD link pointing at a funny place' '\n@@ -172,7 +172,7 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '\n \tgit worktree add other &&\n \tgit -C other symbolic-ref HEAD refs/funny/place &&\n \ttest_must_fail git fsck 2>out &&\n-\ttest_grep \"worktrees/other/HEAD points to something strange\" out\n+\ttest_grep \"worktrees/other-.*/HEAD points to something strange\" out\n '\n \n test_expect_success 'commit with multiple signatures is okay' '\n@@ -1033,7 +1033,7 @@ test_expect_success 'fsck error on gitattributes with excessive size' '\n \n test_expect_success 'fsck detects problems in worktree index' '\n \ttest_when_finished \"git worktree remove -f wt\" &&\n-\tgit worktree add wt &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add wt &&\n \n \techo \"this will be removed to break the worktree index\" >wt/file &&\n \tgit -C wt add file &&\n@@ -1042,7 +1042,7 @@ test_expect_success 'fsck detects problems in worktree index' '\n \n \ttest_must_fail git fsck --name-objects >actual 2>&1 &&\n \tcat >expect <<-EOF &&\n-\tmissing blob $blob (.git/worktrees/wt/index:file)\n+\tmissing blob $blob (.git/worktrees/wt-123/index:file)\n \tEOF\n \ttest_cmp expect actual\n '\ndiff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh\nindex 30c31918fde6539d52800e18dfbb3423b5b73491..49e0ac68858d922336a498caaa743f2f4011d28a 100755\n--- a/t/t1500-rev-parse.sh\n+++ b/t/t1500-rev-parse.sh\n@@ -80,7 +80,7 @@ test_expect_success 'setup' '\n \tgit checkout -b side &&\n \ttest_commit def &&\n \tgit checkout main &&\n-\tgit worktree add worktree side\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add worktree side\n '\n \n test_rev_parse toplevel false false true '' .git \"$ROOT/.git\"\n@@ -113,7 +113,7 @@ test_expect_success 'rev-parse --path-format=absolute' '\n \ttest_one \".\" \"$ROOT/.git\" --path-format=absolute --git-common-dir &&\n \ttest_one \"sub/dir\" \"$ROOT/.git\" --path-format=absolute --git-dir &&\n \ttest_one \"sub/dir\" \"$ROOT/.git\" --path-format=absolute --git-common-dir &&\n-\ttest_one \"worktree\" \"$ROOT/.git/worktrees/worktree\" --path-format=absolute --git-dir &&\n+\ttest_one \"worktree\" \"$ROOT/.git/worktrees/worktree-123\" --path-format=absolute --git-dir &&\n \ttest_one \"worktree\" \"$ROOT/.git\" --path-format=absolute --git-common-dir &&\n \ttest_one \".\" \"$ROOT\" --path-format=absolute --show-toplevel &&\n \ttest_one \".\" \"$ROOT/.git/objects\" --path-format=absolute --git-path objects &&\n@@ -125,7 +125,7 @@ test_expect_success 'rev-parse --path-format=relative' '\n \ttest_one \".\" \".git\" --path-format=relative --git-common-dir &&\n \ttest_one \"sub/dir\" \"../../.git\" --path-format=relative --git-dir &&\n \ttest_one \"sub/dir\" \"../../.git\" --path-format=relative --git-common-dir &&\n-\ttest_one \"worktree\" \"../.git/worktrees/worktree\" --path-format=relative --git-dir &&\n+\ttest_one \"worktree\" \"../.git/worktrees/worktree-123\" --path-format=relative --git-dir &&\n \ttest_one \"worktree\" \"../.git\" --path-format=relative --git-common-dir &&\n \ttest_one \".\" \"./\" --path-format=relative --show-toplevel &&\n \ttest_one \".\" \".git/objects\" --path-format=relative --git-path objects &&\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex bc4f4e90d6ecfedbde9082bda6f9e4eec3e3575d..33262b49f18521c805f188a10f944dbfa9f285ba 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -71,21 +71,21 @@ test_expect_success '\"add\" worktree' '\n test_expect_success '\"add\" worktree with lock' '\n \tgit worktree add --detach --lock here-with-lock main &&\n \ttest_when_finished \"git worktree unlock here-with-lock || :\" &&\n-\ttest -f .git/worktrees/here-with-lock/locked\n+\ttest -f .git/worktrees/here-with-lock-*/locked\n '\n \n test_expect_success '\"add\" worktree with lock and reason' '\n \tlock_reason=\"why not\" &&\n \tgit worktree add --detach --lock --reason \"$lock_reason\" here-with-lock-reason main &&\n \ttest_when_finished \"git worktree unlock here-with-lock-reason || :\" &&\n-\ttest -f .git/worktrees/here-with-lock-reason/locked &&\n+\ttest -f .git/worktrees/here-with-lock-reason-*/locked &&\n \techo \"$lock_reason\" >expect &&\n-\ttest_cmp expect .git/worktrees/here-with-lock-reason/locked\n+\ttest_cmp expect .git/worktrees/here-with-lock-reason-*/locked\n '\n \n test_expect_success '\"add\" worktree with reason but no lock' '\n \ttest_must_fail git worktree add --detach --reason \"why not\" here-with-reason-only main &&\n-\ttest_path_is_missing .git/worktrees/here-with-reason-only/locked\n+\ttest_path_is_missing .git/worktrees/here-with-reason-only-*/locked\n '\n \n test_expect_success '\"add\" worktree from a subdir' '\n@@ -413,16 +413,16 @@ test_expect_success '\"add --orphan\" with empty repository' '\n test_expect_success '\"add\" worktree with orphan branch and lock' '\n \tgit worktree add --lock --orphan -b orphanbr orphan-with-lock &&\n \ttest_when_finished \"git worktree unlock orphan-with-lock || :\" &&\n-\ttest -f .git/worktrees/orphan-with-lock/locked\n+\ttest -f .git/worktrees/orphan-with-lock-*/locked\n '\n \n test_expect_success '\"add\" worktree with orphan branch, lock, and reason' '\n \tlock_reason=\"why not\" &&\n \tgit worktree add --detach --lock --reason \"$lock_reason\" orphan-with-lock-reason main &&\n \ttest_when_finished \"git worktree unlock orphan-with-lock-reason || :\" &&\n-\ttest -f .git/worktrees/orphan-with-lock-reason/locked &&\n+\ttest -f .git/worktrees/orphan-with-lock-reason-*/locked &&\n \techo \"$lock_reason\" >expect &&\n-\ttest_cmp expect .git/worktrees/orphan-with-lock-reason/locked\n+\ttest_cmp expect .git/worktrees/orphan-with-lock-reason-*/locked\n '\n \n # Note: Quoted arguments containing spaces are not supported.\n@@ -1088,10 +1088,10 @@ test_expect_success '\"add\" invokes post-checkout hook (branch)' '\n \tpost_checkout_hook &&\n \t{\n \t\techo $ZERO_OID $(git rev-parse HEAD) 1 &&\n-\t\techo $(pwd)/.git/worktrees/gumby &&\n+\t\techo $(pwd)/.git/worktrees/gumby-123 &&\n \t\techo $(pwd)/gumby\n \t} >hook.expect &&\n-\tgit worktree add gumby &&\n+\tGIT_TEST_WORKTREE_SUFFIX=\"123\" git worktree add gumby &&\n \ttest_cmp hook.expect gumby/hook.actual\n '\n \n@@ -1099,10 +1099,10 @@ test_expect_success '\"add\" invokes post-checkout hook (detached)' '\n \tpost_checkout_hook &&\n \t{\n \t\techo $ZERO_OID $(git rev-parse HEAD) 1 &&\n-\t\techo $(pwd)/.git/worktrees/grumpy &&\n+\t\techo $(pwd)/.git/worktrees/grumpy-456 &&\n \t\techo $(pwd)/grumpy\n \t} >hook.expect &&\n-\tgit worktree add --detach grumpy &&\n+\tGIT_TEST_WORKTREE_SUFFIX=\"456\" git worktree add --detach grumpy &&\n \ttest_cmp hook.expect grumpy/hook.actual\n '\n \n@@ -1117,10 +1117,10 @@ test_expect_success '\"add\" in other worktree invokes post-checkout hook' '\n \tpost_checkout_hook &&\n \t{\n \t\techo $ZERO_OID $(git rev-parse HEAD) 1 &&\n-\t\techo $(pwd)/.git/worktrees/guppy &&\n+\t\techo $(pwd)/.git/worktrees/guppy-789 &&\n \t\techo $(pwd)/guppy\n \t} >hook.expect &&\n-\tgit -C gloopy worktree add --detach ../guppy &&\n+\tGIT_TEST_WORKTREE_SUFFIX=\"789\" git -C gloopy worktree add --detach ../guppy &&\n \ttest_cmp hook.expect guppy/hook.actual\n '\n \n@@ -1129,11 +1129,11 @@ test_expect_success '\"add\" in bare repo invokes post-checkout hook' '\n \tgit clone --bare . bare &&\n \t{\n \t\techo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 &&\n-\t\techo $(pwd)/bare/worktrees/goozy &&\n+\t\techo $(pwd)/bare/worktrees/goozy-651 &&\n \t\techo $(pwd)/goozy\n \t} >hook.expect &&\n \tpost_checkout_hook bare &&\n-\tgit -C bare worktree add --detach ../goozy &&\n+\tGIT_TEST_WORKTREE_SUFFIX=\"651\" git -C bare worktree add --detach ../goozy &&\n \ttest_cmp hook.expect goozy/hook.actual\n '\n \n@@ -1165,8 +1165,9 @@ test_expect_success '\"add\" not tripped up by magic worktree matching\"' '\n '\n \n test_expect_success FUNNYNAMES 'sanitize generated worktree name' '\n-\tgit worktree add --detach \".  weird*..?.lock.lock\" &&\n-\ttest -d .git/worktrees/---weird-.-\n+\tGIT_TEST_WORKTREE_SUFFIX=\"1234\" \\\n+\t\tgit worktree add --detach \".  weird*..?.lock.lock\" &&\n+\ttest -d .git/worktrees/---weird-.--1234\n '\n \n test_expect_success '\"add\" should not fail because of another bad worktree' '\n@@ -1210,23 +1211,23 @@ test_expect_success '\"add\" with initialized submodule, with submodule.recurse se\n test_expect_success 'can create worktrees with relative paths' '\n \ttest_when_finished \"git worktree remove relative\" &&\n \ttest_config worktree.useRelativePaths false &&\n-\tgit worktree add --relative-paths ./relative &&\n-\techo \"gitdir: ../.git/worktrees/relative\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add --relative-paths ./relative &&\n+\techo \"gitdir: ../.git/worktrees/relative-123\" >expect &&\n \ttest_cmp expect relative/.git &&\n \techo \"../../../relative/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/relative/gitdir\n+\ttest_cmp expect .git/worktrees/relative-123/gitdir\n '\n \n test_expect_success 'can create worktrees with absolute paths' '\n \ttest_config worktree.useRelativePaths true &&\n-\tgit worktree add ./relative &&\n-\techo \"gitdir: ../.git/worktrees/relative\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add ./relative &&\n+\techo \"gitdir: ../.git/worktrees/relative-123\" >expect &&\n \ttest_cmp expect relative/.git &&\n-\tgit worktree add --no-relative-paths ./absolute &&\n-\techo \"gitdir: $(pwd)/.git/worktrees/absolute\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git worktree add --no-relative-paths ./absolute &&\n+\techo \"gitdir: $(pwd)/.git/worktrees/absolute-456\" >expect &&\n \ttest_cmp expect absolute/.git &&\n \techo \"$(pwd)/absolute/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute/gitdir\n+\ttest_cmp expect .git/worktrees/absolute-456/gitdir\n '\n \n test_expect_success 'move repo without breaking relative internal links' '\ndiff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh\nindex 5eb52b9abbf29514dc082c260ebb7a5e8e63aae0..856fcdd19d376d3448ee0be46592fe68d44617c4 100755\n--- a/t/t2401-worktree-prune.sh\n+++ b/t/t2401-worktree-prune.sh\n@@ -83,29 +83,29 @@ test_expect_success 'not prune locked checkout' '\n test_expect_success 'not prune recent checkouts' '\n \ttest_when_finished rm -r .git/worktrees &&\n \tgit worktree add jlm HEAD &&\n-\ttest -d .git/worktrees/jlm &&\n+\ttest -d .git/worktrees/jlm-* &&\n \trm -rf jlm &&\n \tgit worktree prune --verbose --expire=2.days.ago &&\n-\ttest -d .git/worktrees/jlm\n+\ttest -d .git/worktrees/jlm-*\n '\n \n test_expect_success 'not prune proper checkouts' '\n \ttest_when_finished rm -r .git/worktrees &&\n \tgit worktree add --detach \"$PWD/nop\" main &&\n \tgit worktree prune &&\n-\ttest -d .git/worktrees/nop\n+\ttest -d .git/worktrees/nop-*\n '\n \n test_expect_success 'prune duplicate (linked/linked)' '\n \ttest_when_finished rm -fr .git/worktrees w1 w2 &&\n-\tgit worktree add --detach w1 &&\n-\tgit worktree add --detach w2 &&\n-\tsed \"s/w2/w1/\" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&\n-\tmv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&\n+\tGIT_TEST_WORKTREE_SUFFIX=1 git worktree add --detach w1 &&\n+\tGIT_TEST_WORKTREE_SUFFIX=2 git worktree add --detach w2 &&\n+\tsed \"s/w2/w1/\" .git/worktrees/w2-2/gitdir >.git/worktrees/w2-2/gitdir.new &&\n+\tmv .git/worktrees/w2-2/gitdir.new .git/worktrees/w2-2/gitdir &&\n \tgit worktree prune --verbose 2>actual &&\n \ttest_grep \"duplicate entry\" actual &&\n-\ttest -d .git/worktrees/w1 &&\n-\t! test -d .git/worktrees/w2\n+\ttest -d .git/worktrees/w1-1 &&\n+\t! test -d .git/worktrees/w2-2\n '\n \n test_expect_success 'prune duplicate (main/linked)' '\n@@ -117,7 +117,7 @@ test_expect_success 'prune duplicate (main/linked)' '\n \tmv repo wt &&\n \tgit -C wt worktree prune --verbose 2>actual &&\n \ttest_grep \"duplicate entry\" actual &&\n-\t! test -d .git/worktrees/wt\n+\t! test -d .git/worktrees/wt-*\n '\n \n test_expect_success 'not prune proper worktrees inside linked worktree with relative paths' '\ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex 422c1a05580057b18ab8bfdfe38da4d723749493..ba3f05c16a4969fb84d98052ae375ef162f3e73a 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -24,27 +24,27 @@ test_expect_success 'lock main worktree' '\n test_expect_success 'lock linked worktree' '\n \tgit worktree lock --reason hahaha source &&\n \techo hahaha >expected &&\n-\ttest_cmp expected .git/worktrees/source/locked\n+\ttest_cmp expected .git/worktrees/source-*/locked\n '\n \n test_expect_success 'lock linked worktree from another worktree' '\n-\trm .git/worktrees/source/locked &&\n+\trm .git/worktrees/source-*/locked &&\n \tgit worktree add elsewhere &&\n \tgit -C elsewhere worktree lock --reason hahaha ../source &&\n \techo hahaha >expected &&\n-\ttest_cmp expected .git/worktrees/source/locked\n+\ttest_cmp expected .git/worktrees/source-*/locked\n '\n \n test_expect_success 'lock worktree twice' '\n \ttest_must_fail git worktree lock source &&\n \techo hahaha >expected &&\n-\ttest_cmp expected .git/worktrees/source/locked\n+\ttest_cmp expected .git/worktrees/source-*/locked\n '\n \n test_expect_success 'lock worktree twice (from the locked worktree)' '\n \ttest_must_fail git -C source worktree lock . &&\n \techo hahaha >expected &&\n-\ttest_cmp expected .git/worktrees/source/locked\n+\ttest_cmp expected .git/worktrees/source-*/locked\n '\n \n test_expect_success 'unlock main worktree' '\n@@ -183,19 +183,19 @@ test_expect_success 'force remove worktree with untracked file' '\n \n test_expect_success 'remove missing worktree' '\n \tgit worktree add to-be-gone &&\n-\ttest -d .git/worktrees/to-be-gone &&\n+\ttest -d .git/worktrees/to-be-gone-* &&\n \tmv to-be-gone gone &&\n \tgit worktree remove to-be-gone &&\n-\ttest_path_is_missing .git/worktrees/to-be-gone\n+\ttest_path_is_missing .git/worktrees/to-be-gone-*\n '\n \n test_expect_success 'NOT remove missing-but-locked worktree' '\n \tgit worktree add gone-but-locked &&\n \tgit worktree lock gone-but-locked &&\n-\ttest -d .git/worktrees/gone-but-locked &&\n+\ttest -d .git/worktrees/gone-but-locked-* &&\n \tmv gone-but-locked really-gone-now &&\n \ttest_must_fail git worktree remove gone-but-locked &&\n-\ttest_path_is_dir .git/worktrees/gone-but-locked\n+\ttest_path_is_dir .git/worktrees/gone-but-locked-*\n '\n \n test_expect_success 'proper error when worktree not found' '\n@@ -249,27 +249,27 @@ test_expect_success 'not remove a repo with initialized submodule' '\n \n test_expect_success 'move worktree with absolute path to relative path' '\n \ttest_config worktree.useRelativePaths false &&\n-\tgit worktree add ./absolute &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add ./absolute &&\n \tgit worktree move --relative-paths absolute relative &&\n-\techo \"gitdir: ../.git/worktrees/absolute\" >expect &&\n+\techo \"gitdir: ../.git/worktrees/absolute-123\" >expect &&\n \ttest_cmp expect relative/.git &&\n \techo \"../../../relative/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute/gitdir &&\n+\ttest_cmp expect .git/worktrees/absolute-123/gitdir &&\n \ttest_config worktree.useRelativePaths true &&\n \tgit worktree move relative relative2 &&\n-\techo \"gitdir: ../.git/worktrees/absolute\" >expect &&\n+\techo \"gitdir: ../.git/worktrees/absolute-123\" >expect &&\n \ttest_cmp expect relative2/.git &&\n \techo \"../../../relative2/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute/gitdir\n+\ttest_cmp expect .git/worktrees/absolute-123/gitdir\n '\n \n test_expect_success 'move worktree with relative path to absolute path' '\n \ttest_config worktree.useRelativePaths true &&\n \tgit worktree move --no-relative-paths relative2 absolute &&\n-\techo \"gitdir: $(pwd)/.git/worktrees/absolute\" >expect &&\n+\techo \"gitdir: $(pwd)/.git/worktrees/absolute-123\" >expect &&\n \ttest_cmp expect absolute/.git &&\n \techo \"$(pwd)/absolute/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute/gitdir\n+\ttest_cmp expect .git/worktrees/absolute-123/gitdir\n '\n \n test_done\ndiff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh\nindex 1d7f60563387f9c2f53dfc3a79ac0289afe57611..5479b2a74aa0d9b1e8880ed7c038307ffa1d0c54 100755\n--- a/t/t2405-worktree-submodule.sh\n+++ b/t/t2405-worktree-submodule.sh\n@@ -9,6 +9,7 @@ TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n base_path=$(pwd -P)\n+suffix=4567\n \n test_expect_success 'setup: create origin repos'  '\n \tgit config --global protocol.file.allow always &&\n@@ -61,9 +62,10 @@ test_expect_success 'submodule is checked out after manually adding submodule wo\n '\n \n test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules in a linked worktree' '\n-\tgit -C main worktree add \"$base_path/checkout-recurse\" --detach  &&\n+\tGIT_TEST_WORKTREE_SUFFIX=$suffix \\\n+\t\tgit -C main worktree add \"$base_path/checkout-recurse\" --detach  &&\n \tgit -C checkout-recurse submodule update --init &&\n-\techo \"gitdir: ../../main/.git/worktrees/checkout-recurse/modules/sub\" >expect-gitfile &&\n+\techo \"gitdir: ../../main/.git/worktrees/checkout-recurse-$suffix/modules/sub\" >expect-gitfile &&\n \tcat checkout-recurse/sub/.git >actual-gitfile &&\n \ttest_cmp expect-gitfile actual-gitfile &&\n \tgit -C main/sub rev-parse HEAD >expect-head-main &&\n@@ -82,14 +84,14 @@ test_expect_success 'core.worktree is removed in $GIT_DIR/modules/<name>/config,\n \tgit -C checkout-recurse/sub config --get core.worktree >actual-linked &&\n \ttest_cmp expect-linked actual-linked &&\n \tgit -C checkout-recurse checkout --recurse-submodules first &&\n-\ttest_expect_code 1 git -C main/.git/worktrees/checkout-recurse/modules/sub config --get core.worktree >linked-config &&\n+\ttest_expect_code 1 git -C main/.git/worktrees/checkout-recurse-$suffix/modules/sub config --get core.worktree >linked-config &&\n \ttest_must_be_empty linked-config &&\n \tgit -C main/sub config --get core.worktree >actual-main &&\n \ttest_cmp expect-main actual-main\n '\n \n test_expect_success 'unsetting core.worktree does not prevent running commands directly against the submodule repository' '\n-\tgit -C main/.git/worktrees/checkout-recurse/modules/sub log\n+\tgit -C main/.git/worktrees/checkout-recurse-$suffix/modules/sub log\n '\n \n test_done\ndiff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh\nindex 49b70b999518d47e1edd72a61a847b427f4c67a1..49d020f5fe786014ddc428bcb74cb706f8cef3d1 100755\n--- a/t/t2406-worktree-repair.sh\n+++ b/t/t2406-worktree-repair.sh\n@@ -106,8 +106,8 @@ test_expect_success 'repo not found; .git not file' '\n \n test_expect_success 'repo not found; .git not referencing repo' '\n \ttest_when_finished \"rm -rf side not-a-repo && git worktree prune\" &&\n-\tgit worktree add --detach side &&\n-\tsed s,\\.git/worktrees/side$,not-a-repo, side/.git >side/.newgit &&\n+\tGIT_TEST_WORKTREE_SUFFIX=1234 git worktree add --detach side &&\n+\tsed s,\\.git/worktrees/side-1234$,not-a-repo, side/.git >side/.newgit &&\n \tmv side/.newgit side/.git &&\n \tmkdir not-a-repo &&\n \ttest_must_fail git worktree repair side 2>err &&\n@@ -127,41 +127,41 @@ test_expect_success 'repo not found; .git file broken' '\n test_expect_success 'repair broken gitdir' '\n \ttest_when_finished \"rm -rf orig moved && git worktree prune\" &&\n \tgit worktree add --detach orig &&\n-\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&\n-\trm .git/worktrees/orig/gitdir &&\n+\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig-*/gitdir >expect &&\n+\trm .git/worktrees/orig-*/gitdir &&\n \tmv orig moved &&\n \tgit worktree repair moved 2>err &&\n-\ttest_cmp expect .git/worktrees/orig/gitdir &&\n+\ttest_cmp expect .git/worktrees/orig-*/gitdir &&\n \ttest_grep \"gitdir unreadable\" err\n '\n \n test_expect_success 'repair incorrect gitdir' '\n \ttest_when_finished \"rm -rf orig moved && git worktree prune\" &&\n \tgit worktree add --detach orig &&\n-\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&\n+\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig-*/gitdir >expect &&\n \tmv orig moved &&\n \tgit worktree repair moved 2>err &&\n-\ttest_cmp expect .git/worktrees/orig/gitdir &&\n+\ttest_cmp expect .git/worktrees/orig-*/gitdir &&\n \ttest_grep \"gitdir incorrect\" err\n '\n \n test_expect_success 'repair gitdir (implicit) from linked worktree' '\n \ttest_when_finished \"rm -rf orig moved && git worktree prune\" &&\n \tgit worktree add --detach orig &&\n-\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&\n+\tsed s,orig/\\.git$,moved/.git, .git/worktrees/orig-*/gitdir >expect &&\n \tmv orig moved &&\n \tgit -C moved worktree repair 2>err &&\n-\ttest_cmp expect .git/worktrees/orig/gitdir &&\n+\ttest_cmp expect .git/worktrees/orig-*/gitdir &&\n \ttest_grep \"gitdir incorrect\" err\n '\n \n test_expect_success 'unable to repair gitdir (implicit) from main worktree' '\n \ttest_when_finished \"rm -rf orig moved && git worktree prune\" &&\n \tgit worktree add --detach orig &&\n-\tcat .git/worktrees/orig/gitdir >expect &&\n+\tcat .git/worktrees/orig-*/gitdir >expect &&\n \tmv orig moved &&\n \tgit worktree repair 2>err &&\n-\ttest_cmp expect .git/worktrees/orig/gitdir &&\n+\ttest_cmp expect .git/worktrees/orig-*/gitdir &&\n \ttest_must_be_empty err\n '\n \n@@ -170,15 +170,15 @@ test_expect_success 'repair multiple gitdir files' '\n \t\tgit worktree prune\" &&\n \tgit worktree add --detach orig1 &&\n \tgit worktree add --detach orig2 &&\n-\tsed s,orig1/\\.git$,moved1/.git, .git/worktrees/orig1/gitdir >expect1 &&\n-\tsed s,orig2/\\.git$,moved2/.git, .git/worktrees/orig2/gitdir >expect2 &&\n+\tsed s,orig1/\\.git$,moved1/.git, .git/worktrees/orig1-*/gitdir >expect1 &&\n+\tsed s,orig2/\\.git$,moved2/.git, .git/worktrees/orig2-*/gitdir >expect2 &&\n \tmv orig1 moved1 &&\n \tmv orig2 moved2 &&\n \tgit worktree repair moved1 moved2 2>err &&\n-\ttest_cmp expect1 .git/worktrees/orig1/gitdir &&\n-\ttest_cmp expect2 .git/worktrees/orig2/gitdir &&\n-\ttest_grep \"gitdir incorrect:.*orig1/gitdir$\" err &&\n-\ttest_grep \"gitdir incorrect:.*orig2/gitdir$\" err\n+\ttest_cmp expect1 .git/worktrees/orig1-*/gitdir &&\n+\ttest_cmp expect2 .git/worktrees/orig2-*/gitdir &&\n+\ttest_grep \"gitdir incorrect:.*orig1-.*/gitdir$\" err &&\n+\ttest_grep \"gitdir incorrect:.*orig2-.*/gitdir$\" err\n '\n \n test_expect_success 'repair moved main and linked worktrees' '\n@@ -186,14 +186,12 @@ test_expect_success 'repair moved main and linked worktrees' '\n \ttest_create_repo main &&\n \ttest_commit -C main init &&\n \tgit -C main worktree add --detach ../side &&\n-\tsed \"s,side/\\.git$,sidemoved/.git,\" \\\n-\t\tmain/.git/worktrees/side/gitdir >expect-gitdir &&\n-\tsed \"s,main/.git/worktrees/side$,mainmoved/.git/worktrees/side,\" \\\n-\t\tside/.git >expect-gitfile &&\n+\tsed \"s,side,sidemoved,\" main/.git/worktrees/side-*/gitdir >expect-gitdir &&\n+\tsed \"s,main,mainmoved,\" side/.git >expect-gitfile &&\n \tmv main mainmoved &&\n \tmv side sidemoved &&\n \tgit -C mainmoved worktree repair ../sidemoved &&\n-\ttest_cmp expect-gitdir mainmoved/.git/worktrees/side/gitdir &&\n+\ttest_cmp expect-gitdir mainmoved/.git/worktrees/side-*/gitdir &&\n \ttest_cmp expect-gitfile sidemoved/.git\n '\n \n@@ -203,16 +201,15 @@ test_expect_success 'repair copied main and linked worktrees' '\n \tgit -C orig init main &&\n \ttest_commit -C orig/main nothing &&\n \tgit -C orig/main worktree add ../linked &&\n-\tcp orig/main/.git/worktrees/linked/gitdir orig/main.expect &&\n+\tcp orig/main/.git/worktrees/linked-*/gitdir orig/main.expect &&\n \tcp orig/linked/.git orig/linked.expect &&\n \tcp -R orig dup &&\n \tsed \"s,orig/linked/\\.git$,dup/linked/.git,\" orig/main.expect >dup/main.expect &&\n-\tsed \"s,orig/main/\\.git/worktrees/linked$,dup/main/.git/worktrees/linked,\" \\\n-\t\torig/linked.expect >dup/linked.expect &&\n+\tsed \"s,orig,dup,\" orig/linked.expect >dup/linked.expect &&\n \tgit -C dup/main worktree repair ../linked &&\n-\ttest_cmp orig/main.expect orig/main/.git/worktrees/linked/gitdir &&\n+\ttest_cmp orig/main.expect orig/main/.git/worktrees/linked-*/gitdir &&\n \ttest_cmp orig/linked.expect orig/linked/.git &&\n-\ttest_cmp dup/main.expect dup/main/.git/worktrees/linked/gitdir &&\n+\ttest_cmp dup/main.expect dup/main/.git/worktrees/linked-*/gitdir &&\n \ttest_cmp dup/linked.expect dup/linked/.git\n '\n \n@@ -221,11 +218,11 @@ test_expect_success 'repair worktree with relative path with missing gitfile' '\n \ttest_create_repo main &&\n \tgit -C main config worktree.useRelativePaths true &&\n \ttest_commit -C main init &&\n-\tgit -C main worktree add --detach ../wt &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git -C main worktree add --detach ../wt &&\n \trm wt/.git &&\n \ttest_path_is_missing wt/.git &&\n \tgit -C main worktree repair &&\n-\techo \"gitdir: ../main/.git/worktrees/wt\" >expect &&\n+\techo \"gitdir: ../main/.git/worktrees/wt-123\" >expect &&\n \ttest_cmp expect wt/.git\n '\n \n@@ -233,12 +230,12 @@ test_expect_success 'repair absolute worktree to use relative paths' '\n \ttest_when_finished \"rm -rf main side sidemoved\" &&\n \ttest_create_repo main &&\n \ttest_commit -C main init &&\n-\tgit -C main worktree add --detach ../side &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git -C main worktree add --detach ../side &&\n \techo \"../../../../sidemoved/.git\" >expect-gitdir &&\n-\techo \"gitdir: ../main/.git/worktrees/side\" >expect-gitfile &&\n+\techo \"gitdir: ../main/.git/worktrees/side-456\" >expect-gitfile &&\n \tmv side sidemoved &&\n \tgit -C main worktree repair --relative-paths ../sidemoved &&\n-\ttest_cmp expect-gitdir main/.git/worktrees/side/gitdir &&\n+\ttest_cmp expect-gitdir main/.git/worktrees/side-456/gitdir &&\n \ttest_cmp expect-gitfile sidemoved/.git\n '\n \n@@ -246,13 +243,39 @@ test_expect_success 'repair relative worktree to use absolute paths' '\n \ttest_when_finished \"rm -rf main side sidemoved\" &&\n \ttest_create_repo main &&\n \ttest_commit -C main init &&\n-\tgit -C main worktree add --relative-paths --detach ../side &&\n+\tGIT_TEST_WORKTREE_SUFFIX=789 git -C main worktree add --relative-paths --detach ../side &&\n \techo \"$(pwd)/sidemoved/.git\" >expect-gitdir &&\n-\techo \"gitdir: $(pwd)/main/.git/worktrees/side\" >expect-gitfile &&\n+\techo \"gitdir: $(pwd)/main/.git/worktrees/side-789\" >expect-gitfile &&\n \tmv side sidemoved &&\n \tgit -C main worktree repair ../sidemoved &&\n-\ttest_cmp expect-gitdir main/.git/worktrees/side/gitdir &&\n+\ttest_cmp expect-gitdir main/.git/worktrees/side-789/gitdir &&\n \ttest_cmp expect-gitfile sidemoved/.git\n '\n \n+test_expect_success 'does not repair worktrees from another repo' '\n+\ttest_when_finished \"rm -rf repo1 repo2\" &&\n+\tmkdir -p repo1 &&\n+\tgit -C repo1 init main &&\n+\ttest_commit -C repo1/main nothing &&\n+\tgit -C repo1/main worktree add ../linked &&\n+\tcp repo1/main/.git/worktrees/linked-*/gitdir repo1/main.expect &&\n+\tcp repo1/linked/.git repo1/linked.expect &&\n+\tmkdir -p repo2 &&\n+\tgit -C repo2 init main &&\n+\ttest_commit -C repo2/main nothing &&\n+\tgit -C repo2/main worktree add ../linked &&\n+\tcp repo2/main/.git/worktrees/linked-*/gitdir repo2/main.expect &&\n+\tcp repo2/linked/.git repo2/linked.expect &&\n+\tgit -C repo1/main worktree repair ../../repo2/linked &&\n+\ttest_cmp repo1/main.expect repo1/main/.git/worktrees/linked-*/gitdir &&\n+\ttest_cmp repo1/linked.expect repo1/linked/.git &&\n+\ttest_cmp repo2/main.expect repo2/main/.git/worktrees/linked-*/gitdir &&\n+\ttest_cmp repo2/linked.expect repo2/linked/.git &&\n+\tgit -C repo2/main worktree repair ../../repo1/linked &&\n+\ttest_cmp repo1/main.expect repo1/main/.git/worktrees/linked-*/gitdir &&\n+\ttest_cmp repo1/linked.expect repo1/linked/.git &&\n+\ttest_cmp repo2/main.expect repo2/main/.git/worktrees/linked-*/gitdir &&\n+\ttest_cmp repo2/linked.expect repo2/linked/.git\n+'\n+\n test_done\ndiff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh\nindex f6835c91dcc49cfeb23881fe0ef7a96629bfb2e6..1587dadfd1e1fa122edccc62ffd9aa4c20f0ec80 100755\n--- a/t/t2407-worktree-heads.sh\n+++ b/t/t2407-worktree-heads.sh\n@@ -19,7 +19,8 @@ test_expect_success 'setup' '\n \t\ttest_commit $i &&\n \t\tgit branch wt-$i &&\n \t\tgit branch fake-$i &&\n-\t\tgit worktree add wt-$i wt-$i || return 1\n+\t\tGIT_TEST_WORKTREE_SUFFIX=$i \\\n+\t\t\tgit worktree add wt-$i wt-$i || return 1\n \tdone &&\n \n \t# Create a server that updates each branch by one commit\n@@ -132,20 +133,20 @@ test_expect_success 'refuse to overwrite when in error states' '\n \ttest_when_finished rm -rf .git/worktrees/wt-*/BISECT_* &&\n \n \t# Both branches are currently under rebase.\n-\tmkdir -p .git/worktrees/wt-3/rebase-merge &&\n-\ttouch .git/worktrees/wt-3/rebase-merge/interactive &&\n-\techo refs/heads/fake-1 >.git/worktrees/wt-3/rebase-merge/head-name &&\n-\techo refs/heads/fake-2 >.git/worktrees/wt-3/rebase-merge/onto &&\n-\tmkdir -p .git/worktrees/wt-4/rebase-merge &&\n-\ttouch .git/worktrees/wt-4/rebase-merge/interactive &&\n-\techo refs/heads/fake-2 >.git/worktrees/wt-4/rebase-merge/head-name &&\n-\techo refs/heads/fake-1 >.git/worktrees/wt-4/rebase-merge/onto &&\n+\tmkdir -p .git/worktrees/wt-3-3/rebase-merge &&\n+\ttouch .git/worktrees/wt-3-3/rebase-merge/interactive &&\n+\techo refs/heads/fake-1 >.git/worktrees/wt-3-3/rebase-merge/head-name &&\n+\techo refs/heads/fake-2 >.git/worktrees/wt-3-3/rebase-merge/onto &&\n+\tmkdir -p .git/worktrees/wt-4-4/rebase-merge &&\n+\ttouch .git/worktrees/wt-4-4/rebase-merge/interactive &&\n+\techo refs/heads/fake-2 >.git/worktrees/wt-4-4/rebase-merge/head-name &&\n+\techo refs/heads/fake-1 >.git/worktrees/wt-4-4/rebase-merge/onto &&\n \n \t# Both branches are currently under bisect.\n-\ttouch .git/worktrees/wt-4/BISECT_LOG &&\n-\techo refs/heads/fake-2 >.git/worktrees/wt-4/BISECT_START &&\n-\ttouch .git/worktrees/wt-1/BISECT_LOG &&\n-\techo refs/heads/fake-1 >.git/worktrees/wt-1/BISECT_START &&\n+\ttouch .git/worktrees/wt-4-4/BISECT_LOG &&\n+\techo refs/heads/fake-2 >.git/worktrees/wt-4-4/BISECT_START &&\n+\ttouch .git/worktrees/wt-1-1/BISECT_LOG &&\n+\techo refs/heads/fake-1 >.git/worktrees/wt-1-1/BISECT_START &&\n \n \tfor i in 1 2\n \tdo\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex ccfa6a720d090c2f7f2a085f60065bdcfaf8d1d9..e44497ac94394119662115b1f6aa035c7f0565d2 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -246,14 +246,14 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou\n '\n \n test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '\n-\tgit worktree add -b baz bazdir1 &&\n-\tgit worktree add -f bazdir2 baz &&\n-\ttouch .git/worktrees/bazdir1/HEAD.lock &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add -b baz bazdir1 &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git worktree add -f bazdir2 baz &&\n+\ttouch .git/worktrees/bazdir1-123/HEAD.lock &&\n \ttest_must_fail git branch -M baz bam &&\n \ttest $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&\n \tgit branch -M bam baz &&\n-\trm .git/worktrees/bazdir1/HEAD.lock &&\n-\ttouch .git/worktrees/bazdir2/HEAD.lock &&\n+\trm .git/worktrees/bazdir1-123/HEAD.lock &&\n+\ttouch .git/worktrees/bazdir2-456/HEAD.lock &&\n \ttest_must_fail git branch -M baz bam &&\n \ttest $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&\n \trm -rf bazdir1 bazdir2 &&\ndiff --git a/t/t5304-prune.sh b/t/t5304-prune.sh\nindex e641df0116c24404e4892a0e30af4ef4bf8db493..0e98c6627a98ed197d7ab1ff41e8dd41eeaff3ac 100755\n--- a/t/t5304-prune.sh\n+++ b/t/t5304-prune.sh\n@@ -293,7 +293,7 @@ test_expect_success 'prune: handle HEAD in multiple worktrees' '\n \techo \"new blob for third-worktree\" >third-worktree/blob &&\n \tgit -C third-worktree add blob &&\n \tgit -C third-worktree commit -m \"third\" &&\n-\trm .git/worktrees/third-worktree/index &&\n+\trm .git/worktrees/third-worktree-*/index &&\n \ttest_must_fail git -C third-worktree show :blob &&\n \tgit prune --expire=now &&\n \tgit -C third-worktree show HEAD:blob >actual &&\ndiff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh\nindex f77832185765585e2bda1677f8cbbe13841127f7..acf9544e35966f054cbc90c37e84377c9f461f2b 100755\n--- a/t/t7412-submodule-absorbgitdirs.sh\n+++ b/t/t7412-submodule-absorbgitdirs.sh\n@@ -123,7 +123,7 @@ test_expect_success 'absorb the git dir outside of primary worktree' '\n \ttest_when_finished \"rm -rf repo-bare.git\" &&\n \tgit clone --bare . repo-bare.git &&\n \ttest_when_finished \"rm -rf repo-wt\" &&\n-\tgit -C repo-bare.git worktree add ../repo-wt &&\n+\tGIT_TEST_WORKTREE_SUFFIX=123 git -C repo-bare.git worktree add ../repo-wt &&\n \n \ttest_when_finished \"rm -f .gitconfig\" &&\n \ttest_config_global protocol.file.allow always &&\n@@ -134,7 +134,7 @@ test_expect_success 'absorb the git dir outside of primary worktree' '\n \tcat >expect <<-EOF &&\n \tMigrating git directory of '\\''sub2'\\'' from\n \t'\\''$cwd/repo-wt/sub2/.git'\\'' to\n-\t'\\''$cwd/repo-bare.git/worktrees/repo-wt/modules/sub2'\\''\n+\t'\\''$cwd/repo-bare.git/worktrees/repo-wt-123/modules/sub2'\\''\n \tEOF\n \tgit -C repo-wt submodule absorbgitdirs 2>actual &&\n \ttest_cmp expect actual\n\n-- \n2.47.0\n\n\n"},{"id":"508316","messageId":"20241128-wt_unique_ids-v1-2-30345d010e43@pm.me","threadId":"62561","inReplyTo":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","subject":"[PATCH 2/2] worktree: rename worktree id during worktree move","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T02:44:40Z","receivedAt":"2024-11-29T02:44:54Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"During a `worktree move` the worktree directory is moved/renamed but the\nrepository under `worktrees/<id>` is not updated. For example, given the\nfollowing structure:\n\n    foo/\n    ├── .git/worktrees/develop-5445874156/\n    └── develop/\n\nmoving `develop` to `master` results in\n\n    foo/\n    ├── .git/worktrees/develop-5445874156/\n    └── master/\n\nThis works because the linking files still point to the correct\nrepository, but this is a little weird. This teaches Git to also\nmove/rename the repository / worktree id during a `move` so that the\nstructure now looks like:\n\n    foo/\n    ├── .git/worktrees/master-1565465986/\n    └── master/\n\nNote that a new unique suffix is assigned to reduce the complexity of\ntrying to parse and reuse the existing suffix.\n\nSigned-off-by: Caleb White <cdwhite3@pm.me>\n---\n builtin/worktree.c       | 24 ++++++++++++++++++++++++\n t/t2403-worktree-move.sh | 18 +++++++++---------\n 2 files changed, 33 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 3ad355ca762729401fc0c8625f4fd05b154a84ec..36235546b492803707707ff208b13fe777bff1b4 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -1202,9 +1202,14 @@ static int move_worktree(int ac, const char **av, const char *prefix)\n \t};\n \tstruct worktree **worktrees, *wt;\n \tstruct strbuf dst = STRBUF_INIT;\n+\tstruct strbuf repo = STRBUF_INIT;\n+\tstruct strbuf repo_dst = STRBUF_INIT;\n \tstruct strbuf errmsg = STRBUF_INIT;\n \tconst char *reason = NULL;\n+\tconst char *new_id;\n+\tconst char *suffix;\n \tchar *path;\n+\tint len;\n \n \tac = parse_options(ac, av, prefix, options, git_worktree_move_usage,\n \t\t\t   0);\n@@ -1250,9 +1255,28 @@ static int move_worktree(int ac, const char **av, const char *prefix)\n \tif (rename(wt->path, dst.buf) == -1)\n \t\tdie_errno(_(\"failed to move '%s' to '%s'\"), wt->path, dst.buf);\n \n+\tstrbuf_realpath(&repo, git_common_path(\"worktrees/%s\", wt->id), 1);\n+\tnew_id = worktree_basename(dst.buf, &len);\n+\tstrbuf_add(&repo_dst, new_id, dst.buf + len - new_id);\n+\tstrbuf_realpath(&repo_dst, git_common_path(\"worktrees/%s\", repo_dst.buf), 1);\n+\tsuffix = getenv(\"GIT_TEST_WORKTREE_SUFFIX\");\n+\tif (suffix)\n+\t\tstrbuf_addf(&repo_dst, \"-%s\", suffix);\n+\telse\n+\t\tstrbuf_addf(&repo_dst, \"-%u\", git_rand());\n+\tnew_id = strrchr(repo_dst.buf, '/') + 1;\n+\tif (rename(repo.buf, repo_dst.buf) == -1)\n+\t\tdie_errno(_(\"failed to move '%s' to '%s'\"), repo.buf, repo_dst.buf);\n+\telse {\n+\t\tfree(wt->id);\n+\t\twt->id = xstrdup(new_id);\n+\t}\n+\n \tupdate_worktree_location(wt, dst.buf, use_relative_paths);\n \n \tstrbuf_release(&dst);\n+\tstrbuf_release(&repo);\n+\tstrbuf_release(&repo_dst);\n \tfree_worktrees(worktrees);\n \treturn 0;\n }\ndiff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh\nindex ba3f05c16a4969fb84d98052ae375ef162f3e73a..703aa58d10643e99ffaf803aa38dabfd4af68a10 100755\n--- a/t/t2403-worktree-move.sh\n+++ b/t/t2403-worktree-move.sh\n@@ -250,26 +250,26 @@ test_expect_success 'not remove a repo with initialized submodule' '\n test_expect_success 'move worktree with absolute path to relative path' '\n \ttest_config worktree.useRelativePaths false &&\n \tGIT_TEST_WORKTREE_SUFFIX=123 git worktree add ./absolute &&\n-\tgit worktree move --relative-paths absolute relative &&\n-\techo \"gitdir: ../.git/worktrees/absolute-123\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=456 git worktree move --relative-paths absolute relative &&\n+\techo \"gitdir: ../.git/worktrees/relative-456\" >expect &&\n \ttest_cmp expect relative/.git &&\n \techo \"../../../relative/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute-123/gitdir &&\n+\ttest_cmp expect .git/worktrees/relative-456/gitdir &&\n \ttest_config worktree.useRelativePaths true &&\n-\tgit worktree move relative relative2 &&\n-\techo \"gitdir: ../.git/worktrees/absolute-123\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=789 git worktree move relative relative2 &&\n+\techo \"gitdir: ../.git/worktrees/relative2-789\" >expect &&\n \ttest_cmp expect relative2/.git &&\n \techo \"../../../relative2/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute-123/gitdir\n+\ttest_cmp expect .git/worktrees/relative2-789/gitdir\n '\n \n test_expect_success 'move worktree with relative path to absolute path' '\n \ttest_config worktree.useRelativePaths true &&\n-\tgit worktree move --no-relative-paths relative2 absolute &&\n-\techo \"gitdir: $(pwd)/.git/worktrees/absolute-123\" >expect &&\n+\tGIT_TEST_WORKTREE_SUFFIX=851 git worktree move --no-relative-paths relative2 absolute &&\n+\techo \"gitdir: $(pwd)/.git/worktrees/absolute-851\" >expect &&\n \ttest_cmp expect absolute/.git &&\n \techo \"$(pwd)/absolute/.git\" >expect &&\n-\ttest_cmp expect .git/worktrees/absolute-123/gitdir\n+\ttest_cmp expect .git/worktrees/absolute-851/gitdir\n '\n \n test_done\n\n-- \n2.47.0\n\n\n"},{"id":"508317","messageId":"D5YBJRUXYLUR.8TB0DXCJ6654@pm.me","threadId":"62561","inReplyTo":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T02:49:32Z","receivedAt":"2024-11-29T02:49:44Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Thu Nov 28, 2024 at 8:44 PM CST, Caleb White wrote:\n> The `es/worktree-repair-copied` topic added support for repairing a\n> worktree from a copy scenario. I noted[1,2] that the topic added the\n> ability for a repository to \"take over\" a worktree from another\n> repository if the worktree_id matched a worktree inside the current\n> repository which can happen if two repositories use the same worktree name.\n>\n> This series teaches Git to create worktrees with a unique suffix so\n> that the worktree_id is unique across all repositories even if they have\n> the same name. For example creating a worktree `develop` would look like:\n>\n>     foo/\n>     ├── .git/worktrees/develop-5445874156/\n>     └── develop/\n>     bar/\n>     ├── .git/worktrees/develop-1549518426/\n>     └── develop/\n>\n> The actual worktree directory name is still `develop`, but the\n> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n> is given by the `git_rand()` function, but I'm open to suggestions if\n> there's a better random or hashing function to use.\n>\n> [1]: https://lore.kernel.org/git/20241008153035.71178-1-cdwhite3@pm.me/\n> [2]: https://lore.kernel.org/git/r4zmcET41Skr_FMop47AKd7cms9E8bKPSvHuAUpnYavzKEY6JybJta0_7GfuYB0q-gD-XNcvh5VDTfiT3qthGKjqhS1sbT4M2lUABynOz2Q=@pm.me/\n\nI forgot to mention, but the base for this series is obtained by merging\nthe `cw/worktree-extension` topic (2024-11-26, 20241125-wt_relative_options-v5-0-356d122ff3db@pm.me)\nonto 090d24e9af.\n\nBest,\n\nCaleb\n\n"},{"id":"508318","messageId":"xmqqr06un33g.fsf@gitster.g","threadId":"62561","inReplyTo":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-11-29T03:14:27Z","receivedAt":"2024-11-29T03:14:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Caleb White <cdwhite3@pm.me> writes:\n\n> The `es/worktree-repair-copied` topic added support for repairing a\n> worktree from a copy scenario. I noted[1,2] that the topic added the\n> ability for a repository to \"take over\" a worktree from another\n> repository if the worktree_id matched a worktree inside the current\n> repository which can happen if two repositories use the same worktree name.\n\nProblem worth solving.  Another would be to fail if the worktree ID\nproposed to be used is already in use, but the ID is supposed to be\nalmost invisible (unless the user is doing some adiministrative work\non the repository), generating a unique ID is a good approach.\n\n> This series teaches Git to create worktrees with a unique suffix so\n> that the worktree_id is unique across all repositories even if they have\n> the same name. For example creating a worktree `develop` would look like:\n>\n>     foo/\n>     ├── .git/worktrees/develop-5445874156/\n>     └── develop/\n>     bar/\n>     ├── .git/worktrees/develop-1549518426/\n>     └── develop/\n>\n> The actual worktree directory name is still `develop`, but the\n> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n> is given by the `git_rand()` function, but I'm open to suggestions if\n> there's a better random or hashing function to use.\n\nI do not think it matters much what hash/rand algorithm is chosen.\nWhat is important is what you do when the suffix suggested by that\nchosen algorithm collides with an existing worktree ID.  IOW, there\nis no way a \"random\" can guarantee uniqueness.  Attempt to create and\nif you find a collision, retry from the generation of another suffix,\nor something like that, is necessary.\n\nAnd as long as that \"make sure it is unique\" part is done right, it\ndoes not even have to be random.  Just generating a sequence number\nand using the first one that is available would work as well.\n\nThanks.\n"},{"id":"508319","messageId":"D5YCFP1AV64F.36B116IUDDEE2@pm.me","threadId":"62561","inReplyTo":"xmqqr06un33g.fsf@gitster.g","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T03:31:09Z","receivedAt":"2024-11-29T03:31:21Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Thu Nov 28, 2024 at 9:14 PM CST, Junio C Hamano wrote:\n> Caleb White <cdwhite3@pm.me> writes:\n>\n>> The `es/worktree-repair-copied` topic added support for repairing a\n>> worktree from a copy scenario. I noted[1,2] that the topic added the\n>> ability for a repository to \"take over\" a worktree from another\n>> repository if the worktree_id matched a worktree inside the current\n>> repository which can happen if two repositories use the same worktree name.\n>\n> Problem worth solving.  Another would be to fail if the worktree ID\n> proposed to be used is already in use, but the ID is supposed to be\n> almost invisible (unless the user is doing some adiministrative work\n> on the repository), generating a unique ID is a good approach.\n\nThere's already a `while` loop that tries incrementing the proposed id\n(e.g., `develop1` if `develop` is taken). However, this is on\na per-repository basis, so there's no way to know what's already in use\nin another repository. The problem arises when trying to run `worktree\nrepair` on a worktree that has a matching id (like `develop`) from\nanother repository. The goal here is that the same worktree name can be\ncreated in different repositories and the id will be (effectively)\nunique.\n\n>> This series teaches Git to create worktrees with a unique suffix so\n>> that the worktree_id is unique across all repositories even if they have\n>> the same name. For example creating a worktree `develop` would look like:\n>>\n>>     foo/\n>>     ├── .git/worktrees/develop-5445874156/\n>>     └── develop/\n>>     bar/\n>>     ├── .git/worktrees/develop-1549518426/\n>>     └── develop/\n>>\n>> The actual worktree directory name is still `develop`, but the\n>> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n>> is given by the `git_rand()` function, but I'm open to suggestions if\n>> there's a better random or hashing function to use.\n>\n> I do not think it matters much what hash/rand algorithm is chosen.\n> What is important is what you do when the suffix suggested by that\n> chosen algorithm collides with an existing worktree ID.  IOW, there\n> is no way a \"random\" can guarantee uniqueness.  Attempt to create and\n> if you find a collision, retry from the generation of another suffix,\n> or something like that, is necessary.\n>\n> And as long as that \"make sure it is unique\" part is done right, it\n> does not even have to be random.  Just generating a sequence number\n> and using the first one that is available would work as well.\n\nThe `while` loop mentioned earlier still exists, so in the (unlikely)\nevent that the suffix collides with an existing worktree_id, it will\nincrement the suffix and try again.\n\nBest,\n\nCaleb\n\n"},{"id":"508323","messageId":"Z0mgAt9ssu_32tTQ@ArchLinux","threadId":"62561","inReplyTo":"20241128-wt_unique_ids-v1-0-30345d010e43@pm.me","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"shejialuo","fromEmail":"shejialuo@gmail.com","sentAt":"2024-11-29T11:05:38Z","receivedAt":"2024-11-29T11:05:20Z","isPatch":true,"sender":{"key":"shejialuo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56911263?v=4"},"body":"On Fri, Nov 29, 2024 at 02:44:24AM +0000, Caleb White wrote:\n> The `es/worktree-repair-copied` topic added support for repairing a\n> worktree from a copy scenario. I noted[1,2] that the topic added the\n> ability for a repository to \"take over\" a worktree from another\n> repository if the worktree_id matched a worktree inside the current\n> repository which can happen if two repositories use the same worktree name.\n> \n\nI somehow understand why we need to append a hash or a random number\ninto the current \"id\" field of the \"struct worktree *\". But I don't see\na _strong_ reason.\n\nI think we need to figure out the following things:\n\n    1. In what situation, there is a possibility that the user will\n    repair the worktree from another repository.\n    2. Why we need to hash to make sure the worktree is unique? From the\n    expression, my intuitive way is that we need to distinguish whether\n    the repository is the same.\n\n> This series teaches Git to create worktrees with a unique suffix so\n> that the worktree_id is unique across all repositories even if they have\n> the same name. For example creating a worktree `develop` would look like:\n> \n>     foo/\n>     ├── .git/worktrees/develop-5445874156/\n>     └── develop/\n>     bar/\n>     ├── .git/worktrees/develop-1549518426/\n>     └── develop/\n> \n> The actual worktree directory name is still `develop`, but the\n> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n> is given by the `git_rand()` function, but I'm open to suggestions if\n> there's a better random or hashing function to use.\n> \n\nThe actual worktree directory name is unchanged. But we have changed the\n\"worktree->id\" and the git filesystem. Now, we will encounter much\ntrouble. The main reason is that we make the worktree name and worktree\nid inconsistent. There are many tools which assume that worktree id is\nthe worktree name. In other words, there is no difference between the\nworktree id and worktree name at current.\n\nLet me give you an example.\n\nThe user could use \"git update-ref\" to update a ref from another ref.\nSo, a situation is that the user want to update(create) the\nmain-worktree ref from linked-worktree ref.\n\n    ```sh\n    git init repo && cd repo\n    git commit --allow-empty -m initial\n    git branch branch-1\n    git worktree add ./worktree-1 branch-1\n    (cd worktree-1 && git update-ref refs/worktree/branch-2 HEAD)\n    ```\nBy the above operations, we will create a worktree-specified ref under\nthe \".git/worktrees/<worktree_id>/refs/worktree\".\n\nWhat if we want to this in the main worktree:\n\n    ```sh\n    git update-ref refs/heads/branch-3 \\\n        worktrees/worktree-1/refs/worktree/branch-2\n    ```\n\nSo, with this patch, we make worktree-id not the same as worktree name.\nIf we do this. \"git update-ref\" cannot find the\n\".git/worktrees/worktree-1/refs/worktree/branch-2\". This is because the\nfilesystem is changed to \".git/worktrees/worktree-1-<hash>/...\".\n\nIf we use hash / random number to distinguish. We also need to change\nthe ref-related code to ignore the \"-<hash>\". It's impossible to let the\nuser type the extra hash / random number. However, this requires a lot\nof effort.\n\nSo, I think we need a _strong_ reason to indicate that we must append\nsome chars into worktree id to do this.\n\nThanks,\nJialuo\n"},{"id":"508338","messageId":"D5YSBQXU7FYD.25KOIM1N3US88@pm.me","threadId":"62561","inReplyTo":"Z0mgAt9ssu_32tTQ@ArchLinux","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T15:58:16Z","receivedAt":"2024-11-29T15:58:35Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Fri Nov 29, 2024 at 5:05 AM CST, shejialuo wrote:\n> On Fri, Nov 29, 2024 at 02:44:24AM +0000, Caleb White wrote:\n>> The `es/worktree-repair-copied` topic added support for repairing a\n>> worktree from a copy scenario. I noted[1,2] that the topic added the\n>> ability for a repository to \"take over\" a worktree from another\n>> repository if the worktree_id matched a worktree inside the current\n>> repository which can happen if two repositories use the same worktree name.\n>\n> I somehow understand why we need to append a hash or a random number\n> into the current \"id\" field of the \"struct worktree *\". But I don't see\n> a _strong_ reason.\n>\n> I think we need to figure out the following things:\n>\n>     1. In what situation, there is a possibility that the user will\n>     repair the worktree from another repository.\n\nThis can happen if a user accidentally mistypes a directory name when\nexecuting `git worktree repair`. Or if a user copies a worktree from one\nrepository and the executes `git worktree repair` in a different repository.\n\nThe point is to prevent this from happening before it becomes a problem.\n\n>     2. Why we need to hash to make sure the worktree is unique? From the\n>     expression, my intuitive way is that we need to distinguish whether\n>     the repository is the same.\n\nDuring `worktree repair`, an \"inferred backlink\" is established by\nparsing the worktree id from the `.git` file and checking if that\nmatches an existing worktree id in the current repository. If so, then\nthe link is established even if that worktree belonged to another\nrepository. The easiest way I thought to prevent this from happening is\nto ensure that no \"inferred backlink\" can be established by using an\neffectively unique worktree_id. So two repositories can have the same\nworktree name (e.g., `develop`) but the actual ids would be different.\nAdditionally, if the ids **do match**, then this would be indicative of\na copy situation like the original topic addressed.\n\nHow do you propose to distinguish whether the repository is the same?\n\n>> This series teaches Git to create worktrees with a unique suffix so\n>> that the worktree_id is unique across all repositories even if they have\n>> the same name. For example creating a worktree `develop` would look like:\n>>\n>>     foo/\n>>     ├── .git/worktrees/develop-5445874156/\n>>     └── develop/\n>>     bar/\n>>     ├── .git/worktrees/develop-1549518426/\n>>     └── develop/\n>>\n>> The actual worktree directory name is still `develop`, but the\n>> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n>> is given by the `git_rand()` function, but I'm open to suggestions if\n>> there's a better random or hashing function to use.\n>\n> The actual worktree directory name is unchanged. But we have changed the\n> \"worktree->id\" and the git filesystem. Now, we will encounter much\n> trouble. The main reason is that we make the worktree name and worktree\n> id inconsistent. There are many tools which assume that worktree id is\n> the worktree name.\n\nDo you have any sources for these tools? Because I'm not aware of any.\nAny tool that needs the actual worktree id should be extracting the id\nfrom the `.git` file and not using the worktree directory name.\n\n> In other words, there is no difference between the worktree id and\n> worktree name at current.\n\nThis is NOT true, there are several scenarios where they can currently differ:\n\n1. git currently will append a number to the worktree name if it already\n   exists in the repository. This means that you can create a `develop`\n   worktree and wind up with an id of `develop2`.\n2. git does not currently rename the id during a `worktree move`. This\n   means that I can create a worktree with a name of `develop` and then\n   execute `git worktree move develop master` and the id will still be\n   `develop` while the directory is now `master`.\n3. a user can manually move/rename the directory and then repair the\n   worktree and wind up in the same situation as 2).\n\nThe suffix is not a new concept, I've just changed it from occasionally\nadding a suffix to always adding a (unique) suffix. The worktree id has\nnever been guaranteed to be the same as the worktree name, and as\nmentioned above, any tools/scripts/intelligence that need the id should\nalways be extracting it from the `.git` file.\n\nOne thing we can do is to add the worktree id to the `git worktree list`\noutput so that users can see the id and the name together. This would\nmake it easier for users/tools obtain the id if they need it without\nhaving to parse the `.git` file.\n\n> Let me give you an example.\n>\n> So, with this patch, we make worktree-id not the same as worktree name.\n> If we do this. \"git update-ref\" cannot find the\n> \".git/worktrees/worktree-1/refs/worktree/branch-2\". This is because the\n> filesystem is changed to \".git/worktrees/worktree-1-<hash>/...\".\n\nYes this is expected because the worktree id is not the same as the\nname.\n\n> If we use hash / random number to distinguish. We also need to change\n> the ref-related code to ignore the \"-<hash>\". It's impossible to let the\n> user type the extra hash / random number. However, this requires a lot\n> of effort.\n\nThis would be possible as long as the given worktree slug is unambiguous.\nHowever, I think this is more trouble than it's worth.\n\n> So, I think we need a _strong_ reason to indicate that we must append\n> some chars into worktree id to do this.\n\nAs stated above, git already appends a number to the worktree name if it\ncollides with an existing directory. Always appending a unique suffix\nshould actually make things simpler / more consistent in the long run\nbecause the worktree id will always be different from the name instead\nof occasionally being different.\n\nBest,\n\nCaleb\n\n"},{"id":"508341","messageId":"Z0n6uuNdQZCLqAz2@ArchLinux","threadId":"62561","inReplyTo":"D5YSBQXU7FYD.25KOIM1N3US88@pm.me","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"shejialuo","fromEmail":"shejialuo@gmail.com","sentAt":"2024-11-29T17:32:42Z","receivedAt":"2024-11-29T17:32:23Z","isPatch":true,"sender":{"key":"shejialuo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/56911263?v=4"},"body":"On Fri, Nov 29, 2024 at 03:58:16PM +0000, Caleb White wrote:\n> On Fri Nov 29, 2024 at 5:05 AM CST, shejialuo wrote:\n> > On Fri, Nov 29, 2024 at 02:44:24AM +0000, Caleb White wrote:\n> >> The `es/worktree-repair-copied` topic added support for repairing a\n> >> worktree from a copy scenario. I noted[1,2] that the topic added the\n> >> ability for a repository to \"take over\" a worktree from another\n> >> repository if the worktree_id matched a worktree inside the current\n> >> repository which can happen if two repositories use the same worktree name.\n> >\n> > I somehow understand why we need to append a hash or a random number\n> > into the current \"id\" field of the \"struct worktree *\". But I don't see\n> > a _strong_ reason.\n> >\n> > I think we need to figure out the following things:\n> >\n> >     1. In what situation, there is a possibility that the user will\n> >     repair the worktree from another repository.\n> \n> This can happen if a user accidentally mistypes a directory name when\n> executing `git worktree repair`. Or if a user copies a worktree from one\n> repository and the executes `git worktree repair` in a different repository.\n> \n> The point is to prevent this from happening before it becomes a problem.\n> \n\nIf we could prevent this, this is great. But the current implementation\nwill cause much burden for the refs-related code. In other words, my\nconcern is that if we use this way, we may put a lot of efforts to\nchange the ref-related codes to adapt into this new design. So, we\nshould think carefully whether we should put so many efforts to solve\nthis corner case by this way.\n\nI somehow know the background, because we have allowed both the absolute\nand relative paths for worktree, we need to handle this problem.\n\nI agree with your motivation, but we need to consider the burden\nintroduced. This is my point.\n\n> >     2. Why we need to hash to make sure the worktree is unique? From the\n> >     expression, my intuitive way is that we need to distinguish whether\n> >     the repository is the same.\n> \n> During `worktree repair`, an \"inferred backlink\" is established by\n> parsing the worktree id from the `.git` file and checking if that\n> matches an existing worktree id in the current repository. If so, then\n> the link is established even if that worktree belonged to another\n> repository. The easiest way I thought to prevent this from happening is\n> to ensure that no \"inferred backlink\" can be established by using an\n> effectively unique worktree_id. So two repositories can have the same\n> worktree name (e.g., `develop`) but the actual ids would be different.\n> Additionally, if the ids **do match**, then this would be indicative of\n> a copy situation like the original topic addressed.\n> \n\nOK.\n\n> How do you propose to distinguish whether the repository is the same?\n> \n\nI am sorry that I cannot tell you the answer. I haven't dived into the\nworktree. I just gave my thoughts above.\n\n> >> This series teaches Git to create worktrees with a unique suffix so\n> >> that the worktree_id is unique across all repositories even if they have\n> >> the same name. For example creating a worktree `develop` would look like:\n> >>\n> >>     foo/\n> >>     ├── .git/worktrees/develop-5445874156/\n> >>     └── develop/\n> >>     bar/\n> >>     ├── .git/worktrees/develop-1549518426/\n> >>     └── develop/\n> >>\n> >> The actual worktree directory name is still `develop`, but the\n> >> worktree_id is unique and prevents the \"take over\" scenario. The suffix\n> >> is given by the `git_rand()` function, but I'm open to suggestions if\n> >> there's a better random or hashing function to use.\n> >\n> > The actual worktree directory name is unchanged. But we have changed the\n> > \"worktree->id\" and the git filesystem. Now, we will encounter much\n> > trouble. The main reason is that we make the worktree name and worktree\n> > id inconsistent. There are many tools which assume that worktree id is\n> > the worktree name.\n> \n> Do you have any sources for these tools? Because I'm not aware of any.\n> Any tool that needs the actual worktree id should be extracting the id\n> from the `.git` file and not using the worktree directory name.\n> \n\nI am sorry that my words may confuse you here. These tools are just the\ngit builtins. Such as \"git update-ref\" and \"git symbolic-ref\".\n\n> > In other words, there is no difference between the worktree id and\n> > worktree name at current.\n> \n> This is NOT true, there are several scenarios where they can currently differ:\n> \n> 1. git currently will append a number to the worktree name if it already\n>    exists in the repository. This means that you can create a `develop`\n>    worktree and wind up with an id of `develop2`.\n> 2. git does not currently rename the id during a `worktree move`. This\n>    means that I can create a worktree with a name of `develop` and then\n>    execute `git worktree move develop master` and the id will still be\n>    `develop` while the directory is now `master`.\n> 3. a user can manually move/rename the directory and then repair the\n>    worktree and wind up in the same situation as 2).\n> \n\nThanks for this information. I am not familiar with the worktree. I just\nhave learned the worktree when doing something related to the refs. So,\nit is true that worktree id and worktree name are not the same.\n\nBut that's wired. For any situation above, it won't cause any trouble\nwhen the user is in the worktree. Because the user could just use\n\"refs/worktree/foo\" to indicate the worktree specified ref without\nknowing the worktree id.\n\nSo if a user moves the path from \"worktree_1\" to \"worktree_2\" and wants\nto do the following operation in the main worktree:\n\n    ```sh\n    git update-ref refs/heads/master \\\n        worktrees/worktree_2/refs/worktree/foo\n    ```\nIt will encounter error, because the worktree id is still the\n\"worktree_1\".\n\nBut when the user create the worktree using the following command:\n\n    ```sh\n    git worktree add ./worktree_1 branch-1\n    ```\n\nThe name `worktree_1`(path) will be the worktree id. So, when moving the\npath \"worktree_1\" to \"worktree_2\". The user won't know the detail about\nthe worktree id. The user (like me) will just think that \"worktree_2\"\nwill be the new worktree id.\n\nThat does not make sense. It's impossible for the user know the mapping\nbetween the worktree name and worktree id.\n\nHi Eric and Junio, I am wondering what the purpose of worktree is. When\nI implemented the consistent check for ref contents, Junio has told me\nthat the main-worktree symref could point to the linked-worktree ref.\nThe linked-worktree symref could also point to another linked-worktree\nref.\n\nBut from above, it gives a feeling that the purpose of the worktree is\nto make the environment totally independent. And we shouldn't allow any\nref interactions between the main-worktree and linked-worktree. But we\nDO allow at now by using \"git symbolic-ref\" and \"git update-ref\".\n\n> The suffix is not a new concept, I've just changed it from occasionally\n> adding a suffix to always adding a (unique) suffix. The worktree id has\n> never been guaranteed to be the same as the worktree name, and as\n> mentioned above, any tools/scripts/intelligence that need the id should\n> always be extracting it from the `.git` file.\n> \n\nYes, I agree. Thanks.\n\n> One thing we can do is to add the worktree id to the `git worktree list`\n> output so that users can see the id and the name together. This would\n> make it easier for users/tools obtain the id if they need it without\n> having to parse the `.git` file.\n> \n\nThis is a good idea, if we need to use this way. And we may also need to\nadd documentation.\n\n> > Let me give you an example.\n> >\n> > So, with this patch, we make worktree-id not the same as worktree name.\n> > If we do this. \"git update-ref\" cannot find the\n> > \".git/worktrees/worktree-1/refs/worktree/branch-2\". This is because the\n> > filesystem is changed to \".git/worktrees/worktree-1-<hash>/...\".\n> \n> Yes this is expected because the worktree id is not the same as the\n> name.\n> \n> > If we use hash / random number to distinguish. We also need to change\n> > the ref-related code to ignore the \"-<hash>\". It's impossible to let the\n> > user type the extra hash / random number. However, this requires a lot\n> > of effort.\n> \n> This would be possible as long as the given worktree slug is unambiguous.\n> However, I think this is more trouble than it's worth.\n> \n\nAs you can see from above, the main problem is that we allow some ref\ninteractions between the main-worktree and linked-worktrees.\n\nThe reason why I use this example is that I am afraid that the user will\ncreate a new symbolic ref in the main worktree which points to the\nlinked-worktree ref.\n\nWhen the user create a worktree, it is natural to think that there is no\ndifference between the worktree id and the worktree name. And will use\n\"worktrees/<worktree_id>/refs/worktree/foo\" to access the ref in the\nworktree.\n\nAnd It's OK to add hash / random number to the worktree id if worktree\nis totally independent. However, at now, we allow some interactions\nbetween the main-worktree and linked worktrees (even the linked\nworktree and another linked worktree).\n\nWhen doing above, the user must know the worktree id. But at now\nworktree name is not the same as worktree id.\n\nSo, I don't know...\n\n> > So, I think we need a _strong_ reason to indicate that we must append\n> > some chars into worktree id to do this.\n> \n> As stated above, git already appends a number to the worktree name if it\n> collides with an existing directory. Always appending a unique suffix\n> should actually make things simpler / more consistent in the long run\n> because the worktree id will always be different from the name instead\n> of occasionally being different.\n> \n\nIn general, I agree with your way now after knowing the truth that\nworktree name is not the same as the worktree id. However, it seems that\nthe situation is a little complicated.\n\n> Best,\n> \n> Caleb\n\nThanks,\nJialuo\n"},{"id":"508347","messageId":"D5YYB5XQF1QG.1T6WKWPNUVXX2@pm.me","threadId":"62561","inReplyTo":"Z0n6uuNdQZCLqAz2@ArchLinux","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Caleb White","fromEmail":"cdwhite3@pm.me","sentAt":"2024-11-29T20:39:36Z","receivedAt":"2024-11-29T20:39:44Z","isPatch":true,"sender":{"key":"cdwhite3@pm.me","avatar":"https://avatars.githubusercontent.com/u/4176520?v=4"},"body":"On Fri Nov 29, 2024 at 11:32 AM CST, shejialuo wrote:\n> On Fri, Nov 29, 2024 at 03:58:16PM +0000, Caleb White wrote:\n>> On Fri Nov 29, 2024 at 5:05 AM CST, shejialuo wrote:\n>> > I somehow understand why we need to append a hash or a random number\n>> > into the current \"id\" field of the \"struct worktree *\". But I don't see\n>> > a _strong_ reason.\n>> >\n>> > I think we need to figure out the following things:\n>> >\n>> >     1. In what situation, there is a possibility that the user will\n>> >     repair the worktree from another repository.\n>>\n>> This can happen if a user accidentally mistypes a directory name when\n>> executing `git worktree repair`. Or if a user copies a worktree from one\n>> repository and the executes `git worktree repair` in a different repository.\n>>\n>> The point is to prevent this from happening before it becomes a problem.\n>\n> If we could prevent this, this is great. But the current implementation\n> will cause much burden for the refs-related code. In other words, my\n> concern is that if we use this way, we may put a lot of efforts to\n> change the ref-related codes to adapt into this new design. So, we\n> should think carefully whether we should put so many efforts to solve\n> this corner case by this way.\n\nI'm not sure that any of the ref related code would need to change,\nall the tests are passing and the ref code uses the worktree id\nfor anything worktree related.\n\n> I somehow know the background, because we have allowed both the absolute\n> and relative paths for worktree, we need to handle this problem.\n\nThis edge case exists when using absolute paths, so this is not\na consequence of now allowing relative paths.\n\n>> >     2. Why we need to hash to make sure the worktree is unique? From the\n>> >     expression, my intuitive way is that we need to distinguish whether\n>> >     the repository is the same.\n>> \n>> How do you propose to distinguish whether the repository is the same?\n>\n> I am sorry that I cannot tell you the answer. I haven't dived into the\n> worktree. I just gave my thoughts above.\n\nThat's fine, I'm just not really sure about how such a thing would be\ndone. To me, the unique id is the easiest and most intuitive. The\nrepository generally has no knowledge of other repositories on the\nsystem (as far as I am aware).\n\n>> Do you have any sources for these tools? Because I'm not aware of any.\n>> Any tool that needs the actual worktree id should be extracting the id\n>> from the `.git` file and not using the worktree directory name.\n>\n> I am sorry that my words may confuse you here. These tools are just the\n> git builtins. Such as \"git update-ref\" and \"git symbolic-ref\".\n\nAh, I understand now---I thought you were talking about external tools.\nAll internal git builtins use the worktree id, the actual directory name\nof the worktree is inconsequential. So nothing should need to change.\n\n>> > In other words, there is no difference between the worktree id and\n>> > worktree name at current.\n>>\n>> This is NOT true, there are several scenarios where they can currently differ:\n>>\n>> 1. git currently will append a number to the worktree name if it already\n>>    exists in the repository. This means that you can create a `develop`\n>>    worktree and wind up with an id of `develop2`.\n>> 2. git does not currently rename the id during a `worktree move`. This\n>>    means that I can create a worktree with a name of `develop` and then\n>>    execute `git worktree move develop master` and the id will still be\n>>    `develop` while the directory is now `master`.\n>> 3. a user can manually move/rename the directory and then repair the\n>>    worktree and wind up in the same situation as 2).\n>>\n>\n> Thanks for this information. I am not familiar with the worktree. I just\n> have learned the worktree when doing something related to the refs. So,\n> it is true that worktree id and worktree name are not the same.\n>\n> But that's wired. For any situation above, it won't cause any trouble\n> when the user is in the worktree. Because the user could just use\n> \"refs/worktree/foo\" to indicate the worktree specified ref without\n> knowing the worktree id.\n>\n> So if a user moves the path from \"worktree_1\" to \"worktree_2\" and wants\n> to do the following operation in the main worktree:\n>\n>     ```sh\n>     git update-ref refs/heads/master \\\n>         worktrees/worktree_2/refs/worktree/foo\n>     ```\n> It will encounter error, because the worktree id is still the\n> \"worktree_1\".\n>\n> But when the user create the worktree using the following command:\n>\n>     ```sh\n>     git worktree add ./worktree_1 branch-1\n>     ```\n>\n> The name `worktree_1`(path) will be the worktree id. So, when moving the\n> path \"worktree_1\" to \"worktree_2\". The user won't know the detail about\n> the worktree id. The user (like me) will just think that \"worktree_2\"\n> will be the new worktree id.\n>\n> That does not make sense. It's impossible for the user know the mapping\n> between the worktree name and worktree id.\n\nIt's not impossible, the user can always look in the `.git` file.\nHowever, I have added the id to the `worktree list` output to more easily\nassociate the id with the worktree.\n\n>> One thing we can do is to add the worktree id to the `git worktree list`\n>> output so that users can see the id and the name together. This would\n>> make it easier for users/tools obtain the id if they need it without\n>> having to parse the `.git` file.\n>>\n> This is a good idea, if we need to use this way. And we may also need to\n> add documentation.\n\nI have implemented this and added documentation.\n\n> As you can see from above, the main problem is that we allow some ref\n> interactions between the main-worktree and linked-worktrees.\n>\n> The reason why I use this example is that I am afraid that the user will\n> create a new symbolic ref in the main worktree which points to the\n> linked-worktree ref.\n>\n> When the user create a worktree, it is natural to think that there is no\n> difference between the worktree id and the worktree name. And will use\n> \"worktrees/<worktree_id>/refs/worktree/foo\" to access the ref in the\n> worktree.\n>\n> And It's OK to add hash / random number to the worktree id if worktree\n> is totally independent. However, at now, we allow some interactions\n> between the main-worktree and linked worktrees (even the linked\n> worktree and another linked worktree).\n>\n> When doing above, the user must know the worktree id. But at now\n> worktree name is not the same as worktree id.\n>\n> So, I don't know...\n\nInteractions between the main and linked (and linked with other linked)\nworktrees is not a limiting factor here.\n\n>> As stated above, git already appends a number to the worktree name if it\n>> collides with an existing directory. Always appending a unique suffix\n>> should actually make things simpler / more consistent in the long run\n>> because the worktree id will always be different from the name instead\n>> of occasionally being different.\n>\n> In general, I agree with your way now after knowing the truth that\n> worktree name is not the same as the worktree id. However, it seems that\n> the situation is a little complicated.\n\nI see where you're coming from, but I do not believe that the situation\nis any more complicated than it already is. All internal git code uses\nthe worktree id which is already not guaranteed to be the same as the\nworktree directory name, so nothing is changing on that front.\n\nBest,\n\nCaleb\n\n"},{"id":"508386","messageId":"xmqq34j8m30t.fsf@gitster.g","threadId":"62561","inReplyTo":"D5YCFP1AV64F.36B116IUDDEE2@pm.me","subject":"Re: [PATCH 0/2] Ensure unique worktree ids across repositories","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-12-01T04:38:10Z","receivedAt":"2024-12-01T04:38:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Caleb White <cdwhite3@pm.me> writes:\n\n> On Thu Nov 28, 2024 at 9:14 PM CST, Junio C Hamano wrote:\n>> Caleb White <cdwhite3@pm.me> writes:\n>>\n>>> The `es/worktree-repair-copied` topic added support for repairing a\n>>> worktree from a copy scenario. I noted[1,2] that the topic added the\n>>> ability for a repository to \"take over\" a worktree from another\n>>> repository if the worktree_id matched a worktree inside the current\n>>> repository which can happen if two repositories use the same worktree name.\n>>\n>> Problem worth solving.  Another would be to fail if the worktree ID\n>> proposed to be used is already in use, but the ID is supposed to be\n>> almost invisible (unless the user is doing some adiministrative work\n>> on the repository), generating a unique ID is a good approach.\n>\n> There's already a `while` loop that tries incrementing the proposed id\n> (e.g., `develop1` if `develop` is taken). However, this is on\n> a per-repository basis, so there's no way to know what's already in use\n> in another repository.\n\nUsually repositories on a single host are not aware of each other,\nso I am not sure if it is a sensible goal to begin with, to try to\nensure a worktree ID is unique \"across repositories\".\n\n> The problem arises when trying to run `worktree\n> repair` on a worktree that has a matching id (like `develop`) from\n> another repository. The goal here is that the same worktree name can be\n> created in different repositories and the id will be (effectively)\n> unique.\n\nOK, but wouldn't that change the problem we need to solve greatly?\n\nThe problem is very much simplified, in fact.  When we are adding to\n.git/worktrees/ of a single repository, we need a unique worktree ID\ngiven to the new worktree.  And the ID like `develop` taken from\nanother repository may or may not be already in use here.\n\nIn a repository, a directory ../foo/develop may already be\nregistered as its worktree, and the user may try to add yet another\ndirectory ../bar/develop as a new worktree.  The first one has been\nusing 'develop' as its ID, and the \"worktree add\" command to create\nthe new one needs to tweak the basename 'develop' to make it unique\nwithin this single repository.  Shouldn't `repair` that tries to\nbring in an orphaned worktree that used to be given a worktree ID by\na potentially different repository (or it could be initially created\nin this repository and then forgotten, and in the meantime there\ncould have been many iterations of `develop` worktrees created for\nthe repository and while it was missing, its ID plus serial number\nmay have already taken) follow the same pattern?  Whatever worktree\nID that the other repository gave it is invalid in the context of\nthis repository, anyway.\n\nSo I am not sure why we need to complicate the system by adding\nrandom number, which does not help in ensuring uniqueness (it may\nmake it less likely to collide, but that is different from\nguaranteeing uniqueness), while misleading readers that somehow\nthese numbers after the worktree IDs are serving some purpose.\n\nAm I missing something?\n\nThanks.\n"}]}