提交 abcb8655 编写于 作者: J Jeff King 提交者: Junio C Hamano

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: NJeff King <peff@peff.net>
Signed-off-by: NJunio C Hamano <gitster@pobox.com>
上级 d0d46abc
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include "streaming.h" #include "streaming.h"
#include "thread-utils.h" #include "thread-utils.h"
#include "pack-bitmap.h" #include "pack-bitmap.h"
#include "reachable.h"
#include "sha1-array.h"
static const char *pack_usage[] = { static const char *pack_usage[] = {
N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"), N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
...@@ -2407,6 +2409,15 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) ...@@ -2407,6 +2409,15 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
return 0; return 0;
} }
/*
* Store a list of sha1s that are should not be discarded
* because they are either written too recently, or are
* reachable from another object that was.
*
* This is filled by get_object_list.
*/
static struct sha1_array recent_objects;
static int loosened_object_can_be_discarded(const unsigned char *sha1, static int loosened_object_can_be_discarded(const unsigned char *sha1,
unsigned long mtime) unsigned long mtime)
{ {
...@@ -2414,6 +2425,8 @@ static int loosened_object_can_be_discarded(const unsigned char *sha1, ...@@ -2414,6 +2425,8 @@ static int loosened_object_can_be_discarded(const unsigned char *sha1,
return 0; return 0;
if (mtime > unpack_unreachable_expiration) if (mtime > unpack_unreachable_expiration)
return 0; return 0;
if (sha1_array_lookup(&recent_objects, sha1) >= 0)
return 0;
return 1; return 1;
} }
...@@ -2470,6 +2483,19 @@ static int get_object_list_from_bitmap(struct rev_info *revs) ...@@ -2470,6 +2483,19 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
return 0; return 0;
} }
static void record_recent_object(struct object *obj,
const struct name_path *path,
const char *last,
void *data)
{
sha1_array_append(&recent_objects, obj->sha1);
}
static void record_recent_commit(struct commit *commit, void *data)
{
sha1_array_append(&recent_objects, commit->object.sha1);
}
static void get_object_list(int ac, const char **av) static void get_object_list(int ac, const char **av)
{ {
struct rev_info revs; struct rev_info revs;
...@@ -2517,10 +2543,23 @@ static void get_object_list(int ac, const char **av) ...@@ -2517,10 +2543,23 @@ static void get_object_list(int ac, const char **av)
mark_edges_uninteresting(&revs, show_edge); mark_edges_uninteresting(&revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object, NULL); traverse_commit_list(&revs, show_commit, show_object, NULL);
if (unpack_unreachable_expiration) {
revs.ignore_missing_links = 1;
if (add_unseen_recent_objects_to_traversal(&revs,
unpack_unreachable_expiration))
die("unable to add recent objects");
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
traverse_commit_list(&revs, record_recent_commit,
record_recent_object, NULL);
}
if (keep_unreachable) if (keep_unreachable)
add_objects_in_unpacked_packs(&revs); add_objects_in_unpacked_packs(&revs);
if (unpack_unreachable) if (unpack_unreachable)
loosen_unused_packed_objects(&revs); loosen_unused_packed_objects(&revs);
sha1_array_clear(&recent_objects);
} }
static int option_parse_index_version(const struct option *opt, static int option_parse_index_version(const struct option *opt,
......
...@@ -183,8 +183,8 @@ static int add_recent_packed(const unsigned char *sha1, ...@@ -183,8 +183,8 @@ static int add_recent_packed(const unsigned char *sha1,
return 0; return 0;
} }
static int add_unseen_recent_objects_to_traversal(struct rev_info *revs, int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
unsigned long timestamp) unsigned long timestamp)
{ {
struct recent_data data; struct recent_data data;
int r; int r;
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#define REACHEABLE_H #define REACHEABLE_H
struct progress; struct progress;
extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
unsigned long timestamp);
extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
unsigned long mark_recent, struct progress *); unsigned long mark_recent, struct progress *);
......
...@@ -39,50 +39,67 @@ commit () { ...@@ -39,50 +39,67 @@ commit () {
git commit -m "$1" git commit -m "$1"
} }
test_expect_success 'disable reflogs' ' maybe_repack () {
git config core.logallrefupdates false && if test -n "$repack"; then
rm -rf .git/logs git repack -ad
' fi
}
for repack in '' true; do
title=${repack:+repack}
title=${title:-loose}
test_expect_success "make repo completely empty ($title)" '
rm -rf .git &&
git init
'
test_expect_success "disable reflogs ($title)" '
git config core.logallrefupdates false &&
rm -rf .git/logs
'
test_expect_success 'setup basic history' ' test_expect_success "setup basic history ($title)" '
commit base commit base
' '
test_expect_success 'create and abandon some objects' ' test_expect_success "create and abandon some objects ($title)" '
git checkout -b experiment && git checkout -b experiment &&
commit abandon && commit abandon &&
git checkout master && maybe_repack &&
git branch -D experiment git checkout master &&
' git branch -D experiment
'
test_expect_success 'simulate time passing' ' test_expect_success "simulate time passing ($title)" '
find .git/objects -type f | find .git/objects -type f |
xargs test-chmtime -v -86400 xargs test-chmtime -v -86400
' '
test_expect_success 'start writing new commit with old blob' ' test_expect_success "start writing new commit with old blob ($title)" '
tree=$( tree=$(
GIT_INDEX_FILE=index.tmp && GIT_INDEX_FILE=index.tmp &&
export GIT_INDEX_FILE && export GIT_INDEX_FILE &&
git read-tree HEAD && git read-tree HEAD &&
add unrelated && add unrelated &&
add abandon && add abandon &&
git write-tree git write-tree
) )
' '
test_expect_success 'simultaneous gc' ' test_expect_success "simultaneous gc ($title)" '
git gc --prune=12.hours.ago git gc --prune=12.hours.ago
' '
test_expect_success 'finish writing out commit' ' test_expect_success "finish writing out commit ($title)" '
commit=$(echo foo | git commit-tree -p HEAD $tree) && commit=$(echo foo | git commit-tree -p HEAD $tree) &&
git update-ref HEAD $commit git update-ref HEAD $commit
' '
# "abandon" blob should have been rescued by reference from new tree # "abandon" blob should have been rescued by reference from new tree
test_expect_success 'repository passes fsck' ' test_expect_success "repository passes fsck ($title)" '
git fsck git fsck
' '
done
test_done test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册