[PATCH] diff-cache/tree compatible output for show-diff (take 2).
[git.git] / fsck-cache.c
1 #include "cache.h"
2
3 #include <sys/types.h>
4 #include <dirent.h>
5
6 #include "commit.h"
7 #include "tree.h"
8 #include "blob.h"
9
10 #define REACHABLE 0x0001
11
12 static int show_root = 0;
13 static int show_tags = 0;
14 static int show_unreachable = 0;
15 static unsigned char head_sha1[20];
16
17 static void check_connectivity(void)
18 {
19         int i;
20
21         /* Look up all the requirements, warn about missing objects.. */
22         for (i = 0; i < nr_objs; i++) {
23                 struct object *obj = objs[i];
24
25                 if (show_unreachable && !(obj->flags & REACHABLE)) {
26                         printf("unreachable %s %s\n", obj->type, sha1_to_hex(obj->sha1));
27                         continue;
28                 }
29
30                 if (!obj->parsed) {
31                         printf("missing %s %s\n", obj->type, 
32                                sha1_to_hex(obj->sha1));
33                 }
34                 if (!obj->used) {
35                         printf("dangling %s %s\n", obj->type, 
36                                sha1_to_hex(obj->sha1));
37                 }
38         }
39 }
40
41 static int fsck_tree(unsigned char *sha1, void *data, unsigned long size)
42 {
43         struct tree *item = lookup_tree(sha1);
44         if (parse_tree(item))
45                 return -1;
46         if (item->has_full_path) {
47                 fprintf(stderr, "warning: fsck-cache: tree %s "
48                         "has full pathnames in it\n", sha1_to_hex(sha1));
49         }
50         return 0;
51 }
52
53 static int fsck_commit(unsigned char *sha1, void *data, unsigned long size)
54 {
55         struct commit *commit = lookup_commit(sha1);
56         if (parse_commit(commit))
57                 return -1;
58         if (!commit->tree)
59                 return -1;
60         if (!commit->parents && show_root)
61                 printf("root %s\n", sha1_to_hex(sha1));
62         if (!commit->date)
63                 printf("bad commit date in %s\n", sha1_to_hex(sha1));
64         return 0;
65 }
66
67 static int fsck_blob(unsigned char *sha1, void *data, unsigned long size)
68 {
69         struct blob *blob = lookup_blob(sha1);
70         blob->object.parsed = 1;
71         return 0;
72 }
73
74 static int fsck_tag(unsigned char *sha1, void *data, unsigned long size)
75 {
76         int typelen, taglen;
77         unsigned char object[20];
78         char object_hex[60];
79         const char *type_line, *tag_line, *sig_line;
80
81         if (size < 64)
82                 return -1;
83         if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
84                 return -1;
85
86         type_line = data + 48;
87         if (memcmp("\ntype ", type_line-1, 6))
88                 return -1;
89
90         tag_line = strchr(type_line, '\n');
91         if (!tag_line || memcmp("tag ", ++tag_line, 4))
92                 return -1;
93
94         sig_line = strchr(tag_line, '\n');
95         if (!sig_line)
96                 return -1;
97         sig_line++;
98
99         typelen = tag_line - type_line - strlen("type \n");
100         if (typelen >= 20)
101                 return -1;
102         taglen = sig_line - tag_line - strlen("tag \n");
103
104         if (!show_tags)
105                 return 0;
106
107         strcpy(object_hex, sha1_to_hex(object));
108         printf("tagged %.*s %s (%.*s) in %s\n",
109                 typelen, type_line + 5,
110                 object_hex,
111                 taglen, tag_line + 4,
112                 sha1_to_hex(sha1));
113         return 0;
114 }
115
116 static int fsck_entry(unsigned char *sha1, char *tag, void *data, 
117                       unsigned long size)
118 {
119         if (!strcmp(tag, "blob")) {
120                 if (fsck_blob(sha1, data, size) < 0)
121                         return -1;
122         } else if (!strcmp(tag, "tree")) {
123                 if (fsck_tree(sha1, data, size) < 0)
124                         return -1;
125         } else if (!strcmp(tag, "commit")) {
126                 if (fsck_commit(sha1, data, size) < 0)
127                         return -1;
128         } else if (!strcmp(tag, "tag")) {
129                 if (fsck_tag(sha1, data, size) < 0)
130                         return -1;
131         } else
132                 return -1;
133         return 0;
134 }
135
136 static int fsck_name(char *hex)
137 {
138         unsigned char sha1[20];
139         if (!get_sha1_hex(hex, sha1)) {
140                 unsigned long mapsize;
141                 void *map = map_sha1_file(sha1, &mapsize);
142                 if (map) {
143                         char type[100];
144                         unsigned long size;
145                         void *buffer = unpack_sha1_file(map, mapsize, type, &size);
146                         if (!buffer)
147                                 return -1;
148                         if (check_sha1_signature(sha1, buffer, size, type) < 0)
149                                 printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
150                         munmap(map, mapsize);
151                         if (!fsck_entry(sha1, type, buffer, size))
152                                 return 0;
153                 }
154         }
155         return -1;
156 }
157
158 static int fsck_dir(int i, char *path)
159 {
160         DIR *dir = opendir(path);
161         struct dirent *de;
162
163         if (!dir) {
164                 return error("missing sha1 directory '%s'", path);
165         }
166
167         while ((de = readdir(dir)) != NULL) {
168                 char name[100];
169                 int len = strlen(de->d_name);
170
171                 switch (len) {
172                 case 2:
173                         if (de->d_name[1] != '.')
174                                 break;
175                 case 1:
176                         if (de->d_name[0] != '.')
177                                 break;
178                         continue;
179                 case 38:
180                         sprintf(name, "%02x", i);
181                         memcpy(name+2, de->d_name, len+1);
182                         if (!fsck_name(name))
183                                 continue;
184                 }
185                 fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
186         }
187         closedir(dir);
188         return 0;
189 }
190
191 int main(int argc, char **argv)
192 {
193         int i, heads;
194         char *sha1_dir;
195
196         for (i = 1; i < argc; i++) {
197                 const char *arg = argv[i];
198
199                 if (!strcmp(arg, "--unreachable")) {
200                         show_unreachable = 1;
201                         continue;
202                 }
203                 if (!strcmp(arg, "--tags")) {
204                         show_tags = 1;
205                         continue;
206                 }
207                 if (!strcmp(arg, "--root")) {
208                         show_root = 1;
209                         continue;
210                 }
211                 if (*arg == '-')
212                         usage("fsck-cache [--tags] [[--unreachable] <head-sha1>*]");
213         }
214
215         sha1_dir = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT;
216         for (i = 0; i < 256; i++) {
217                 static char dir[4096];
218                 sprintf(dir, "%s/%02x", sha1_dir, i);
219                 fsck_dir(i, dir);
220         }
221
222         heads = 0;
223         for (i = 1; i < argc; i++) {
224                 const char *arg = argv[i]; 
225
226                 if (*arg == '-')
227                         continue;
228
229                 if (!get_sha1_hex(arg, head_sha1)) {
230                         struct object *obj = &lookup_commit(head_sha1)->object;
231                         obj->used = 1;
232                         mark_reachable(obj, REACHABLE);
233                         heads++;
234                         continue;
235                 }
236                 error("expected sha1, got %s", arg);
237         }
238
239         if (!heads) {
240                 if (show_unreachable) {
241                         fprintf(stderr, "unable to do reachability without a head\n");
242                         show_unreachable = 0; 
243                 }
244                 fprintf(stderr, "expect dangling commits - potential heads - due to lack of head information\n");
245         }
246
247         check_connectivity();
248         return 0;
249 }