summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2020-03-02 23:07:18 (GMT)
committerJunio C Hamano <gitster@pobox.com>2020-03-02 23:07:18 (GMT)
commit0df82d99dae85dbd4f667e95020a146ea0167975 (patch)
treeb5af782c64f9515ca57938b5bbb1414bc9e14ead
parent80648bb3f2353bc90bec12e9dcbb0ba3e2032a72 (diff)
parent20a5fd881a98cfe153fa5a81754994c7046a6e41 (diff)
downloadgit-0df82d99dae85dbd4f667e95020a146ea0167975.zip
git-0df82d99dae85dbd4f667e95020a146ea0167975.tar.gz
git-0df82d99dae85dbd4f667e95020a146ea0167975.tar.bz2
Merge branch 'jk/object-filter-with-bitmap'
The object reachability bitmap machinery and the partial cloning machinery were not prepared to work well together, because some object-filtering criteria that partial clones use inherently rely on object traversal, but the bitmap machinery is an optimization to bypass that object traversal. There however are some cases where they can work together, and they were taught about them. * jk/object-filter-with-bitmap: rev-list --count: comment on the use of count_right++ pack-objects: support filters with bitmaps pack-bitmap: implement BLOB_LIMIT filtering pack-bitmap: implement BLOB_NONE filtering bitmap: add bitmap_unset() function rev-list: use bitmap filters for traversal pack-bitmap: basic noop bitmap filter infrastructure rev-list: allow commit-only bitmap traversals t5310: factor out bitmap traversal comparison rev-list: allow bitmaps when counting objects rev-list: make --count work with --objects rev-list: factor out bitmap-optimized routines pack-bitmap: refuse to do a bitmap traversal with pathspecs rev-list: fallback to non-bitmap traversal when filtering pack-bitmap: fix leak of haves/wants object lists pack-bitmap: factor out type iterator initialization
-rw-r--r--builtin/pack-objects.c6
-rw-r--r--builtin/rev-list.c121
-rw-r--r--ewah/bitmap.c8
-rw-r--r--ewah/ewok.h1
-rw-r--r--object.c9
-rw-r--r--object.h2
-rw-r--r--pack-bitmap.c272
-rw-r--r--pack-bitmap.h5
-rw-r--r--reachable.c4
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh22
-rwxr-xr-xt/t5310-pack-bitmaps.sh36
-rwxr-xr-xt/t6000-rev-list-misc.sh12
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh56
-rw-r--r--t/test-lib-functions.sh27
14 files changed, 511 insertions, 70 deletions
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 940fbcb..89e7ba4 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3184,7 +3184,7 @@ static int pack_options_allow_reuse(void)
static int get_object_list_from_bitmap(struct rev_info *revs)
{
- if (!(bitmap_git = prepare_bitmap_walk(revs)))
+ if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options)))
return -1;
if (pack_options_allow_reuse() &&
@@ -3198,7 +3198,8 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
display_progress(progress_state, nr_result);
}
- traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
+ traverse_bitmap_commit_list(bitmap_git, revs,
+ &add_object_entry_from_bitmap);
return 0;
}
@@ -3562,7 +3563,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (filter_options.choice) {
if (!pack_to_stdout)
die(_("cannot use --filter without --stdout"));
- use_bitmap_index = 0;
}
/*
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index e28d62e..f520111 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -253,11 +253,26 @@ static int finish_object(struct object *obj, const char *name, void *cb_data)
static void show_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
+ struct rev_info *revs = info->revs;
+
if (finish_object(obj, name, cb_data))
return;
display_progress(progress, ++progress_counter);
if (info->flags & REV_LIST_QUIET)
return;
+
+ if (revs->count) {
+ /*
+ * The object count is always accumulated in the .count_right
+ * field for traversal that is not a left-right traversal,
+ * and cmd_rev_list() made sure that a .count request that
+ * wants to count non-commit objects, which is handled by
+ * the show_object() callback, does not ask for .left_right.
+ */
+ revs->count_right++;
+ return;
+ }
+
if (arg_show_object_names)
show_object_with_name(stdout, obj, name);
else
@@ -364,6 +379,79 @@ static inline int parse_missing_action_value(const char *value)
return 0;
}
+static int try_bitmap_count(struct rev_info *revs,
+ struct list_objects_filter_options *filter)
+{
+ uint32_t commit_count = 0,
+ tag_count = 0,
+ tree_count = 0,
+ blob_count = 0;
+ int max_count;
+ struct bitmap_index *bitmap_git;
+
+ /* This function only handles counting, not general traversal. */
+ if (!revs->count)
+ return -1;
+
+ /*
+ * A bitmap result can't know left/right, etc, because we don't
+ * actually traverse.
+ */
+ if (revs->left_right || revs->cherry_mark)
+ return -1;
+
+ /*
+ * If we're counting reachable objects, we can't handle a max count of
+ * commits to traverse, since we don't know which objects go with which
+ * commit.
+ */
+ if (revs->max_count >= 0 &&
+ (revs->tag_objects || revs->tree_objects || revs->blob_objects))
+ return -1;
+
+ /*
+ * This must be saved before doing any walking, since the revision
+ * machinery will count it down to zero while traversing.
+ */
+ max_count = revs->max_count;
+
+ bitmap_git = prepare_bitmap_walk(revs, filter);
+ if (!bitmap_git)
+ return -1;
+
+ count_bitmap_commit_list(bitmap_git, &commit_count,
+ revs->tree_objects ? &tree_count : NULL,
+ revs->blob_objects ? &blob_count : NULL,
+ revs->tag_objects ? &tag_count : NULL);
+ if (max_count >= 0 && max_count < commit_count)
+ commit_count = max_count;
+
+ printf("%d\n", commit_count + tree_count + blob_count + tag_count);
+ free_bitmap_index(bitmap_git);
+ return 0;
+}
+
+static int try_bitmap_traversal(struct rev_info *revs,
+ struct list_objects_filter_options *filter)
+{
+ struct bitmap_index *bitmap_git;
+
+ /*
+ * We can't use a bitmap result with a traversal limit, since the set
+ * of commits we'd get would be essentially random.
+ */
+ if (revs->max_count >= 0)
+ return -1;
+
+ bitmap_git = prepare_bitmap_walk(revs, filter);
+ if (!bitmap_git)
+ return -1;
+
+ traverse_bitmap_commit_list(bitmap_git, revs, &show_object_fast);
+ free_bitmap_index(bitmap_git);
+ return 0;
+}
+
int cmd_rev_list(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -521,8 +609,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (revs.show_notes)
die(_("rev-list does not support display of notes"));
- if (filter_options.choice && use_bitmap_index)
- die(_("cannot combine --use-bitmap-index with object filtering"));
+ if (revs.count &&
+ (revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
+ (revs.left_right || revs.cherry_mark))
+ die(_("marked counting is incompatible with --objects"));
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
@@ -533,28 +623,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
if (show_progress)
progress = start_delayed_progress(show_progress, 0);
- if (use_bitmap_index && !revs.prune) {
- if (revs.count && !revs.left_right && !revs.cherry_mark) {
- uint32_t commit_count;
- int max_count = revs.max_count;
- struct bitmap_index *bitmap_git;
- if ((bitmap_git = prepare_bitmap_walk(&revs))) {
- count_bitmap_commit_list(bitmap_git, &commit_count, NULL, NULL, NULL);
- if (max_count >= 0 && max_count < commit_count)
- commit_count = max_count;
- printf("%d\n", commit_count);
- free_bitmap_index(bitmap_git);
- return 0;
- }
- } else if (revs.max_count < 0 &&
- revs.tag_objects && revs.tree_objects && revs.blob_objects) {
- struct bitmap_index *bitmap_git;
- if ((bitmap_git = prepare_bitmap_walk(&revs))) {
- traverse_bitmap_commit_list(bitmap_git, &show_object_fast);
- free_bitmap_index(bitmap_git);
- return 0;
- }
- }
+ if (use_bitmap_index) {
+ if (!try_bitmap_count(&revs, &filter_options))
+ return 0;
+ if (!try_bitmap_traversal(&revs, &filter_options))
+ return 0;
}
if (prepare_revision_walk(&revs))
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index b5fed96..d8cec58 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -50,6 +50,14 @@ void bitmap_set(struct bitmap *self, size_t pos)
self->words[block] |= EWAH_MASK(pos);
}
+void bitmap_unset(struct bitmap *self, size_t pos)
+{
+ size_t block = EWAH_BLOCK(pos);
+
+ if (block < self->word_alloc)
+ self->words[block] &= ~EWAH_MASK(pos);
+}
+
int bitmap_get(struct bitmap *self, size_t pos)
{
size_t block = EWAH_BLOCK(pos);
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 1b98b57..011852b 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -174,6 +174,7 @@ struct bitmap {
struct bitmap *bitmap_new(void);
struct bitmap *bitmap_word_alloc(size_t word_alloc);
void bitmap_set(struct bitmap *self, size_t pos);
+void bitmap_unset(struct bitmap *self, size_t pos);
int bitmap_get(struct bitmap *self, size_t pos);
void bitmap_reset(struct bitmap *self);
void bitmap_free(struct bitmap *self);
diff --git a/object.c b/object.c
index c94be94..794c866 100644
--- a/object.c
+++ b/object.c
@@ -308,6 +308,15 @@ int object_list_contains(struct object_list *list, struct object *obj)
return 0;
}
+void object_list_free(struct object_list **list)
+{
+ while (*list) {
+ struct object_list *p = *list;
+ *list = p->next;
+ free(p);
+ }
+}
+
/*
* A zero-length string to which object_array_entry::name can be
* initialized without requiring a malloc/free.
diff --git a/object.h b/object.h
index 25f5ab3..2dbabfc 100644
--- a/object.h
+++ b/object.h
@@ -151,6 +151,8 @@ struct object_list *object_list_insert(struct object *item,
int object_list_contains(struct object_list *list, struct object *obj);
+void object_list_free(struct object_list **list);
+
/* Object array handling .. */
void add_object_array(struct object *obj, const char *name, struct object_array *array);
void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 5a8689c..82bfd66 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -12,6 +12,7 @@
#include "packfile.h"
#include "repository.h"
#include "object-store.h"
+#include "list-objects-filter-options.h"
/*
* An entry on the bitmap index, representing the bitmap for a given
@@ -606,6 +607,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
}
static void show_extended_objects(struct bitmap_index *bitmap_git,
+ struct rev_info *revs,
show_reachable_fn show_reach)
{
struct bitmap *objects = bitmap_git->result;
@@ -619,13 +621,44 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
continue;
obj = eindex->objects[i];
+ if ((obj->type == OBJ_BLOB && !revs->blob_objects) ||
+ (obj->type == OBJ_TREE && !revs->tree_objects) ||
+ (obj->type == OBJ_TAG && !revs->tag_objects))
+ continue;
+
show_reach(&obj->oid, obj->type, 0, eindex->hashes[i], NULL, 0);
}
}
+static void init_type_iterator(struct ewah_iterator *it,
+ struct bitmap_index *bitmap_git,
+ enum object_type type)
+{
+ switch (type) {
+ case OBJ_COMMIT:
+ ewah_iterator_init(it, bitmap_git->commits);
+ break;
+
+ case OBJ_TREE:
+ ewah_iterator_init(it, bitmap_git->trees);
+ break;
+
+ case OBJ_BLOB:
+ ewah_iterator_init(it, bitmap_git->blobs);
+ break;
+
+ case OBJ_TAG:
+ ewah_iterator_init(it, bitmap_git->tags);
+ break;
+
+ default:
+ BUG("object type %d not stored by bitmap type index", type);
+ break;
+ }
+}
+
static void show_objects_for_type(
struct bitmap_index *bitmap_git,
- struct ewah_bitmap *type_filter,
enum object_type object_type,
show_reachable_fn show_reach)
{
@@ -637,7 +670,7 @@ static void show_objects_for_type(
struct bitmap *objects = bitmap_git->result;
- ewah_iterator_init(&it, type_filter);
+ init_type_iterator(&it, bitmap_git, object_type);
for (i = 0; i < objects->word_alloc &&
ewah_iterator_next(&filter, &it); i++) {
@@ -682,7 +715,179 @@ static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
return 0;
}
-struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
+static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects)
+{
+ struct bitmap *result = bitmap_new();
+ struct object_list *p;
+
+ for (p = tip_objects; p; p = p->next) {
+ int pos;
+
+ if (p->item->type != OBJ_BLOB)
+ continue;
+
+ pos = bitmap_position(bitmap_git, &p->item->oid);
+ if (pos < 0)
+ continue;
+
+ bitmap_set(result, pos);
+ }
+
+ return result;
+}
+
+static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter)
+{
+ struct eindex *eindex = &bitmap_git->ext_index;
+ struct bitmap *tips;
+ struct ewah_iterator it;
+ eword_t mask;
+ uint32_t i;
+
+ /*
+ * The non-bitmap version of this filter never removes
+ * blobs which the other side specifically asked for,
+ * so we must match that behavior.
+ */
+ tips = find_tip_blobs(bitmap_git, tip_objects);
+
+ /*
+ * We can use the blob type-bitmap to work in whole words
+ * for the objects that are actually in the bitmapped packfile.
+ */
+ for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
+ i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i++) {
+ if (i < tips->word_alloc)
+ mask &= ~tips->words[i];
+ to_filter->words[i] &= ~mask;
+ }
+
+ /*
+ * Clear any blobs that weren't in the packfile (and so would not have
+ * been caught by the loop above. We'll have to check them
+ * individually.
+ */
+ for (i = 0; i < eindex->count; i++) {
+ uint32_t pos = i + bitmap_git->pack->num_objects;
+ if (eindex->objects[i]->type == OBJ_BLOB &&
+ bitmap_get(to_filter, pos) &&
+ !bitmap_get(tips, pos))
+ bitmap_unset(to_filter, pos);
+ }
+
+ bitmap_free(tips);
+}
+
+static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
+ uint32_t pos)
+{
+ struct packed_git *pack = bitmap_git->pack;
+ unsigned long size;
+ struct object_info oi = OBJECT_INFO_INIT;
+
+ oi.sizep = &size;
+
+ if (pos < pack->num_objects) {
+ struct revindex_entry *entry = &pack->revindex[pos];
+ if (packed_object_info(the_repository, pack,
+ entry->offset, &oi) < 0) {
+ struct object_id oid;
+ nth_packed_object_oid(&oid, pack, entry->nr);
+ die(_("unable to get size of %s"), oid_to_hex(&oid));
+ }
+ } else {
+ struct eindex *eindex = &bitmap_git->ext_index;
+ struct object *obj = eindex->objects[pos - pack->num_objects];
+ if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+ die(_("unable to get size of %s"), oid_to_hex(&obj->oid));
+ }
+
+ return size;
+}
+
+static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter,
+ unsigned long limit)
+{
+ struct eindex *eindex = &bitmap_git->ext_index;
+ struct bitmap *tips;
+ struct ewah_iterator it;
+ eword_t mask;
+ uint32_t i;
+
+ tips = find_tip_blobs(bitmap_git, tip_objects);
+
+ for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
+ i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i++) {
+ eword_t word = to_filter->words[i] & mask;
+ unsigned offset;
+
+ for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+ uint32_t pos;
+
+ if ((word >> offset) == 0)
+ break;
+ offset += ewah_bit_ctz64(word >> offset);
+ pos = i * BITS_IN_EWORD + offset;
+
+ if (!bitmap_get(tips, pos) &&
+ get_size_by_pos(bitmap_git, pos) >= limit)
+ bitmap_unset(to_filter, pos);
+ }
+ }
+
+ for (i = 0; i < eindex->count; i++) {
+ uint32_t pos = i + bitmap_git->pack->num_objects;
+ if (eindex->objects[i]->type == OBJ_BLOB &&
+ bitmap_get(to_filter, pos) &&
+ !bitmap_get(tips, pos) &&
+ get_size_by_pos(bitmap_git, pos) >= limit)
+ bitmap_unset(to_filter, pos);
+ }
+
+ bitmap_free(tips);
+}
+
+static int filter_bitmap(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter,
+ struct list_objects_filter_options *filter)
+{
+ if (!filter || filter->choice == LOFC_DISABLED)
+ return 0;
+
+ if (filter->choice == LOFC_BLOB_NONE) {
+ if (bitmap_git)
+ filter_bitmap_blob_none(bitmap_git, tip_objects,
+ to_filter);
+ return 0;
+ }
+
+ if (filter->choice == LOFC_BLOB_LIMIT) {
+ if (bitmap_git)
+ filter_bitmap_blob_limit(bitmap_git, tip_objects,
+ to_filter,
+ filter->blob_limit_value);
+ return 0;
+ }
+
+ /* filter choice not handled */
+ return -1;
+}
+
+static int can_filter_bitmap(struct list_objects_filter_options *filter)
+{
+ return !filter_bitmap(NULL, NULL, NULL, filter);
+}
+
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
+ struct list_objects_filter_options *filter)
{
unsigned int i;
@@ -692,9 +897,22 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
struct bitmap *wants_bitmap = NULL;
struct bitmap *haves_bitmap = NULL;
- struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
+ struct bitmap_index *bitmap_git;
+
+ /*
+ * We can't do pathspec limiting with bitmaps, because we don't know
+ * which commits are associated with which object changes (let alone
+ * even which objects are associated with which paths).
+ */
+ if (revs->prune)
+ return NULL;
+
+ if (!can_filter_bitmap(filter))
+ return NULL;
+
/* try to open a bitmapped pack, but don't parse it yet
* because we may not need to use it */
+ bitmap_git = xcalloc(1, sizeof(*bitmap_git));
if (open_pack_bitmap(revs->repo, bitmap_git) < 0)
goto cleanup;
@@ -761,13 +979,20 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
if (haves_bitmap)
bitmap_and_not(wants_bitmap, haves_bitmap);
+ filter_bitmap(bitmap_git, wants, wants_bitmap, filter);
+
bitmap_git->result = wants_bitmap;
bitmap_git->haves = haves_bitmap;
+ object_list_free(&wants);
+ object_list_free(&haves);
+
return bitmap_git;
cleanup:
free_bitmap_index(bitmap_git);
+ object_list_free(&wants);
+ object_list_free(&haves);
return NULL;
}
@@ -907,20 +1132,20 @@ int bitmap_walk_contains(struct bitmap_index *bitmap_git,
}
void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
+ struct rev_info *revs,
show_reachable_fn show_reachable)
{
assert(bitmap_git->result);
- show_objects_for_type(bitmap_git, bitmap_git->commits,
- OBJ_COMMIT, show_reachable);
- show_objects_for_type(bitmap_git, bitmap_git->trees,
- OBJ_TREE, show_reachable);
- show_objects_for_type(bitmap_git, bitmap_git->blobs,
- OBJ_BLOB, show_reachable);
- show_objects_for_type(bitmap_git, bitmap_git->tags,
- OBJ_TAG, show_reachable);
+ show_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable);
+ if (revs->tree_objects)
+ show_objects_for_type(bitmap_git, OBJ_TREE, show_reachable);
+ if (revs->blob_objects)
+ show_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable);
+ if (revs->tag_objects)
+ show_objects_for_type(bitmap_git, OBJ_TAG, show_reachable);
- show_extended_objects(bitmap_git, show_reachable);
+ show_extended_objects(bitmap_git, revs, show_reachable);
}
static uint32_t count_object_type(struct bitmap_index *bitmap_git,
@@ -933,26 +1158,7 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
struct ewah_iterator it;
eword_t filter;
- switch (type) {
- case OBJ_COMMIT:
- ewah_iterator_init(&it, bitmap_git->commits);
- break;
-
- case OBJ_TREE:
- ewah_iterator_init(&it, bitmap_git->trees);
- break;
-
- case OBJ_BLOB:
- ewah_iterator_init(&it, bitmap_git->blobs);
- break;
-
- case OBJ_TAG:
- ewah_iterator_init(&it, bitmap_git->tags);
- break;
-
- default:
- return 0;
- }
+ init_type_iterator(&it, bitmap_git, type);
while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
eword_t word = objects->words[i++] & filter;
diff --git a/pack-bitmap.h b/pack-bitmap.h
index bcd03b8..1203120 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -9,6 +9,7 @@
struct commit;
struct repository;
struct rev_info;
+struct list_objects_filter_options;
static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};
@@ -45,9 +46,11 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r);
void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
uint32_t *trees, uint32_t *blobs, uint32_t *tags);
void traverse_bitmap_commit_list(struct bitmap_index *,
+ struct rev_info *revs,
show_reachable_fn show_reachable);
void test_bitmap_walk(struct rev_info *revs);
-struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs);
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
+ struct list_objects_filter_options *filter);
int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
struct packed_git **packfile,
uint32_t *entries,
diff --git a/reachable.c b/reachable.c
index 8f50235..77a60c7 100644
--- a/reachable.c
+++ b/reachable.c
@@ -223,9 +223,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
cp.progress = progress;
cp.count = 0;
- bitmap_git = prepare_bitmap_walk(revs);
+ bitmap_git = prepare_bitmap_walk(revs, NULL);
if (bitmap_git) {
- traverse_bitmap_commit_list(bitmap_git, mark_object_seen);
+ traverse_bitmap_commit_list(bitmap_git, revs, mark_object_seen);
free_bitmap_index(bitmap_git);
return;
}
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index 6a3a425..7743f4f 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -39,6 +39,28 @@ test_perf 'pack to file (bitmap)' '
git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null
'
+test_perf 'rev-list (commits)' '
+ git rev-list --all --use-bitmap-index >/dev/null
+'
+
+test_perf 'rev-list (objects)' '
+ git rev-list --all --use-bitmap-index --objects >/dev/null
+'
+
+test_perf 'rev-list count with blob:none' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:none >/dev/null
+'
+
+test_perf 'rev-list count with blob:limit=1k' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=blob:limit=1k >/dev/null
+'
+
+test_perf 'simulated partial clone' '
+ git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
+'
+
test_expect_success 'create partial bitmap state' '
# pick a commit to represent the repo tip in the past
cutoff=$(git rev-list HEAD~100 -1) &&
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 6640329..8318781 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -74,16 +74,24 @@ rev_list_tests() {
test_cmp expect actual
'
- test_expect_success "enumerate --objects ($state)" '
- git rev-list --objects --use-bitmap-index HEAD >tmp &&
- cut -d" " -f1 <tmp >tmp2 &&
- sort <tmp2 >actual &&
- git rev-list --objects HEAD >tmp &&
- cut -d" " -f1 <tmp >tmp2 &&
- sort <tmp2 >expect &&
+ test_expect_success "counting objects via bitmap ($state)" '
+ git rev-list --count --objects HEAD >expect &&
+ git rev-list --use-bitmap-index --count --objects HEAD >actual &&
test_cmp expect actual
'
+ test_expect_success "enumerate commits ($state)" '
+ git rev-list --use-bitmap-index HEAD >actual &&
+ git rev-list HEAD >expect &&
+ test_bitmap_traversal --no-confirm-bitmaps expect actual
+ '
+
+ test_expect_success "enumerate --objects ($state)" '
+ git rev-list --objects --use-bitmap-index HEAD >actual &&
+ git rev-list --objects HEAD >expect &&
+ test_bitmap_traversal expect actual
+ '
+
test_expect_success "bitmap --objects handles non-commit objects ($state)" '
git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
grep $blob actual
@@ -99,6 +107,20 @@ test_expect_success 'clone from bitmapped repository' '
test_cmp expect actual
'
+test_expect_success 'partial clone from bitmapped repository' '
+ test_config uploadpack.allowfilter true &&
+ git clone --no-local --bare --filter=blob:none . partial-clone.git &&
+ (
+ cd partial-clone.git &&
+ pack=$(echo objects/pack/*.pack) &&
+ git verify-pack -v "$pack" >have &&
+ awk "/blob/ { print \$1 }" <have >blobs &&
+ # we expect this single blob because of the direct ref
+ git rev-parse refs/tags/tagged-blob >expect &&
+ test_cmp expect blobs
+ )
+'
+
test_expect_success 'setup further non-bitmapped commits' '
test_commit_bulk --id=further 10
'
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index a0baf9e..3dc1ad8 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -151,4 +151,16 @@ test_expect_success 'rev-list --end-of-options' '
test_cmp expect actual
'
+test_expect_success 'rev-list --count' '
+ count=$(git rev-list --count HEAD) &&
+ git rev-list HEAD >actual &&
+ test_line_count = $count actual
+'
+
+test_expect_success 'rev-list --count --objects' '
+ count=$(git rev-list --count --objects HEAD) &&
+ git rev-list --objects HEAD >actual &&
+ test_line_count = $count actual
+'
+
test_done
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
new file mode 100755
index 0000000..145603f
--- /dev/null
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='rev-list combining bitmaps and filters'
+. ./test-lib.sh
+
+test_expect_success 'set up bitmapped repo' '
+ # one commit will have bitmaps, the other will not
+ test_commit one &&
+ test_commit much-larger-blob-one &&
+ git repack -adb &&
+ test_commit two &&
+ test_commit much-larger-blob-two
+'
+
+test_expect_success 'filters fallback to non-bitmap traversal' '
+ # use a path-based filter, since they are inherently incompatible with
+ # bitmaps (i.e., this test will never get confused by later code to
+ # combine the features)
+ filter=$(echo "!one" | git hash-object -w --stdin) &&
+ git rev-list --objects --filter=sparse:oid=$filter HEAD >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=sparse:oid=$filter HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'blob:none filter' '
+ git rev-list --objects --filter=blob:none HEAD >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=blob:none HEAD >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:none filter with specified blob' '
+ git rev-list --objects --filter=blob:none HEAD HEAD:two.t >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=blob:none HEAD HEAD:two.t >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:limit filter' '
+ git rev-list --objects --filter=blob:limit=5 HEAD >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=blob:limit=5 HEAD >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:limit filter with specified blob' '
+ git rev-list --objects --filter=blob:limit=5 \
+ HEAD HEAD:much-larger-blob-two.t >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=blob:limit=5 \
+ HEAD HEAD:much-larger-blob-two.t >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 284c52d..352c213 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1516,3 +1516,30 @@ test_set_port () {
port=$(($port + ${GIT_TEST_STRESS_JOB_NR:-0}))
eval $var=$port
}
+
+# Compare a file containing rev-list bitmap traversal output to its non-bitmap
+# counterpart. You can't just use test_cmp for this, because the two produce
+# subtly different output:
+#
+# - regular output is in traversal order, whereas bitmap is split by type,
+# with non-packed objects at the end
+#
+# - regular output has a space and the pathname appended to non-commit
+# objects; bitmap output omits this
+#
+# This function normalizes and compares the two. The second file should
+# always be the bitmap output.
+test_bitmap_traversal () {
+ if test "$1" = "--no-confirm-bitmaps"
+ then
+ shift
+ elif cmp "$1" "$2"
+ then
+ echo >&2 "identical raw outputs; are you sure bitmaps were used?"
+ return 1
+ fi &&
+ cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
+ sort "$2" >"$2.normalized" &&
+ test_cmp "$1.normalized" "$2.normalized" &&
+ rm -f "$1.normalized" "$2.normalized"
+}