worktree.con commit worktree: add top-level worktree.c (ac6c561)
   1#include "cache.h"
   2#include "refs.h"
   3#include "strbuf.h"
   4#include "worktree.h"
   5
   6static char *find_linked_symref(const char *symref, const char *branch,
   7                                const char *id)
   8{
   9        struct strbuf sb = STRBUF_INIT;
  10        struct strbuf path = STRBUF_INIT;
  11        struct strbuf gitdir = STRBUF_INIT;
  12        char *existing = NULL;
  13
  14        /*
  15         * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside
  16         * $GIT_DIR so resolve_ref_unsafe() won't work (it uses
  17         * git_path). Parse the ref ourselves.
  18         */
  19        if (id)
  20                strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref);
  21        else
  22                strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref);
  23
  24        if (!strbuf_readlink(&sb, path.buf, 0)) {
  25                if (!starts_with(sb.buf, "refs/") ||
  26                    check_refname_format(sb.buf, 0))
  27                        goto done;
  28        } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 &&
  29            starts_with(sb.buf, "ref:")) {
  30                strbuf_remove(&sb, 0, strlen("ref:"));
  31                strbuf_trim(&sb);
  32        } else
  33                goto done;
  34        if (strcmp(sb.buf, branch))
  35                goto done;
  36        if (id) {
  37                strbuf_reset(&path);
  38                strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
  39                if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
  40                        goto done;
  41                strbuf_rtrim(&gitdir);
  42        } else
  43                strbuf_addstr(&gitdir, get_git_common_dir());
  44        strbuf_strip_suffix(&gitdir, ".git");
  45
  46        existing = strbuf_detach(&gitdir, NULL);
  47done:
  48        strbuf_release(&path);
  49        strbuf_release(&sb);
  50        strbuf_release(&gitdir);
  51
  52        return existing;
  53}
  54
  55char *find_shared_symref(const char *symref, const char *target)
  56{
  57        struct strbuf path = STRBUF_INIT;
  58        DIR *dir;
  59        struct dirent *d;
  60        char *existing;
  61
  62        if ((existing = find_linked_symref(symref, target, NULL)))
  63                return existing;
  64
  65        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
  66        dir = opendir(path.buf);
  67        strbuf_release(&path);
  68        if (!dir)
  69                return NULL;
  70
  71        while ((d = readdir(dir)) != NULL) {
  72                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
  73                        continue;
  74                existing = find_linked_symref(symref, target, d->d_name);
  75                if (existing)
  76                        goto done;
  77        }
  78done:
  79        closedir(dir);
  80
  81        return existing;
  82}