]> git.ipfire.org Git - thirdparty/git.git/blobdiff - worktree.c
Merge branch 'jc/add-i-use-builtin-experimental'
[thirdparty/git.git] / worktree.c
index 62217b4a6b26b4efad52643d25f21e7c98a2fa3e..46a5fb844766a74afee9223354801f26b7e3a908 100644 (file)
@@ -571,3 +571,138 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
        free_worktrees(worktrees);
        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);
+}