git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/2] worktree: rename worktree id during worktree move

From
Caleb White <cdwhite3@pm.me>
Date
Nov 29, 2024, 02:44 UTC
Message-ID
<20241128-wt_unique_ids-v1-2-30345d010e43@pm.me>
In-Reply-To
<20241128-wt_unique_ids-v1-0-30345d010e43@pm.me>

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@pm.me>
---
 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
Previous: Caleb WhiteNext: Caleb White
Message 3 of 11 in “Ensure unique worktree ids across repositories”
  1. 0/2 Ensure unique worktree ids across repositoriesCaleb White, Nov 29, 2024
  2. 1/2 worktree: add worktree with unique suffixCaleb White, Nov 29, 2024
  3. 2/2 worktree: rename worktree id during worktree moveCaleb White, Nov 29, 2024
  4. Caleb WhiteNov 29, 2024
  5. Junio C HamanoNov 29, 2024
  6. Caleb WhiteNov 29, 2024
  7. Junio C HamanoDec 1, 2024
  8. shejialuoNov 29, 2024
  9. Caleb WhiteNov 29, 2024
  10. shejialuoNov 29, 2024
  11. Caleb WhiteNov 29, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.