worktree.c: add validate_worktree()
[gitweb.git] / worktree.c
index f5da7d286d537fa99a1bd2dd5180068b9d85da2f..b238d87bf1355f2b0234db5b057a1e8bd01a81f2 100644 (file)
@@ -254,6 +254,78 @@ const char *is_worktree_locked(struct worktree *wt)
        return wt->lock_reason;
 }
 
+/* convenient wrapper to deal with NULL strbuf */
+static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
+{
+       va_list params;
+
+       if (!buf)
+               return;
+
+       va_start(params, fmt);
+       strbuf_vaddf(buf, fmt, params);
+       va_end(params);
+}
+
+int validate_worktree(const struct worktree *wt, struct strbuf *errmsg)
+{
+       struct strbuf wt_path = STRBUF_INIT;
+       char *path = NULL;
+       int err, ret = -1;
+
+       strbuf_addf(&wt_path, "%s/.git", wt->path);
+
+       if (is_main_worktree(wt)) {
+               if (is_directory(wt_path.buf)) {
+                       ret = 0;
+                       goto done;
+               }
+               /*
+                * Main worktree using .git file to point to the
+                * repository would make it impossible to know where
+                * the actual worktree is if this function is executed
+                * from another worktree. No .git file support for now.
+                */
+               strbuf_addf_gently(errmsg,
+                                  _("'%s' at main working tree is not the repository directory"),
+                                  wt_path.buf);
+               goto done;
+       }
+
+       /*
+        * Make sure "gitdir" file points to a real .git file and that
+        * file points back here.
+        */
+       if (!is_absolute_path(wt->path)) {
+               strbuf_addf_gently(errmsg,
+                                  _("'%s' file does not contain absolute path to the working tree location"),
+                                  git_common_path("worktrees/%s/gitdir", wt->id));
+               goto done;
+       }
+
+       if (!file_exists(wt_path.buf)) {
+               strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
+               goto done;
+       }
+
+       path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
+       if (!path) {
+               strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
+                                  wt_path.buf, err);
+               goto done;
+       }
+
+       ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id)));
+
+       if (ret)
+               strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
+                                  wt->path, git_common_path("worktrees/%s", wt->id));
+done:
+       free(path);
+       strbuf_release(&wt_path);
+       return ret;
+}
+
 int is_worktree_being_rebased(const struct worktree *wt,
                              const char *target)
 {