]> git.ipfire.org Git - thirdparty/git.git/blobdiff - worktree.c
Merge branch 'ma/list-object-filter-opt-msgfix'
[thirdparty/git.git] / worktree.c
index 6a236d2e7251873f3f26023f0bd4407e684cbd31..f84ceae87d3847019c45c335f1015ff15b8bfe0e 100644 (file)
@@ -352,11 +352,11 @@ int is_worktree_being_rebased(const struct worktree *wt,
 
        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/")));
+                      (state.rebase_in_progress ||
+                       state.rebase_interactive_in_progress) &&
+                      state.branch &&
+                      skip_prefix(target, "refs/heads/", &target) &&
+                      !strcmp(state.branch, target);
        wt_status_state_free_buffers(&state);
        return found_rebase;
 }
@@ -370,8 +370,8 @@ int is_worktree_being_bisected(const struct worktree *wt,
        memset(&state, 0, sizeof(state));
        found_bisect = wt_status_check_bisect(wt, &state) &&
                       state.branch &&
-                      starts_with(target, "refs/heads/") &&
-                      !strcmp(state.branch, target + strlen("refs/heads/"));
+                      skip_prefix(target, "refs/heads/", &target) &&
+                      !strcmp(state.branch, target);
        wt_status_state_free_buffers(&state);
        return found_bisect;
 }
@@ -565,3 +565,138 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
        strbuf_release(&refname);
        return ret;
 }
+
+/*
+ * Repair worktree's /path/to/worktree/.git file if missing, corrupt, or not
+ * pointing at <repo>/worktrees/<id>.
+ */
+static void repair_gitfile(struct worktree *wt,
+                          worktree_repair_fn fn, void *cb_data)
+{
+       struct strbuf dotgit = STRBUF_INIT;
+       struct strbuf repo = STRBUF_INIT;
+       char *backlink;
+       const char *repair = NULL;
+       int err;
+
+       /* missing worktree can't be repaired */
+       if (!file_exists(wt->path))
+               return;
+
+       if (!is_directory(wt->path)) {
+               fn(1, wt->path, _("not a directory"), cb_data);
+               return;
+       }
+
+       strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+       strbuf_addf(&dotgit, "%s/.git", wt->path);
+       backlink = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
+
+       if (err == READ_GITFILE_ERR_NOT_A_FILE)
+               fn(1, wt->path, _(".git is not a file"), cb_data);
+       else if (err)
+               repair = _(".git file broken");
+       else if (fspathcmp(backlink, repo.buf))
+               repair = _(".git file incorrect");
+
+       if (repair) {
+               fn(0, wt->path, repair, cb_data);
+               write_file(dotgit.buf, "gitdir: %s", repo.buf);
+       }
+
+       free(backlink);
+       strbuf_release(&repo);
+       strbuf_release(&dotgit);
+}
+
+static void repair_noop(int iserr, const char *path, const char *msg,
+                       void *cb_data)
+{
+       /* nothing */
+}
+
+void repair_worktrees(worktree_repair_fn fn, void *cb_data)
+{
+       struct worktree **worktrees = get_worktrees();
+       struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
+
+       if (!fn)
+               fn = repair_noop;
+       for (; *wt; wt++)
+               repair_gitfile(*wt, fn, cb_data);
+       free_worktrees(worktrees);
+}
+
+static int is_main_worktree_path(const char *path)
+{
+       struct strbuf target = STRBUF_INIT;
+       struct strbuf maindir = STRBUF_INIT;
+       int cmp;
+
+       strbuf_add_real_path(&target, path);
+       strbuf_strip_suffix(&target, "/.git");
+       strbuf_add_real_path(&maindir, get_git_common_dir());
+       strbuf_strip_suffix(&maindir, "/.git");
+       cmp = fspathcmp(maindir.buf, target.buf);
+
+       strbuf_release(&maindir);
+       strbuf_release(&target);
+       return !cmp;
+}
+
+/*
+ * Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
+ * the worktree's path.
+ */
+void repair_worktree_at_path(const char *path,
+                            worktree_repair_fn fn, void *cb_data)
+{
+       struct strbuf dotgit = STRBUF_INIT;
+       struct strbuf realdotgit = STRBUF_INIT;
+       struct strbuf gitdir = STRBUF_INIT;
+       struct strbuf olddotgit = STRBUF_INIT;
+       char *backlink = NULL;
+       const char *repair = NULL;
+       int err;
+
+       if (!fn)
+               fn = repair_noop;
+
+       if (is_main_worktree_path(path))
+               goto done;
+
+       strbuf_addf(&dotgit, "%s/.git", path);
+       if (!strbuf_realpath(&realdotgit, dotgit.buf, 0)) {
+               fn(1, path, _("not a valid path"), cb_data);
+               goto done;
+       }
+
+       backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
+       if (err == READ_GITFILE_ERR_NOT_A_FILE) {
+               fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
+               goto done;
+       } else if (err) {
+               fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
+               goto done;
+       }
+
+       strbuf_addf(&gitdir, "%s/gitdir", backlink);
+       if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
+               repair = _("gitdir unreadable");
+       else {
+               strbuf_rtrim(&olddotgit);
+               if (fspathcmp(olddotgit.buf, realdotgit.buf))
+                       repair = _("gitdir incorrect");
+       }
+
+       if (repair) {
+               fn(0, gitdir.buf, repair, cb_data);
+               write_file(gitdir.buf, "%s", realdotgit.buf);
+       }
+done:
+       free(backlink);
+       strbuf_release(&olddotgit);
+       strbuf_release(&gitdir);
+       strbuf_release(&realdotgit);
+       strbuf_release(&dotgit);
+}