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

[PATCH v3 1/5] worktree: refactor infer_backlink() to use strbuf*

From
Caleb White <cdwhite3@pm.me>
Date
Oct 25, 2024, 20:57 UTC
Message-ID
<20241025-wt_relative_paths-v3-1-8860a5321c01@pm.me>
In-Reply-To
<20241025-wt_relative_paths-v3-0-8860a5321c01@pm.me>

The infer_backlink() function initializes a `strbuf` for the inferred backlink and returns the result as a `char*` by detaching the `strbuf`.

The next patch needs the backlink returned from infer_backlink() as a `strbuf`. It seemed inefficient to convert from `strbuf` to `char*` and back to `strbuf` again. This refactors infer_backlink() to return an integer result and accept a pre-allocated `strbuf` for the inferred backlink path, improving efficiency.

Signed-off-by: Caleb White <cdwhite3@pm.me>
---
 worktree.c | 52 ++++++++++++++++++++++++++--------------------------
 1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/worktree.c b/worktree.c
index ec95ea2986107b3bc12d38b0825d7c6e87402bc6..ad60ba0b5843f1676e89b05eca3c82aace5fb49b 100644
--- a/worktree.c
+++ b/worktree.c
@@ -641,13 +641,15 @@ static int is_main_worktree_path(const char *path)
  * won't know which <repo>/worktrees/<id>/gitdir to repair. However, we may
  * be able to infer the gitdir by manually reading /path/to/worktree/.git,
  * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
+ *
+ * Returns -1 on failure and strbuf.len on success.
  */
-static char *infer_backlink(const char *gitfile)
+static int infer_backlink(const char *gitfile, struct strbuf *inferred)
 {
 	struct strbuf actual = STRBUF_INIT;
-	struct strbuf inferred = STRBUF_INIT;
 	const char *id;
 
+	strbuf_reset(inferred);
 	if (strbuf_read_file(&actual, gitfile, 0) < 0)
 		goto error;
 	if (!starts_with(actual.buf, "gitdir:"))
@@ -658,17 +660,16 @@ static char *infer_backlink(const char *gitfile)
 	id++; /* advance past '/' to point at <id> */
 	if (!*id)
 		goto error;
-	strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
-	if (!is_directory(inferred.buf))
+	strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
+	if (!is_directory(inferred->buf))
 		goto error;
 
 	strbuf_release(&actual);
-	return strbuf_detach(&inferred, NULL);
-
+	return inferred->len;
 error:
 	strbuf_release(&actual);
-	strbuf_release(&inferred);
-	return NULL;
+	strbuf_reset(inferred); /* clear invalid path */
+	return -1;
 }
 
 /*
@@ -680,10 +681,11 @@ void repair_worktree_at_path(const char *path,
 {
 	struct strbuf dotgit = STRBUF_INIT;
 	struct strbuf realdotgit = STRBUF_INIT;
+	struct strbuf backlink = STRBUF_INIT;
+	struct strbuf inferred_backlink = STRBUF_INIT;
 	struct strbuf gitdir = STRBUF_INIT;
 	struct strbuf olddotgit = STRBUF_INIT;
-	char *backlink = NULL;
-	char *inferred_backlink = NULL;
+	char *dotgit_contents = NULL;
 	const char *repair = NULL;
 	int err;
 
@@ -699,23 +701,23 @@ void repair_worktree_at_path(const char *path,
 		goto done;
 	}
 
-	inferred_backlink = infer_backlink(realdotgit.buf);
-	backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
-	if (err == READ_GITFILE_ERR_NOT_A_FILE) {
+	infer_backlink(realdotgit.buf, &inferred_backlink);
+	dotgit_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
+	if (dotgit_contents) {
+		strbuf_addstr(&backlink, dotgit_contents);
+	} else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
 		fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
 		goto done;
 	} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
-		if (inferred_backlink) {
+		if (inferred_backlink.len) {
 			/*
 			 * Worktree's .git file does not point at a repository
 			 * but we found a .git/worktrees/<id> in this
 			 * repository with the same <id> as recorded in the
 			 * worktree's .git file so make the worktree point at
-			 * the discovered .git/worktrees/<id>. (Note: backlink
-			 * is already NULL, so no need to free it first.)
+			 * the discovered .git/worktrees/<id>.
 			 */
-			backlink = inferred_backlink;
-			inferred_backlink = NULL;
+			strbuf_swap(&backlink, &inferred_backlink);
 		} else {
 			fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
 			goto done;
@@ -743,13 +745,10 @@ void repair_worktree_at_path(const char *path,
 	 * in the "copy" repository. In this case, point the "copy" worktree's
 	 * .git file at the "copy" repository.
 	 */
-	if (inferred_backlink && fspathcmp(backlink, inferred_backlink)) {
-		free(backlink);
-		backlink = inferred_backlink;
-		inferred_backlink = NULL;
-	}
+	if (inferred_backlink.len && fspathcmp(backlink.buf, inferred_backlink.buf))
+		strbuf_swap(&backlink, &inferred_backlink);
 
-	strbuf_addf(&gitdir, "%s/gitdir", backlink);
+	strbuf_addf(&gitdir, "%s/gitdir", backlink.buf);
 	if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
 		repair = _("gitdir unreadable");
 	else {
@@ -763,9 +762,10 @@ void repair_worktree_at_path(const char *path,
 		write_file(gitdir.buf, "%s", realdotgit.buf);
 	}
 done:
-	free(backlink);
-	free(inferred_backlink);
+	free(dotgit_contents);
 	strbuf_release(&olddotgit);
+	strbuf_release(&backlink);
+	strbuf_release(&inferred_backlink);
 	strbuf_release(&gitdir);
 	strbuf_release(&realdotgit);
 	strbuf_release(&dotgit);
-- 
2.47.0
Previous: Caleb WhiteNext: Caleb White
Message 2 of 11 in “Optionally link worktrees with relative paths”
  1. 0/5 Optionally link worktrees with relative pathsCaleb White, Oct 25, 2024
  2. 1/5 worktree: refactor infer_backlink() to use strbuf*Caleb White, Oct 25, 2024
  3. 2/5 worktree: support worktrees linked with relative pathsCaleb White, Oct 25, 2024
  4. 4/5 worktree: add test for path handling in linked worktreesCaleb White, Oct 25, 2024
  5. 3/5 worktree: optionally link worktrees with relative pathsCaleb White, Oct 25, 2024
  6. 5/5 worktree: add `relativeWorktrees` extensionCaleb White, Oct 25, 2024
  7. Caleb WhiteOct 25, 2024
  8. Taylor BlauOct 25, 2024
  9. Caleb WhiteOct 25, 2024
  10. Taylor BlauOct 25, 2024
  11. Caleb WhiteOct 25, 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.