worktree: return allocated string from `get_worktree_git_dir()`

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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Patrick Steinhardt 2025-02-07 12:03:34 +01:00 committed by Junio C Hamano
parent 3859e39659
commit 8e4710f011
8 changed files with 40 additions and 15 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}


View File

@ -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"));

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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