Skip to content
Newer
Older
100644 243 lines (203 sloc) 5.74 KB
1196ce1 @jonas Move refs code to separate file
jonas authored
1 /* Copyright (c) 2006-2012 Jonas Fonseca <fonseca@diku.dk>
2 *
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.
7 *
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.
12 */
13
14 #include "tig.h"
15 #include "io.h"
16 #include "refs.h"
17
18 static struct ref **refs = NULL;
19 static size_t refs_size = 0;
20 static struct ref *refs_head = NULL;
21
22 static struct ref_list **ref_lists = NULL;
23 static size_t ref_lists_size = 0;
24
25 DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
26 DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
27 DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
28
29 static int
30 compare_refs(const void *ref1_, const void *ref2_)
31 {
32 const struct ref *ref1 = *(const struct ref **)ref1_;
33 const struct ref *ref2 = *(const struct ref **)ref2_;
34
35 if (ref1->tag != ref2->tag)
36 return ref2->tag - ref1->tag;
37 if (ref1->ltag != ref2->ltag)
38 return ref2->ltag - ref1->ltag;
39 if (ref1->head != ref2->head)
40 return ref2->head - ref1->head;
41 if (ref1->tracked != ref2->tracked)
42 return ref2->tracked - ref1->tracked;
43 if (ref1->replace != ref2->replace)
44 return ref2->replace - ref1->replace;
45 /* Order remotes last. */
46 if (ref1->remote != ref2->remote)
47 return ref1->remote - ref2->remote;
48 return strcmp(ref1->name, ref2->name);
49 }
50
51 void
52 foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data)
53 {
54 size_t i;
55
56 for (i = 0; i < refs_size; i++)
57 if (!visitor(data, refs[i]))
58 break;
59 }
60
61 struct ref *
62 get_ref_head()
63 {
64 return refs_head;
65 }
66
67 struct ref_list *
68 get_ref_list(const char *id)
69 {
70 struct ref_list *list;
71 size_t i;
72
73 for (i = 0; i < ref_lists_size; i++)
74 if (!strcmp(id, ref_lists[i]->id))
75 return ref_lists[i];
76
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
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];
87 }
88
89 if (!list->refs) {
90 free(list);
91 return NULL;
92 }
93
94 qsort(list->refs, list->size, sizeof(*list->refs), compare_refs);
95 ref_lists[ref_lists_size++] = list;
96 return list;
97 }
98
99 struct ref_opt {
100 const char *remote;
101 const char *head;
102 };
103
104 static int
105 read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data)
106 {
107 struct ref_opt *opt = data;
108 struct ref *ref = NULL;
109 bool tag = FALSE;
110 bool ltag = FALSE;
111 bool remote = FALSE;
112 bool replace = FALSE;
113 bool tracked = FALSE;
114 bool head = FALSE;
115 int pos;
116
117 if (!prefixcmp(name, "refs/tags/")) {
118 if (!suffixcmp(name, namelen, "^{}")) {
119 namelen -= 3;
120 name[namelen] = 0;
121 } else {
122 ltag = TRUE;
123 }
124
125 tag = TRUE;
126 namelen -= STRING_SIZE("refs/tags/");
127 name += STRING_SIZE("refs/tags/");
128
129 } else if (!prefixcmp(name, "refs/remotes/")) {
130 remote = TRUE;
131 namelen -= STRING_SIZE("refs/remotes/");
132 name += STRING_SIZE("refs/remotes/");
133 tracked = !strcmp(opt->remote, name);
134
135 } else if (!prefixcmp(name, "refs/replace/")) {
136 replace = TRUE;
137 id = name + strlen("refs/replace/");
138 idlen = namelen - strlen("refs/replace/");
139 name = "replaced";
140 namelen = strlen(name);
141
142 } else if (!prefixcmp(name, "refs/heads/")) {
143 namelen -= STRING_SIZE("refs/heads/");
144 name += STRING_SIZE("refs/heads/");
145 head = strlen(opt->head) == namelen
146 && !strncmp(opt->head, name, namelen);
147
148 } else if (!strcmp(name, "HEAD")) {
9fd859c @jonas Fix regression to not handle non-symbolic-ref during rebase action
jonas authored
149 /* Handle the case of HEAD not being a symbolic ref,
150 * i.e. during a rebase. */
151 if (*opt->head)
152 return OK;
153 head = TRUE;
1196ce1 @jonas Move refs code to separate file
jonas authored
154 }
155
156 /* If we are reloading or it's an annotated tag, replace the
157 * previous SHA1 with the resolved commit id; relies on the fact
158 * git-ls-remote lists the commit id of an annotated tag right
159 * before the commit id it points to. */
160 for (pos = 0; pos < refs_size; pos++) {
161 int cmp = replace ? strcmp(id, refs[pos]->id) : strcmp(name, refs[pos]->name);
162
163 if (!cmp) {
164 ref = refs[pos];
165 break;
166 }
167 }
168
169 if (!ref) {
170 if (!realloc_refs(&refs, refs_size, 1))
171 return ERR;
172 ref = calloc(1, sizeof(*ref) + namelen);
173 if (!ref)
174 return ERR;
175 refs[refs_size++] = ref;
176 strncpy(ref->name, name, namelen);
177 }
178
179 ref->head = head;
180 ref->tag = tag;
181 ref->ltag = ltag;
182 ref->remote = remote;
183 ref->replace = replace;
184 ref->tracked = tracked;
bed4eee @Oblomov read_ref: use string_ncopy_do directly
Oblomov authored
185 string_ncopy_do(ref->id, SIZEOF_REV, id, idlen);
1196ce1 @jonas Move refs code to separate file
jonas authored
186
187 if (head)
188 refs_head = ref;
189 return OK;
190 }
191
192 int
193 reload_refs(const char *git_dir, const char *remote_name, char *head, size_t headlen)
194 {
195 const char *head_argv[] = {
196 "git", "symbolic-ref", "HEAD", NULL
197 };
198 const char *ls_remote_argv[SIZEOF_ARG] = {
199 "git", "ls-remote", git_dir, NULL
200 };
201 static bool init = FALSE;
202 struct ref_opt opt = { remote_name, head };
203 size_t i;
204
205 if (!init) {
206 if (!argv_from_env(ls_remote_argv, "TIG_LS_REMOTE"))
207 return ERR;
208 init = TRUE;
209 }
210
211 if (!*git_dir)
212 return OK;
213
214 if (io_run_buf(head_argv, head, headlen) &&
215 !prefixcmp(head, "refs/heads/")) {
216 char *offset = head + STRING_SIZE("refs/heads/");
217
218 memmove(head, offset, strlen(offset) + 1);
219 }
220
221 refs_head = NULL;
222 for (i = 0; i < refs_size; i++)
223 refs[i]->id[0] = 0;
224
225 if (io_run_load(ls_remote_argv, "\t", read_ref, &opt) == ERR)
226 return ERR;
227
228 /* Update the ref lists to reflect changes. */
229 for (i = 0; i < ref_lists_size; i++) {
230 struct ref_list *list = ref_lists[i];
231 size_t old, new;
232
233 for (old = new = 0; old < list->size; old++)
234 if (!strcmp(list->id, list->refs[old]->id))
235 list->refs[new++] = list->refs[old];
236 list->size = new;
237 }
238
239 qsort(refs, refs_size, sizeof(*refs), compare_refs);
240
241 return OK;
242 }
Something went wrong with that request. Please try again.