Merge branch 'lt/maint-diff-reduce-lstat'
[git] / builtin-count-objects.c
1 /*
2  * Builtin "git count-objects".
3  *
4  * Copyright (c) 2006 Junio C Hamano
5  */
6
7 #include "cache.h"
8 #include "dir.h"
9 #include "builtin.h"
10 #include "parse-options.h"
11
12 static void count_objects(DIR *d, char *path, int len, int verbose,
13                           unsigned long *loose,
14                           unsigned long *loose_size,
15                           unsigned long *packed_loose,
16                           unsigned long *garbage)
17 {
18         struct dirent *ent;
19         while ((ent = readdir(d)) != NULL) {
20                 char hex[41];
21                 unsigned char sha1[20];
22                 const char *cp;
23                 int bad = 0;
24
25                 if (is_dot_or_dotdot(ent->d_name))
26                         continue;
27                 for (cp = ent->d_name; *cp; cp++) {
28                         int ch = *cp;
29                         if (('0' <= ch && ch <= '9') ||
30                             ('a' <= ch && ch <= 'f'))
31                                 continue;
32                         bad = 1;
33                         break;
34                 }
35                 if (cp - ent->d_name != 38)
36                         bad = 1;
37                 else {
38                         struct stat st;
39                         memcpy(path + len + 3, ent->d_name, 38);
40                         path[len + 2] = '/';
41                         path[len + 41] = 0;
42                         if (lstat(path, &st) || !S_ISREG(st.st_mode))
43                                 bad = 1;
44                         else
45                                 (*loose_size) += xsize_t(on_disk_bytes(st));
46                 }
47                 if (bad) {
48                         if (verbose) {
49                                 error("garbage found: %.*s/%s",
50                                       len + 2, path, ent->d_name);
51                                 (*garbage)++;
52                         }
53                         continue;
54                 }
55                 (*loose)++;
56                 if (!verbose)
57                         continue;
58                 memcpy(hex, path+len, 2);
59                 memcpy(hex+2, ent->d_name, 38);
60                 hex[40] = 0;
61                 if (get_sha1_hex(hex, sha1))
62                         die("internal error");
63                 if (has_sha1_pack(sha1))
64                         (*packed_loose)++;
65         }
66 }
67
68 static char const * const count_objects_usage[] = {
69         "git count-objects [-v]",
70         NULL
71 };
72
73 int cmd_count_objects(int argc, const char **argv, const char *prefix)
74 {
75         int i, verbose = 0;
76         const char *objdir = get_object_directory();
77         int len = strlen(objdir);
78         char *path = xmalloc(len + 50);
79         unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
80         unsigned long loose_size = 0;
81         struct option opts[] = {
82                 OPT__VERBOSE(&verbose),
83                 OPT_END(),
84         };
85
86         argc = parse_options(argc, argv, opts, count_objects_usage, 0);
87         /* we do not take arguments other than flags for now */
88         if (argc)
89                 usage_with_options(count_objects_usage, opts);
90         memcpy(path, objdir, len);
91         if (len && objdir[len-1] != '/')
92                 path[len++] = '/';
93         for (i = 0; i < 256; i++) {
94                 DIR *d;
95                 sprintf(path + len, "%02x", i);
96                 d = opendir(path);
97                 if (!d)
98                         continue;
99                 count_objects(d, path, len, verbose,
100                               &loose, &loose_size, &packed_loose, &garbage);
101                 closedir(d);
102         }
103         if (verbose) {
104                 struct packed_git *p;
105                 unsigned long num_pack = 0;
106                 unsigned long size_pack = 0;
107                 if (!packed_git)
108                         prepare_packed_git();
109                 for (p = packed_git; p; p = p->next) {
110                         if (!p->pack_local)
111                                 continue;
112                         if (open_pack_index(p))
113                                 continue;
114                         packed += p->num_objects;
115                         size_pack += p->pack_size + p->index_size;
116                         num_pack++;
117                 }
118                 printf("count: %lu\n", loose);
119                 printf("size: %lu\n", loose_size / 1024);
120                 printf("in-pack: %lu\n", packed);
121                 printf("packs: %lu\n", num_pack);
122                 printf("size-pack: %lu\n", size_pack / 1024);
123                 printf("prune-packable: %lu\n", packed_loose);
124                 printf("garbage: %lu\n", garbage);
125         }
126         else
127                 printf("%lu objects, %lu kilobytes\n",
128                        loose, loose_size / 1024);
129         return 0;
130 }