count-objects: report garbage files in pack directory too
prepare_packed_git_one() is modified to allow count-objects to hook a report function to so we don't need to duplicate the pack searching logic in count-objects.c. When report_pack_garbage is NULL, the overhead is insignificant. The garbage is reported with warning() instead of error() in packed garbage case because it's not an error to have garbage. Loose garbage is still reported as errors and will be converted to warnings later. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Feb 15, 2013 at 19:07 UTC
543c5caa6c93bb3d42545aeef334c6a175384db8
5 files changed
+124
-4
Documentation/git-count-objects.txt
+2
-2
@@ -33,8 +33,8 @@ size-pack: disk space consumed by the packs, in KiB
33
prune-packable: the number of loose objects that are also present in
34
the packs. These objects could be pruned using `git prune-packed`.
35
+
36
-garbage: the number of files in loose object database that are not
37
-valid loose objects
36
+garbage: the number of files in object database that are not valid
37
+loose objects nor valid packs
38
39
GIT
40
---
builtin/count-objects.c
+11
-1
@@ -9,6 +9,14 @@
9
#include "builtin.h"
10
#include "parse-options.h"
11
12
+static unsigned long garbage;
13
+
14
+static void real_report_garbage(const char *desc, const char *path)
15
+{
16
+ warning("%s: %s", desc, path);
17
+ garbage++;
18
+}
19
+
20
static void count_objects(DIR *d, char *path, int len, int verbose,
21
unsigned long *loose,
22
off_t *loose_size,
@@ -76,7 +84,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
84
const char *objdir = get_object_directory();
85
int len = strlen(objdir);
86
char *path = xmalloc(len + 50);
79
- unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
87
+ unsigned long loose = 0, packed = 0, packed_loose = 0;
88
off_t loose_size = 0;
89
struct option opts[] = {
90
OPT__VERBOSE(&verbose, N_("be verbose")),
@@ -87,6 +95,8 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
95
/* we do not take arguments other than flags for now */
96
if (argc)
97
usage_with_options(count_objects_usage, opts);
98
+ if (verbose)
99
+ report_garbage = real_report_garbage;
100
memcpy(path, objdir, len);
101
if (len && objdir[len-1] != '/')
102
path[len++] = '/';
cache.h
+3
@@ -1057,6 +1057,9 @@ extern const char *parse_feature_value(const char *feature_list, const char *fea
1057
1058
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
1059
1060
+/* A hook for count-objects to report invalid files in pack directory */
1061
+extern void (*report_garbage)(const char *desc, const char *path);
1062
+
1063
extern void prepare_packed_git(void);
1064
extern void reprepare_packed_git(void);
1065
extern void install_packed_git(struct packed_git *pack);
sha1_file.c
+82
-1
@@ -21,6 +21,7 @@
21
#include "sha1-lookup.h"
22
#include "bulk-checkin.h"
23
#include "streaming.h"
24
+#include "dir.h"
25
26
#ifndef O_NOATIME
27
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1000,6 +1001,63 @@ void install_packed_git(struct packed_git *pack)
1001
packed_git = pack;
1002
}
1003
1004
+void (*report_garbage)(const char *desc, const char *path);
1005
+
1006
+static void report_helper(const struct string_list *list,
1007
+ int seen_bits, int first, int last)
1008
+{
1009
+ const char *msg;
1010
+ switch (seen_bits) {
1011
+ case 0:
1012
+ msg = "no corresponding .idx nor .pack";
1013
+ break;
1014
+ case 1:
1015
+ msg = "no corresponding .idx";
1016
+ break;
1017
+ case 2:
1018
+ msg = "no corresponding .pack";
1019
+ break;
1020
+ default:
1021
+ return;
1022
+ }
1023
+ for (; first < last; first++)
1024
+ report_garbage(msg, list->items[first].string);
1025
+}
1026
+
1027
+static void report_pack_garbage(struct string_list *list)
1028
+{
1029
+ int i, baselen = -1, first = 0, seen_bits = 0;
1030
+
1031
+ if (!report_garbage)
1032
+ return;
1033
+
1034
+ sort_string_list(list);
1035
+
1036
+ for (i = 0; i < list->nr; i++) {
1037
+ const char *path = list->items[i].string;
1038
+ if (baselen != -1 &&
1039
+ strncmp(path, list->items[first].string, baselen)) {
1040
+ report_helper(list, seen_bits, first, i);
1041
+ baselen = -1;
1042
+ seen_bits = 0;
1043
+ }
1044
+ if (baselen == -1) {
1045
+ const char *dot = strrchr(path, '.');
1046
+ if (!dot) {
1047
+ report_garbage("garbage found", path);
1048
+ continue;
1049
+ }
1050
+ baselen = dot - path + 1;
1051
+ first = i;
1052
+ }
1053
+ if (!strcmp(path + baselen, "pack"))
1054
+ seen_bits |= 1;
1055
+ else if (!strcmp(path + baselen, "idx"))
1056
+ seen_bits |= 2;
1057
+ }
1058
+ report_helper(list, seen_bits, first, list->nr);
1059
+}
1060
+
1061
static void prepare_packed_git_one(char *objdir, int local)
1062
{
1063
/* Ensure that this buffer is large enough so that we can
@@ -1009,6 +1067,7 @@ static void prepare_packed_git_one(char *objdir, int local)
1067
int len;
1068
DIR *dir;
1069
struct dirent *de;
1070
+ struct string_list garbage = STRING_LIST_INIT_DUP;
1071
1072
sprintf(path, "%s/pack", objdir);
1073
len = strlen(path);
@@ -1024,7 +1083,17 @@ static void prepare_packed_git_one(char *objdir, int local)
1083
int namelen = strlen(de->d_name);
1084
struct packed_git *p;
1085
1027
- if (len + namelen + 1 > sizeof(path))
1086
+ if (len + namelen + 1 > sizeof(path)) {
1087
+ if (report_garbage) {
1088
+ struct strbuf sb = STRBUF_INIT;
1089
+ strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
1090
+ report_garbage("path too long", sb.buf);
1091
+ strbuf_release(&sb);
1092
+ }
1093
+ continue;
1094
+ }
1095
+
1096
+ if (is_dot_or_dotdot(de->d_name))
1097
continue;
1098
1099
strcpy(path + len, de->d_name);
@@ -1043,8 +1112,20 @@ static void prepare_packed_git_one(char *objdir, int local)
1112
(p = add_packed_git(path, len + namelen, local)) != NULL)
1113
install_packed_git(p);
1114
}
1115
+
1116
+ if (!report_garbage)
1117
+ continue;
1118
+
1119
+ if (has_extension(de->d_name, ".idx") ||
1120
+ has_extension(de->d_name, ".pack") ||
1121
+ has_extension(de->d_name, ".keep"))
1122
+ string_list_append(&garbage, path);
1123
+ else
1124
+ report_garbage("garbage found", path);
1125
}
1126
closedir(dir);
1127
+ report_pack_garbage(&garbage);
1128
+ string_list_clear(&garbage, 0);
1129
}
1130
1131
static int sort_pack(const void *a_, const void *b_)
t/t5304-prune.sh
+26
@@ -195,4 +195,30 @@ test_expect_success 'gc: prune old objects after local clone' '
195
)
196
'
197
198
+test_expect_success 'garbage report in count-objects -v' '
199
+ : >.git/objects/pack/foo &&
200
+ : >.git/objects/pack/foo.bar &&
201
+ : >.git/objects/pack/foo.keep &&
202
+ : >.git/objects/pack/foo.pack &&
203
+ : >.git/objects/pack/fake.bar &&
204
+ : >.git/objects/pack/fake.keep &&
205
+ : >.git/objects/pack/fake.pack &&
206
+ : >.git/objects/pack/fake.idx &&
207
+ : >.git/objects/pack/fake2.keep &&
208
+ : >.git/objects/pack/fake3.idx &&
209
+ git count-objects -v 2>stderr &&
210
+ grep "index file .git/objects/pack/fake.idx is too small" stderr &&
211
+ grep "^warning:" stderr | sort >actual &&
212
+ cat >expected <<\EOF &&
213
+warning: garbage found: .git/objects/pack/fake.bar
214
+warning: garbage found: .git/objects/pack/foo
215
+warning: garbage found: .git/objects/pack/foo.bar
216
+warning: no corresponding .idx nor .pack: .git/objects/pack/fake2.keep
217
+warning: no corresponding .idx: .git/objects/pack/foo.keep
218
+warning: no corresponding .idx: .git/objects/pack/foo.pack
219
+warning: no corresponding .pack: .git/objects/pack/fake3.idx
220
+EOF
221
+ test_cmp expected actual
222
+'
223
+
224
test_done