1 /* Copyright (c) 2006-2015 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.
18 #include "tig/watch.h"
19 #include "tig/options.h"
21 #include "tig/refdb.h"
23 static struct ref
*refs_head
= NULL
;
24 static bool refs_tags
;
26 DEFINE_STRING_MAP(refs_by_name
, struct ref
*, name
, 32)
27 DEFINE_STRING_MAP(refs_by_id
, struct ref
*, id
, 16)
30 ref_compare(const struct ref
*ref1
, const struct ref
*ref2
)
32 if (ref1
->type
!= ref2
->type
)
33 return ref1
->type
- ref2
->type
;
34 return strcmp_numeric(ref1
->name
, ref2
->name
);
38 ref_canonical_compare(const struct ref
*ref1
, const struct ref
*ref2
)
40 int tag_diff
= !!ref_is_tag(ref2
) - !!ref_is_tag(ref1
);
44 if (ref1
->type
!= ref2
->type
)
45 return !tag_diff
? ref1
->type
- ref2
->type
: ref2
->type
- ref1
->type
;
46 return strcmp_numeric(ref1
->name
, ref2
->name
);
49 struct ref_visitor_data
{
50 ref_visitor_fn visitor
;
55 foreach_ref_visitor(void *data
, void *value
)
57 struct ref_visitor_data
*visitor_data
= data
;
58 const struct ref
*ref
= value
;
62 return visitor_data
->visitor(visitor_data
->data
, ref
);
66 foreach_ref(ref_visitor_fn visitor
, void *data
)
68 struct ref_visitor_data visitor_data
= { visitor
, data
};
70 string_map_foreach(&refs_by_name
, foreach_ref_visitor
, &visitor_data
);
80 get_ref_list(const char *id
)
82 return string_map_get(&refs_by_id
, id
);
86 get_canonical_ref(const char *id
)
88 const struct ref
*ref
= NULL
;
89 const struct ref
*pos
;
91 foreach_ref_list(pos
, id
)
92 if (!ref
|| ref_canonical_compare(pos
, ref
) < 0)
99 ref_list_contains_tag(const char *id
)
101 const struct ref
*ref
;
103 foreach_ref_list(ref
, id
)
113 enum watch_trigger changed
;
117 add_ref_to_id_map(struct ref
*ref
)
119 void **ref_lists_slot
= string_map_put_to(&refs_by_id
, ref
->id
);
124 /* First remove the ref from the ID list, to ensure that it is
125 * reinserted at the right position if the type changes. */
127 struct ref
*list
, *prev
;
129 for (list
= *ref_lists_slot
, prev
= NULL
; list
; prev
= list
, list
= list
->next
)
132 *ref_lists_slot
= ref
->next
;
134 prev
->next
= ref
->next
;
140 if (*ref_lists_slot
== NULL
|| ref_compare(ref
, *ref_lists_slot
) <= 0) {
141 ref
->next
= *ref_lists_slot
;
142 *ref_lists_slot
= ref
;
147 for (list
= *ref_lists_slot
; list
->next
; list
= list
->next
) {
148 if (ref_compare(ref
, list
->next
) <= 0)
152 ref
->next
= list
->next
;
160 remove_ref_from_id_map(struct ref
*ref
)
162 void **ref_slot
= string_map_get_at(&refs_by_id
, ref
->id
);
163 struct ref
*list
= ref_slot
? *ref_slot
: NULL
;
164 struct ref
*prev
= NULL
;
166 for (; list
; prev
= list
, list
= list
->next
) {
171 *ref_slot
= ref
->next
;
173 prev
->next
= ref
->next
;
178 if (ref_slot
&& !*ref_slot
)
179 string_map_remove(&refs_by_id
, ref
->id
);
183 add_to_refs(const char *id
, size_t idlen
, char *name
, size_t namelen
, struct ref_opt
*opt
)
185 struct ref
*ref
= NULL
;
186 enum reference_type type
= REFERENCE_BRANCH
;
187 void **ref_slot
= NULL
;
189 if (!prefixcmp(name
, "refs/tags/")) {
190 type
= REFERENCE_TAG
;
191 if (!suffixcmp(name
, namelen
, "^{}")) {
195 type
= REFERENCE_LOCAL_TAG
;
198 namelen
-= STRING_SIZE("refs/tags/");
199 name
+= STRING_SIZE("refs/tags/");
201 } else if (!prefixcmp(name
, "refs/remotes/")) {
202 type
= REFERENCE_REMOTE
;
203 namelen
-= STRING_SIZE("refs/remotes/");
204 name
+= STRING_SIZE("refs/remotes/");
205 if (!strcmp(opt
->remote
, name
))
206 type
= REFERENCE_TRACKED_REMOTE
;
208 } else if (!prefixcmp(name
, "refs/replace/")) {
209 type
= REFERENCE_REPLACE
;
210 id
= name
+ strlen("refs/replace/");
211 idlen
= namelen
- strlen("refs/replace/");
213 namelen
= strlen(name
);
215 } else if (!prefixcmp(name
, "refs/heads/")) {
216 namelen
-= STRING_SIZE("refs/heads/");
217 name
+= STRING_SIZE("refs/heads/");
218 if (strlen(opt
->head
) == namelen
&&
219 !strncmp(opt
->head
, name
, namelen
))
220 type
= REFERENCE_HEAD
;
222 } else if (!strcmp(name
, "HEAD")) {
223 /* Handle the case of HEAD not being a symbolic ref,
224 * i.e. during a rebase. */
227 type
= REFERENCE_HEAD
;
230 /* If we are reloading or it's an annotated tag, replace the
231 * previous SHA1 with the resolved commit id; relies on the fact
232 * git-ls-remote lists the commit id of an annotated tag right
233 * before the commit id it points to. */
234 if (type
== REFERENCE_REPLACE
) {
235 ref
= string_map_remove(&refs_by_id
, id
);
238 ref_slot
= string_map_put_to(&refs_by_name
, name
);
245 ref
= calloc(1, sizeof(*ref
) + namelen
);
248 strncpy(ref
->name
, name
, namelen
);
253 if (strncmp(ref
->id
, id
, idlen
) || ref
->type
!= type
) {
254 opt
->changed
|= WATCH_REFS
;
256 remove_ref_from_id_map(ref
);
261 string_ncopy_do(ref
->id
, SIZEOF_REV
, id
, idlen
);
263 if (type
== REFERENCE_HEAD
) {
265 (refs_head
!= ref
&& memcmp(refs_head
, ref
, sizeof(*ref
))))
266 opt
->changed
|= WATCH_HEAD
;
270 if (type
== REFERENCE_TAG
)
273 return add_ref_to_id_map(ref
);
277 read_ref(char *id
, size_t idlen
, char *name
, size_t namelen
, void *data
)
279 return add_to_refs(id
, idlen
, name
, namelen
, data
);
283 invalidate_refs(void *data
, void *ref_
)
285 struct ref
*ref
= ref_
;
293 cleanup_refs(void *data
, void *ref_
)
295 struct ref_opt
*opt
= data
;
296 struct ref
*ref
= ref_
;
300 opt
->changed
|= WATCH_REFS
;
307 reload_refs(bool force
)
309 const char *ls_remote_argv
[SIZEOF_ARG
] = {
310 "git", "ls-remote", repo
.git_dir
, NULL
312 static bool init
= FALSE
;
313 struct ref_opt opt
= { repo
.remote
, repo
.head
, WATCH_NONE
};
314 struct repo_info old_repo
= repo
;
317 if (!argv_from_env(ls_remote_argv
, "TIG_LS_REMOTE"))
325 if (force
|| !*repo
.head
)
328 if (strcmp(old_repo
.head
, repo
.head
))
329 opt
.changed
|= WATCH_HEAD
;
333 string_map_clear(&refs_by_id
);
334 string_map_foreach(&refs_by_name
, invalidate_refs
, NULL
);
336 if (io_run_load(ls_remote_argv
, "\t", read_ref
, &opt
) == ERR
)
339 string_map_foreach(&refs_by_name
, cleanup_refs
, &opt
);
342 watch_apply(NULL
, opt
.changed
);
348 load_refs(bool force
)
350 static bool loaded
= FALSE
;
352 if (!force
&& loaded
)
356 return reload_refs(force
);
360 add_ref(const char *id
, char *name
, const char *remote_name
, const char *head
)
362 struct ref_opt opt
= { remote_name
, head
};
364 return add_to_refs(id
, strlen(id
), name
, strlen(name
), &opt
);
368 ref_update_env(struct argv_env
*env
, const struct ref
*ref
, bool clear
)
371 env
->tag
[0] = env
->remote
[0] = env
->branch
[0] = 0;
373 string_copy_rev(env
->commit
, ref
->id
);
375 if (ref_is_tag(ref
)) {
376 string_ncopy(env
->tag
, ref
->name
, strlen(ref
->name
));
378 } else if (ref_is_remote(ref
)) {
379 const char *sep
= strchr(ref
->name
, '/');
383 string_ncopy(env
->remote
, ref
->name
, sep
- ref
->name
);
384 string_ncopy(env
->branch
, sep
+ 1, strlen(sep
+ 1));
386 } else if (ref
->type
== REFERENCE_BRANCH
) {
387 string_ncopy(env
->branch
, ref
->name
, strlen(ref
->name
));
392 refs_contain_tag(void)
394 return refs_tags
> 0;
397 const struct ref_format
*
398 get_ref_format(struct ref_format
**ref_formats
, const struct ref
*ref
)
400 static const struct ref_format default_format
= { "", "" };
403 struct ref_format
*format
= ref_formats
[ref
->type
];
405 if (!format
&& ref_is_tag(ref
))
406 format
= ref_formats
[REFERENCE_TAG
];
407 if (!format
&& ref_is_remote(ref
))
408 format
= ref_formats
[REFERENCE_REMOTE
];
410 format
= ref_formats
[REFERENCE_BRANCH
];
415 return &default_format
;
418 static enum status_code
419 parse_ref_format_arg(struct ref_format
**ref_formats
, const char *arg
, const struct enum_map
*map
)
421 size_t arglen
= strlen(arg
);
424 for (pos
= arg
; *pos
&& arglen
> 0; pos
++, arglen
--) {
425 enum reference_type type
;
427 for (type
= 0; type
< map
->size
; type
++) {
428 const struct enum_map_entry
*entry
= &map
->entries
[type
];
429 struct ref_format
*format
;
431 if (arglen
< entry
->namelen
||
432 string_enum_compare(pos
, entry
->name
, entry
->namelen
))
435 format
= malloc(sizeof(*format
));
437 return ERROR_OUT_OF_MEMORY
;
438 format
->start
= strndup(arg
, pos
- arg
);
439 format
->end
= strdup(pos
+ entry
->namelen
);
440 if (!format
->start
|| !format
->end
) {
441 free((void *) format
->start
);
442 free((void *) format
->end
);
444 return ERROR_OUT_OF_MEMORY
;
447 ref_formats
[type
] = format
;
452 return error("Unknown ref format: %s", arg
);
456 parse_ref_formats(struct ref_format
***formats
, const char *argv
[])
458 const struct enum_map
*map
= reference_type_map
;
462 *formats
= calloc(reference_type_map
->size
, sizeof(struct ref_format
*));
464 return ERROR_OUT_OF_MEMORY
;
467 for (argc
= 0; argv
[argc
]; argc
++) {
468 enum status_code code
= parse_ref_format_arg(*formats
, argv
[argc
], map
);
477 format_ref_formats(struct ref_format
**formats
, char buf
[], size_t bufsize
)
479 const struct enum_map
*map
= reference_type_map
;
480 char name
[SIZEOF_STR
];
481 enum reference_type type
;
483 const char *sep
= "";
485 for (type
= 0; type
< map
->size
; type
++) {
486 struct ref_format
*format
= formats
[type
];
491 if (!enum_name_copy(name
, sizeof(name
), map
->entries
[type
].name
)
492 || !string_nformat(buf
, bufsize
, &bufpos
, "%s%s%s%s",
493 sep
, format
->start
, name
, format
->end
))
494 return error("No space left in buffer");
502 /* vim: set ts=8 sw=8 noexpandtab: */