Skip to content

Commit abcb865

Browse files
peffgitster
authored andcommitted
pack-objects: match prune logic for discarding objects
A recent commit taught git-prune to keep non-recent objects that are reachable from recent ones. However, pack-objects, when loosening unreachable objects, tries to optimize out the write in the case that the object will be immediately pruned. It now gets this wrong, since its rule does not reflect the new prune code (and this can be seen by running t6501 with a strategically placed repack). Let's teach pack-objects similar logic. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d0d46ab commit abcb865

File tree

4 files changed

+98
-40
lines changed

4 files changed

+98
-40
lines changed

builtin/pack-objects.c

+39
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "streaming.h"
2121
#include "thread-utils.h"
2222
#include "pack-bitmap.h"
23+
#include "reachable.h"
24+
#include "sha1-array.h"
2325

2426
static const char *pack_usage[] = {
2527
N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -2407,13 +2409,24 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
24072409
return 0;
24082410
}
24092411

2412+
/*
2413+
* Store a list of sha1s that are should not be discarded
2414+
* because they are either written too recently, or are
2415+
* reachable from another object that was.
2416+
*
2417+
* This is filled by get_object_list.
2418+
*/
2419+
static struct sha1_array recent_objects;
2420+
24102421
static int loosened_object_can_be_discarded(const unsigned char *sha1,
24112422
unsigned long mtime)
24122423
{
24132424
if (!unpack_unreachable_expiration)
24142425
return 0;
24152426
if (mtime > unpack_unreachable_expiration)
24162427
return 0;
2428+
if (sha1_array_lookup(&recent_objects, sha1) >= 0)
2429+
return 0;
24172430
return 1;
24182431
}
24192432

@@ -2470,6 +2483,19 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
24702483
return 0;
24712484
}
24722485

2486+
static void record_recent_object(struct object *obj,
2487+
const struct name_path *path,
2488+
const char *last,
2489+
void *data)
2490+
{
2491+
sha1_array_append(&recent_objects, obj->sha1);
2492+
}
2493+
2494+
static void record_recent_commit(struct commit *commit, void *data)
2495+
{
2496+
sha1_array_append(&recent_objects, commit->object.sha1);
2497+
}
2498+
24732499
static void get_object_list(int ac, const char **av)
24742500
{
24752501
struct rev_info revs;
@@ -2517,10 +2543,23 @@ static void get_object_list(int ac, const char **av)
25172543
mark_edges_uninteresting(&revs, show_edge);
25182544
traverse_commit_list(&revs, show_commit, show_object, NULL);
25192545

2546+
if (unpack_unreachable_expiration) {
2547+
revs.ignore_missing_links = 1;
2548+
if (add_unseen_recent_objects_to_traversal(&revs,
2549+
unpack_unreachable_expiration))
2550+
die("unable to add recent objects");
2551+
if (prepare_revision_walk(&revs))
2552+
die("revision walk setup failed");
2553+
traverse_commit_list(&revs, record_recent_commit,
2554+
record_recent_object, NULL);
2555+
}
2556+
25202557
if (keep_unreachable)
25212558
add_objects_in_unpacked_packs(&revs);
25222559
if (unpack_unreachable)
25232560
loosen_unused_packed_objects(&revs);
2561+
2562+
sha1_array_clear(&recent_objects);
25242563
}
25252564

25262565
static int option_parse_index_version(const struct option *opt,

reachable.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ static int add_recent_packed(const unsigned char *sha1,
183183
return 0;
184184
}
185185

186-
static int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
187-
unsigned long timestamp)
186+
int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
187+
unsigned long timestamp)
188188
{
189189
struct recent_data data;
190190
int r;

reachable.h

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#define REACHEABLE_H
33

44
struct progress;
5+
extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
6+
unsigned long timestamp);
57
extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
68
unsigned long mark_recent, struct progress *);
79

t/t6501-freshen-objects.sh

+55-38
Original file line numberDiff line numberDiff line change
@@ -39,50 +39,67 @@ commit () {
3939
git commit -m "$1"
4040
}
4141

42-
test_expect_success 'disable reflogs' '
43-
git config core.logallrefupdates false &&
44-
rm -rf .git/logs
45-
'
42+
maybe_repack () {
43+
if test -n "$repack"; then
44+
git repack -ad
45+
fi
46+
}
47+
48+
for repack in '' true; do
49+
title=${repack:+repack}
50+
title=${title:-loose}
51+
52+
test_expect_success "make repo completely empty ($title)" '
53+
rm -rf .git &&
54+
git init
55+
'
56+
57+
test_expect_success "disable reflogs ($title)" '
58+
git config core.logallrefupdates false &&
59+
rm -rf .git/logs
60+
'
4661

47-
test_expect_success 'setup basic history' '
48-
commit base
49-
'
62+
test_expect_success "setup basic history ($title)" '
63+
commit base
64+
'
5065

51-
test_expect_success 'create and abandon some objects' '
52-
git checkout -b experiment &&
53-
commit abandon &&
54-
git checkout master &&
55-
git branch -D experiment
56-
'
66+
test_expect_success "create and abandon some objects ($title)" '
67+
git checkout -b experiment &&
68+
commit abandon &&
69+
maybe_repack &&
70+
git checkout master &&
71+
git branch -D experiment
72+
'
5773

58-
test_expect_success 'simulate time passing' '
59-
find .git/objects -type f |
60-
xargs test-chmtime -v -86400
61-
'
74+
test_expect_success "simulate time passing ($title)" '
75+
find .git/objects -type f |
76+
xargs test-chmtime -v -86400
77+
'
6278

63-
test_expect_success 'start writing new commit with old blob' '
64-
tree=$(
65-
GIT_INDEX_FILE=index.tmp &&
66-
export GIT_INDEX_FILE &&
67-
git read-tree HEAD &&
68-
add unrelated &&
69-
add abandon &&
70-
git write-tree
71-
)
72-
'
79+
test_expect_success "start writing new commit with old blob ($title)" '
80+
tree=$(
81+
GIT_INDEX_FILE=index.tmp &&
82+
export GIT_INDEX_FILE &&
83+
git read-tree HEAD &&
84+
add unrelated &&
85+
add abandon &&
86+
git write-tree
87+
)
88+
'
7389

74-
test_expect_success 'simultaneous gc' '
75-
git gc --prune=12.hours.ago
76-
'
90+
test_expect_success "simultaneous gc ($title)" '
91+
git gc --prune=12.hours.ago
92+
'
7793

78-
test_expect_success 'finish writing out commit' '
79-
commit=$(echo foo | git commit-tree -p HEAD $tree) &&
80-
git update-ref HEAD $commit
81-
'
94+
test_expect_success "finish writing out commit ($title)" '
95+
commit=$(echo foo | git commit-tree -p HEAD $tree) &&
96+
git update-ref HEAD $commit
97+
'
8298

83-
# "abandon" blob should have been rescued by reference from new tree
84-
test_expect_success 'repository passes fsck' '
85-
git fsck
86-
'
99+
# "abandon" blob should have been rescued by reference from new tree
100+
test_expect_success "repository passes fsck ($title)" '
101+
git fsck
102+
'
103+
done
87104

88105
test_done

0 commit comments

Comments
 (0)