worktree: add top-level worktree.c
[git/git.git] / worktree.c
CommitLineData
ac6c561b
MR
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}