1 /* Copyright (c) 2006-2013 Jonas Fonseca <fonseca@diku.dk>
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.
18 static struct ref
**refs
= NULL
;
19 static size_t refs_size
= 0;
20 static struct ref
*refs_head
= NULL
;
22 static struct ref_list
**ref_lists
= NULL
;
23 static size_t ref_lists_size
= 0;
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)
30 compare_refs(const void *ref1_
, const void *ref2_
)
32 const struct ref
*ref1
= *(const struct ref
**)ref1_
;
33 const struct ref
*ref2
= *(const struct ref
**)ref2_
;
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
);
52 foreach_ref(bool (*visitor
)(void *data
, const struct ref
*ref
), void *data
)
56 for (i
= 0; i
< refs_size
; i
++)
57 if (refs
[i
]->id
[0] && !visitor(data
, refs
[i
]))
68 get_ref_list(const char *id
)
70 struct ref_list
*list
;
73 for (i
= 0; i
< ref_lists_size
; i
++)
74 if (!strcmp(id
, ref_lists
[i
]->id
))
77 if (!realloc_ref_lists(&ref_lists
, ref_lists_size
, 1))
79 list
= calloc(1, sizeof(*list
));
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
];
95 qsort(list
->refs
, list
->size
, sizeof(*list
->refs
), compare_refs
);
96 ref_lists
[ref_lists_size
++] = list
;
106 add_to_refs(const char *id
, size_t idlen
, char *name
, size_t namelen
, struct ref_opt
*opt
)
108 struct ref
*ref
= NULL
;
112 bool replace
= FALSE
;
113 bool tracked
= FALSE
;
117 if (!prefixcmp(name
, "refs/tags/")) {
118 if (!suffixcmp(name
, namelen
, "^{}")) {
126 namelen
-= STRING_SIZE("refs/tags/");
127 name
+= STRING_SIZE("refs/tags/");
129 } else if (!prefixcmp(name
, "refs/remotes/")) {
131 namelen
-= STRING_SIZE("refs/remotes/");
132 name
+= STRING_SIZE("refs/remotes/");
133 tracked
= !strcmp(opt
->remote
, name
);
135 } else if (!prefixcmp(name
, "refs/replace/")) {
137 id
= name
+ strlen("refs/replace/");
138 idlen
= namelen
- strlen("refs/replace/");
140 namelen
= strlen(name
);
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
);
148 } else if (!strcmp(name
, "HEAD")) {
149 /* Handle the case of HEAD not being a symbolic ref,
150 * i.e. during a rebase. */
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
);
170 if (!realloc_refs(&refs
, refs_size
, 1))
172 ref
= calloc(1, sizeof(*ref
) + namelen
);
175 refs
[refs_size
++] = ref
;
176 strncpy(ref
->name
, name
, namelen
);
183 ref
->remote
= remote
;
184 ref
->replace
= replace
;
185 ref
->tracked
= tracked
;
186 string_ncopy_do(ref
->id
, SIZEOF_REV
, id
, idlen
);
194 read_ref(char *id
, size_t idlen
, char *name
, size_t namelen
, void *data
)
196 return add_to_refs(id
, idlen
, name
, namelen
, data
);
200 reload_refs(const char *git_dir
, const char *remote_name
, char *head
, size_t headlen
)
202 const char *head_argv
[] = {
203 "git", "symbolic-ref", "HEAD", NULL
205 const char *ls_remote_argv
[SIZEOF_ARG
] = {
206 "git", "ls-remote", git_dir
, NULL
208 static bool init
= FALSE
;
209 struct ref_opt opt
= { remote_name
, head
};
213 if (!argv_from_env(ls_remote_argv
, "TIG_LS_REMOTE"))
221 if (!*head
&& io_run_buf(head_argv
, head
, headlen
) &&
222 !prefixcmp(head
, "refs/heads/")) {
223 char *offset
= head
+ STRING_SIZE("refs/heads/");
225 memmove(head
, offset
, strlen(offset
) + 1);
229 for (i
= 0; i
< refs_size
; i
++)
232 if (io_run_load(ls_remote_argv
, "\t", read_ref
, &opt
) == ERR
)
235 for (i
= 0; i
< refs_size
; i
++)
239 /* Update the ref lists to reflect changes. */
240 for (i
= 0; i
< ref_lists_size
; i
++) {
241 struct ref_list
*list
= ref_lists
[i
];
244 for (old
= new = 0; old
< list
->size
; old
++)
245 if (!strcmp(list
->id
, list
->refs
[old
]->id
))
246 list
->refs
[new++] = list
->refs
[old
];
250 qsort(refs
, refs_size
, sizeof(*refs
), compare_refs
);
256 add_ref(const char *id
, char *name
, const char *remote_name
, const char *head
)
258 struct ref_opt opt
= { remote_name
, head
};
260 return add_to_refs(id
, strlen(id
), name
, strlen(name
), &opt
);
263 /* vim: set ts=8 sw=8 noexpandtab: */