Merge branch 'pw/no-more-NULL-means-current-worktree' into jch

API clean-up for the worktree subsystem.

* pw/no-more-NULL-means-current-worktree:
  path: remove repository argument from worktree_git_path()
  wt-status: avoid passing NULL worktree
This commit is contained in:
Junio C Hamano
2026-03-03 11:08:21 -08:00
9 changed files with 66 additions and 25 deletions

View File

@@ -1105,7 +1105,7 @@ int cmd_fsck(int argc,
* and may get overwritten by other calls
* while we're examining the index.
*/
path = xstrdup(worktree_git_path(the_repository, wt, "index"));
path = xstrdup(worktree_git_path(wt, "index"));
wt_gitdir = get_worktree_git_dir(wt);
read_index_from(&istate, path, wt_gitdir);

View File

@@ -1225,14 +1225,14 @@ static void validate_no_submodules(const struct worktree *wt)
wt_gitdir = get_worktree_git_dir(wt);
if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
if (is_directory(worktree_git_path(wt, "modules"))) {
/*
* There could be false positives, e.g. the "modules"
* directory exists but is empty. But it's a rare case and
* this simpler check is probably good enough for now.
*/
found_submodules = 1;
} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
} else if (read_index_from(&istate, worktree_git_path(wt, "index"),
wt_gitdir) > 0) {
for (i = 0; i < istate.cache_nr; i++) {
struct cache_entry *ce = istate.cache[i];

9
path.c
View File

@@ -486,17 +486,16 @@ const char *mkpath(const char *fmt, ...)
return cleanup_path(pathname->buf);
}
const char *worktree_git_path(struct repository *r,
const struct worktree *wt, const char *fmt, ...)
const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
{
struct strbuf *pathname = get_pathname();
va_list args;
if (wt && wt->repo != r)
BUG("worktree not connected to expected repository");
if (!wt)
BUG("%s() called with NULL worktree", __func__);
va_start(args, fmt);
repo_git_pathv(r, wt, pathname, fmt, args);
repo_git_pathv(wt->repo, wt, pathname, fmt, args);
va_end(args);
return pathname->buf;
}

8
path.h
View File

@@ -66,13 +66,11 @@ const char *repo_git_path_replace(struct repository *repo,
/*
* Similar to repo_git_path() but can produce paths for a specified
* worktree instead of current one. When no worktree is given, then the path is
* computed relative to main worktree of the given repository.
* worktree instead of current one.
*/
const char *worktree_git_path(struct repository *r,
const struct worktree *wt,
const char *worktree_git_path(const struct worktree *wt,
const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
__attribute__((format (printf, 2, 3)));
/*
* The `repo_worktree_path` family of functions will construct a path into a

View File

@@ -1848,7 +1848,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
wt_gitdir = get_worktree_git_dir(wt);
if (read_index_from(&istate,
worktree_git_path(the_repository, wt, "index"),
worktree_git_path(wt, "index"),
wt_gitdir) > 0)
do_add_index_objects_to_pending(revs, &istate, flags);

View File

@@ -594,6 +594,15 @@ EOF
test_cmp expected actual
'
test_expect_success 'rebase in a linked worktree' '
test_might_fail git rebase --abort &&
git worktree add wt &&
test_when_finished "test_might_fail git -C wt rebase --abort;
git worktree remove wt" &&
GIT_SEQUENCE_EDITOR="echo break >" git -C wt rebase -i HEAD &&
git -C wt status >actual &&
test_grep "interactive rebase in progress" actual
'
test_expect_success 'prepare am_session' '
git reset --hard main &&

View File

@@ -66,6 +66,26 @@ static int is_current_worktree(struct worktree *wt)
return is_current;
}
struct worktree *get_worktree_from_repository(struct repository *repo)
{
struct worktree *wt = xcalloc(1, sizeof(*wt));
char *gitdir = absolute_pathdup(repo->gitdir);
char *commondir = absolute_pathdup(repo->commondir);
wt->repo = repo;
wt->path = absolute_pathdup(repo->worktree ? repo->worktree
: repo->gitdir);
wt->is_bare = !repo->worktree;
if (fspathcmp(gitdir, commondir))
wt->id = xstrdup(find_last_dir_sep(gitdir) + 1);
wt->is_current = is_current_worktree(wt);
add_head_info(wt);
free(gitdir);
free(commondir);
return wt;
}
/*
* When in a secondary worktree, and when extensions.worktreeConfig
* is true, only $commondir/config and $commondir/worktrees/<id>/
@@ -288,7 +308,7 @@ const char *worktree_lock_reason(struct worktree *wt)
if (!wt->lock_reason_valid) {
struct strbuf path = STRBUF_INIT;
strbuf_addstr(&path, worktree_git_path(the_repository, wt, "locked"));
strbuf_addstr(&path, worktree_git_path(wt, "locked"));
if (file_exists(path.buf)) {
struct strbuf lock_reason = STRBUF_INIT;
if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)

View File

@@ -38,6 +38,12 @@ struct worktree **get_worktrees(void);
*/
struct worktree **get_worktrees_without_reading_head(void);
/*
* Construct a struct worktree corresponding to repo->gitdir and
* repo->worktree.
*/
struct worktree *get_worktree_from_repository(struct repository *repo);
/*
* Returns 1 if linked worktrees exist, 0 otherwise.
*/

View File

@@ -1648,7 +1648,7 @@ static char *get_branch(const struct worktree *wt, const char *path)
struct object_id oid;
const char *branch_name;
if (strbuf_read_file(&sb, worktree_git_path(the_repository, wt, "%s", path), 0) <= 0)
if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
goto got_nothing;
while (sb.len && sb.buf[sb.len - 1] == '\n')
@@ -1747,18 +1747,21 @@ int wt_status_check_rebase(const struct worktree *wt,
{
struct stat st;
if (!stat(worktree_git_path(the_repository, wt, "rebase-apply"), &st)) {
if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/applying"), &st)) {
if (!wt)
BUG("wt_status_check_rebase() called with NULL worktree");
if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
state->am_in_progress = 1;
if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/patch"), &st) && !st.st_size)
if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
state->am_empty_patch = 1;
} else {
state->rebase_in_progress = 1;
state->branch = get_branch(wt, "rebase-apply/head-name");
state->onto = get_branch(wt, "rebase-apply/onto");
}
} else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) {
if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st))
} else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
state->rebase_interactive_in_progress = 1;
else
state->rebase_in_progress = 1;
@@ -1774,7 +1777,10 @@ int wt_status_check_bisect(const struct worktree *wt,
{
struct stat st;
if (!stat(worktree_git_path(the_repository, wt, "BISECT_LOG"), &st)) {
if (!wt)
BUG("wt_status_check_bisect() called with NULL worktree");
if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
state->bisect_in_progress = 1;
state->bisecting_from = get_branch(wt, "BISECT_START");
return 1;
@@ -1819,18 +1825,19 @@ void wt_status_get_state(struct repository *r,
struct stat st;
struct object_id oid;
enum replay_action action;
struct worktree *wt = get_worktree_from_repository(r);
if (!stat(git_path_merge_head(r), &st)) {
wt_status_check_rebase(NULL, state);
wt_status_check_rebase(wt, state);
state->merge_in_progress = 1;
} else if (wt_status_check_rebase(NULL, state)) {
} else if (wt_status_check_rebase(wt, state)) {
; /* all set */
} else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
!repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
state->cherry_pick_in_progress = 1;
oidcpy(&state->cherry_pick_head_oid, &oid);
}
wt_status_check_bisect(NULL, state);
wt_status_check_bisect(wt, state);
if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
!repo_get_oid(r, "REVERT_HEAD", &oid)) {
state->revert_in_progress = 1;
@@ -1848,6 +1855,8 @@ void wt_status_get_state(struct repository *r,
if (get_detached_from)
wt_status_get_detached_from(r, state);
wt_status_check_sparse_checkout(r, state);
free_worktree(wt);
}
static void wt_longstatus_print_state(struct wt_status *s)