Skip to content

Commit b50ceab

Browse files
committed
Merge branch 'dk/gc-idx-wo-pack' into maint
Having a leftover .idx file without corresponding .pack file in the repository hurts performance; "git gc" learned to prune them. We may want to do the same for .bitmap (and notice but not prune .keep) without corresponding .pack, but that can be a separate topic. * dk/gc-idx-wo-pack: gc: remove garbage .idx files from pack dir t5304: test cleaning pack garbage prepare_packed_git(): refactor garbage reporting in pack directory
2 parents 908a6e4 + 478f34d commit b50ceab

File tree

6 files changed

+78
-22
lines changed

6 files changed

+78
-22
lines changed

builtin/count-objects.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,31 @@ static int verbose;
1515
static unsigned long loose, packed, packed_loose;
1616
static off_t loose_size;
1717

18-
static void real_report_garbage(const char *desc, const char *path)
18+
static const char *bits_to_msg(unsigned seen_bits)
19+
{
20+
switch (seen_bits) {
21+
case 0:
22+
return "no corresponding .idx or .pack";
23+
case PACKDIR_FILE_GARBAGE:
24+
return "garbage found";
25+
case PACKDIR_FILE_PACK:
26+
return "no corresponding .idx";
27+
case PACKDIR_FILE_IDX:
28+
return "no corresponding .pack";
29+
case PACKDIR_FILE_PACK|PACKDIR_FILE_IDX:
30+
default:
31+
return NULL;
32+
}
33+
}
34+
35+
static void real_report_garbage(unsigned seen_bits, const char *path)
1936
{
2037
struct stat st;
38+
const char *desc = bits_to_msg(seen_bits);
39+
40+
if (!desc)
41+
return;
42+
2143
if (!stat(path, &st))
2244
size_garbage += st.st_size;
2345
warning("%s: %s", desc, path);
@@ -27,7 +49,7 @@ static void real_report_garbage(const char *desc, const char *path)
2749
static void loose_garbage(const char *path)
2850
{
2951
if (verbose)
30-
report_garbage("garbage found", path);
52+
report_garbage(PACKDIR_FILE_GARBAGE, path);
3153
}
3254

3355
static int count_loose(const unsigned char *sha1, const char *path, void *data)

builtin/gc.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ static struct argv_array rerere = ARGV_ARRAY_INIT;
4646
static struct tempfile pidfile;
4747
static struct lock_file log_lock;
4848

49+
static struct string_list pack_garbage = STRING_LIST_INIT_DUP;
50+
51+
static void clean_pack_garbage(void)
52+
{
53+
int i;
54+
for (i = 0; i < pack_garbage.nr; i++)
55+
unlink_or_warn(pack_garbage.items[i].string);
56+
string_list_clear(&pack_garbage, 0);
57+
}
58+
59+
static void report_pack_garbage(unsigned seen_bits, const char *path)
60+
{
61+
if (seen_bits == PACKDIR_FILE_IDX)
62+
string_list_append(&pack_garbage, path);
63+
}
64+
4965
static void git_config_date_string(const char *key, const char **output)
5066
{
5167
if (git_config_get_string_const(key, output))
@@ -416,6 +432,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
416432
if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
417433
return error(FAILED_RUN, rerere.argv[0]);
418434

435+
report_garbage = report_pack_garbage;
436+
reprepare_packed_git();
437+
if (pack_garbage.nr > 0)
438+
clean_pack_garbage();
439+
419440
if (auto_gc && too_many_loose_objects())
420441
warning(_("There are too many unreachable loose objects; "
421442
"run 'git prune' to remove them."));

cache.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,8 +1258,11 @@ struct pack_entry {
12581258

12591259
extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
12601260

1261-
/* A hook for count-objects to report invalid files in pack directory */
1262-
extern void (*report_garbage)(const char *desc, const char *path);
1261+
/* A hook to report invalid files in pack directory */
1262+
#define PACKDIR_FILE_PACK 1
1263+
#define PACKDIR_FILE_IDX 2
1264+
#define PACKDIR_FILE_GARBAGE 4
1265+
extern void (*report_garbage)(unsigned seen_bits, const char *path);
12631266

12641267
extern void prepare_packed_git(void);
12651268
extern void reprepare_packed_git(void);

path.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ void report_linked_checkout_garbage(void)
147147
strbuf_setlen(&sb, len);
148148
strbuf_addstr(&sb, path);
149149
if (file_exists(sb.buf))
150-
report_garbage("unused in linked checkout", sb.buf);
150+
report_garbage(PACKDIR_FILE_GARBAGE, sb.buf);
151151
}
152152
strbuf_release(&sb);
153153
}

sha1_file.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,27 +1228,16 @@ void install_packed_git(struct packed_git *pack)
12281228
packed_git = pack;
12291229
}
12301230

