Merge branch 'nd/worktree-various-heads'
The experimental "multiple worktree" feature gains more safety to forbid operations on a branch that is checked out or being actively worked on elsewhere, by noticing that e.g. it is being rebased. * nd/worktree-various-heads: branch: do not rename a branch under bisect or rebase worktree.c: check whether branch is bisected in another worktree wt-status.c: split bisect detection out of wt_status_get_state() worktree.c: check whether branch is rebased in another worktree worktree.c: avoid referencing to worktrees[i] multiple times wt-status.c: make wt_status_check_rebase() work on any worktree wt-status.c: split rebase detection out of wt_status_get_state() path.c: refactor and add worktree_git_path() worktree.c: mark current worktree worktree.c: make find_shared_symref() return struct worktree * worktree.c: store "id" instead of "git_dir" path.c: add git_common_path() and strbuf_git_common_path() dir.c: rename str(n)cmp_icase to fspath(n)cmp
This commit is contained in:
122
worktree.c
122
worktree.c
@ -2,6 +2,8 @@
|
||||
#include "refs.h"
|
||||
#include "strbuf.h"
|
||||
#include "worktree.h"
|
||||
#include "dir.h"
|
||||
#include "wt-status.h"
|
||||
|
||||
void free_worktrees(struct worktree **worktrees)
|
||||
{
|
||||
@ -9,7 +11,7 @@ void free_worktrees(struct worktree **worktrees)
|
||||
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
free(worktrees[i]->path);
|
||||
free(worktrees[i]->git_dir);
|
||||
free(worktrees[i]->id);
|
||||
free(worktrees[i]->head_ref);
|
||||
free(worktrees[i]);
|
||||
}
|
||||
@ -74,13 +76,11 @@ static struct worktree *get_main_worktree(void)
|
||||
struct worktree *worktree = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf worktree_path = STRBUF_INIT;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
struct strbuf head_ref = STRBUF_INIT;
|
||||
int is_bare = 0;
|
||||
int is_detached = 0;
|
||||
|
||||
strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
|
||||
strbuf_addbuf(&worktree_path, &gitdir);
|
||||
strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
|
||||
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
|
||||
if (is_bare)
|
||||
strbuf_strip_suffix(&worktree_path, "/.");
|
||||
@ -92,15 +92,15 @@ static struct worktree *get_main_worktree(void)
|
||||
|
||||
worktree = xmalloc(sizeof(struct worktree));
|
||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||
worktree->git_dir = strbuf_detach(&gitdir, NULL);
|
||||
worktree->id = NULL;
|
||||
worktree->is_bare = is_bare;
|
||||
worktree->head_ref = NULL;
|
||||
worktree->is_detached = is_detached;
|
||||
worktree->is_current = 0;
|
||||
add_head_info(&head_ref, worktree);
|
||||
|
||||
done:
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&gitdir);
|
||||
strbuf_release(&worktree_path);
|
||||
strbuf_release(&head_ref);
|
||||
return worktree;
|
||||
@ -111,16 +111,13 @@ static struct worktree *get_linked_worktree(const char *id)
|
||||
struct worktree *worktree = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf worktree_path = STRBUF_INIT;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
struct strbuf head_ref = STRBUF_INIT;
|
||||
int is_detached = 0;
|
||||
|
||||
if (!id)
|
||||
die("Missing linked worktree name");
|
||||
|
||||
strbuf_addf(&gitdir, "%s/worktrees/%s",
|
||||
absolute_path(get_git_common_dir()), id);
|
||||
strbuf_addf(&path, "%s/gitdir", gitdir.buf);
|
||||
strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
|
||||
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
|
||||
/* invalid gitdir file */
|
||||
goto done;
|
||||
@ -140,20 +137,39 @@ static struct worktree *get_linked_worktree(const char *id)
|
||||
|
||||
worktree = xmalloc(sizeof(struct worktree));
|
||||
worktree->path = strbuf_detach(&worktree_path, NULL);
|
||||
worktree->git_dir = strbuf_detach(&gitdir, NULL);
|
||||
worktree->id = xstrdup(id);
|
||||
worktree->is_bare = 0;
|
||||
worktree->head_ref = NULL;
|
||||
worktree->is_detached = is_detached;
|
||||
worktree->is_current = 0;
|
||||
add_head_info(&head_ref, worktree);
|
||||
|
||||
done:
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&gitdir);
|
||||
strbuf_release(&worktree_path);
|
||||
strbuf_release(&head_ref);
|
||||
return worktree;
|
||||
}
|
||||
|
||||
static void mark_current_worktree(struct worktree **worktrees)
|
||||
{
|
||||
struct strbuf git_dir = STRBUF_INIT;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
int i;
|
||||
|
||||
strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
struct worktree *wt = worktrees[i];
|
||||
strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
|
||||
wt->is_current = !fspathcmp(git_dir.buf, path.buf);
|
||||
strbuf_reset(&path);
|
||||
if (wt->is_current)
|
||||
break;
|
||||
}
|
||||
strbuf_release(&git_dir);
|
||||
strbuf_release(&path);
|
||||
}
|
||||
|
||||
struct worktree **get_worktrees(void)
|
||||
{
|
||||
struct worktree **list = NULL;
|
||||
@ -185,35 +201,105 @@ struct worktree **get_worktrees(void)
|
||||
}
|
||||
ALLOC_GROW(list, counter + 1, alloc);
|
||||
list[counter] = NULL;
|
||||
|
||||
mark_current_worktree(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
char *find_shared_symref(const char *symref, const char *target)
|
||||
const char *get_worktree_git_dir(const struct worktree *wt)
|
||||
{
|
||||
char *existing = NULL;
|
||||
if (!wt)
|
||||
return get_git_dir();
|
||||
else if (!wt->id)
|
||||
return get_git_common_dir();
|
||||
else
|
||||
return git_common_path("worktrees/%s", wt->id);
|
||||
}
|
||||
|
||||
int is_worktree_being_rebased(const struct worktree *wt,
|
||||
const char *target)
|
||||
{
|
||||
struct wt_status_state state;
|
||||
int found_rebase;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
found_rebase = wt_status_check_rebase(wt, &state) &&
|
||||
((state.rebase_in_progress ||
|
||||
state.rebase_interactive_in_progress) &&
|
||||
state.branch &&
|
||||
starts_with(target, "refs/heads/") &&
|
||||
!strcmp(state.branch, target + strlen("refs/heads/")));
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
return found_rebase;
|
||||
}
|
||||
|
||||
int is_worktree_being_bisected(const struct worktree *wt,
|
||||
const char *target)
|
||||
{
|
||||
struct wt_status_state state;
|
||||
int found_rebase;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
found_rebase = wt_status_check_bisect(wt, &state) &&
|
||||
state.branch &&
|
||||
starts_with(target, "refs/heads/") &&
|
||||
!strcmp(state.branch, target + strlen("refs/heads/"));
|
||||
free(state.branch);
|
||||
return found_rebase;
|
||||
}
|
||||
|
||||
/*
|
||||
* note: this function should be able to detect shared symref even if
|
||||
* HEAD is temporarily detached (e.g. in the middle of rebase or
|
||||
* bisect). New commands that do similar things should update this
|
||||
* function as well.
|
||||
*/
|
||||
const struct worktree *find_shared_symref(const char *symref,
|
||||
const char *target)
|
||||
{
|
||||
const struct worktree *existing = NULL;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
struct worktree **worktrees = get_worktrees();
|
||||
static struct worktree **worktrees;
|
||||
int i = 0;
|
||||
|
||||
if (worktrees)
|
||||
free_worktrees(worktrees);
|
||||
worktrees = get_worktrees();
|
||||
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
struct worktree *wt = worktrees[i];
|
||||
|
||||
if (wt->is_detached && !strcmp(symref, "HEAD")) {
|
||||
if (is_worktree_being_rebased(wt, target)) {
|
||||
existing = wt;
|
||||
break;
|
||||
}
|
||||
if (is_worktree_being_bisected(wt, target)) {
|
||||
existing = wt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_reset(&path);
|
||||
strbuf_reset(&sb);
|
||||
strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
|
||||
strbuf_addf(&path, "%s/%s",
|
||||
get_worktree_git_dir(wt),
|
||||
symref);
|
||||
|
||||
if (parse_ref(path.buf, &sb, NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(sb.buf, target)) {
|
||||
existing = xstrdup(worktrees[i]->path);
|
||||
existing = wt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_release(&path);
|
||||
strbuf_release(&sb);
|
||||
free_worktrees(worktrees);
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user