From 5f78a431ab222189b11a9233a5902db61aa32976 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Oct 2014 18:37:28 -0400 Subject: reachable: use traverse_commit_list instead of custom walk To find the set of reachable objects, we add a bunch of possible sources to our rev_info, call prepare_revision_walk, and then launch into a custom walker that handles each object top. This is a subset of what traverse_commit_list does, so we can just reuse that code (it can also handle more complex cases like UNINTERESTING commits and pathspecs, but we don't use those features). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- reachable.c | 130 ++++++++---------------------------------------------------- 1 file changed, 17 insertions(+), 113 deletions(-) (limited to 'reachable.c') diff --git a/reachable.c b/reachable.c index 6f6835bf27..02bf6c2fe4 100644 --- a/reachable.c +++ b/reachable.c @@ -8,6 +8,7 @@ #include "reachable.h" #include "cache-tree.h" #include "progress.h" +#include "list-objects.h" struct connectivity_progress { struct progress *progress; @@ -21,118 +22,6 @@ static void update_progress(struct connectivity_progress *cp) display_progress(cp->progress, cp->count); } -static void process_blob(struct blob *blob, - struct object_array *p, - struct name_path *path, - const char *name, - struct connectivity_progress *cp) -{ - struct object *obj = &blob->object; - - if (!blob) - die("bad blob object"); - if (obj->flags & SEEN) - return; - obj->flags |= SEEN; - update_progress(cp); - /* Nothing to do, really .. The blob lookup was the important part */ -} - -static void process_gitlink(const unsigned char *sha1, - struct object_array *p, - struct name_path *path, - const char *name) -{ - /* I don't think we want to recurse into this, really. */ -} - -static void process_tree(struct tree *tree, - struct object_array *p, - struct name_path *path, - const char *name, - struct connectivity_progress *cp) -{ - struct object *obj = &tree->object; - struct tree_desc desc; - struct name_entry entry; - struct name_path me; - - if (!tree) - die("bad tree object"); - if (obj->flags & SEEN) - return; - obj->flags |= SEEN; - update_progress(cp); - if (parse_tree(tree) < 0) - die("bad tree object %s", sha1_to_hex(obj->sha1)); - add_object(obj, p, path, name); - me.up = path; - me.elem = name; - me.elem_len = strlen(name); - - init_tree_desc(&desc, tree->buffer, tree->size); - - while (tree_entry(&desc, &entry)) { - if (S_ISDIR(entry.mode)) - process_tree(lookup_tree(entry.sha1), p, &me, entry.path, cp); - else if (S_ISGITLINK(entry.mode)) - process_gitlink(entry.sha1, p, &me, entry.path); - else - process_blob(lookup_blob(entry.sha1), p, &me, entry.path, cp); - } - free_tree_buffer(tree); -} - -static void process_tag(struct tag *tag, struct object_array *p, - const char *name, struct connectivity_progress *cp) -{ - struct object *obj = &tag->object; - - if (obj->flags & SEEN) - return; - obj->flags |= SEEN; - update_progress(cp); - - if (parse_tag(tag) < 0) - die("bad tag object %s", sha1_to_hex(obj->sha1)); - if (tag->tagged) - add_object(tag->tagged, p, NULL, name); -} - -static void walk_commit_list(struct rev_info *revs, - struct connectivity_progress *cp) -{ - int i; - struct commit *commit; - struct object_array objects = OBJECT_ARRAY_INIT; - - /* Walk all commits, process their trees */ - while ((commit = get_revision(revs)) != NULL) { - process_tree(commit->tree, &objects, NULL, "", cp); - update_progress(cp); - } - - /* Then walk all the pending objects, recursively processing them too */ - for (i = 0; i < revs->pending.nr; i++) { - struct object_array_entry *pending = revs->pending.objects + i; - struct object *obj = pending->item; - const char *name = pending->name; - if (obj->type == OBJ_TAG) { - process_tag((struct tag *) obj, &objects, name, cp); - continue; - } - if (obj->type == OBJ_TREE) { - process_tree((struct tree *)obj, &objects, NULL, name, cp); - continue; - } - if (obj->type == OBJ_BLOB) { - process_blob((struct blob *)obj, &objects, NULL, name, cp); - continue; - } - die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name); - } -} - static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) @@ -210,6 +99,21 @@ static void add_cache_refs(struct rev_info *revs) add_cache_tree(active_cache_tree, revs); } +/* + * The traversal will have already marked us as SEEN, so we + * only need to handle any progress reporting here. + */ +static void mark_object(struct object *obj, const struct name_path *path, + const char *name, void *data) +{ + update_progress(data); +} + +static void mark_commit(struct commit *c, void *data) +{ + mark_object(&c->object, NULL, NULL, data); +} + void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *progress) { @@ -245,6 +149,6 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog, */ if (prepare_revision_walk(revs)) die("revision walk setup failed"); - walk_commit_list(revs, &cp); + traverse_commit_list(revs, mark_commit, mark_object, &cp); display_progress(cp.progress, cp.count); } -- cgit v1.3 From 718ccc9731c4e98b123436c22c1cccf2beed5e29 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Oct 2014 18:38:31 -0400 Subject: reachable: reuse revision.c "add all reflogs" code We want to add all reflog entries as tips for finding reachable objects. The revision machinery can already do this (to support "rev-list --reflog"); we can reuse that code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- reachable.c | 24 +----------------------- revision.c | 4 ++-- revision.h | 1 + 3 files changed, 4 insertions(+), 25 deletions(-) (limited to 'reachable.c') diff --git a/reachable.c b/reachable.c index 02bf6c2fe4..4e68cfadbf 100644 --- a/reachable.c +++ b/reachable.c @@ -22,22 +22,6 @@ static void update_progress(struct connectivity_progress *cp) display_progress(cp->progress, cp->count); } -static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, - const char *message, void *cb_data) -{ - struct object *object; - struct rev_info *revs = (struct rev_info *)cb_data; - - object = parse_object(osha1); - if (object) - add_pending_object(revs, object, ""); - object = parse_object(nsha1); - if (object) - add_pending_object(revs, object, ""); - return 0; -} - static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *object = parse_object_or_die(sha1, path); @@ -48,12 +32,6 @@ static int add_one_ref(const char *path, const unsigned char *sha1, int flag, vo return 0; } -static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data) -{ - for_each_reflog_ent(path, add_one_reflog_ent, cb_data); - return 0; -} - static void add_one_tree(const unsigned char *sha1, struct rev_info *revs) { struct tree *tree = lookup_tree(sha1); @@ -138,7 +116,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog, /* Add all reflog info */ if (mark_reflog) - for_each_reflog(add_one_reflog, revs); + add_reflogs_to_pending(revs, 0); cp.progress = progress; cp.count = 0; diff --git a/revision.c b/revision.c index 01cc276fa2..b8e02e2798 100644 --- a/revision.c +++ b/revision.c @@ -1275,7 +1275,7 @@ static int handle_one_reflog(const char *path, const unsigned char *sha1, int fl return 0; } -static void handle_reflog(struct rev_info *revs, unsigned flags) +void add_reflogs_to_pending(struct rev_info *revs, unsigned flags) { struct all_refs_cb cb; cb.all_revs = revs; @@ -2061,7 +2061,7 @@ static int handle_revision_pseudo_opt(const char *submodule, for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb); clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--reflog")) { - handle_reflog(revs, *flags); + add_reflogs_to_pending(revs, *flags); } else if (!strcmp(arg, "--not")) { *flags ^= UNINTERESTING | BOTTOM; } else if (!strcmp(arg, "--no-walk")) { diff --git a/revision.h b/revision.h index a6205307cf..e644044174 100644 --- a/revision.h +++ b/revision.h @@ -276,6 +276,7 @@ extern void add_pending_sha1(struct rev_info *revs, unsigned int flags); extern void add_head_to_pending(struct rev_info *); +extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags); enum commit_action { commit_ignore, -- cgit v1.3 From 3725427945657da052f2f833f5fd4616c949be3e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Oct 2014 18:40:37 -0400 Subject: reachable: mark index blobs as SEEN When we mark all reachable objects for pruning, that includes blobs mentioned by the index. However, we do not mark these with the SEEN flag, as we do for objects that we find by traversing (we also do not add them to the pending list, but that is because there is nothing further to traverse with them). This doesn't cause any problems with prune, because it checks only that the object exists in the global object hash, and not its flags. However, let's mark these objects to be consistent and avoid any later surprises. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- reachable.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'reachable.c') diff --git a/reachable.c b/reachable.c index 4e68cfadbf..d03f8294ee 100644 --- a/reachable.c +++ b/reachable.c @@ -55,6 +55,8 @@ static void add_cache_refs(struct rev_info *revs) read_cache(); for (i = 0; i < active_nr; i++) { + struct blob *blob; + /* * The index can contain blobs and GITLINKs, GITLINKs are hashes * that don't actually point to objects in the repository, it's @@ -65,7 +67,10 @@ static void add_cache_refs(struct rev_info *revs) if (S_ISGITLINK(active_cache[i]->ce_mode)) continue; - lookup_blob(active_cache[i]->sha1); + blob = lookup_blob(active_cache[i]->sha1); + if (blob) + blob->object.flags |= SEEN; + /* * We could add the blobs to the pending list, but quite * frankly, we don't care. Once we've looked them up, and -- cgit v1.3 From d3038d22f91aad9620bd8e6fc43fc67c16219738 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Oct 2014 18:41:35 -0400 Subject: prune: keep objects reachable from recent objects Our current strategy with prune is that an object falls into one of three categories: 1. Reachable (from ref tips, reflogs, index, etc). 2. Not reachable, but recent (based on the --expire time). 3. Not reachable and not recent. We keep objects from (1) and (2), but prune objects in (3). The point of (2) is that these objects may be part of an in-progress operation that has not yet updated any refs. However, it is not always the case that objects for an in-progress operation will have a recent mtime. For example, the object database may have an old copy of a blob (from an abandoned operation, a branch that was deleted, etc). If we create a new tree that points to it, a simultaneous prune will leave our tree, but delete the blob. Referencing that tree with a commit will then work (we check that the tree is in the object database, but not that all of its referred objects are), as will mentioning the commit in a ref. But the resulting repo is corrupt; we are missing the blob reachable from a ref. One way to solve this is to be more thorough when referencing a sha1: make sure that not only do we have that sha1, but that we have objects it refers to, and so forth recursively. The problem is that this is very expensive. Creating a parent link would require traversing the entire object graph! Instead, this patch pushes the extra work onto prune, which runs less frequently (and has to look at the whole object graph anyway). It creates a new category of objects: objects which are not recent, but which are reachable from a recent object. We do not prune these objects, just like the reachable and recent ones. This lets us avoid the recursive check above, because if we have an object, even if it is unreachable, we should have its referent. We can make a simple inductive argument that with this patch, this property holds (that there are no objects with missing referents in the repository): 0. When we have no objects, we have nothing to refer or be referred to, so the property holds. 1. If we add objects to the repository, their direct referents must generally exist (e.g., if you create a tree, the blobs it references must exist; if you create a commit to point at the tree, the tree must exist). This is already the case before this patch. And it is not 100% foolproof (you can make bogus objects using `git hash-object`, for example), but it should be the case for normal usage. Therefore for any sequence of object additions, the property will continue to hold. 2. If we remove objects from the repository, then we will not remove a child object (like a blob) if an object that refers to it is being kept. That is the part implemented by this patch. Note, however, that our reachability check and the actual pruning are not atomic. So it _is_ still possible to violate the property (e.g., an object becomes referenced just as we are deleting it). This patch is shooting for eliminating problems where the mtimes of dependent objects differ by hours or days, and one is dropped without the other. It does nothing to help with short races. Naively, the simplest way to implement this would be to add all recent objects as tips to the reachability traversal. However, this does not perform well. In a recently-packed repository, all reachable objects will also be recent, and therefore we have to look at each object twice. This patch instead performs the reachability traversal, then follows up with a second traversal for recent objects, skipping any that have already been marked. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/prune.c | 2 +- builtin/reflog.c | 2 +- reachable.c | 112 +++++++++++++++++++++++++++++++++++++++++++++ reachable.h | 3 +- t/t6501-freshen-objects.sh | 88 +++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 3 deletions(-) create mode 100755 t/t6501-freshen-objects.sh (limited to 'reachable.c') diff --git a/builtin/prune.c b/builtin/prune.c index 763f53e06a..04d3b12ae4 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -135,7 +135,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) if (show_progress) progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); - mark_reachable_objects(&revs, 1, progress); + mark_reachable_objects(&revs, 1, expire, progress); stop_progress(&progress); for_each_loose_file_in_objdir(get_object_directory(), prune_object, prune_cruft, prune_subdir, NULL); diff --git a/builtin/reflog.c b/builtin/reflog.c index e8a8fb13b9..80bddc259b 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -649,7 +649,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) init_revisions(&cb.revs, prefix); if (cb.verbose) printf("Marking reachable objects..."); - mark_reachable_objects(&cb.revs, 0, NULL); + mark_reachable_objects(&cb.revs, 0, 0, NULL); if (cb.verbose) putchar('\n'); } diff --git a/reachable.c b/reachable.c index d03f8294ee..55589a02eb 100644 --- a/reachable.c +++ b/reachable.c @@ -97,7 +97,109 @@ static void mark_commit(struct commit *c, void *data) mark_object(&c->object, NULL, NULL, data); } +struct recent_data { + struct rev_info *revs; + unsigned long timestamp; +}; + +static void add_recent_object(const unsigned char *sha1, + unsigned long mtime, + struct recent_data *data) +{ + struct object *obj; + enum object_type type; + + if (mtime <= data->timestamp) + return; + + /* + * We do not want to call parse_object here, because + * inflating blobs and trees could be very expensive. + * However, we do need to know the correct type for + * later processing, and the revision machinery expects + * commits and tags to have been parsed. + */ + type = sha1_object_info(sha1, NULL); + if (type < 0) + die("unable to get object info for %s", sha1_to_hex(sha1)); + + switch (type) { + case OBJ_TAG: + case OBJ_COMMIT: + obj = parse_object_or_die(sha1, NULL); + break; + case OBJ_TREE: + obj = (struct object *)lookup_tree(sha1); + break; + case OBJ_BLOB: + obj = (struct object *)lookup_blob(sha1); + break; + default: + die("unknown object type for %s: %s", + sha1_to_hex(sha1), typename(type)); + } + + if (!obj) + die("unable to lookup %s", sha1_to_hex(sha1)); + + add_pending_object(data->revs, obj, ""); +} + +static int add_recent_loose(const unsigned char *sha1, + const char *path, void *data) +{ + struct stat st; + struct object *obj = lookup_object(sha1); + + if (obj && obj->flags & SEEN) + return 0; + + if (stat(path, &st) < 0) { + /* + * It's OK if an object went away during our iteration; this + * could be due to a simultaneous repack. But anything else + * we should abort, since we might then fail to mark objects + * which should not be pruned. + */ + if (errno == ENOENT) + return 0; + return error("unable to stat %s: %s", + sha1_to_hex(sha1), strerror(errno)); + } + + add_recent_object(sha1, st.st_mtime, data); + return 0; +} + +static int add_recent_packed(const unsigned char *sha1, + struct packed_git *p, uint32_t pos, + void *data) +{ + struct object *obj = lookup_object(sha1); + + if (obj && obj->flags & SEEN) + return 0; + add_recent_object(sha1, p->mtime, data); + return 0; +} + +static int add_unseen_recent_objects_to_traversal(struct rev_info *revs, + unsigned long timestamp) +{ + struct recent_data data; + int r; + + data.revs = revs; + data.timestamp = timestamp; + + r = for_each_loose_object(add_recent_loose, &data); + if (r) + return r; + return for_each_packed_object(add_recent_packed, &data); +} + void mark_reachable_objects(struct rev_info *revs, int mark_reflog, + unsigned long mark_recent, struct progress *progress) { struct connectivity_progress cp; @@ -133,5 +235,15 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog, if (prepare_revision_walk(revs)) die("revision walk setup failed"); traverse_commit_list(revs, mark_commit, mark_object, &cp); + + if (mark_recent) { + revs->ignore_missing_links = 1; + if (add_unseen_recent_objects_to_traversal(revs, mark_recent)) + die("unable to mark recent objects"); + if (prepare_revision_walk(revs)) + die("revision walk setup failed"); + traverse_commit_list(revs, mark_commit, mark_object, &cp); + } + display_progress(cp.progress, cp.count); } diff --git a/reachable.h b/reachable.h index 5d082adfec..141fe30879 100644 --- a/reachable.h +++ b/reachable.h @@ -2,6 +2,7 @@ #define REACHEABLE_H struct progress; -extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *); +extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, + unsigned long mark_recent, struct progress *); #endif diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh new file mode 100755 index 0000000000..de941c2cb2 --- /dev/null +++ b/t/t6501-freshen-objects.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# +# This test covers the handling of objects which might have old +# mtimes in the filesystem (because they were used previously) +# and are just now becoming referenced again. +# +# We're going to do two things that are a little bit "fake" to +# help make our simulation easier: +# +# 1. We'll turn off reflogs. You can still run into +# problems with reflogs on, but your objects +# don't get pruned until both the reflog expiration +# has passed on their references, _and_ they are out +# of prune's expiration period. Dropping reflogs +# means we only have to deal with one variable in our tests, +# but the results generalize. +# +# 2. We'll use a temporary index file to create our +# works-in-progress. Most workflows would mention +# referenced objects in the index, which prune takes +# into account. However, many operations don't. For +# example, a partial commit with "git commit foo" +# will use a temporary index. Or they may not need +# an index at all (e.g., creating a new commit +# to refer to an existing tree). + +test_description='check pruning of dependent objects' +. ./test-lib.sh + +# We care about reachability, so we do not want to use +# the normal test_commit, which creates extra tags. +add () { + echo "$1" >"$1" && + git add "$1" +} +commit () { + test_tick && + add "$1" && + git commit -m "$1" +} + +test_expect_success 'disable reflogs' ' + git config core.logallrefupdates false && + rm -rf .git/logs +' + +test_expect_success 'setup basic history' ' + commit base +' + +test_expect_success 'create and abandon some objects' ' + git checkout -b experiment && + commit abandon && + git checkout master && + git branch -D experiment +' + +test_expect_success 'simulate time passing' ' + find .git/objects -type f | + xargs test-chmtime -v -86400 +' + +test_expect_success 'start writing new commit with old blob' ' + tree=$( + GIT_INDEX_FILE=index.tmp && + export GIT_INDEX_FILE && + git read-tree HEAD && + add unrelated && + add abandon && + git write-tree + ) +' + +test_expect_success 'simultaneous gc' ' + git gc --prune=12.hours.ago +' + +test_expect_success 'finish writing out commit' ' + commit=$(echo foo | git commit-tree -p HEAD $tree) && + git update-ref HEAD $commit +' + +# "abandon" blob should have been rescued by reference from new tree +test_expect_success 'repository passes fsck' ' + git fsck +' + +test_done -- cgit v1.3 From abcb86553d3ec4afffa4e3963089dffe0559740e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Oct 2014 18:42:09 -0400 Subject: 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 Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 39 +++++++++++++++++++ reachable.c | 4 +- reachable.h | 2 + t/t6501-freshen-objects.sh | 93 +++++++++++++++++++++++++++------------------- 4 files changed, 98 insertions(+), 40 deletions(-) (limited to 'reachable.c') diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 2fe2ab0603..4df9499040 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -20,6 +20,8 @@ #include "streaming.h" #include "thread-utils.h" #include "pack-bitmap.h" +#include "reachable.h" +#include "sha1-array.h" static const char *pack_usage[] = { 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) 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, unsigned long mtime) { @@ -2414,6 +2425,8 @@ static int loosened_object_can_be_discarded(const unsigned char *sha1, return 0; if (mtime > unpack_unreachable_expiration) return 0; + if (sha1_array_lookup(&recent_objects, sha1) >= 0) + return 0; return 1; } @@ -2470,6 +2483,19 @@ static int get_object_list_from_bitmap(struct rev_info *revs) 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) { struct rev_info revs; @@ -2517,10 +2543,23 @@ static void get_object_list(int ac, const char **av) mark_edges_uninteresting(&revs, show_edge); 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) add_objects_in_unpacked_packs(&revs); if (unpack_unreachable) loosen_unused_packed_objects(&revs); + + sha1_array_clear(&recent_objects); } static int option_parse_index_version(const struct option *opt, diff --git a/reachable.c b/reachable.c index 55589a02eb..0176a88b81 100644 --- a/reachable.c +++ b/reachable.c @@ -183,8 +183,8 @@ static int add_recent_packed(const unsigned char *sha1, return 0; } -static int add_unseen_recent_objects_to_traversal(struct rev_info *revs, - unsigned long timestamp) +int add_unseen_recent_objects_to_traversal(struct rev_info *revs, + unsigned long timestamp) { struct recent_data data; int r; diff --git a/reachable.h b/reachable.h index 141fe30879..d23efc36ec 100644 --- a/reachable.h +++ b/reachable.h @@ -2,6 +2,8 @@ #define REACHEABLE_H 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, unsigned long mark_recent, struct progress *); diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh index de941c2cb2..e25c47dd5c 100755 --- a/t/t6501-freshen-objects.sh +++ b/t/t6501-freshen-objects.sh @@ -39,50 +39,67 @@ commit () { git commit -m "$1" } -test_expect_success 'disable reflogs' ' - git config core.logallrefupdates false && - rm -rf .git/logs -' +maybe_repack () { + if test -n "$repack"; then + 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' ' - commit base -' + test_expect_success "setup basic history ($title)" ' + commit base + ' -test_expect_success 'create and abandon some objects' ' - git checkout -b experiment && - commit abandon && - git checkout master && - git branch -D experiment -' + test_expect_success "create and abandon some objects ($title)" ' + git checkout -b experiment && + commit abandon && + maybe_repack && + git checkout master && + git branch -D experiment + ' -test_expect_success 'simulate time passing' ' - find .git/objects -type f | - xargs test-chmtime -v -86400 -' + test_expect_success "simulate time passing ($title)" ' + find .git/objects -type f | + xargs test-chmtime -v -86400 + ' -test_expect_success 'start writing new commit with old blob' ' - tree=$( - GIT_INDEX_FILE=index.tmp && - export GIT_INDEX_FILE && - git read-tree HEAD && - add unrelated && - add abandon && - git write-tree - ) -' + test_expect_success "start writing new commit with old blob ($title)" ' + tree=$( + GIT_INDEX_FILE=index.tmp && + export GIT_INDEX_FILE && + git read-tree HEAD && + add unrelated && + add abandon && + git write-tree + ) + ' -test_expect_success 'simultaneous gc' ' - git gc --prune=12.hours.ago -' + test_expect_success "simultaneous gc ($title)" ' + git gc --prune=12.hours.ago + ' -test_expect_success 'finish writing out commit' ' - commit=$(echo foo | git commit-tree -p HEAD $tree) && - git update-ref HEAD $commit -' + test_expect_success "finish writing out commit ($title)" ' + commit=$(echo foo | git commit-tree -p HEAD $tree) && + git update-ref HEAD $commit + ' -# "abandon" blob should have been rescued by reference from new tree -test_expect_success 'repository passes fsck' ' - git fsck -' + # "abandon" blob should have been rescued by reference from new tree + test_expect_success "repository passes fsck ($title)" ' + git fsck + ' +done test_done -- cgit v1.3 From 1be111d88f526130dcf3b7c7ad0f5df17c6d2bae Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 16 Oct 2014 20:44:30 -0400 Subject: reachable: use revision machinery's --indexed-objects code This does the same thing as our custom code, so let's not repeat ourselves. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- reachable.c | 52 +--------------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) (limited to 'reachable.c') diff --git a/reachable.c b/reachable.c index 0176a88b81..a647267ae9 100644 --- a/reachable.c +++ b/reachable.c @@ -32,56 +32,6 @@ static int add_one_ref(const char *path, const unsigned char *sha1, int flag, vo return 0; } -static void add_one_tree(const unsigned char *sha1, struct rev_info *revs) -{ - struct tree *tree = lookup_tree(sha1); - if (tree) - add_pending_object(revs, &tree->object, ""); -} - -static void add_cache_tree(struct cache_tree *it, struct rev_info *revs) -{ - int i; - - if (it->entry_count >= 0) - add_one_tree(it->sha1, revs); - for (i = 0; i < it->subtree_nr; i++) - add_cache_tree(it->down[i]->cache_tree, revs); -} - -static void add_cache_refs(struct rev_info *revs) -{ - int i; - - read_cache(); - for (i = 0; i < active_nr; i++) { - struct blob *blob; - - /* - * The index can contain blobs and GITLINKs, GITLINKs are hashes - * that don't actually point to objects in the repository, it's - * almost guaranteed that they are NOT blobs, so we don't call - * lookup_blob() on them, to avoid populating the hash table - * with invalid information - */ - if (S_ISGITLINK(active_cache[i]->ce_mode)) - continue; - - blob = lookup_blob(active_cache[i]->sha1); - if (blob) - blob->object.flags |= SEEN; - - /* - * We could add the blobs to the pending list, but quite - * frankly, we don't care. Once we've looked them up, and - * added them as objects, we've really done everything - * there is to do for a blob - */ - } - if (active_cache_tree) - add_cache_tree(active_cache_tree, revs); -} - /* * The traversal will have already marked us as SEEN, so we * only need to handle any progress reporting here. @@ -213,7 +163,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog, revs->tree_objects = 1; /* Add all refs from the index file */ - add_cache_refs(revs); + add_index_objects_to_pending(revs, 0); /* Add all external refs */ for_each_ref(add_one_ref, revs); -- cgit v1.3