summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: da8b99d)
raw | patch | inline | side by side (parent: da8b99d)
author | Jonas Fonseca <fonseca@diku.dk> | |
Sat, 21 Feb 2009 14:45:52 +0000 (15:45 +0100) | ||
committer | Jonas Fonseca <fonseca@diku.dk> | |
Sat, 21 Feb 2009 15:15:12 +0000 (16:15 +0100) |
Make the allocation of refs stable across reloads (of either the main,
branch or log view) by changing the storage method and introducing a
struct ref_list to keep track of lists of references.
read_ref now always scans the already allocated refs. To speed this up
keep the list sorted and use binary search when inserting and updating.
branch or log view) by changing the storage method and introducing a
struct ref_list to keep track of lists of references.
read_ref now always scans the already allocated refs. To speed this up
keep the list sorted and use binary search when inserting and updating.
tig.c | patch | blob | history |
index 9dba5e9f6c3377b726422d78d0940b843ffa8af2..7a996dca08ca43e21b13428ebfdb3f4d0109cd42 100644 (file)
--- a/tig.c
+++ b/tig.c
static void warn(const char *msg, ...);
static void report(const char *msg, ...);
static void set_nonblocking_input(bool loading);
-static int load_refs(void);
static size_t utf8_length(const char **string, size_t col, int *width, size_t max_width, int *trimmed, bool reserve);
#define ABS(x) ((x) >= 0 ? (x) : -(x))
@@ -129,18 +128,24 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
struct ref {
- char *name; /* Ref name; tag or head names are shortened. */
char id[SIZEOF_REV]; /* Commit SHA1 ID */
unsigned int head:1; /* Is it the current HEAD? */
unsigned int tag:1; /* Is it a tag? */
unsigned int ltag:1; /* If so, is the tag local? */
unsigned int remote:1; /* Is it a remote ref? */
unsigned int tracked:1; /* Is it the remote for the current HEAD? */
- unsigned int next:1; /* For ref lists: are there more refs? */
+ char name[1]; /* Ref name; tag or head names are shortened. */
+};
+
+struct ref_list {
+ char id[SIZEOF_REV]; /* Commit SHA1 ID */
+ size_t size; /* Number of refs. */
+ struct ref **refs; /* References for this ID. */
};
-static struct ref **get_refs(const char *id);
+static struct ref_list *get_ref_list(const char *id);
static void foreach_ref(bool (*visitor)(void *data, struct ref *ref), void *data);
+static int load_refs(void);
enum format_flags {
FORMAT_ALL, /* Perform replacement in all arguments. */
{
char buf[SIZEOF_STR];
char *commit_id = (char *)line->data + STRING_SIZE("commit ");
- struct ref **refs;
- size_t bufpos = 0, refpos = 0;
+ struct ref_list *list;
+ size_t bufpos = 0, i;
const char *sep = "Refs: ";
bool is_tag = FALSE;
assert(line->type == LINE_COMMIT);
- refs = get_refs(commit_id);
- if (!refs) {
+ list = get_ref_list(commit_id);
+ if (!list) {
if (view == VIEW(REQ_VIEW_DIFF))
goto try_add_describe_ref;
return;
}
- do {
- struct ref *ref = refs[refpos];
+ for (i = 0; i < list->size; i++) {
+ struct ref *ref = list->refs[i];
const char *fmt = ref->tag ? "%s[%s]" :
ref->remote ? "%s<%s>" : "%s%s";
sep = ", ";
if (ref->tag)
is_tag = TRUE;
- } while (refs[refpos++]->next);
+ }
if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
try_add_describe_ref:
char title[128]; /* First line of the commit message. */
const char *author; /* Author of the commit. */
time_t time; /* Date from the author ident. */
- struct ref **refs; /* Repository references. */
+ struct ref_list *refs; /* Repository references. */
chtype graph[SIZEOF_REVGRAPH]; /* Ancestry chain graphics. */
size_t graph_size; /* The width of the graph array. */
bool has_parents; /* Rewritten --parents seen. */
return TRUE;
if (opt_show_refs && commit->refs) {
- size_t i = 0;
+ size_t i;
- do {
- struct ref *ref = commit->refs[i];
+ for (i = 0; i < commit->refs->size; i++) {
+ struct ref *ref = commit->refs->refs[i];
enum line_type type;
if (ref->head)
if (draw_text(view, LINE_DEFAULT, " ", TRUE))
return TRUE;
- } while (commit->refs[i++]->next);
+ }
}
draw_text(view, LINE_DEFAULT, commit->title, TRUE);
}
string_copy_rev(commit->id, line);
- commit->refs = get_refs(commit->id);
+ commit->refs = get_ref_list(commit->id);
graph->commit = commit;
add_line_data(view, commit, LINE_MAIN_COMMIT);
}
static bool
-grep_refs(struct ref **refs, regex_t *regex)
+grep_refs(struct ref_list *list, regex_t *regex)
{
regmatch_t pmatch;
- size_t i = 0;
+ size_t i;
- if (!opt_show_refs || !refs)
+ if (!opt_show_refs || !list)
return FALSE;
- do {
- if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
+
+ for (i = 0; i < list->size; i++) {
+ if (regexec(regex, list->refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
return TRUE;
- } while (refs[i++]->next);
+ }
return FALSE;
}
* Repository properties
*/
-static struct ref *refs = NULL;
+static struct ref **refs = NULL;
static size_t refs_size = 0;
-/* Id <-> ref store */
-static struct ref ***id_refs = NULL;
-static size_t id_refs_size = 0;
+static struct ref_list **ref_lists = NULL;
+static size_t ref_lists_size = 0;
-DEFINE_ALLOCATOR(realloc_refs, struct ref, 256)
+DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
-DEFINE_ALLOCATOR(realloc_refs_lists, struct ref **, 8)
+DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
static int
compare_refs(const void *ref1_, const void *ref2_)
size_t i;
for (i = 0; i < refs_size; i++)
- if (!visitor(data, &refs[i]))
+ if (!visitor(data, refs[i]))
break;
}
-static struct ref **
-get_refs(const char *id)
+static struct ref_list *
+get_ref_list(const char *id)
{
- struct ref **ref_list = NULL;
- size_t ref_list_size = 0;
+ struct ref_list *list;
size_t i;
- for (i = 0; i < id_refs_size; i++)
- if (!strcmp(id, id_refs[i][0]->id))
- return id_refs[i];
+ for (i = 0; i < ref_lists_size; i++)
+ if (!strcmp(id, ref_lists[i]->id))
+ return ref_lists[i];
- if (!realloc_refs_lists(&id_refs, id_refs_size, 1))
+ if (!realloc_ref_lists(&ref_lists, ref_lists_size, 1))
+ return NULL;
+ list = calloc(1, sizeof(*list));
+ if (!list)
return NULL;
for (i = 0; i < refs_size; i++) {
- if (strcmp(id, refs[i].id))
- continue;
-
- if (!realloc_refs_list(&ref_list, ref_list_size, 1))
- break;
-
- ref_list[ref_list_size] = &refs[i];
- /* XXX: The properties of the commit chains ensures that we can
- * safely modify the shared ref. The repo references will
- * always be similar for the same id. */
- ref_list[ref_list_size]->next = 1;
- ref_list_size++;
+ if (!strcmp(id, refs[i]->id) &&
+ realloc_refs_list(&list->refs, list->size, 1))
+ list->refs[list->size++] = refs[i];
}
- if (ref_list) {
- qsort(ref_list, ref_list_size, sizeof(*ref_list), compare_refs);
- ref_list[ref_list_size - 1]->next = 0;
- id_refs[id_refs_size++] = ref_list;
+ if (!list->refs) {
+ free(list);
+ return NULL;
}
- return ref_list;
+ qsort(list->refs, list->size, sizeof(*list->refs), compare_refs);
+ ref_lists[ref_lists_size++] = list;
+ return list;
}
static int
read_ref(char *id, size_t idlen, char *name, size_t namelen)
{
- struct ref *ref;
+ struct ref *ref = NULL;
bool tag = FALSE;
bool ltag = FALSE;
bool remote = FALSE;
bool tracked = FALSE;
- bool check_replace = FALSE;
bool head = FALSE;
+ int from = 0, to = refs_size - 1;
if (!prefixcmp(name, "refs/tags/")) {
if (!suffixcmp(name, namelen, "^{}")) {
namelen -= 3;
name[namelen] = 0;
- if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
- check_replace = TRUE;
} else {
ltag = TRUE;
}
return OK;
}
- if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
- /* it's an annotated tag, replace the previous SHA1 with the
- * resolved commit id; relies on the fact git-ls-remote lists
- * the commit id of an annotated tag right before the commit id
- * it points to. */
- refs[refs_size - 1].ltag = ltag;
- string_copy_rev(refs[refs_size - 1].id, id);
+ /* If we are reloading or it's an annotated tag, replace the
+ * previous SHA1 with the resolved commit id; relies on the fact
+ * git-ls-remote lists the commit id of an annotated tag right
+ * before the commit id it points to. */
+ while (from <= to) {
+ size_t pos = (to + from) / 2;
+ int cmp = strcmp(name, refs[pos]->name);
- return OK;
- }
+ if (!cmp) {
+ ref = refs[pos];
+ break;
+ }
- if (!realloc_refs(&refs, refs_size, 1))
- return ERR;
+ if (cmp < 0)
+ to = pos - 1;
+ else
+ from = pos + 1;
+ }
- ref = &refs[refs_size++];
- ref->name = malloc(namelen + 1);
- if (!ref->name)
- return ERR;
+ if (!ref) {
+ if (!realloc_refs(&refs, refs_size, 1))
+ return ERR;
+ ref = calloc(1, sizeof(*ref) + namelen);
+ if (!ref)
+ return ERR;
+ memmove(refs + from + 1, refs + from,
+ (refs_size - from) * sizeof(*refs));
+ refs[from] = ref;
+ strncpy(ref->name, name, namelen);
+ refs_size++;
+ }
- strncpy(ref->name, name, namelen);
- ref->name[namelen] = 0;
ref->head = head;
ref->tag = tag;
ref->ltag = ltag;
"git", "ls-remote", opt_git_dir, NULL
};
static bool init = FALSE;
+ size_t i;
if (!init) {
argv_from_env(ls_remote_argv, "TIG_LS_REMOTE");
memmove(opt_head, offset, strlen(offset) + 1);
}
- while (refs_size > 0)
- free(refs[--refs_size].name);
- while (id_refs_size > 0)
- free(id_refs[--id_refs_size]);
+ for (i = 0; i < refs_size; i++)
+ refs[i]->id[0] = 0;
+
+ if (run_io_load(ls_remote_argv, "\t", read_ref) == ERR)
+ return ERR;
+
+ /* Update the ref lists to reflect changes. */
+ for (i = 0; i < ref_lists_size; i++) {
+ struct ref_list *list = ref_lists[i];
+ size_t old, new;
- return run_io_load(ls_remote_argv, "\t", read_ref);
+ for (old = new = 0; old < list->size; old++)
+ if (!strcmp(list->id, list->refs[old]->id))
+ list->refs[new++] = list->refs[old];
+ list->size = new;
+ }
+
+ return OK;
}
static void