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

[PATCH v2 3/4] worktree: sync worktree paths after gitdir move

From
Caleb White <cdwhite3@pm.me>
Date
Oct 6, 2024, 06:01 UTC
Message-ID
<20241006060017.171788-4-cdwhite3@pm.me>
In-Reply-To
<20241006060017.171788-1-cdwhite3@pm.me>

When re-initializing a repository with a separate gitdir (the original gitdir is moved to a new location), any linked worktrees become broken and must be repaired to reflect the new gitdir location. For absolute paths, this breakage is one-way, but is both ways for relative paths (the `<worktree>/.git` and the `<repo>/worktrees/<id>/gitdir`).

Previously, `repair_worktrees` was being called which loops through all the worktrees in the repository and updates the `<worktree>/.git` files to point to the new gitdir. However, when both sides of the worktrees are broken, the previous gitdir location is required to reestablish the link.

To fix this, the function `repair_worktrees_after_gitdir_move` is introduced. It takes the old gitdir path as an argument and repairs both sides of the worktree.

This change fixes the following test cases in t0001-init.sh:
- re-init to move gitdir with linked worktrees
- re-init to move gitdir within linked worktree
Signed-off-by: Caleb 
White <cdwhite3@pm.me>
---
 setup.c    |  2 +-
 worktree.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 worktree.h | 10 ++++++++++
 3 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/setup.c b/setup.c
index 94e79b2..7b648de 100644
--- a/setup.c
+++ b/setup.c
@@ -2420,7 +2420,7 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
 
 		if (rename(src, git_dir))
 			die_errno(_("unable to move %s to %s"), src, git_dir);
-		repair_worktrees(NULL, NULL);
+		repair_worktrees_after_gitdir_move(src);
 	}
 
 	write_file(git_link, "gitdir: %s", git_dir);
diff --git a/worktree.c b/worktree.c
index fc14e9a..b08ecce 100644
--- a/worktree.c
+++ b/worktree.c
@@ -650,6 +650,60 @@ void repair_worktrees(worktree_repair_fn fn, void *cb_data)
 	free_worktrees(worktrees);
 }
 
+void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct strbuf repo = STRBUF_
INIT;
+	struct strbuf gitdir = STRBUF_INIT;
+	struct strbuf dotgit = STRBUF_INIT;
+	struct strbuf olddotgit = STRBUF_INIT;
+	struct strbuf tmp = STRBUF_INIT;
+
+	if (is_main_worktree(wt))
+		goto done;
+
+	strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+	strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
+
+	if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
+		goto done;
+
+	strbuf_rtrim(&olddotgit);
+	if (is_absolute_path(olddotgit.buf)) {
+		strbuf_addbuf(&dotgit, &olddotgit);
+	} else {
+		strbuf_addf(&dotgit, "%s/worktrees/%s/%s", old_path, wt->id, olddotgit.buf);
+		strbuf_realpath_forgiving(&dotgit, dotgit.buf, 0);
+	}
+
+	if (!file_exists(dotgit.buf))
+		goto done;
+
+	strbuf_addbuf(&path, &dotgit);
+	strbuf_strip_suffix(&path, "/.git");
+
+	write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));
+	strbuf_reset(&tmp);
+	write_file(gitdir.buf, "%s", relative_path(dotgit.buf, repo.buf, &tmp));
+done:
+	strbu
f_release(&path);
+	strbuf_release(&repo);
+	strbuf_release(&gitdir);
+	strbuf_release(&dotgit);
+	strbuf_release(&olddotgit);
+	strbuf_release(&tmp);
+}
+
+void repair_worktrees_after_gitdir_move(const char *old_path)
+{
+	struct worktree **worktrees = get_worktrees_internal(1);
+	struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
+
+	for (; *wt; wt++)
+		repair_worktree_after_gitdir_move(*wt, old_path);
+	free_worktrees(worktrees);
+}
+
 static int is_main_worktree_path(const char *path)
 {
 	struct strbuf target = STRBUF_INIT;
diff --git a/worktree.h b/worktree.h
index 11279d0..e961186 100644
--- a/worktree.h
+++ b/worktree.h
@@ -131,6 +131,16 @@ typedef void (* worktree_repair_fn)(int iserr, const char *path,
  */
 void repair_worktrees(worktree_repair_fn, void *cb_data);
 
+/*
+ * Repair the linked worktrees after the gitdir has been moved.
+ */
+void repair_worktrees_after_gitdir_move(const char *old_path);
+
+/*
+ * Repair the l
inked worktree after the gitdir has been moved.
+ */
+void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path);
+
 /*
  * Repair administrative files corresponding to the worktree at the given path.
  * The worktree's .git file pointing at the repository must be intact for the
-- 
2.46.2
Previous: Caleb WhiteNext: Eric Sunshine
Message 27 of 38 in “Link worktrees with relative paths”
  1. 0/4 Link worktrees with relative pathsCaleb White, Oct 6, 2024
  2. 1/4 worktree: refactor infer_backlink() to use *strbufCaleb White, Oct 6, 2024
  3. shejialuoOct 6, 2024
  4. Kristoffer HaugsbakkOct 6, 2024
  5. Eric SunshineOct 6, 2024
  6. Caleb WhiteOct 7, 2024
  7. shejialuoOct 7, 2024
  8. Caleb WhiteOct 7, 2024
  9. shejialuoOct 7, 2024
  10. Caleb WhiteOct 7, 2024
  11. shejialuoOct 7, 2024
  12. Caleb WhiteOct 7, 2024
  13. shejialuoOct 7, 2024
  14. Caleb WhiteOct 6, 2024
  15. Eric SunshineOct 6, 2024
  16. Caleb WhiteOct 7, 2024
  17. Eric SunshineOct 7, 2024
  18. Caleb WhiteOct 7, 2024
  19. 2/4 worktree: link worktrees with relative pathsCaleb White, Oct 6, 2024
  20. Eric SunshineOct 6, 2024
  21. Caleb WhiteOct 6, 2024
  22. shejialuoOct 6, 2024
  23. Caleb WhiteOct 6, 2024
  24. shejialuoOct 7, 2024
  25. Eric SunshineOct 7, 2024
  26. Caleb WhiteOct 7, 2024
  27. 3/4 worktree: sync worktree paths after gitdir moveCaleb White, Oct 6, 2024
  28. Eric SunshineOct 6, 2024
  29. Caleb WhiteOct 6, 2024
  30. Eric SunshineOct 6, 2024
  31. Caleb WhiteOct 6, 2024
  32. Eric SunshineOct 6, 2024
  33. 4/4 worktree: prevent null pointer dereferenceCaleb White, Oct 6, 2024
  34. Eric SunshineOct 6, 2024
  35. Caleb WhiteOct 6, 2024
  36. Eric SunshineOct 6, 2024
  37. Caleb WhiteOct 7, 2024
  38. Eric SunshineOct 6, 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.