aboutsummaryrefslogtreecommitdiffstats
path: root/worktree.c
diff options
context:
space:
mode:
Diffstat (limited to 'worktree.c')
-rw-r--r--worktree.c175
1 files changed, 98 insertions, 77 deletions
diff --git a/worktree.c b/worktree.c
index aa43c64119..70844d023a 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1,24 +1,34 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "environment.h"
+#include "gettext.h"
+#include "path.h"
#include "repository.h"
#include "refs.h"
+#include "setup.h"
#include "strbuf.h"
#include "worktree.h"
#include "dir.h"
#include "wt-status.h"
#include "config.h"
+void free_worktree(struct worktree *worktree)
+{
+ if (!worktree)
+ return;
+ free(worktree->path);
+ free(worktree->id);
+ free(worktree->head_ref);
+ free(worktree->lock_reason);
+ free(worktree->prune_reason);
+ free(worktree);
+}
+
void free_worktrees(struct worktree **worktrees)
{
int i = 0;
-
- for (i = 0; worktrees[i]; i++) {
- free(worktrees[i]->path);
- free(worktrees[i]->id);
- free(worktrees[i]->head_ref);
- free(worktrees[i]->lock_reason);
- free(worktrees[i]->prune_reason);
- free(worktrees[i]);
- }
+ for (i = 0; worktrees[i]; i++)
+ free_worktree(worktrees[i]);
free (worktrees);
}
@@ -43,10 +53,19 @@ static void add_head_info(struct worktree *wt)
wt->is_detached = 1;
}
+static int is_current_worktree(struct worktree *wt)
+{
+ char *git_dir = absolute_pathdup(get_git_dir());
+ const char *wt_git_dir = get_worktree_git_dir(wt);
+ int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+ free(git_dir);
+ return is_current;
+}
+
/**
* get the main worktree
*/
-static struct worktree *get_main_worktree(void)
+static struct worktree *get_main_worktree(int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf worktree_path = STRBUF_INIT;
@@ -55,6 +74,7 @@ static struct worktree *get_main_worktree(void)
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
/*
* NEEDSWORK: If this function is called from a secondary worktree and
@@ -65,11 +85,14 @@ static struct worktree *get_main_worktree(void)
*/
worktree->is_bare = (is_bare_repository_cfg == 1) ||
is_bare_repository();
- add_head_info(worktree);
+ worktree->is_current = is_current_worktree(worktree);
+ if (!skip_reading_head)
+ add_head_info(worktree);
return worktree;
}
-static struct worktree *get_linked_worktree(const char *id)
+struct worktree *get_linked_worktree(const char *id,
+ int skip_reading_head)
{
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
@@ -86,9 +109,12 @@ static struct worktree *get_linked_worktree(const char *id)
strbuf_strip_suffix(&worktree_path, "/.git");
CALLOC_ARRAY(worktree, 1);
+ worktree->repo = the_repository;
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
- add_head_info(worktree);
+ worktree->is_current = is_current_worktree(worktree);
+ if (!skip_reading_head)
+ add_head_info(worktree);
done:
strbuf_release(&path);
@@ -96,24 +122,14 @@ done:
return worktree;
}
-static void mark_current_worktree(struct worktree **worktrees)
-{
- char *git_dir = absolute_pathdup(get_git_dir());
- int i;
-
- for (i = 0; worktrees[i]; i++) {
- struct worktree *wt = worktrees[i];
- const char *wt_git_dir = get_worktree_git_dir(wt);
-
- if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
- wt->is_current = 1;
- break;
- }
- }
- free(git_dir);
-}
-
-struct worktree **get_worktrees(void)
+/*
+ * NEEDSWORK: This function exists so that we can look up metadata of a
+ * worktree without trying to access any of its internals like the refdb. It
+ * would be preferable to instead have a corruption-tolerant function for
+ * retrieving worktree metadata that could be used when the worktree is known
+ * to not be in a healthy state, e.g. when creating or repairing it.
+ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
{
struct worktree **list = NULL;
struct strbuf path = STRBUF_INIT;
@@ -123,7 +139,7 @@ struct worktree **get_worktrees(void)
ALLOC_ARRAY(list, alloc);
- list[counter++] = get_main_worktree();
+ list[counter++] = get_main_worktree(skip_reading_head);
strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
dir = opendir(path.buf);
@@ -132,7 +148,7 @@ struct worktree **get_worktrees(void)
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
struct worktree *linked = NULL;
- if ((linked = get_linked_worktree(d->d_name))) {
+ if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
ALLOC_GROW(list, counter + 1, alloc);
list[counter++] = linked;
}
@@ -142,10 +158,14 @@ struct worktree **get_worktrees(void)
ALLOC_GROW(list, counter + 1, alloc);
list[counter] = NULL;
- mark_current_worktree(list);
return list;
}
+struct worktree **get_worktrees(void)
+{
+ return get_worktrees_internal(0);
+}
+
const char *get_worktree_git_dir(const struct worktree *wt)
{
if (!wt)
@@ -390,9 +410,9 @@ int is_worktree_being_bisected(const struct worktree *wt,
memset(&state, 0, sizeof(state));
found_bisect = wt_status_check_bisect(wt, &state) &&
- state.branch &&
+ state.bisecting_from &&
skip_prefix(target, "refs/heads/", &target) &&
- !strcmp(state.branch, target);
+ !strcmp(state.bisecting_from, target);
wt_status_state_free_buffers(&state);
return found_bisect;
}
@@ -403,44 +423,43 @@ int is_worktree_being_bisected(const struct worktree *wt,
* bisect). New commands that do similar things should update this
* function as well.
*/
-const struct worktree *find_shared_symref(struct worktree **worktrees,
- const char *symref,
- const char *target)
+int is_shared_symref(const struct worktree *wt, const char *symref,
+ const char *target)
{
- const struct worktree *existing = NULL;
- int i = 0;
+ const char *symref_target;
+ struct ref_store *refs;
+ int flags;
- for (i = 0; worktrees[i]; i++) {
- struct worktree *wt = worktrees[i];
- const char *symref_target;
- struct ref_store *refs;
- int flags;
+ if (wt->is_bare)
+ return 0;
- if (wt->is_bare)
- continue;
+ if (wt->is_detached && !strcmp(symref, "HEAD")) {
+ if (is_worktree_being_rebased(wt, target))
+ return 1;
+ if (is_worktree_being_bisected(wt, target))
+ return 1;
+ }
- 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;
- }
- }
+ refs = get_worktree_ref_store(wt);
+ symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
+ NULL, &flags);
+ if ((flags & REF_ISSYMREF) &&
+ symref_target && !strcmp(symref_target, target))
+ return 1;
- refs = get_worktree_ref_store(wt);
- symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
- NULL, &flags);
- if ((flags & REF_ISSYMREF) &&
- symref_target && !strcmp(symref_target, target)) {
- existing = wt;
- break;
- }
- }
+ return 0;
+}
- return existing;
+const struct worktree *find_shared_symref(struct worktree **worktrees,
+ const char *symref,
+ const char *target)
+{
+
+ for (int i = 0; worktrees[i]; i++)
+ if (is_shared_symref(worktrees[i], symref, target))
+ return worktrees[i];
+
+ return NULL;
}
int submodule_uses_worktrees(const char *path)
@@ -577,15 +596,17 @@ static void repair_gitfile(struct worktree *wt,
strbuf_release(&dotgit);
}
-static void repair_noop(int iserr, const char *path, const char *msg,
- void *cb_data)
+static void repair_noop(int iserr UNUSED,
+ const char *path UNUSED,
+ const char *msg UNUSED,
+ void *cb_data UNUSED)
{
/* nothing */
}
void repair_worktrees(worktree_repair_fn fn, void *cb_data)
{
- struct worktree **worktrees = get_worktrees();
+ struct worktree **worktrees = get_worktrees_internal(1);
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
if (!fn)
@@ -781,9 +802,9 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
static int move_config_setting(const char *key, const char *value,
const char *from_file, const char *to_file)
{
- if (git_config_set_in_file_gently(to_file, key, value))
+ if (git_config_set_in_file_gently(to_file, key, NULL, value))
return error(_("unable to set %s in '%s'"), key, to_file);
- if (git_config_set_in_file_gently(from_file, key, NULL))
+ if (git_config_set_in_file_gently(from_file, key, NULL, NULL))
return error(_("unable to unset %s in '%s'"), key, from_file);
return 0;
}
@@ -801,7 +822,7 @@ int init_worktree_config(struct repository *r)
* If the extension is already enabled, then we can skip the
* upgrade process.
*/
- if (repository_format_worktree_config)
+ if (r->repository_format_worktree_config)
return 0;
if ((res = git_config_set_gently("extensions.worktreeConfig", "true")))
return error(_("failed to set extensions.worktreeConfig setting"));
@@ -830,7 +851,7 @@ int init_worktree_config(struct repository *r)
* Relocate that value to avoid breaking all worktrees with this
* upgrade to worktree config.
*/
- if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+ if (!git_configset_get_value(&cs, "core.worktree", &core_worktree, NULL)) {
if ((res = move_config_setting("core.worktree", core_worktree,
common_config_file,
main_worktree_file)))
@@ -841,7 +862,7 @@ int init_worktree_config(struct repository *r)
* Ensure that we use worktree config for the remaining lifetime
* of the current process.
*/
- repository_format_worktree_config = 1;
+ r->repository_format_worktree_config = 1;
cleanup:
git_configset_clear(&cs);