summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin/prune.c43
-rwxr-xr-xt/perf/p5304-prune.sh24
-rwxr-xr-xt/t5304-prune.sh12
3 files changed, 68 insertions, 11 deletions
diff --git a/builtin/prune.c b/builtin/prune.c
index 1ec9ddd..04b6573 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -31,16 +31,40 @@ static int prune_tmp_file(const char *fullpath)
return 0;
}
-static int prune_object(const struct object_id *oid, const char *fullpath,
- void *data)
+static void perform_reachability_traversal(struct rev_info *revs)
{
- struct stat st;
+ static int initialized;
+ struct progress *progress = NULL;
+
+ if (initialized)
+ return;
+
+ if (show_progress)
+ progress = start_delayed_progress(_("Checking connectivity"), 0);
+ mark_reachable_objects(revs, 1, expire, progress);
+ stop_progress(&progress);
+ initialized = 1;
+}
+
+static int is_object_reachable(const struct object_id *oid,
+ struct rev_info *revs)
+{
+ perform_reachability_traversal(revs);
/*
* Do we know about this object?
* It must have been reachable
*/
- if (lookup_object(the_repository, oid->hash))
+ return !!lookup_object(the_repository, oid->hash);
+}
+
+static int prune_object(const struct object_id *oid, const char *fullpath,
+ void *data)
+{
+ struct rev_info *revs = data;
+ struct stat st;
+
+ if (is_object_reachable(oid, revs))
return 0;
if (lstat(fullpath, &st)) {
@@ -102,7 +126,6 @@ static void remove_temporary_files(const char *path)
int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct progress *progress = NULL;
int exclude_promisor_objects = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
@@ -142,17 +165,13 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
if (show_progress == -1)
show_progress = isatty(2);
- if (show_progress)
- progress = start_delayed_progress(_("Checking connectivity"), 0);
if (exclude_promisor_objects) {
fetch_if_missing = 0;
revs.exclude_promisor_objects = 1;
}
- 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);
+ prune_cruft, prune_subdir, &revs);
prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
remove_temporary_files(get_object_directory());
@@ -160,8 +179,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
remove_temporary_files(s);
free(s);
- if (is_repository_shallow(the_repository))
+ if (is_repository_shallow(the_repository)) {
+ perform_reachability_traversal(&revs);
prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
+ }
return 0;
}
diff --git a/t/perf/p5304-prune.sh b/t/perf/p5304-prune.sh
new file mode 100755
index 0000000..3c85208
--- /dev/null
+++ b/t/perf/p5304-prune.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='performance tests of prune'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'remove reachable loose objects' '
+ git repack -ad
+'
+
+test_expect_success 'remove unreachable loose objects' '
+ git prune
+'
+
+test_expect_success 'confirm there are no loose objects' '
+ git count-objects | grep ^0
+'
+
+test_perf 'prune with no objects' '
+ git prune
+'
+
+test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 270da21..2c19a79 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -274,6 +274,18 @@ test_expect_success 'prune .git/shallow' '
test_path_is_missing .git/shallow
'
+test_expect_success 'prune .git/shallow when there are no loose objects' '
+ SHA1=$(echo hi|git commit-tree HEAD^{tree}) &&
+ echo $SHA1 >.git/shallow &&
+ git update-ref refs/heads/shallow-tip $SHA1 &&
+ git repack -ad &&
+ # verify assumption that all loose objects are gone
+ git count-objects | grep ^0 &&
+ git prune &&
+ echo $SHA1 >expect &&
+ test_cmp expect .git/shallow
+'
+
test_expect_success 'prune: handle alternate object database' '
test_create_repo A &&
git -C A commit --allow-empty -m "initial commit" &&