worktree: add a function to get worktree details
[git/git-svn.git] / worktree.c
blobc2e6db0a2c5120509b958014ccf4f882df4ddf29
1 #include "cache.h"
2 #include "refs.h"
3 #include "strbuf.h"
4 #include "worktree.h"
6 void free_worktrees(struct worktree **worktrees)
8 int i = 0;
10 for (i = 0; worktrees[i]; i++) {
11 free(worktrees[i]->path);
12 free(worktrees[i]);
14 free (worktrees);
18 * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
19 * set is_detached to 1 (0) if the ref is detatched (is not detached).
21 * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
22 * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
23 * git_path). Parse the ref ourselves.
25 * return -1 if the ref is not a proper ref, 0 otherwise (success)
27 static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
29 if (is_detached)
30 *is_detached = 0;
31 if (!strbuf_readlink(ref, path_to_ref, 0)) {
32 /* HEAD is symbolic link */
33 if (!starts_with(ref->buf, "refs/") ||
34 check_refname_format(ref->buf, 0))
35 return -1;
36 } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
37 /* textual symref or detached */
38 if (!starts_with(ref->buf, "ref:")) {
39 if (is_detached)
40 *is_detached = 1;
41 } else {
42 strbuf_remove(ref, 0, strlen("ref:"));
43 strbuf_trim(ref);
44 if (check_refname_format(ref->buf, 0))
45 return -1;
47 } else
48 return -1;
49 return 0;
52 /**
53 * get the main worktree
55 static struct worktree *get_main_worktree(void)
57 struct worktree *worktree = NULL;
58 struct strbuf path = STRBUF_INIT;
59 struct strbuf worktree_path = STRBUF_INIT;
60 struct strbuf gitdir = STRBUF_INIT;
61 struct strbuf head_ref = STRBUF_INIT;
63 strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
64 strbuf_addbuf(&worktree_path, &gitdir);
65 if (!strbuf_strip_suffix(&worktree_path, "/.git"))
66 strbuf_strip_suffix(&worktree_path, "/.");
68 strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
70 if (parse_ref(path.buf, &head_ref, NULL) >= 0) {
71 worktree = xmalloc(sizeof(struct worktree));
72 worktree->path = strbuf_detach(&worktree_path, NULL);
73 worktree->git_dir = strbuf_detach(&gitdir, NULL);
75 strbuf_release(&path);
76 strbuf_release(&gitdir);
77 strbuf_release(&worktree_path);
78 strbuf_release(&head_ref);
79 return worktree;
82 static struct worktree *get_linked_worktree(const char *id)
84 struct worktree *worktree = NULL;
85 struct strbuf path = STRBUF_INIT;
86 struct strbuf worktree_path = STRBUF_INIT;
87 struct strbuf gitdir = STRBUF_INIT;
88 struct strbuf head_ref = STRBUF_INIT;
90 if (!id)
91 die("Missing linked worktree name");
93 strbuf_addf(&gitdir, "%s/worktrees/%s",
94 absolute_path(get_git_common_dir()), id);
95 strbuf_addf(&path, "%s/gitdir", gitdir.buf);
96 if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
97 /* invalid gitdir file */
98 goto done;
100 strbuf_rtrim(&worktree_path);
101 if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
102 strbuf_reset(&worktree_path);
103 strbuf_addstr(&worktree_path, absolute_path("."));
104 strbuf_strip_suffix(&worktree_path, "/.");
107 strbuf_reset(&path);
108 strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
110 if (parse_ref(path.buf, &head_ref, NULL) >= 0) {
111 worktree = xmalloc(sizeof(struct worktree));
112 worktree->path = strbuf_detach(&worktree_path, NULL);
113 worktree->git_dir = strbuf_detach(&gitdir, NULL);
116 done:
117 strbuf_release(&path);
118 strbuf_release(&gitdir);
119 strbuf_release(&worktree_path);
120 strbuf_release(&head_ref);
121 return worktree;
124 struct worktree **get_worktrees(void)
126 struct worktree **list = NULL;
127 struct strbuf path = STRBUF_INIT;
128 DIR *dir;
129 struct dirent *d;
130 int counter = 0, alloc = 2;
132 list = xmalloc(alloc * sizeof(struct worktree *));
134 if ((list[counter] = get_main_worktree()))
135 counter++;
137 strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
138 dir = opendir(path.buf);
139 strbuf_release(&path);
140 if (dir) {
141 while ((d = readdir(dir)) != NULL) {
142 struct worktree *linked = NULL;
143 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
144 continue;
146 if ((linked = get_linked_worktree(d->d_name))) {
147 ALLOC_GROW(list, counter + 1, alloc);
148 list[counter++] = linked;
151 closedir(dir);
153 ALLOC_GROW(list, counter + 1, alloc);
154 list[counter] = NULL;
155 return list;
158 char *find_shared_symref(const char *symref, const char *target)
160 char *existing = NULL;
161 struct strbuf path = STRBUF_INIT;
162 struct strbuf sb = STRBUF_INIT;
163 struct worktree **worktrees = get_worktrees();
164 int i = 0;
166 for (i = 0; worktrees[i]; i++) {
167 strbuf_reset(&path);
168 strbuf_reset(&sb);
169 strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
171 if (parse_ref(path.buf, &sb, NULL)) {
172 continue;
175 if (!strcmp(sb.buf, target)) {
176 existing = xstrdup(worktrees[i]->path);
177 break;
181 strbuf_release(&path);
182 strbuf_release(&sb);
183 free_worktrees(worktrees);
185 return existing;