3 list_packs.c -- list_packs utility to count Git packs and their objects
4 Copyright (C) 2016 Kyle J. McKay. All rights reserved.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 This utility is intended to be used by a script to assist in determining
24 whether or not it's time to run gc in the case where gc.auto=0 and when
25 it is to provide a convenient mechanism to feed selected pack names to
26 a script for futher processing at gc time.
28 Various options are available to select which .pack files to inspect
29 including supplying the names. This utility is intended to be able
30 to read the pack names from the --export-pack-edges file that may be
31 produced by git fast-import without needing any preprocessing.
33 See the list_packs.txt file or run the command with no arguments for help.
36 #define _XOPEN_SOURCE 600
37 #undef _FILE_OFFSET_BITS
38 #define _FILE_OFFSET_BITS 64
44 #include <sys/types.h> /* in some cases required before dirent.h or sys/stat.h */
50 #include <arpa/inet.h>
57 #define PATH_BUFF 4096
59 #define PATH_BUFF PATH_MAX
62 static int fgetfn(FILE *f
, char *b
, size_t s
);
63 static void process_pack_filename(char *fn
, size_t bl
, int hasps
);
66 #include "list_packs.inc"
70 static int opt_xkp
= -1;
71 static int opt_xbt
= -1;
72 static int opt_xbn
= -1;
73 static int opt_xix
= -1;
74 static long opt_limit
= 0;
75 static int opt_count
= 0;
76 static uint64_t count
= 0;
78 static char fnbuff
[PATH_BUFF
];
80 static void die(const char *fmt
, ...)
86 vfprintf(stderr
, fmt
, args
);
91 static void dieusage(int err
)
93 FILE *f
= err
? stderr
: stdout
;
96 fprintf(f
, "%s", USAGE
);
100 static int has_suffix(const char *s
, size_t l
, const char *x
, size_t b
)
102 if (!s
|| !x
|| !b
|| l
< (b
+ 1) /* ?<suffix> */)
104 return !strncmp(s
+ l
- b
, x
, b
);
106 #define has_idx_suffix(s,l) has_suffix((s),(l),".idx",4)
107 #define has_bndl_suffix(s,l) has_suffix((s),(l),".bndl",5)
108 #define has_keep_suffix(s,l) has_suffix((s),(l),".keep",5)
109 #define has_pack_suffix(s,l) has_suffix((s),(l),".pack",5)
110 #define has_bitmap_suffix(s,l) has_suffix((s),(l),".bitmap",7)
112 static int is_pack_sha1_name(const char *s
, size_t l
)
116 if (strncmp(s
, "pack-", 5) || strncmp(s
+ l
- 5, ".pack", 5))
118 return strspn(s
+ 5, "0123456789abcdefABCDEF") == 40;
121 int main(int argc
, char *argv
[])
125 const char *only
= NULL
;
126 const char *dir
= NULL
;
130 for (argn
= 1; argn
< argc
; ++argn
) {
131 if (!strcmp(argv
[argn
], "-h") || !strcmp(argv
[argn
], "--help")) {
132 dieusage(EXIT_SUCCESS
);
133 } else if (!strcmp(argv
[argn
], "-q") || !strcmp(argv
[argn
], "--quiet")) {
135 } else if (!strcmp(argv
[argn
], "-a") || !strcmp(argv
[argn
], "--all")) {
137 } else if (!strcmp(argv
[argn
], "--exclude-keep")) {
139 } else if (!strcmp(argv
[argn
], "--exclude-no-keep")) {
141 } else if (!strcmp(argv
[argn
], "--exclude-bitmap")) {
143 } else if (!strcmp(argv
[argn
], "--exclude-no-bitmap")) {
145 } else if (!strcmp(argv
[argn
], "--exclude-bndl")) {
147 } else if (!strcmp(argv
[argn
], "--exclude-no-bndl")) {
149 } else if (!strcmp(argv
[argn
], "--exclude-idx")) {
151 } else if (!strcmp(argv
[argn
], "--exclude-no-idx")) {
153 } else if (!strcmp(argv
[argn
], "--count")) {
155 } else if (!strcmp(argv
[argn
], "--count-objects")) {
157 } else if (!strcmp(argv
[argn
], "--exclude-limit")) {
162 dieusage(EXIT_FAILURE
);
163 limit
= strtol(argv
[argn
], &end
, 0);
164 if (!*argv
[argn
] || *end
|| !limit
)
165 dieusage(EXIT_FAILURE
);
167 } else if (!strcmp(argv
[argn
], "--only")) {
168 if (++argn
>= argc
|| !*argv
[argn
])
169 dieusage(EXIT_FAILURE
);
171 } else if (!strcmp(argv
[argn
], "-C")) {
172 if (++argn
>= argc
|| !*argv
[argn
])
173 dieusage(EXIT_FAILURE
);
174 if (chdir(argv
[argn
])) {
176 fprintf(stderr
, "list_packs: error: "
177 "chdir '%s' failed\n", argv
[argn
]);
180 } else if (!strcmp(argv
[argn
], "--")) {
183 } else if (argv
[argn
][0] == '-' && argv
[argn
][1]) {
184 dieusage(EXIT_FAILURE
);
189 if (argn
< argc
&& *argv
[argn
])
191 if (argn
!= argc
|| (!only
&& !dir
) || (only
&& dir
) || (only
&& opt_a
))
192 dieusage(EXIT_FAILURE
);
194 if (!strcmp(only
, "-")) {
198 in
= fopen(only
, "r");
200 die("list_packs: error: could not open %s\n", only
);
202 while (fgetfn(in
, fnbuff
, sizeof(fnbuff
) - 7 /* .bitmap */)) {
204 size_t l
= strlen(fn
);
209 if (l
> 2 && !strncmp(fn
, "./", 2)) {
213 ips
= has_pack_suffix(fn
, l
);
214 process_pack_filename(fn
, (ips
? l
- 5 : l
), ips
);
224 while (l
> 1 && dir
[l
-1] == '/') {
227 if (l
> 2 && !strncmp(dir
, "./", 2)) {
231 if (l
+ 10 /* "/?.bitmap\0" */ > PATH_BUFF
)
232 die("list_packs: error: dirname too long\n");
233 memcpy(fnbuff
, dir
, l
);
237 die("list_packs: error: could not read directory %s\n", fnbuff
);
238 if (!strcmp(fnbuff
, ".")) {
242 if (l
&& fnbuff
[l
-1] != '/')
244 while ((e
= readdir(d
)) != NULL
) {
245 /* d_namlen is a nice, but non-POSIX extension */
246 size_t el
= strlen(e
->d_name
);
248 if (has_pack_suffix(e
->d_name
, el
) &&
249 (opt_a
|| is_pack_sha1_name(e
->d_name
, el
))) {
250 if (l
+ el
+ 3 /* "ap\0" */ > PATH_BUFF
) {
252 fprintf(stderr
, "list_packs: warning: "
253 "ignored input filename greater "
254 "than %d characters long\n",
258 memcpy(fnbuff
+ l
, e
->d_name
, el
+ 1 /* \0 */);
259 process_pack_filename(fnbuff
, l
+ el
- 5 /* .pack */, 1);
265 printf("%"PRIu64
"\n", count
);
270 #define FNDELIM "\t\n\v\f\r :"
272 static int fgetfn(FILE *f
, char *b
, size_t s
)
277 if (!fgets(b
, (int)s
, f
)) {
280 fprintf(stderr
, "list_packs: error: an error "
281 "occurred reading pack name list file\n");
289 fnl
= strcspn(b
, FNDELIM
);
290 if (b
[l
-1] != '\n' && !feof(f
)) {
293 while ((ch
= getc_unlocked(f
)) != EOF
&& ch
!= '\n') {
301 if (fnl
< l
|| (!ferror(f
) && !trunc
)) {
307 fprintf(stderr
, "list_packs: error: an error "
308 "occurred reading pack name list file\n");
312 fprintf(stderr
, "list_packs: warning: ignored input filename "
313 "greater than %d characters long\n", (int)s
- 2);
318 static int file_exists(const char *fn
, struct stat
*s
)
321 if (S_ISREG(s
->st_mode
))
324 fprintf(stderr
, "list_packs: warning: ignoring "
325 "non-file '%s'\n", fn
);
330 static void process_pack_filename(char *fn
, size_t bl
, int hasps
)
341 if (stat(fn
, &ps
) || !S_ISREG(ps
.st_mode
)) {
343 fprintf(stderr
, "list_packs: warning: ignoring "
344 "non-file '%s'\n", fn
);
347 if (ps
.st_size
< 32) {
349 fprintf(stderr
, "list_packs: warning: ignoring "
350 "invalid pack file '%s'\n", fn
);
356 memcpy(fn
+ bl
, ".keep", 6);
357 hk
= file_exists(fn
, &es
);
358 if ((opt_xkp
&& hk
) || (!opt_xkp
&& !hk
))
364 memcpy(fn
+ bl
, ".bitmap", 8);
365 hb
= file_exists(fn
, &es
);
366 if ((opt_xbt
&& hb
) || (!opt_xbt
&& !hb
))
372 memcpy(fn
+ bl
, ".bndl", 6);
373 hn
= file_exists(fn
, &es
);
374 if ((opt_xbn
&& hn
) || (!opt_xbn
&& !hn
))
380 memcpy(fn
+ bl
, ".idx", 5);
381 hx
= file_exists(fn
, &es
);
382 if ((opt_xix
&& hx
) || (!opt_xix
&& !hx
))
386 memcpy(fn
+ bl
, ".pack", 6);
392 fprintf(stderr
, "list_packs: warning: ignoring "
393 "unopenable pack file '%s'\n", fn
);
396 if (fread(&hdr
, 12, 1, f
) != 1) {
399 fprintf(stderr
, "list_packs: warning: ignoring "
400 "unreadable pack file '%s'\n", fn
);
404 packver
= ntohl(hdr
.u
[1]);
405 objcnt
= ntohl(hdr
.u
[2]);
406 if (memcmp(hdr
.c
, "PACK", 4) || (packver
!= 2 && packver
!= 3) ||
407 ps
.st_size
< ((off_t
)objcnt
+ 32)) {
409 fprintf(stderr
, "list_packs: warning: ignoring "
410 "invalid pack file '%s'\n", fn
);
413 if (!opt_xix
&& es
.st_size
< ((off_t
)objcnt
* 28 + 1072)) {
415 fprintf(stderr
, "list_packs: warning: ignoring pack "
416 "with invalid idx file '%.*s.idx'\n", (int)bl
, fn
);
420 if ((opt_limit
> 0 && objcnt
>= (uint32_t)opt_limit
) ||
421 (opt_limit
< 0 && objcnt
< (uint32_t)-opt_limit
))
424 /* the PACK file passed all the checks, process it */