git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Patrick Steinhardt <ps@pks.im>
To: git@vger.kernel.org
Cc: Karthik Nayak <karthik.188@gmail.com>, shejialuo <shejialuo@gmail.com>
Subject: [PATCH v2 09/16] worktree: return allocated string from `get_worktree_git_dir()`
Date: Fri, 07 Feb 2025 12:03:34 +0100	[thread overview]
Message-ID: <20250207-b4-pks-path-drop-the-repository-v2-9-13cad3c11b8a@pks.im> (raw)
In-Reply-To: <20250207-b4-pks-path-drop-the-repository-v2-0-13cad3c11b8a@pks.im>

The `get_worktree_git_dir()` function returns a string constant that
does not need to be free'd by the caller. This string is computed for
three different cases:

  - If we don't have a worktree we return a path into the Git directory.
    The returned string is owned by `the_repository`, so there is no
    need for the caller to free it.

  - If we have a worktree, but no worktree ID then the caller requests
    the main worktree. In this case we return a path into the common
    directory, which again is owned by `the_repository` and thus does
    not need to be free'd.

  - In the third case, where we have an actual worktree, we compute the
    path relative to "$GIT_COMMON_DIR/worktrees/". This string does not
    need to be released either, even though `git_common_path()` ends up
    allocating memory. But this doesn't result in a memory leak either
    because we write into a buffer returned by `get_pathname()`, which
    returns one out of four static buffers.

We're about to drop `git_common_path()` in favor of `repo_common_path()`,
which doesn't use the same mechanism but instead returns an allocated
string owned by the caller. While we could adapt `get_worktree_git_dir()`
to also use `get_pathname()` and print the derived common path into that
buffer, the whole schema feels a lot like premature optimization in this
context. There are some callsites where we call `get_worktree_git_dir()`
in a loop that iterates through all worktrees. But none of these loops
seem to be even remotely in the hot path, so saving a single allocation
there does not feel worth it.

Refactor the function to instead consistently return an allocated path
so that we can start using `repo_common_path()` in a subsequent commit.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 branch.c               |  7 +++++--
 builtin/fsck.c         |  8 ++++++--
 builtin/receive-pack.c |  4 +++-
 builtin/worktree.c     | 10 ++++++++--
 reachable.c            |  6 +++++-
 revision.c             |  7 ++++++-
 worktree.c             | 11 ++++++-----
 worktree.h             |  2 +-
 8 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/branch.c b/branch.c
index 77716966fe..91297d55ac 100644
--- a/branch.c
+++ b/branch.c
@@ -397,7 +397,7 @@ static void prepare_checked_out_branches(void)
 	worktrees = get_worktrees();
 
 	while (worktrees[i]) {
-		char *old;
+		char *old, *wt_gitdir;
 		struct wt_status_state state = { 0 };
 		struct worktree *wt = worktrees[i++];
 		struct string_list update_refs = STRING_LIST_INIT_DUP;
@@ -437,7 +437,8 @@ static void prepare_checked_out_branches(void)
 		}
 		wt_status_state_free_buffers(&state);
 
-		if (!sequencer_get_update_refs_state(get_worktree_git_dir(wt),
+		wt_gitdir = get_worktree_git_dir(wt);
+		if (!sequencer_get_update_refs_state(wt_gitdir,
 						     &update_refs)) {
 			struct string_list_item *item;
 			for_each_string_list_item(item, &update_refs) {
@@ -448,6 +449,8 @@ static void prepare_checked_out_branches(void)
 			}
 			string_list_clear(&update_refs, 1);
 		}
+
+		free(wt_gitdir);
 	}
 
 	free_worktrees(worktrees);
diff --git a/builtin/fsck.c b/builtin/fsck.c
index c12203e012..eea1d43647 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1057,7 +1057,7 @@ int cmd_fsck(int argc,
 			struct worktree *wt = *p;
 			struct index_state istate =
 				INDEX_STATE_INIT(the_repository);
-			char *path;
+			char *path, *wt_gitdir;
 
 			/*
 			 * Make a copy since the buffer is reusable
@@ -1065,9 +1065,13 @@ int cmd_fsck(int argc,
 			 * while we're examining the index.
 			 */
 			path = xstrdup(worktree_git_path(the_repository, wt, "index"));
-			read_index_from(&istate, path, get_worktree_git_dir(wt));
+			wt_gitdir = get_worktree_git_dir(wt);
+
+			read_index_from(&istate, path, wt_gitdir);
 			fsck_index(&istate, path, wt->is_current);
+
 			discard_index(&istate);
+			free(wt_gitdir);
 			free(path);
 		}
 		free_worktrees(worktrees);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b7ea774609..d65a441f32 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1435,7 +1435,8 @@ static const char *push_to_checkout(unsigned char *hash,
 
 static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
 {
-	const char *retval, *git_dir;
+	const char *retval;
+	char *git_dir;
 	struct strvec env = STRVEC_INIT;
 	int invoked_hook;
 
@@ -1453,6 +1454,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 		retval = push_to_deploy(sha1, &env, worktree->path);
 
 	strvec_clear(&env);
+	free(git_dir);
 	return retval;
 }
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7959b10d26..2cea9441a6 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -657,8 +657,9 @@ static int can_use_local_refs(const struct add_opts *opts)
 		if (!opts->quiet) {
 			struct strbuf path = STRBUF_INIT;
 			struct strbuf contents = STRBUF_INIT;
+			char *wt_gitdir = get_worktree_git_dir(NULL);
 
-			strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+			strbuf_add_real_path(&path, wt_gitdir);
 			strbuf_addstr(&path, "/HEAD");
 			strbuf_read_file(&contents, path.buf, 64);
 			strbuf_stripspace(&contents, NULL);
@@ -670,6 +671,7 @@ static int can_use_local_refs(const struct add_opts *opts)
 				  path.buf, contents.buf);
 			strbuf_release(&path);
 			strbuf_release(&contents);
+			free(wt_gitdir);
 		}
 		return 1;
 	}
