1 /* Copyright (c) 2006-2014 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.
17 #include "tig/watch.h"
18 #include "tig/options.h"
20 #include "tig/refdb.h"
22 static struct ref
**refs
= NULL
;
23 static size_t refs_size
= 0;
24 static struct ref
*refs_head
= NULL
;
26 static struct ref_list
**ref_lists
= NULL
;
27 static size_t ref_lists_size
= 0;
29 DEFINE_ALLOCATOR(realloc_refs
, struct ref
*, 256)
30 DEFINE_ALLOCATOR(realloc_refs_list
, struct ref
*, 8)
31 DEFINE_ALLOCATOR(realloc_ref_lists
, struct ref_list
*, 8)
34 compare_refs(const void *ref1_
, const void *ref2_
)
36 const struct ref
*ref1
= *(const struct ref
**)ref1_
;
37 const struct ref
*ref2
= *(const struct ref
**)ref2_
;
39 return ref_compare(ref1
, ref2
);
43 ref_compare(const struct ref
*ref1
, const struct ref
*ref2
)
45 if (ref1
->type
!= ref2
->type
)
46 return ref1
->type
- ref2
->type
;
47 return strcmp_numeric(ref1
->name
, ref2
->name
);
51 foreach_ref(bool (*visitor
)(void *data
, const struct ref
*ref
), void *data
)
55 for (i
= 0; i
< refs_size
; i
++)
56 if (refs
[i
]->id
[0] && !visitor(data
, refs
[i
]))
67 get_ref_list(const char *id
)
69 struct ref_list
*list
;
72 for (i
= 0; i
< ref_lists_size
; i
++)
73 if (!strcmp(id
, ref_lists
[i
]->id
))
76 if (!realloc_ref_lists(&ref_lists
, ref_lists_size
, 1))
78 list
= calloc(1, sizeof(*list
));
81 string_copy_rev(list
->id
, id
);
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
];
94 qsort(list
->refs
, list
->size
, sizeof(*list
->refs
), compare_refs
);
95 ref_lists
[ref_lists_size
++] = list
;
102 enum watch_trigger changed
;
110 for (i
= 0; i
< ref_lists_size
; i
++) {
111 struct ref_list
*list
= ref_lists
[i
];
123 add_to_refs(const char *id
, size_t idlen
, char *name
, size_t namelen
, struct ref_opt
*opt
)
125 struct ref
*ref
= NULL
;
126 enum reference_type type
= REFERENCE_BRANCH
;
129 if (!prefixcmp(name
, "refs/tags/")) {
130 type
= REFERENCE_TAG
;
131 if (!suffixcmp(name
, namelen
, "^{}")) {
135 type
= REFERENCE_LOCAL_TAG
;
138 namelen
-= STRING_SIZE("refs/tags/");
139 name
+= STRING_SIZE("refs/tags/");
141 } else if (!prefixcmp(name
, "refs/remotes/")) {
142 type
= REFERENCE_REMOTE
;
143 namelen
-= STRING_SIZE("refs/remotes/");
144 name
+= STRING_SIZE("refs/remotes/");
145 if (!strcmp(opt
->remote
, name
))
146 type
= REFERENCE_TRACKED_REMOTE
;
148 } else if (!prefixcmp(name
, "refs/replace/")) {
149 type
= REFERENCE_REPLACE
;
150 id
= name
+ strlen("refs/replace/");
151 idlen
= namelen
- strlen("refs/replace/");
153 namelen
= strlen(name
);
155 } else if (!prefixcmp(name
, "refs/heads/")) {
156 namelen
-= STRING_SIZE("refs/heads/");
157 name
+= STRING_SIZE("refs/heads/");
158 if (strlen(opt
->head
) == namelen
&&
159 !strncmp(opt
->head
, name
, namelen
))
160 type
= REFERENCE_HEAD
;
162 } else if (!strcmp(name
, "HEAD")) {
163 /* Handle the case of HEAD not being a symbolic ref,
164 * i.e. during a rebase. */
167 type
= REFERENCE_HEAD
;
170 /* If we are reloading or it's an annotated tag, replace the
171 * previous SHA1 with the resolved commit id; relies on the fact
172 * git-ls-remote lists the commit id of an annotated tag right
173 * before the commit id it points to. */
174 for (pos
= 0; pos
< refs_size
; pos
++) {
175 int cmp
= type
== REFERENCE_REPLACE
176 ? strcmp(id
, refs
[pos
]->id
) : strcmp(name
, refs
[pos
]->name
);
185 if (!realloc_refs(&refs
, refs_size
, 1))
187 ref
= calloc(1, sizeof(*ref
) + namelen
);
190 refs
[refs_size
++] = ref
;
191 strncpy(ref
->name
, name
, namelen
);
194 if (strncmp(ref
->id
, id
, idlen
))
195 opt
->changed
|= WATCH_REFS
;
199 string_ncopy_do(ref
->id
, SIZEOF_REV
, id
, idlen
);
201 if (type
== REFERENCE_HEAD
) {
203 (refs_head
!= ref
&& memcmp(refs_head
, ref
, sizeof(*ref
))))
204 opt
->changed
|= WATCH_HEAD
;
211 read_ref(char *id
, size_t idlen
, char *name
, size_t namelen
, void *data
)
213 return add_to_refs(id
, idlen
, name
, namelen
, data
);
217 reload_refs(bool force
)
219 const char *head_argv
[] = {
220 "git", "symbolic-ref", "HEAD", NULL
222 const char *ls_remote_argv
[SIZEOF_ARG
] = {
223 "git", "ls-remote", repo
.git_dir
, NULL
225 static bool init
= FALSE
;
226 struct ref_opt opt
= { repo
.remote
, repo
.head
, WATCH_NONE
};
227 struct repo_info old_repo
= repo
;
231 if (!argv_from_env(ls_remote_argv
, "TIG_LS_REMOTE"))
239 if ((force
|| !*repo
.head
) && io_run_buf(head_argv
, repo
.head
, sizeof(repo
.head
)) &&
240 !prefixcmp(repo
.head
, "refs/heads/")) {
241 char *offset
= repo
.head
+ STRING_SIZE("refs/heads/");
243 memmove(repo
.head
, offset
, strlen(offset
) + 1);
246 if (strcmp(old_repo
.head
, repo
.head
))
247 opt
.changed
|= WATCH_HEAD
;
250 for (i
= 0; i
< refs_size
; i
++)
255 if (io_run_load(ls_remote_argv
, "\t", read_ref
, &opt
) == ERR
)
258 for (i
= 0; i
< refs_size
; i
++)
259 if (!refs
[i
]->valid
) {
261 opt
.changed
|= WATCH_REFS
;
266 watch_apply(NULL
, opt
.changed
);
267 qsort(refs
, refs_size
, sizeof(*refs
), compare_refs
);
273 load_refs(bool force
)
275 static bool loaded
= FALSE
;
277 if (!force
&& loaded
)
281 return reload_refs(force
);
285 add_ref(const char *id
, char *name
, const char *remote_name
, const char *head
)
287 struct ref_opt opt
= { remote_name
, head
};
289 return add_to_refs(id
, strlen(id
), name
, strlen(name
), &opt
);
293 ref_update_env(struct argv_env
*env
, const struct ref
*ref
, bool clear
)
296 env
->tag
[0] = env
->remote
[0] = env
->branch
[0] = 0;
298 string_copy_rev(env
->commit
, ref
->id
);
300 if (ref_is_tag(ref
)) {
301 string_ncopy(env
->tag
, ref
->name
, strlen(ref
->name
));
303 } else if (ref_is_remote(ref
)) {
304 const char *sep
= strchr(ref
->name
, '/');
308 string_ncopy(env
->remote
, ref
->name
, sep
- ref
->name
);
309 string_ncopy(env
->branch
, sep
+ 1, strlen(sep
+ 1));
311 } else if (ref
->type
== REFERENCE_BRANCH
) {
312 string_ncopy(env
->branch
, ref
->name
, strlen(ref
->name
));
316 static struct ref_format
**ref_formats
;
318 const struct ref_format
*
319 get_ref_format(struct ref
*ref
)
321 static const struct ref_format default_format
= { "", "" };
324 struct ref_format
*format
= ref_formats
[ref
->type
];
326 if (!format
&& ref_is_tag(ref
))
327 format
= ref_formats
[REFERENCE_TAG
];
328 if (!format
&& ref_is_remote(ref
))
329 format
= ref_formats
[REFERENCE_REMOTE
];
331 format
= ref_formats
[REFERENCE_BRANCH
];
336 return &default_format
;
339 static enum status_code
340 parse_ref_format_arg(const char *arg
, const struct enum_map
*map
)
342 size_t arglen
= strlen(arg
);
345 for (pos
= arg
; *pos
&& arglen
> 0; pos
++, arglen
--) {
346 enum reference_type type
;
348 for (type
= 0; type
< map
->size
; type
++) {
349 const struct enum_map_entry
*entry
= &map
->entries
[type
];
350 struct ref_format
*format
;
352 if (arglen
< entry
->namelen
||
353 string_enum_compare(pos
, entry
->name
, entry
->namelen
))
356 format
= malloc(sizeof(*format
));
358 return ERROR_OUT_OF_MEMORY
;
359 format
->start
= strndup(arg
, pos
- arg
);
360 format
->end
= strdup(pos
+ entry
->namelen
);
361 if (!format
->start
|| !format
->end
) {
362 free((void *) format
->start
);
363 free((void *) format
->end
);
365 return ERROR_OUT_OF_MEMORY
;
368 ref_formats
[type
] = format
;
373 return error("Unknown ref format: %s", arg
);
377 parse_ref_formats(const char *argv
[])
379 const struct enum_map
*map
= reference_type_map
;
383 ref_formats
= calloc(reference_type_map
->size
, sizeof(struct ref_format
*));
385 return ERROR_OUT_OF_MEMORY
;
388 for (argc
= 0; argv
[argc
]; argc
++) {
389 enum status_code code
= parse_ref_format_arg(argv
[argc
], map
);
397 /* vim: set ts=8 sw=8 noexpandtab: */