Partly restore old refresh behavior
[tig.git] / src / refdb.c
blobbf82b2e8f6b2d3ea96141054a4dd3e47c3c98c7c
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/watch.h"
19 #include "tig/options.h"
20 #include "tig/repo.h"
21 #include "tig/refdb.h"
23 static struct ref **refs = NULL;
24 static size_t refs_size = 0;
25 static struct ref *refs_head = NULL;
27 static struct ref_list **ref_lists = NULL;
28 static size_t ref_lists_size = 0;
30 DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
31 DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
32 DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
34 static int
35 compare_refs(const void *ref1_, const void *ref2_)
37 const struct ref *ref1 = *(const struct ref **)ref1_;
38 const struct ref *ref2 = *(const struct ref **)ref2_;
40 return ref_compare(ref1, ref2);
43 int
44 ref_compare(const struct ref *ref1, const struct ref *ref2)
46 if (ref1->type != ref2->type)
47 return ref1->type - ref2->type;
48 return strcmp_numeric(ref1->name, ref2->name);
51 void
52 foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data)
54 size_t i;
56 for (i = 0; i < refs_size; i++)
57 if (refs[i]->id[0] && !visitor(data, refs[i]))
58 break;
61 struct ref *
62 get_ref_head()
64 return refs_head;
67 struct ref_list *
68 get_ref_list(const char *id)
70 struct ref_list *list;
71 size_t i;
73 for (i = 0; i < ref_lists_size; i++)
74 if (!strcmp(id, ref_lists[i]->id))
75 return ref_lists[i];
77 if (!realloc_ref_lists(&ref_lists, ref_lists_size, 1))
78 return NULL;
79 list = calloc(1, sizeof(*list));
80 if (!list)
81 return NULL;
82 string_copy_rev(list->id, id);
84 for (i = 0; i < refs_size; i++) {
85 if (!strcmp(id, refs[i]->id) &&
86 realloc_refs_list(&list->refs, list->size, 1))
87 list->refs[list->size++] = refs[i];
90 if (!list->refs) {
91 free(list);
92 return NULL;
95 qsort(list->refs, list->size, sizeof(*list->refs), compare_refs);
96 ref_lists[ref_lists_size++] = list;
97 return list;
100 struct ref_opt {
101 const char *remote;
102 const char *head;
103 enum watch_trigger changed;
106 static void
107 done_ref_lists(void)
109 int i;
111 for (i = 0; i < ref_lists_size; i++) {
112 struct ref_list *list = ref_lists[i];
114 free(list->refs);
115 free(list);
118 free(ref_lists);
119 ref_lists = NULL;
120 ref_lists_size = 0;
123 static int
124 add_to_refs(const char *id, size_t idlen, char *name, size_t namelen, struct ref_opt *opt)
126 struct ref *ref = NULL;
127 enum reference_type type = REFERENCE_BRANCH;
128 int pos;
130 if (!prefixcmp(name, "refs/tags/")) {
131 type = REFERENCE_TAG;
132 if (!suffixcmp(name, namelen, "^{}")) {
133 namelen -= 3;
134 name[namelen] = 0;
135 } else {
136 type = REFERENCE_LOCAL_TAG;
139 namelen -= STRING_SIZE("refs/tags/");
140 name += STRING_SIZE("refs/tags/");
142 } else if (!prefixcmp(name, "refs/remotes/")) {
143 type = REFERENCE_REMOTE;
144 namelen -= STRING_SIZE("refs/remotes/");
145 name += STRING_SIZE("refs/remotes/");
146 if (!strcmp(opt->remote, name))
147 type = REFERENCE_TRACKED_REMOTE;
149 } else if (!prefixcmp(name, "refs/replace/")) {
150 type = REFERENCE_REPLACE;
151 id = name + strlen("refs/replace/");
152 idlen = namelen - strlen("refs/replace/");
153 name = "replaced";
154 namelen = strlen(name);
156 } else if (!prefixcmp(name, "refs/heads/")) {
157 namelen -= STRING_SIZE("refs/heads/");
158 name += STRING_SIZE("refs/heads/");
159 if (strlen(opt->head) == namelen &&
160 !strncmp(opt->head, name, namelen))
161 type = REFERENCE_HEAD;
163 } else if (!strcmp(name, "HEAD")) {
164 /* Handle the case of HEAD not being a symbolic ref,
165 * i.e. during a rebase. */
166 if (*opt->head)
167 return OK;
168 type = REFERENCE_HEAD;
171 /* If we are reloading or it's an annotated tag, replace the
172 * previous SHA1 with the resolved commit id; relies on the fact
173 * git-ls-remote lists the commit id of an annotated tag right
174 * before the commit id it points to. */
175 for (pos = 0; pos < refs_size; pos++) {
176 int cmp = type == REFERENCE_REPLACE
177 ? strcmp(id, refs[pos]->id) : strcmp(name, refs[pos]->name);
179 if (!cmp) {
180 ref = refs[pos];
181 break;
185 if (!ref) {
186 if (!realloc_refs(&refs, refs_size, 1))
187 return ERR;
188 ref = calloc(1, sizeof(*ref) + namelen);
189 if (!ref)
190 return ERR;
191 refs[refs_size++] = ref;
192 strncpy(ref->name, name, namelen);
195 if (strncmp(ref->id, id, idlen))
196 opt->changed |= WATCH_REFS;
198 ref->valid = TRUE;
199 ref->type = type;
200 string_ncopy_do(ref->id, SIZEOF_REV, id, idlen);
202 if (type == REFERENCE_HEAD) {
203 if (!refs_head ||
204 (refs_head != ref && memcmp(refs_head, ref, sizeof(*ref))))
205 opt->changed |= WATCH_HEAD;
206 refs_head = ref;
208 return OK;
211 static int
212 read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data)
214 return add_to_refs(id, idlen, name, namelen, data);
217 static int
218 reload_refs(bool force)
220 const char *head_argv[] = {
221 "git", "symbolic-ref", "HEAD", NULL
223 const char *ls_remote_argv[SIZEOF_ARG] = {
224 "git", "ls-remote", repo.git_dir, NULL
226 static bool init = FALSE;
227 struct ref_opt opt = { repo.remote, repo.head, WATCH_NONE };
228 struct repo_info old_repo = repo;
229 size_t i;
231 if (!init) {
232 if (!argv_from_env(ls_remote_argv, "TIG_LS_REMOTE"))
233 return ERR;
234 init = TRUE;
237 if (!*repo.git_dir)
238 return OK;
240 if ((force || !*repo.head) && io_run_buf(head_argv, repo.head, sizeof(repo.head)) &&
241 !prefixcmp(repo.head, "refs/heads/")) {
242 char *offset = repo.head + STRING_SIZE("refs/heads/");
244 memmove(repo.head, offset, strlen(offset) + 1);
247 if (strcmp(old_repo.head, repo.head))
248 opt.changed |= WATCH_HEAD;
250 refs_head = NULL;
251 for (i = 0; i < refs_size; i++)
252 refs[i]->valid = 0;
254 done_ref_lists();
256 if (io_run_load(ls_remote_argv, "\t", read_ref, &opt) == ERR)
257 return ERR;
259 for (i = 0; i < refs_size; i++)
260 if (!refs[i]->valid) {
261 refs[i]->id[0] = 0;
262 opt.changed |= WATCH_REFS;
266 if (opt.changed)
267 watch_apply(NULL, opt.changed);
268 qsort(refs, refs_size, sizeof(*refs), compare_refs);
270 return OK;
274 load_refs(bool force)
276 static bool loaded = FALSE;
278 if (!force && loaded)
279 return OK;
281 loaded = TRUE;
282 return reload_refs(force);
286 add_ref(const char *id, char *name, const char *remote_name, const char *head)
288 struct ref_opt opt = { remote_name, head };
290 return add_to_refs(id, strlen(id), name, strlen(name), &opt);
293 void
294 ref_update_env(struct argv_env *env, const struct ref *ref, bool clear)
296 if (clear)
297 env->tag[0] = env->remote[0] = env->branch[0] = 0;
299 string_copy_rev(env->commit, ref->id);
301 if (ref_is_tag(ref)) {
302 string_ncopy(env->tag, ref->name, strlen(ref->name));
304 } else if (ref_is_remote(ref)) {
305 const char *sep = strchr(ref->name, '/');
307 if (!sep)
308 return;
309 string_ncopy(env->remote, ref->name, sep - ref->name);
310 string_ncopy(env->branch, sep + 1, strlen(sep + 1));
312 } else if (ref->type == REFERENCE_BRANCH) {
313 string_ncopy(env->branch, ref->name, strlen(ref->name));
317 static struct ref_format **ref_formats;
319 const struct ref_format *
320 get_ref_format(struct ref *ref)
322 static const struct ref_format default_format = { "", "" };
324 if (ref_formats) {
325 struct ref_format *format = ref_formats[ref->type];
327 if (!format && ref_is_tag(ref))
328 format = ref_formats[REFERENCE_TAG];
329 if (!format && ref_is_remote(ref))
330 format = ref_formats[REFERENCE_REMOTE];
331 if (!format)
332 format = ref_formats[REFERENCE_BRANCH];
333 if (format)
334 return format;
337 return &default_format;
340 static enum status_code
341 parse_ref_format_arg(const char *arg, const struct enum_map *map)
343 size_t arglen = strlen(arg);
344 const char *pos;
346 for (pos = arg; *pos && arglen > 0; pos++, arglen--) {
347 enum reference_type type;
349 for (type = 0; type < map->size; type++) {
350 const struct enum_map_entry *entry = &map->entries[type];
351 struct ref_format *format;
353 if (arglen < entry->namelen ||
354 string_enum_compare(pos, entry->name, entry->namelen))
355 continue;
357 format = malloc(sizeof(*format));
358 if (!format)
359 return ERROR_OUT_OF_MEMORY;
360 format->start = strndup(arg, pos - arg);
361 format->end = strdup(pos + entry->namelen);
362 if (!format->start || !format->end) {
363 free((void *) format->start);
364 free((void *) format->end);
365 free(format);
366 return ERROR_OUT_OF_MEMORY;
369 ref_formats[type] = format;
370 return SUCCESS;
374 return error("Unknown ref format: %s", arg);
377 enum status_code
378 parse_ref_formats(const char *argv[])
380 const struct enum_map *map = reference_type_map;
381 int argc;
383 if (!ref_formats) {
384 ref_formats = calloc(reference_type_map->size, sizeof(struct ref_format *));
385 if (!ref_formats)
386 return ERROR_OUT_OF_MEMORY;
389 for (argc = 0; argv[argc]; argc++) {
390 enum status_code code = parse_ref_format_arg(argv[argc], map);
391 if (code != SUCCESS)
392 return code;
395 return SUCCESS;
398 /* vim: set ts=8 sw=8 noexpandtab: */