PageRenderTime 34ms CodeModel.GetById 18ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/tig-1.0/refs.c

#
C | 242 lines | 183 code | 39 blank | 20 comment | 51 complexity | d51ef25686e311ed9692d8310a9c039d MD5 | raw file
Possible License(s): GPL-2.0
  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
 18static struct ref **refs = NULL;
 19static size_t refs_size = 0;
 20static struct ref *refs_head = NULL;
 21
 22static struct ref_list **ref_lists = NULL;
 23static size_t ref_lists_size = 0;
 24
 25DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
 26DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
 27DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
 28
 29static int
 30compare_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
 51void
 52foreach_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
 61struct ref *
 62get_ref_head()
 63{
 64	return refs_head;
 65}
 66
 67struct ref_list *
 68get_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
 99struct ref_opt {
100	const char *remote;
101	const char *head;
102};
103
104static int
105read_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")) {
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;
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;
185	string_ncopy_do(ref->id, SIZEOF_REV, id, idlen);
186
187	if (head)
188		refs_head = ref;
189	return OK;
190}
191
192int
193reload_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}