1231-
void (*report_garbage)(const char *desc, const char *path);
1231+
void (*report_garbage)(unsigned seen_bits, const char *path);
12321232

12331233
static void report_helper(const struct string_list *list,
12341234
int seen_bits, int first, int last)
12351235
{
1236-
const char *msg;
1237-
switch (seen_bits) {
1238-
case 0:
1239-
msg = "no corresponding .idx or .pack";
1240-
break;
1241-
case 1:
1242-
msg = "no corresponding .idx";
1243-
break;
1244-
case 2:
1245-
msg = "no corresponding .pack";
1246-
break;
1247-
default:
1236+
if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
12481237
return;
1249-
}
1238+
12501239
for (; first < last; first++)
1251-
report_garbage(msg, list->items[first].string);
1240+
report_garbage(seen_bits, list->items[first].string);
12521241
}
12531242

12541243
static void report_pack_garbage(struct string_list *list)
@@ -1271,7 +1260,7 @@ static void report_pack_garbage(struct string_list *list)
12711260
if (baselen == -1) {
12721261
const char *dot = strrchr(path, '.');
12731262
if (!dot) {
1274-
report_garbage("garbage found", path);
1263+
report_garbage(PACKDIR_FILE_GARBAGE, path);
12751264
continue;
12761265
}
12771266
baselen = dot - path + 1;
@@ -1343,7 +1332,7 @@ static void prepare_packed_git_one(char *objdir, int local)
13431332
ends_with(de->d_name, ".keep"))
13441333
string_list_append(&garbage, path.buf);
13451334
else
1346-
report_garbage("garbage found", path.buf);
1335+
report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
13471336
}
13481337
closedir(dir);
13491338
report_pack_garbage(&garbage);

t/t5304-prune.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ test_expect_success 'gc: prune old objects after local clone' '
219219

220220
test_expect_success 'garbage report in count-objects -v' '
221221
test_when_finished "rm -f .git/objects/pack/fake*" &&
222+
test_when_finished "rm -f .git/objects/pack/foo*" &&
222223
: >.git/objects/pack/foo &&
223224
: >.git/objects/pack/foo.bar &&
224225
: >.git/objects/pack/foo.keep &&
@@ -244,6 +245,26 @@ EOF
244245
test_cmp expected actual
245246
'
246247

248+
test_expect_success 'clean pack garbage with gc' '
249+
test_when_finished "rm -f .git/objects/pack/fake*" &&
250+
test_when_finished "rm -f .git/objects/pack/foo*" &&
251+
: >.git/objects/pack/foo.keep &&
252+
: >.git/objects/pack/foo.pack &&
253+
: >.git/objects/pack/fake.idx &&
254+
: >.git/objects/pack/fake2.keep &&
255+
: >.git/objects/pack/fake2.idx &&
256+
: >.git/objects/pack/fake3.keep &&
257+
git gc &&
258+
git count-objects -v 2>stderr &&
259+
grep "^warning:" stderr | sort >actual &&
260+
cat >expected <<\EOF &&
261+
warning: no corresponding .idx or .pack: .git/objects/pack/fake3.keep
262+
warning: no corresponding .idx: .git/objects/pack/foo.keep
263+
warning: no corresponding .idx: .git/objects/pack/foo.pack
264+
EOF
265+
test_cmp expected actual
266+
'
267+
247268
test_expect_success 'prune .git/shallow' '
248269
SHA1=`echo hi|git commit-tree HEAD^{tree}` &&
249270
echo $SHA1 >.git/shallow &&

0 commit comments

Comments
 (0)