worktree: add a function to get worktree details

The worktree structure provided for an individual worktree includes the absolute path of the worktree. The fuction to get the worktree details is a refactor of the find main/linked symref functions. Signed-off-by: Michael Rappazzo <rappazzo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Rappazzo committed Oct 8, 2015 at 13:01 UTC 51934904425e55a190b05f0a7a3de40ea486f0e5
2 files changed +130 -46
worktree.c
+108 -46
@@ -3,6 +3,17 @@
3 #include "strbuf.h"
4 #include "worktree.h"
5
6 +void free_worktrees(struct worktree **worktrees)
7 +{
8 + int i = 0;
9 +
10 + for (i = 0; worktrees[i]; i++) {
11 + free(worktrees[i]->path);
12 + free(worktrees[i]);
13 + }
14 + free (worktrees);
15 +}
16 +
17 /*
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).
@@ -38,87 +49,138 @@ static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
49 return 0;
50 }
51
41 -static char *find_main_symref(const char *symref, const char *branch)
52 +/**
53 + * get the main worktree
54 + */
55 +static struct worktree *get_main_worktree(void)
56 {
43 - struct strbuf sb = STRBUF_INIT;
57 + struct worktree *worktree = NULL;
58 struct strbuf path = STRBUF_INIT;
59 + struct strbuf worktree_path = STRBUF_INIT;
60 struct strbuf gitdir = STRBUF_INIT;
46 - char *existing = NULL;
61 + struct strbuf head_ref = STRBUF_INIT;
62
48 - strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref);
49 - if (parse_ref(path.buf, &sb, NULL) < 0)
50 - goto done;
51 - if (strcmp(sb.buf, branch))
52 - goto done;
53 - strbuf_addstr(&gitdir, get_git_common_dir());
54 - strbuf_strip_suffix(&gitdir, ".git");
55 - existing = strbuf_detach(&gitdir, NULL);
56 -done:
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, "/.");
67 +
68 + strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
69 +
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);
74 + }
75 strbuf_release(&path);
58 - strbuf_release(&sb);
76 strbuf_release(&gitdir);
60 -
61 - return existing;
77 + strbuf_release(&worktree_path);
78 + strbuf_release(&head_ref);
79 + return worktree;
80 }
81
64 -static char *find_linked_symref(const char *symref, const char *branch,
65 - const char *id)
82 +static struct worktree *get_linked_worktree(const char *id)
83 {
67 - struct strbuf sb = STRBUF_INIT;
84 + struct worktree *worktree = NULL;
85 struct strbuf path = STRBUF_INIT;
86 + struct strbuf worktree_path = STRBUF_INIT;
87 struct strbuf gitdir = STRBUF_INIT;
70 - char *existing = NULL;
88 + struct strbuf head_ref = STRBUF_INIT;
89
90 if (!id)
91 die("Missing linked worktree name");
92
75 - strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref);
76 -
77 - if (parse_ref(path.buf, &sb, NULL) < 0)
78 - goto done;
79 - if (strcmp(sb.buf, branch))
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;
99 +
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, "/.");
105 + }
106 +
107 strbuf_reset(&path);
82 - strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
83 - if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
84 - goto done;
85 - strbuf_rtrim(&gitdir);
86 - strbuf_strip_suffix(&gitdir, ".git");
108 + strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
109 +
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);
114 + }
115
88 - existing = strbuf_detach(&gitdir, NULL);
116 done:
117 strbuf_release(&path);
91 - strbuf_release(&sb);
118 strbuf_release(&gitdir);
93 -
94 - return existing;
119 + strbuf_release(&worktree_path);
120 + strbuf_release(&head_ref);
121 + return worktree;
122 }
123
97 -char *find_shared_symref(const char *symref, const char *target)
124 +struct worktree **get_worktrees(void)
125 {
126 + struct worktree **list = NULL;
127 struct strbuf path = STRBUF_INIT;
128 DIR *dir;
129 struct dirent *d;
102 - char *existing;
130 + int counter = 0, alloc = 2;
131 +
132 + list = xmalloc(alloc * sizeof(struct worktree *));
133
104 - if ((existing = find_main_symref(symref, target)))
105 - return existing;
134 + if ((list[counter] = get_main_worktree()))
135 + counter++;
136
137 strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
138 dir = opendir(path.buf);
139 strbuf_release(&path);
110 - if (!dir)
111 - return NULL;
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;
145
113 - while ((d = readdir(dir)) != NULL) {
114 - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
146 + if ((linked = get_linked_worktree(d->d_name))) {
147 + ALLOC_GROW(list, counter + 1, alloc);
148 + list[counter++] = linked;
149 + }
150 + }
151 + closedir(dir);
152 + }
153 + ALLOC_GROW(list, counter + 1, alloc);
154 + list[counter] = NULL;
155 + return list;
156 +}
157 +
158 +char *find_shared_symref(const char *symref, const char *target)
159 +{
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;
165 +
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);
170 +
171 + if (parse_ref(path.buf, &sb, NULL)) {
172 continue;
116 - existing = find_linked_symref(symref, target, d->d_name);
117 - if (existing)
118 - goto done;
173 + }
174 +
175 + if (!strcmp(sb.buf, target)) {
176 + existing = xstrdup(worktrees[i]->path);
177 + break;
178 + }
179 }
120 -done:
121 - closedir(dir);
180 +
181 + strbuf_release(&path);
182 + strbuf_release(&sb);
183 + free_worktrees(worktrees);
184
185 return existing;
186 }
worktree.h
+22
@@ -1,6 +1,28 @@
1 #ifndef WORKTREE_H
2 #define WORKTREE_H
3
4 +struct worktree {
5 + char *path;
6 + char *git_dir;
7 +};
8 +
9 +/* Functions for acting on the information about worktrees. */
10 +
11 +/*
12 + * Get the worktrees. The primary worktree will always be the first returned,
13 + * and linked worktrees will be pointed to by 'next' in each subsequent
14 + * worktree. No specific ordering is done on the linked worktrees.
15 + *
16 + * The caller is responsible for freeing the memory from the returned
17 + * worktree(s).
18 + */
19 +extern struct worktree **get_worktrees(void);
20 +
21 +/*
22 + * Free up the memory for worktree(s)
23 + */
24 +extern void free_worktrees(struct worktree **);
25 +
26 /*
27 * Check if a per-worktree symref points to a ref in the main worktree
28 * or any linked worktree, and return the path to the exising worktree