Unify view refresh checking
[tig.git] / src / refdb.c
blob0a786d2c94229bc85a6ec34aac81c1b2c14ca34f
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/argv.h"
16 #include "tig/util.h"
17 #include "tig/io.h"
18 #include "tig/options.h"
19 #include "tig/repo.h"
20 #include "tig/refdb.h"
22 static struct ref **refs = NULL;
23 static size_t refs_size = 0;
24 static struct ref *refs_head = NULL;
26 static struct ref_list **ref_lists = NULL;
27 static size_t ref_lists_size = 0;
29 DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
30 DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
31 DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
33 static int
34 compare_refs(const void *ref1_, const void *ref2_)
36 const struct ref *ref1 = *(const struct ref **)ref1_;
37 const struct ref *ref2 = *(const struct ref **)ref2_;
39 return ref_compare(ref1, ref2);
42 int
43 ref_compare(const struct ref *ref1, const struct ref *ref2)
45 if (ref1->type != ref2->type)
46 return ref1->type - ref2->type;
47 return strcmp_numeric(ref1->name, ref2->name);
50 void
51 foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data)
53 size_t i;
55 for (i = 0; i < refs_size; i++)
56 if (refs[i]->id[0] && !visitor(data, refs[i]))
57 break;
60 struct ref *
61 get_ref_head()
63 return refs_head;
66 struct ref_list *
67 get_ref_list(const char *id)
69 struct ref_list *list;
70 size_t i;
72 for (i = 0; i < ref_lists_size; i++)
73 if (!strcmp(id, ref_lists[i]->id))
74 return ref_lists[i];
76 if (!realloc_ref_lists(&ref_lists, ref_lists_size, 1))
77 return NULL;
78 list = calloc(1, sizeof(*list));
79 if (!list)
80 return NULL;
81 string_copy_rev(list->id, id);
83 for (i = 0; i < refs_size; i++) {
84 if (!strcmp(id, refs[i]->id) &&
85 realloc_refs_list(&list->refs, list->size, 1))
86 list->refs[list->size++] = refs[i];
89 if (!list->refs) {
90 free(list);
91 return NULL;
94 qsort(list->refs, list->size, sizeof(*list->refs), compare_refs);
95 ref_lists[ref_lists_size++] = list;
96 return list;
99 struct ref_opt {
100 const char *remote;
101 const char *head;
104 static void
105 done_ref_lists(void)
107 int i;
109 for (i = 0; i < ref_lists_size; i++) {
110 struct ref_list *list = ref_lists[i];
112 free(list->refs);
113 free(list);
116 free(ref_lists);
117 ref_lists = NULL;
118 ref_lists_size = 0;
121 static int
122 add_to_refs(const char *id, size_t idlen, char *name, size_t namelen, struct ref_opt *opt)
124 struct ref *ref = NULL;
125 enum reference_type type = REFERENCE_BRANCH;
126 int pos;
128 if (!prefixcmp(name, "refs/tags/")) {
129 type = REFERENCE_TAG;
130 if (!suffixcmp(name, namelen, "^{}")) {
131 namelen -= 3;
132 name[namelen] = 0;
133 } else {
134 type = REFERENCE_LOCAL_TAG;
137 namelen -= STRING_SIZE("refs/tags/");
138 name += STRING_SIZE("refs/tags/");
140 } else if (!prefixcmp(name, "refs/remotes/")) {
141 type = REFERENCE_REMOTE;
142 namelen -= STRING_SIZE("refs/remotes/");
143 name += STRING_SIZE("refs/remotes/");
144 if (!strcmp(opt->remote, name))
145 type = REFERENCE_TRACKED_REMOTE;
147 } else if (!prefixcmp(name, "refs/replace/")) {
148 type = REFERENCE_REPLACE;
149 id = name + strlen("refs/replace/");
150 idlen = namelen - strlen("refs/replace/");
151 name = "replaced";
152 namelen = strlen(name);
154 } else if (!prefixcmp(name, "refs/heads/")) {
155 namelen -= STRING_SIZE("refs/heads/");
156 name += STRING_SIZE("refs/heads/");
157 if (strlen(opt->head) == namelen &&
158 !strncmp(opt->head, name, namelen))
159 type = REFERENCE_HEAD;
161 } else if (!strcmp(name, "HEAD")) {
162 /* Handle the case of HEAD not being a symbolic ref,
163 * i.e. during a rebase. */
164 if (*opt->head)
165 return OK;
166 type = REFERENCE_HEAD;
169 /* If we are reloading or it's an annotated tag, replace the
170 * previous SHA1 with the resolved commit id; relies on the fact
171 * git-ls-remote lists the commit id of an annotated tag right
172 * before the commit id it points to. */
173 for (pos = 0; pos < refs_size; pos++) {
174 int cmp = type == REFERENCE_REPLACE
175 ? strcmp(id, refs[pos]->id) : strcmp(name, refs[pos]->name);
177 if (!cmp) {
178 ref = refs[pos];
179 break;
183 if (!ref) {
184 if (!realloc_refs(&refs, refs_size, 1))
185 return ERR;
186 ref = calloc(1, sizeof(*ref) + namelen);
187 if (!ref)
188 return ERR;
189 refs[refs_size++] = ref;
190 strncpy(ref->name, name, namelen);
193 ref->valid = TRUE;
194 ref->type = type;
195 string_ncopy_do(ref->id, SIZEOF_REV, id, idlen);
197 if (type == REFERENCE_HEAD)
198 refs_head = ref;
199 return OK;
202 static int
203 read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data)
205 return add_to_refs(id, idlen, name, namelen, data);
208 static int
209 reload_refs(const char *git_dir, const char *remote_name, char *head, size_t headlen)
211 const char *head_argv[] = {
212 "git", "symbolic-ref", "HEAD", NULL
214 const char *ls_remote_argv[SIZEOF_ARG] = {
215 "git", "ls-remote", git_dir, NULL
217 static bool init = FALSE;
218 struct ref_opt opt = { remote_name, head };
219 size_t i;
221 if (!init) {
222 if (!argv_from_env(ls_remote_argv, "TIG_LS_REMOTE"))
223 return ERR;
224 init = TRUE;
227 if (!*git_dir)
228 return OK;
230 if (!*head && io_run_buf(head_argv, head, headlen) &&
231 !prefixcmp(head, "refs/heads/")) {
232 char *offset = head + STRING_SIZE("refs/heads/");
234 memmove(head, offset, strlen(offset) + 1);
237 refs_head = NULL;
238 for (i = 0; i < refs_size; i++)
239 refs[i]->valid = 0;
241 done_ref_lists();
243 if (io_run_load(ls_remote_argv, "\t", read_ref, &opt) == ERR)
244 return ERR;
246 for (i = 0; i < refs_size; i++)
247 if (!refs[i]->valid)
248 refs[i]->id[0] = 0;
250 qsort(refs, refs_size, sizeof(*refs), compare_refs);
252 return OK;
256 load_refs(bool force)
258 static bool loaded = FALSE;
260 if (force)
261 repo.head[0] = 0;
262 else if (loaded)
263 return OK;
265 loaded = TRUE;
266 return reload_refs(repo.git_dir, repo.remote, repo.head, sizeof(repo.head));
270 add_ref(const char *id, char *name, const char *remote_name, const char *head)
272 struct ref_opt opt = { remote_name, head };
274 return add_to_refs(id, strlen(id), name, strlen(name), &opt);
277 void
278 ref_update_env(struct argv_env *env, const struct ref *ref, bool clear)
280 if (clear)
281 env->tag[0] = env->remote[0] = env->branch[0] = 0;
283 string_copy_rev(env->commit, ref->id);
285 if (ref_is_tag(ref)) {
286 string_copy_rev(env->tag, ref->name);
288 } else if (ref_is_remote(ref)) {
289 const char *sep = strchr(ref->name, '/');
291 if (!sep)
292 return;
293 string_ncopy(env->remote, ref->name, sep - ref->name);
294 string_copy_rev(env->branch, sep + 1);
296 } else if (ref->type == REFERENCE_BRANCH) {
297 string_copy_rev(env->branch, ref->name);
301 static struct ref_format **ref_formats;
303 const struct ref_format *
304 get_ref_format(struct ref *ref)
306 static const struct ref_format default_format = { "", "" };
308 if (ref_formats) {
309 struct ref_format *format = ref_formats[ref->type];
311 if (!format && ref_is_tag(ref))
312 format = ref_formats[REFERENCE_TAG];
313 if (!format && ref_is_remote(ref))
314 format = ref_formats[REFERENCE_REMOTE];
315 if (!format)
316 format = ref_formats[REFERENCE_BRANCH];
317 if (format)
318 return format;
321 return &default_format;
324 static enum status_code
325 parse_ref_format_arg(const char *arg, const struct enum_map *map)
327 size_t arglen = strlen(arg);
328 const char *pos;
330 for (pos = arg; *pos && arglen > 0; pos++, arglen--) {
331 enum reference_type type;
333 for (type = 0; type < map->size; type++) {
334 const struct enum_map_entry *entry = &map->entries[type];
335 struct ref_format *format;
337 if (arglen < entry->namelen ||
338 string_enum_compare(pos, entry->name, entry->namelen))
339 continue;
341 format = malloc(sizeof(*format));
342 if (!format)
343 return ERROR_OUT_OF_MEMORY;
344 format->start = strndup(arg, pos - arg);
345 format->end = strdup(pos + entry->namelen);
346 if (!format->start || !format->end) {
347 free((void *) format->start);
348 free((void *) format->end);
349 free(format);
350 return ERROR_OUT_OF_MEMORY;
353 ref_formats[type] = format;
354 return SUCCESS;
358 return error("Unknown ref format: %s", arg);
361 enum status_code
362 parse_ref_formats(const char *argv[])
364 const struct enum_map *map = reference_type_map;
365 int argc;
367 if (!ref_formats) {
368 ref_formats = calloc(reference_type_map->size, sizeof(struct ref_format *));
369 if (!ref_formats)
370 return ERROR_OUT_OF_MEMORY;
373 for (argc = 0; argv[argc]; argc++) {
374 enum status_code code = parse_ref_format_arg(argv[argc], map);
375 if (code != SUCCESS)
376 return code;
379 return SUCCESS;
382 /* vim: set ts=8 sw=8 noexpandtab: */