describe: Store commit_names in a hash table by commit SHA1
describe is currently forced to look up the commit at each tag in order to store the struct commit_name pointers in struct commit.util. For --exact-match queries, those lookups are wasteful. In preparation for removing them, put the commit_names into a hash table, indexed by commit SHA1, that can be used to quickly check for exact matches. Signed-off-by: Anders Kaseorg <andersk@ksplice.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Anders Kaseorg committed
Dec 9, 2010 at 01:46 UTC
3cfa4db3226ccef8845589029090d9398b207ba3
1 file changed
+33
-5
builtin/describe.c
+33
-5
@@ -6,6 +6,7 @@
6
#include "exec_cmd.h"
7
#include "parse-options.h"
8
#include "diff.h"
9
+#include "hash.h"
10
11
#define SEEN (1u<<0)
12
#define MAX_TAGS (FLAG_BITS - 1)
@@ -22,7 +23,7 @@ static int tags; /* Allow lightweight tags */
23
static int longformat;
24
static int abbrev = DEFAULT_ABBREV;
25
static int max_candidates = 10;
25
-static int found_names;
26
+static struct hash_table names;
27
static const char *pattern;
28
static int always;
29
static const char *dirty;
@@ -34,6 +35,8 @@ static const char *diff_index_args[] = {
35
36
37
struct commit_name {
38
+ struct commit_name *next;
39
+ unsigned char peeled[20];
40
struct tag *tag;
41
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
42
unsigned name_checked:1;
@@ -44,6 +47,21 @@ static const char *prio_names[] = {
47
"head", "lightweight", "annotated",
48
};
49
50
+static inline unsigned int hash_sha1(const unsigned char *sha1)
51
+{
52
+ unsigned int hash;
53
+ memcpy(&hash, sha1, sizeof(hash));
54
+ return hash;
55
+}
56
+
57
+static inline struct commit_name *find_commit_name(const unsigned char *peeled)
58
+{
59
+ struct commit_name *n = lookup_hash(hash_sha1(peeled), &names);
60
+ while (n && !!hashcmp(peeled, n->peeled))
61
+ n = n->next;
62
+ return n;
63
+}
64
+
65
static int replace_name(struct commit_name *e,
66
int prio,
67
const unsigned char *sha1,
@@ -82,12 +100,22 @@ static void add_to_known_names(const char *path,
100
int prio,
101
const unsigned char *sha1)
102
{
85
- struct commit_name *e = commit->util;
103
+ const unsigned char *peeled = commit->object.sha1;
104
+ struct commit_name *e = find_commit_name(peeled);
105
struct tag *tag = NULL;
106
if (replace_name(e, prio, sha1, &tag)) {
107
if (!e) {
108
+ void **pos;
109
e = xmalloc(sizeof(struct commit_name));
110
commit->util = e;
111
+ hashcpy(e->peeled, peeled);
112
+ pos = insert_hash(hash_sha1(peeled), e, &names);
113
+ if (pos) {
114
+ e->next = *pos;
115
+ *pos = e;
116
+ } else {
117
+ e->next = NULL;
118
+ }
119
}
120
e->tag = tag;
121
e->prio = prio;
@@ -95,7 +123,6 @@ static void add_to_known_names(const char *path,
123
hashcpy(e->sha1, sha1);
124
e->path = path;
125
}
98
- found_names = 1;
126
}
127
128
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
@@ -240,7 +267,7 @@ static void describe(const char *arg, int last_one)
267
if (!cmit)
268
die("%s is not a valid '%s' object", arg, commit_type);
269
243
- n = cmit->util;
270
+ n = find_commit_name(cmit->object.sha1);
271
if (n && (tags || all || n->prio == 2)) {
272
/*
273
* Exact match to an existing ref.
@@ -418,8 +445,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
445
return cmd_name_rev(i + argc, args, prefix);
446
}
447
448
+ init_hash(&names);
449
for_each_rawref(get_name, NULL);
422
- if (!found_names && !always)
450
+ if (!names.nr && !always)
451
die("No names found, cannot describe anything.");
452
453
if (argc == 0) {