@@ -1157,6 +1159,9 @@ static void validate_no_submodules(const struct worktree *wt)
 	struct index_state istate = INDEX_STATE_INIT(the_repository);
 	struct strbuf path = STRBUF_INIT;
 	int i, found_submodules = 0;
+	char *wt_gitdir;
+
+	wt_gitdir = get_worktree_git_dir(wt);
 
 	if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
 		/*
@@ -1166,7 +1171,7 @@ static void validate_no_submodules(const struct worktree *wt)
 		 */
 		found_submodules = 1;
 	} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
-				   get_worktree_git_dir(wt)) > 0) {
+				   wt_gitdir) > 0) {
 		for (i = 0; i < istate.cache_nr; i++) {
 			struct cache_entry *ce = istate.cache[i];
 			int err;
@@ -1185,6 +1190,7 @@ static void validate_no_submodules(const struct worktree *wt)
 	}
 	discard_index(&istate);
 	strbuf_release(&path);
+	free(wt_gitdir);
 
 	if (found_submodules)
 		die(_("working trees containing submodules cannot be moved or removed"));
diff --git a/reachable.c b/reachable.c
index ecf7ccf504..9ee04c89ec 100644
--- a/reachable.c
+++ b/reachable.c
@@ -65,8 +65,10 @@ static void add_rebase_files(struct rev_info *revs)
 	struct worktree **worktrees = get_worktrees();
 
 	for (struct worktree **wt = worktrees; *wt; wt++) {
+		char *wt_gitdir = get_worktree_git_dir(*wt);
+
 		strbuf_reset(&buf);
-		strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+		strbuf_addstr(&buf, wt_gitdir);
 		strbuf_complete(&buf, '/');
 		len = buf.len;
 		for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
@@ -74,6 +76,8 @@ static void add_rebase_files(struct rev_info *revs)
 			strbuf_addstr(&buf, path[i]);
 			add_one_file(buf.buf, revs);
 		}
+
+		free(wt_gitdir);
 	}
 	strbuf_release(&buf);
 	free_worktrees(worktrees);
diff --git a/revision.c b/revision.c
index 474fa1e767..c4390f0938 100644
--- a/revision.c
+++ b/revision.c
@@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
 	for (p = worktrees; *p; p++) {
 		struct worktree *wt = *p;
 		struct index_state istate = INDEX_STATE_INIT(revs->repo);
+		char *wt_gitdir;
 
 		if (wt->is_current)
 			continue; /* current index already taken care of */
 
+		wt_gitdir = get_worktree_git_dir(wt);
+
 		if (read_index_from(&istate,
 				    worktree_git_path(the_repository, wt, "index"),
-				    get_worktree_git_dir(wt)) > 0)
+				    wt_gitdir) > 0)
 			do_add_index_objects_to_pending(revs, &istate, flags);
+
 		discard_index(&istate);
+		free(wt_gitdir);
 	}
 	free_worktrees(worktrees);
 }
diff --git a/worktree.c b/worktree.c
index 8f4fc10c44..3b94535963 100644
--- a/worktree.c
+++ b/worktree.c
@@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
 static int is_current_worktree(struct worktree *wt)
 {
 	char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
-	const char *wt_git_dir = get_worktree_git_dir(wt);
+	char *wt_git_dir = get_worktree_git_dir(wt);
 	int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+	free(wt_git_dir);
 	free(git_dir);
 	return is_current;
 }
