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 read_ref(char *id
, size_t idlen
, char *name
, size_t namelen
, void *data
)
108 struct ref_opt
*opt
= data
;
109 struct ref
*ref
= NULL
;
113 bool replace
= FALSE
;
114 bool tracked
= FALSE
;
118 if (!prefixcmp(name
, "refs/tags/")) {
119 if (!suffixcmp(name
, namelen
, "^{}")) {
127 namelen
-= STRING_SIZE("refs/tags/");
128 name
+= STRING_SIZE("refs/tags/");
130 } else if (!prefixcmp(name
, "refs/remotes/")) {
132 namelen
-= STRING_SIZE("refs/remotes/");
133 name
+= STRING_SIZE("refs/remotes/");
134 tracked
= !strcmp(opt
->remote
, name
);
136 } else if (!prefixcmp(name
, "refs/replace/")) {
138 id
= name
+ strlen("refs/replace/");
139 idlen
= namelen
- strlen("refs/replace/");
141 namelen
= strlen(name
);
143 } else if (!prefixcmp(name
, "refs/heads/")) {
144 namelen
-= STRING_SIZE("refs/heads/");
145 name
+= STRING_SIZE("refs/heads/");
146 head
= strlen(opt
->head
) == namelen
147 && !strncmp(opt
->head
, name
, namelen
);
149 } else if (!strcmp(name
, "HEAD")) {
150 /* Handle the case of HEAD not being a symbolic ref,
151 * i.e. during a rebase. */
157 /* If we are reloading or it's an annotated tag, replace the
158 * previous SHA1 with the resolved commit id; relies on the fact
159 * git-ls-remote lists the commit id of an annotated tag right
160 * before the commit id it points to. */
161 for (pos
= 0; pos
< refs_size
; pos
++) {
162 int cmp
= replace
? strcmp(id
, refs
[pos
]->id
) : strcmp(name
, refs
[pos
]->name
);
171 if (!realloc_refs(&refs
, refs_size
, 1))
173 ref
= calloc(1, sizeof(*ref
) + namelen
);
176 refs
[refs_size
++] = ref
;
177 strncpy(ref
->name
, name
, namelen
);
184 ref
->remote
= remote
;
185 ref
->replace
= replace
;
186 ref
->tracked
= tracked
;
187 string_ncopy_do(ref
->id
, SIZEOF_REV
, id
, idlen
);
195 reload_refs(const char *git_dir
, const char *remote_name
, char *head
, size_t headlen
)
197 const char *head_argv
[] = {
198 "git", "symbolic-ref", "HEAD", NULL
200 const char *ls_remote_argv
[SIZEOF_ARG
] = {
201 "git", "ls-remote", git_dir
, NULL
203 static bool init
= FALSE
;
204 struct ref_opt opt
= { remote_name
, head
};
208 if (!argv_from_env(ls_remote_argv
, "TIG_LS_REMOTE"))
216 if (io_run_buf(head_argv
, head
, headlen
) &&
217 !prefixcmp(head
, "refs/heads/")) {
218 char *offset
= head
+ STRING_SIZE("refs/heads/");
220 memmove(head
, offset
, strlen(offset
) + 1);
224 for (i
= 0; i
< refs_size
; i
++)
227 if (io_run_load(ls_remote_argv
, "\t", read_ref
, &opt
) == ERR
)
230 for (i
= 0; i
< refs_size
; i
++)
234 /* Update the ref lists to reflect changes. */
235 for (i
= 0; i
< ref_lists_size
; i
++) {
236 struct ref_list
*list
= ref_lists
[i
];
239 for (old
= new = 0; old
< list
->size
; old
++)
240 if (!strcmp(list
->id
, list
->refs
[old
]->id
))
241 list
->refs
[new++] = list
->refs
[old
];
245 qsort(refs
, refs_size
, sizeof(*refs
), compare_refs
);
250 /* vim: set ts=8 sw=8 noexpandtab: */