|
3 | 3 | #include <sys/types.h>
|
4 | 4 | #include <dirent.h>
|
5 | 5 |
|
6 |
| -struct needs { |
7 |
| - unsigned char parent[20]; |
8 |
| - unsigned char needs[20]; |
9 |
| - char tag[10]; |
| 6 | +/* |
| 7 | + * The low 16 bits of the "flags" field shows whether |
| 8 | + * a commit is part of the path to the root for that |
| 9 | + * parent. |
| 10 | + * |
| 11 | + * Bit 16 is an internal flag that we've seen the |
| 12 | + * definition for this rev, and not just seen it as |
| 13 | + * a parent target. |
| 14 | + */ |
| 15 | +#define MAX_COMMITS (16) |
| 16 | +#define marked(rev) ((rev)->flags & 0xffff) |
| 17 | +#define SEEN 0x10000 |
| 18 | +#define USED 0x20000 |
| 19 | +#define REACHABLE 0x40000 |
| 20 | + |
| 21 | +static int show_unreachable = 0; |
| 22 | +static int head_supplied = 0; |
| 23 | +static unsigned char head_sha1[20]; |
| 24 | + |
| 25 | +struct parent { |
| 26 | + struct revision *parent; |
| 27 | + struct parent *next; |
10 | 28 | };
|
11 | 29 |
|
12 |
| -struct seen { |
| 30 | +struct revision { |
| 31 | + unsigned int flags; |
13 | 32 | unsigned char sha1[20];
|
14 |
| - char tag[10]; |
15 |
| - unsigned needed; |
| 33 | + unsigned long date; |
| 34 | + struct parent *parent; |
16 | 35 | };
|
17 | 36 |
|
18 |
| -static struct needs *needs; |
19 |
| -static struct seen *seen; |
| 37 | +static struct revision **revs; |
| 38 | +static int nr_revs, rev_allocs; |
20 | 39 |
|
21 |
| -static int nr_seen, alloc_seen, nr_needs, alloc_needs; |
22 |
| - |
23 |
| -/* |
24 |
| - * These two functions build up a graph in memory about |
25 |
| - * what objects we've referenced, and found, and types.. |
26 |
| - */ |
27 |
| -static int compare_seen(const void *s1, const void *s2) |
| 40 | +static int find_rev(unsigned char *sha1) |
28 | 41 | {
|
29 |
| - return memcmp(s1, s2, 20); |
30 |
| -} |
| 42 | + int first = 0, last = nr_revs; |
31 | 43 |
|
32 |
| -static int lookup_seen(unsigned char *sha1, char *tag) |
33 |
| -{ |
34 |
| - int first = 0, last = nr_seen; |
35 |
| - |
36 |
| - while (last > first) { |
37 |
| - int next = (last + first) / 2; |
38 |
| - struct seen *s = seen + next; |
39 |
| - int cmp = memcmp(sha1, s->sha1, 20); |
| 44 | + while (first < last) { |
| 45 | + int next = (first + last) / 2; |
| 46 | + struct revision *rev = revs[next]; |
| 47 | + int cmp; |
40 | 48 |
|
| 49 | + cmp = memcmp(sha1, rev->sha1, 20); |
| 50 | + if (!cmp) |
| 51 | + return next; |
41 | 52 | if (cmp < 0) {
|
42 | 53 | last = next;
|
43 | 54 | continue;
|
44 | 55 | }
|
45 |
| - if (cmp > 0) { |
46 |
| - first = next+1; |
47 |
| - continue; |
48 |
| - } |
49 |
| - if (strcmp(tag, s->tag)) |
50 |
| - break; |
51 |
| - s->needed++; |
52 |
| - return 1; |
| 56 | + first = next+1; |
| 57 | + } |
| 58 | + return -first-1; |
| 59 | +} |
| 60 | + |
| 61 | +static struct revision *lookup_rev(unsigned char *sha1) |
| 62 | +{ |
| 63 | + int pos = find_rev(sha1); |
| 64 | + struct revision *n; |
| 65 | + |
| 66 | + if (pos >= 0) |
| 67 | + return revs[pos]; |
| 68 | + |
| 69 | + pos = -pos-1; |
| 70 | + |
| 71 | + if (rev_allocs == nr_revs) { |
| 72 | + rev_allocs = alloc_nr(rev_allocs); |
| 73 | + revs = realloc(revs, rev_allocs * sizeof(struct revision *)); |
| 74 | + } |
| 75 | + n = malloc(sizeof(struct revision)); |
| 76 | + |
| 77 | + n->flags = 0; |
| 78 | + memcpy(n->sha1, sha1, 20); |
| 79 | + n->parent = NULL; |
| 80 | + |
| 81 | + /* Insert it into the right place */ |
| 82 | + memmove(revs + pos + 1, revs + pos, (nr_revs - pos) * sizeof(struct revision *)); |
| 83 | + revs[pos] = n; |
| 84 | + nr_revs++; |
| 85 | + |
| 86 | + return n; |
| 87 | +} |
| 88 | + |
| 89 | +static struct revision *add_relationship(struct revision *rev, unsigned char *needs) |
| 90 | +{ |
| 91 | + struct revision *parent_rev = lookup_rev(needs); |
| 92 | + struct parent **pp = &rev->parent, *p; |
| 93 | + |
| 94 | + while ((p = *pp) != NULL) { |
| 95 | + if (p->parent == parent_rev) |
| 96 | + return parent_rev; |
| 97 | + pp = &p->next; |
| 98 | + } |
| 99 | + |
| 100 | + p = malloc(sizeof(*p)); |
| 101 | + p->parent = parent_rev; |
| 102 | + p->next = NULL; |
| 103 | + *pp = p; |
| 104 | + return parent_rev; |
| 105 | +} |
| 106 | + |
| 107 | +static void mark_reachable(struct revision *rev) |
| 108 | +{ |
| 109 | + struct parent *p = rev->parent; |
| 110 | + |
| 111 | + rev->flags |= REACHABLE | USED; |
| 112 | + while (p) { |
| 113 | + mark_reachable(p->parent); |
| 114 | + p = p->next; |
53 | 115 | }
|
54 |
| - return 0; |
55 | 116 | }
|
56 | 117 |
|
57 | 118 | static void check_connectivity(void)
|
58 | 119 | {
|
59 | 120 | int i;
|
60 | 121 |
|
61 |
| - /* Sort the "seen" tags for quicker lookup */ |
62 |
| - qsort(seen, nr_seen, sizeof(struct seen), compare_seen); |
| 122 | + if (head_supplied) |
| 123 | + mark_reachable(lookup_rev(head_sha1)); |
63 | 124 |
|
64 | 125 | /* Look up all the requirements, warn about missing objects.. */
|
65 |
| - for (i = 0; i < nr_needs; i++) { |
66 |
| - struct needs *n = needs + i; |
67 |
| - char hex[60]; |
| 126 | + for (i = 0; i < nr_revs; i++) { |
| 127 | + struct revision *rev = revs[i]; |
68 | 128 |
|
69 |
| - if (lookup_seen(n->needs, n->tag)) |
| 129 | + if (show_unreachable && !(rev->flags & REACHABLE)) { |
| 130 | + printf("unreachable %s\n", sha1_to_hex(rev->sha1)); |
70 | 131 | continue;
|
71 |
| - strcpy(hex, sha1_to_hex(n->parent)); |
72 |
| - printf("missing %s: %s referenced by %s\n", n->tag, sha1_to_hex(n->needs), hex); |
73 |
| - } |
74 |
| - |
75 |
| - /* Tell the user about things not referenced.. */ |
76 |
| - for (i = 0; i < nr_seen; i++) { |
77 |
| - struct seen *s = seen + i; |
| 132 | + } |
78 | 133 |
|
79 |
| - if (s->needed) |
80 |
| - continue; |
81 |
| - printf("unreferenced %s: %s\n", s->tag, sha1_to_hex(s->sha1)); |
| 134 | + switch (rev->flags & (SEEN | USED)) { |
| 135 | + case 0: |
| 136 | + printf("bad %s\n", sha1_to_hex(rev->sha1)); |
| 137 | + break; |
| 138 | + case USED: |
| 139 | + printf("missing %s\n", sha1_to_hex(rev->sha1)); |
| 140 | + break; |
| 141 | + case SEEN: |
| 142 | + printf("dangling %s\n", sha1_to_hex(rev->sha1)); |
| 143 | + break; |
| 144 | + } |
82 | 145 | }
|
83 | 146 | }
|
84 | 147 |
|
85 | 148 | static void mark_needs_sha1(unsigned char *parent, const char * tag, unsigned char *child)
|
86 | 149 | {
|
87 |
| - struct needs *n; |
88 |
| - |
89 |
| - if (nr_needs == alloc_needs) { |
90 |
| - alloc_needs = alloc_nr(alloc_needs); |
91 |
| - needs = realloc(needs, alloc_needs*sizeof(struct needs)); |
92 |
| - } |
93 |
| - n = needs + nr_needs; |
94 |
| - nr_needs++; |
95 |
| - memcpy(n->parent, parent, 20); |
96 |
| - memcpy(n->needs, child, 20); |
97 |
| - strncpy(n->tag, tag, sizeof(n->tag)); |
| 150 | + struct revision * child_rev = add_relationship(lookup_rev(parent), child); |
| 151 | + child_rev->flags |= USED; |
98 | 152 | }
|
99 | 153 |
|
100 | 154 | static int mark_sha1_seen(unsigned char *sha1, char *tag)
|
101 | 155 | {
|
102 |
| - struct seen *s; |
| 156 | + struct revision *rev = lookup_rev(sha1); |
103 | 157 |
|
104 |
| - if (nr_seen == alloc_seen) { |
105 |
| - alloc_seen = alloc_nr(alloc_seen); |
106 |
| - seen = realloc(seen, alloc_seen*sizeof(struct seen)); |
107 |
| - } |
108 |
| - s = seen + nr_seen; |
109 |
| - memset(s, 0, sizeof(*s)); |
110 |
| - nr_seen++; |
111 |
| - memcpy(s->sha1, sha1, 20); |
112 |
| - strncpy(s->tag, tag, sizeof(s->tag)); |
113 |
| - |
| 158 | + rev->flags |= SEEN; |
114 | 159 | return 0;
|
115 | 160 | }
|
116 | 161 |
|
@@ -160,7 +205,7 @@ static int fsck_commit(unsigned char *sha1, void *data, unsigned long size)
|
160 | 205 | parents++;
|
161 | 206 | }
|
162 | 207 | if (!parents)
|
163 |
| - printf("root: %s\n", sha1_to_hex(sha1)); |
| 208 | + printf("root %s\n", sha1_to_hex(sha1)); |
164 | 209 | return 0;
|
165 | 210 | }
|
166 | 211 |
|
@@ -237,8 +282,20 @@ int main(int argc, char **argv)
|
237 | 282 | int i;
|
238 | 283 | char *sha1_dir;
|
239 | 284 |
|
240 |
| - if (argc != 1) |
241 |
| - usage("fsck-cache"); |
| 285 | + for (i = 1; i < argc; i++) { |
| 286 | + if (!strcmp(argv[i], "--unreachable")) { |
| 287 | + show_unreachable = 1; |
| 288 | + continue; |
| 289 | + } |
| 290 | + if (!get_sha1_hex(argv[i], head_sha1)) { |
| 291 | + head_supplied = 1; |
| 292 | + continue; |
| 293 | + } |
| 294 | + usage("fsck-cache [[--unreachable] <head-sha1>]"); |
| 295 | + } |
| 296 | + if (show_unreachable && !head_supplied) |
| 297 | + usage("unable to do reachability checks without a head"); |
| 298 | + |
242 | 299 | sha1_dir = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT;
|
243 | 300 | for (i = 0; i < 256; i++) {
|
244 | 301 | static char dir[4096];
|
|
0 commit comments