@@ -175,14 +176,14 @@ struct worktree **get_worktrees(void)
 	return get_worktrees_internal(0);
 }
 
-const char *get_worktree_git_dir(const struct worktree *wt)
+char *get_worktree_git_dir(const struct worktree *wt)
 {
 	if (!wt)
-		return repo_get_git_dir(the_repository);
+		return xstrdup(repo_get_git_dir(the_repository));
 	else if (!wt->id)
-		return repo_get_common_dir(the_repository);
+		return xstrdup(repo_get_common_dir(the_repository));
 	else
-		return git_common_path("worktrees/%s", wt->id);
+		return xstrdup(git_common_path("worktrees/%s", wt->id));
 }
 
 static struct worktree *find_worktree_by_suffix(struct worktree **list,
diff --git a/worktree.h b/worktree.h
index 38145df80f..16368588a0 100644
--- a/worktree.h
+++ b/worktree.h
@@ -39,7 +39,7 @@ int submodule_uses_worktrees(const char *path);
  * Return git dir of the worktree. Note that the path may be relative.
  * If wt is NULL, git dir of current worktree is returned.
  */
-const char *get_worktree_git_dir(const struct worktree *wt);
+char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
  * Search for the worktree identified unambiguously by `arg` -- typically

-- 
2.48.1.538.gc4cfc42d60.dirty


  parent reply	other threads:[~2025-02-07 11:03 UTC|newest]

Thread overview: 64+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-06  7:57 [PATCH 00/16] path: remove dependency on `the_repository` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
2025-02-06 11:17   ` Karthik Nayak
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06 14:21   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
2025-02-06 11:53   ` Karthik Nayak
2025-02-07  6:15     ` Patrick Steinhardt
2025-02-06  7:57 ` [PATCH 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
2025-02-06 12:05   ` Karthik Nayak
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-07  7:04       ` Karthik Nayak
2025-02-06 15:03   ` shejialuo
2025-02-06  7:58 ` [PATCH 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 09/16] worktree: return allocated string from `get_worktree_git_dir()` Patrick Steinhardt
2025-02-07  7:15   ` Karthik Nayak
2025-02-07 10:49     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
2025-02-06 15:54   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-06 16:01   ` shejialuo
2025-02-07  6:16     ` Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
2025-02-06  7:58 ` [PATCH 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
2025-02-06 16:14 ` [PATCH 00/16] path: remove dependency on `the_repository` shejialuo
2025-02-07  6:16   ` Patrick Steinhardt
2025-02-07  8:17 ` Karthik Nayak
2025-02-07 11:03 ` [PATCH v2 " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 01/16] path: refactor `repo_common_path()` family of functions Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 02/16] path: refactor `repo_git_path()` " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 03/16] path: refactor `repo_worktree_path()` " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 04/16] submodule: refactor `submodule_to_gitdir()` to accept a repo Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 05/16] path: refactor `repo_submodule_path()` family of functions Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 06/16] path: drop unused `strbuf_git_path()` function Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 07/16] path: drop `git_pathdup()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 08/16] path: drop `git_path_buf()` in favor of `repo_git_path_replace()` Patrick Steinhardt
2025-02-07 11:03   ` Patrick Steinhardt [this message]
2025-02-07 11:03   ` [PATCH v2 10/16] path: drop `git_common_path()` in favor of `repo_common_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 11/16] rerere: let `rerere_path()` write paths into a caller-provided buffer Patrick Steinhardt
2025-02-22  7:20     ` Jeff King
2025-02-24 16:14       ` Junio C Hamano
2025-02-24 22:19         ` Jeff King
2025-02-24 22:50           ` Junio C Hamano
2025-02-24 23:10             ` Jeff King
2025-02-24 23:14               ` Junio C Hamano
2025-02-25  6:24                 ` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 12/16] path: drop `git_path()` in favor of `repo_git_path()` Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 13/16] repo-settings: introduce function to clear struct Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 14/16] environment: move access to "core.hooksPath" into repo settings Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 15/16] environment: move access to "core.sharedRepository" " Patrick Steinhardt
2025-02-07 11:03   ` [PATCH v2 16/16] path: adjust last remaining users of `the_repository` Patrick Steinhardt
2025-02-07 11:44   ` [PATCH v2 00/16] path: remove dependency on `the_repository` Karthik Nayak
2025-02-08 15:31   ` shejialuo
2025-02-10 18:32     ` Junio C Hamano
2025-02-11 10:03       ` shejialuo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250207-b4-pks-path-drop-the-repository-v2-9-13cad3c11b8a@pks.im \
    --to=ps@pks.im \
    --cc=git@vger.kernel.org \
    --cc=karthik.188@gmail.com \
    --cc=shejialuo@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).