During a `worktree move` the worktree directory is moved/renamed but the repository under `worktrees/<id>` is not updated. For example, given the following structure: foo/ ├── .git/worktrees/develop-5445874156/ └── develop/ moving `develop` to `master` results in foo/ ├── .git/worktrees/develop-5445874156/ └── master/ This works because the linking files still point to the correct repository, but this is a little weird. This teaches Git to also move/rename the repository / worktree id during a `move` so that the structure now looks like: foo/ ├── .git/worktrees/master-1565465986/ └── master/ Note that a new unique suffix is assigned to reduce the complexity of trying to parse and reuse the existing suffix. Signed-off-by: Caleb White <cdwhite3@xxxxx> --- builtin/worktree.c | 24 ++++++++++++++++++++++++ t/t2403-worktree-move.sh | 18 +++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index 3ad355ca762729401fc0c8625f4fd05b154a84ec..36235546b492803707707ff208b13fe777bff1b4 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1202,9 +1202,14 @@ static int move_worktree(int ac, const char **av, const char *prefix) }; struct worktree **worktrees, *wt; struct strbuf dst = STRBUF_INIT; + struct strbuf repo = STRBUF_INIT; + struct strbuf repo_dst = STRBUF_INIT; struct strbuf errmsg = STRBUF_INIT; const char *reason = NULL; + const char *new_id; + const char *suffix; char *path; + int len; ac = parse_options(ac, av, prefix, options, git_worktree_move_usage, 0); @@ -1250,9 +1255,28 @@ static int move_worktree(int ac, const char **av, const char *prefix) if (rename(wt->path, dst.buf) == -1) die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf); + strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1); + new_id = worktree_basename(dst.buf, &len); + strbuf_add(&repo_dst, new_id, dst.buf + len - new_id); + strbuf_realpath(&repo_dst, git_common_path("worktrees/%s", repo_dst.buf), 1); + suffix = getenv("GIT_TEST_WORKTREE_SUFFIX"); + if (suffix) + strbuf_addf(&repo_dst, "-%s", suffix); + else + strbuf_addf(&repo_dst, "-%u", git_rand()); + new_id = strrchr(repo_dst.buf, '/') + 1; + if (rename(repo.buf, repo_dst.buf) == -1) + die_errno(_("failed to move '%s' to '%s'"), repo.buf, repo_dst.buf); + else { + free(wt->id); + wt->id = xstrdup(new_id); + } + update_worktree_location(wt, dst.buf, use_relative_paths); strbuf_release(&dst); + strbuf_release(&repo); + strbuf_release(&repo_dst); free_worktrees(worktrees); return 0; } diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh index ba3f05c16a4969fb84d98052ae375ef162f3e73a..703aa58d10643e99ffaf803aa38dabfd4af68a10 100755 --- a/t/t2403-worktree-move.sh +++ b/t/t2403-worktree-move.sh @@ -250,26 +250,26 @@ test_expect_success 'not remove a repo with initialized submodule' ' test_expect_success 'move worktree with absolute path to relative path' ' test_config worktree.useRelativePaths false && GIT_TEST_WORKTREE_SUFFIX=123 git worktree add ./absolute && - git worktree move --relative-paths absolute relative && - echo "gitdir: ../.git/worktrees/absolute-123" >expect && + GIT_TEST_WORKTREE_SUFFIX=456 git worktree move --relative-paths absolute relative && + echo "gitdir: ../.git/worktrees/relative-456" >expect && test_cmp expect relative/.git && echo "../../../relative/.git" >expect && - test_cmp expect .git/worktrees/absolute-123/gitdir && + test_cmp expect .git/worktrees/relative-456/gitdir && test_config worktree.useRelativePaths true && - git worktree move relative relative2 && - echo "gitdir: ../.git/worktrees/absolute-123" >expect && + GIT_TEST_WORKTREE_SUFFIX=789 git worktree move relative relative2 && + echo "gitdir: ../.git/worktrees/relative2-789" >expect && test_cmp expect relative2/.git && echo "../../../relative2/.git" >expect && - test_cmp expect .git/worktrees/absolute-123/gitdir + test_cmp expect .git/worktrees/relative2-789/gitdir ' test_expect_success 'move worktree with relative path to absolute path' ' test_config worktree.useRelativePaths true && - git worktree move --no-relative-paths relative2 absolute && - echo "gitdir: $(pwd)/.git/worktrees/absolute-123" >expect && + GIT_TEST_WORKTREE_SUFFIX=851 git worktree move --no-relative-paths relative2 absolute && + echo "gitdir: $(pwd)/.git/worktrees/absolute-851" >expect && test_cmp expect absolute/.git && echo "$(pwd)/absolute/.git" >expect && - test_cmp expect .git/worktrees/absolute-123/gitdir + test_cmp expect .git/worktrees/absolute-851/gitdir ' test_done -- 2.47.0