Merge branch 'ps/rev-list-object-type-filter'

"git rev-list" learns the "--filter=object:type=<type>" option,
which can be used to exclude objects of the given kind from the
packfile generated by pack-objects.

* ps/rev-list-object-type-filter:
  rev-list: allow filtering of provided items
  pack-bitmap: implement combined filter
  pack-bitmap: implement object type filter
  list-objects: implement object type filter
  list-objects: support filtering by tag and commit
  list-objects: move tag processing into its own function
  revision: mark commit parents as NOT_USER_GIVEN
  uploadpack.txt: document implication of `uploadpackfilter.allow`
This commit is contained in:
Junio C Hamano 2021-05-07 12:47:40 +09:00
commit 8585d6c04a
16 changed files with 388 additions and 30 deletions

View File

@ -59,15 +59,16 @@ uploadpack.allowFilter::
uploadpackfilter.allow::
Provides a default value for unspecified object filters (see: the
below configuration variable).
below configuration variable). If set to `true`, this will also
enable all filters which get added in the future.
Defaults to `true`.
uploadpackfilter.<filter>.allow::
Explicitly allow or ban the object filter corresponding to
`<filter>`, where `<filter>` may be one of: `blob:none`,
`blob:limit`, `tree`, `sparse:oid`, or `combine`. If using
combined filters, both `combine` and all of the nested filter
kinds must be allowed. Defaults to `uploadpackfilter.allow`.
`blob:limit`, `object:type`, `tree`, `sparse:oid`, or `combine`.
If using combined filters, both `combine` and all of the nested
filter kinds must be allowed. Defaults to `uploadpackfilter.allow`.
uploadpackfilter.tree.maxDepth::
Only allow `--filter=tree:<n>` when `<n>` is no more than the value of

View File

@ -892,6 +892,9 @@ or units. n may be zero. The suffixes k, m, and g can be used to name
units in KiB, MiB, or GiB. For example, 'blob:limit=1k' is the same
as 'blob:limit=1024'.
+
The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
which are not of the requested type.
+
The form '--filter=sparse:oid=<blob-ish>' uses a sparse-checkout
specification contained in the blob (or blob-expression) '<blob-ish>'
to omit blobs that would not be not required for a sparse checkout on
@ -930,6 +933,11 @@ equivalent.
--no-filter::
Turn off any previous `--filter=` argument.
--filter-provided-objects::
Filter the list of explicitly provided objects, which would otherwise
always be printed even if they did not match any of the filters. Only
useful with `--filter=`.
--filter-print-omitted::
Only useful with `--filter=`; prints a list of the objects omitted
by the filter. Object IDs are prefixed with a ``~'' character.

View File

@ -3516,7 +3516,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, &filter_options)))
if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options, 0)))
return -1;
if (pack_options_allow_reuse() &&

View File

@ -398,7 +398,8 @@ static inline int parse_missing_action_value(const char *value)
}
static int try_bitmap_count(struct rev_info *revs,
struct list_objects_filter_options *filter)
struct list_objects_filter_options *filter,
int filter_provided_objects)
{
uint32_t commit_count = 0,
tag_count = 0,
@ -433,7 +434,7 @@ static int try_bitmap_count(struct rev_info *revs,
*/
max_count = revs->max_count;
bitmap_git = prepare_bitmap_walk(revs, filter);
bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
if (!bitmap_git)
return -1;
@ -450,7 +451,8 @@ static int try_bitmap_count(struct rev_info *revs,
}
static int try_bitmap_traversal(struct rev_info *revs,
struct list_objects_filter_options *filter)
struct list_objects_filter_options *filter,
int filter_provided_objects)
{
struct bitmap_index *bitmap_git;
@ -461,7 +463,7 @@ static int try_bitmap_traversal(struct rev_info *revs,
if (revs->max_count >= 0)
return -1;
bitmap_git = prepare_bitmap_walk(revs, filter);
bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
if (!bitmap_git)
return -1;
@ -471,14 +473,15 @@ static int try_bitmap_traversal(struct rev_info *revs,
}
static int try_bitmap_disk_usage(struct rev_info *revs,
struct list_objects_filter_options *filter)
struct list_objects_filter_options *filter,
int filter_provided_objects)
{
struct bitmap_index *bitmap_git;
if (!show_disk_usage)
return -1;
bitmap_git = prepare_bitmap_walk(revs, filter);
bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
if (!bitmap_git)
return -1;
@ -499,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
int bisect_show_vars = 0;
int bisect_find_all = 0;
int use_bitmap_index = 0;
int filter_provided_objects = 0;
const char *show_progress = NULL;
if (argc == 2 && !strcmp(argv[1], "-h"))
@ -599,6 +603,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
list_objects_filter_set_no_filter(&filter_options);
continue;
}
if (!strcmp(arg, "--filter-provided-objects")) {
filter_provided_objects = 1;
continue;
}
if (!strcmp(arg, "--filter-print-omitted")) {
arg_print_omitted = 1;
continue;
@ -665,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
progress = start_delayed_progress(show_progress, 0);
if (use_bitmap_index) {
if (!try_bitmap_count(&revs, &filter_options))
if (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))
return 0;
if (!try_bitmap_disk_usage(&revs, &filter_options))
if (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))
return 0;
if (!try_bitmap_traversal(&revs, &filter_options))
if (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))
return 0;
}
@ -694,6 +702,16 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
return show_bisect_vars(&info, reaches, all);
}
if (filter_provided_objects) {
struct commit_list *c;
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *pending = revs.pending.objects + i;
pending->item->flags |= NOT_USER_GIVEN;
}
for (c = revs.commits; c; c = c->next)
c->item->object.flags |= NOT_USER_GIVEN;
}
if (arg_print_omitted)
oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
if (arg_missing_action == MA_PRINT)

View File

@ -29,6 +29,8 @@ const char *list_object_filter_config_name(enum list_objects_filter_choice c)
return "tree";
case LOFC_SPARSE_OID:
return "sparse:oid";
case LOFC_OBJECT_TYPE:
return "object:type";
case LOFC_COMBINE:
return "combine";
case LOFC__COUNT:
@ -97,6 +99,19 @@ static int gently_parse_list_objects_filter(
}
return 1;
} else if (skip_prefix(arg, "object:type=", &v0)) {
int type = type_from_string_gently(v0, strlen(v0), 1);
if (type < 0) {
strbuf_addf(errbuf, _("'%s' for 'object:type=<type>' is"
"not a valid object type"), v0);
return 1;
}
filter_options->object_type = type;
filter_options->choice = LOFC_OBJECT_TYPE;
return 0;
} else if (skip_prefix(arg, "combine:", &v0)) {
return parse_combine_filter(filter_options, v0, errbuf);

View File

@ -1,6 +1,7 @@
#ifndef LIST_OBJECTS_FILTER_OPTIONS_H
#define LIST_OBJECTS_FILTER_OPTIONS_H
#include "cache.h"
#include "parse-options.h"
#include "string-list.h"
@ -13,6 +14,7 @@ enum list_objects_filter_choice {
LOFC_BLOB_LIMIT,
LOFC_TREE_DEPTH,
LOFC_SPARSE_OID,
LOFC_OBJECT_TYPE,
LOFC_COMBINE,
LOFC__COUNT /* must be last */
};
@ -54,6 +56,7 @@ struct list_objects_filter_options {
char *sparse_oid_name;
unsigned long blob_limit_value;
unsigned long tree_exclude_depth;
enum object_type object_type;
/* LOFC_COMBINE values */

View File

@ -82,6 +82,16 @@ static enum list_objects_filter_result filter_blobs_none(
default:
BUG("unknown filter_situation: %d", filter_situation);
case LOFS_TAG:
assert(obj->type == OBJ_TAG);
/* always include all tag objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_COMMIT:
assert(obj->type == OBJ_COMMIT);
/* always include all commit objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_BEGIN_TREE:
assert(obj->type == OBJ_TREE);
/* always include all tree objects */
@ -173,6 +183,16 @@ static enum list_objects_filter_result filter_trees_depth(
default:
BUG("unknown filter_situation: %d", filter_situation);
case LOFS_TAG:
assert(obj->type == OBJ_TAG);
/* always include all tag objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_COMMIT:
assert(obj->type == OBJ_COMMIT);
/* always include all commit objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_END_TREE:
assert(obj->type == OBJ_TREE);
filter_data->current_depth--;
@ -267,6 +287,16 @@ static enum list_objects_filter_result filter_blobs_limit(
default:
BUG("unknown filter_situation: %d", filter_situation);
case LOFS_TAG:
assert(obj->type == OBJ_TAG);
/* always include all tag objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_COMMIT:
assert(obj->type == OBJ_COMMIT);
/* always include all commit objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_BEGIN_TREE:
assert(obj->type == OBJ_TREE);
/* always include all tree objects */
@ -371,6 +401,16 @@ static enum list_objects_filter_result filter_sparse(
default:
BUG("unknown filter_situation: %d", filter_situation);
case LOFS_TAG:
assert(obj->type == OBJ_TAG);
/* always include all tag objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_COMMIT:
assert(obj->type == OBJ_COMMIT);
/* always include all commit objects */
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
case LOFS_BEGIN_TREE:
assert(obj->type == OBJ_TREE);
dtype = DT_DIR;
@ -505,6 +545,81 @@ static void filter_sparse_oid__init(
filter->free_fn = filter_sparse_free;
}
/*
* A filter for list-objects to omit large blobs.
* And to OPTIONALLY collect a list of the omitted OIDs.
*/
struct filter_object_type_data {
enum object_type object_type;
};
static enum list_objects_filter_result filter_object_type(
struct repository *r,
enum list_objects_filter_situation filter_situation,
struct object *obj,
const char *pathname,
const char *filename,
struct oidset *omits,
void *filter_data_)
{
struct filter_object_type_data *filter_data = filter_data_;
switch (filter_situation) {
default:
BUG("unknown filter_situation: %d", filter_situation);
case LOFS_TAG:
assert(obj->type == OBJ_TAG);
if (filter_data->object_type == OBJ_TAG)
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
return LOFR_MARK_SEEN;
case LOFS_COMMIT:
assert(obj->type == OBJ_COMMIT);
if (filter_data->object_type == OBJ_COMMIT)
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
return LOFR_MARK_SEEN;
case LOFS_BEGIN_TREE:
assert(obj->type == OBJ_TREE);
/*
* If we only want to show commits or tags, then there is no
* need to walk down trees.
*/
if (filter_data->object_type == OBJ_COMMIT ||
filter_data->object_type == OBJ_TAG)
return LOFR_SKIP_TREE;
if (filter_data->object_type == OBJ_TREE)
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
return LOFR_MARK_SEEN;
case LOFS_BLOB:
assert(obj->type == OBJ_BLOB);
if (filter_data->object_type == OBJ_BLOB)
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
return LOFR_MARK_SEEN;
case LOFS_END_TREE:
return LOFR_ZERO;
}
}
static void filter_object_type__init(
struct list_objects_filter_options *filter_options,
struct filter *filter)
{
struct filter_object_type_data *d = xcalloc(1, sizeof(*d));
d->object_type = filter_options->object_type;
filter->filter_data = d;
filter->filter_object_fn = filter_object_type;
filter->free_fn = free;
}
/* A filter which only shows objects shown by all sub-filters. */
struct combine_filter_data {
struct subfilter *sub;
@ -651,6 +766,7 @@ static filter_init_fn s_filters[] = {
filter_blobs_limit__init,
filter_trees_depth__init,
filter_sparse_oid__init,
filter_object_type__init,
filter_combine__init,
};

View File

@ -55,6 +55,8 @@ enum list_objects_filter_result {
};
enum list_objects_filter_situation {
LOFS_COMMIT,
LOFS_TAG,
LOFS_BEGIN_TREE,
LOFS_END_TREE,
LOFS_BLOB

View File

@ -213,6 +213,21 @@ static void process_tree(struct traversal_context *ctx,
free_tree_buffer(tree);
}
static void process_tag(struct traversal_context *ctx,
struct tag *tag,
const char *name)
{
enum list_objects_filter_result r;
r = list_objects_filter__filter_object(ctx->revs->repo, LOFS_TAG,
&tag->object, NULL, NULL,
ctx->filter);
if (r & LOFR_MARK_SEEN)
tag->object.flags |= SEEN;
if (r & LOFR_DO_SHOW)
ctx->show_object(&tag->object, name, ctx->show_data);
}
static void mark_edge_parents_uninteresting(struct commit *commit,
struct rev_info *revs,
show_edge_fn show_edge)
@ -334,8 +349,7 @@ static void traverse_trees_and_blobs(struct traversal_context *ctx,
if (obj->flags & (UNINTERESTING | SEEN))
continue;
if (obj->type == OBJ_TAG) {
obj->flags |= SEEN;
ctx->show_object(obj, name, ctx->show_data);
process_tag(ctx, (struct tag *)obj, name);
continue;
}
if (!path)
@ -361,6 +375,12 @@ static void do_traverse(struct traversal_context *ctx)
strbuf_init(&csp, PATH_MAX);
while ((commit = get_revision(ctx->revs)) != NULL) {
enum list_objects_filter_result r;
r = list_objects_filter__filter_object(ctx->revs->repo,
LOFS_COMMIT, &commit->object,
NULL, NULL, ctx->filter);
/*
* an uninteresting boundary commit may not have its tree
* parsed yet, but we are not going to show them anyway
@ -375,7 +395,11 @@ static void do_traverse(struct traversal_context *ctx)
die(_("unable to load root tree for commit %s"),
oid_to_hex(&commit->object.oid));
}
ctx->show_commit(commit, ctx->show_data);
if (r & LOFR_MARK_SEEN)
commit->object.flags |= SEEN;
if (r & LOFR_DO_SHOW)
ctx->show_commit(commit, ctx->show_data);
if (ctx->revs->tree_blobs_in_commit_order)
/*

View File

@ -783,9 +783,6 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
eword_t mask;
uint32_t i;
if (type != OBJ_BLOB && type != OBJ_TREE)
BUG("filter_bitmap_exclude_type: unsupported type '%d'", type);
/*
* The non-bitmap version of this filter never removes
* objects which the other side specifically asked for,
@ -915,6 +912,24 @@ static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git,
OBJ_BLOB);
}
static void filter_bitmap_object_type(struct bitmap_index *bitmap_git,
struct object_list *tip_objects,
struct bitmap *to_filter,
enum object_type object_type)
{
if (object_type < OBJ_COMMIT || object_type > OBJ_TAG)
BUG("filter_bitmap_object_type given invalid object");
if (object_type != OBJ_TAG)
filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, OBJ_TAG);
if (object_type != OBJ_COMMIT)
filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, OBJ_COMMIT);
if (object_type != OBJ_TREE)
filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, OBJ_TREE);
if (object_type != OBJ_BLOB)
filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter, OBJ_BLOB);
}
static int filter_bitmap(struct bitmap_index *bitmap_git,
struct object_list *tip_objects,
struct bitmap *to_filter,
@ -947,6 +962,24 @@ static int filter_bitmap(struct bitmap_index *bitmap_git,
return 0;
}
if (filter->choice == LOFC_OBJECT_TYPE) {
if (bitmap_git)
filter_bitmap_object_type(bitmap_git, tip_objects,
to_filter,
filter->object_type);
return 0;
}
if (filter->choice == LOFC_COMBINE) {
int i;
for (i = 0; i < filter->sub_nr; i++) {
if (filter_bitmap(bitmap_git, tip_objects, to_filter,
&filter->sub[i]) < 0)
return -1;
}
return 0;
}
/* filter choice not handled */
return -1;
}
@ -957,7 +990,8 @@ static int can_filter_bitmap(struct list_objects_filter_options *filter)
}
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
struct list_objects_filter_options *filter)
struct list_objects_filter_options *filter,
int filter_provided_objects)
{
unsigned int i;
@ -1052,7 +1086,8 @@ 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);
filter_bitmap(bitmap_git, (filter && filter_provided_objects) ? NULL : wants,
wants_bitmap, filter);
bitmap_git->result = wants_bitmap;
bitmap_git->haves = haves_bitmap;

View File

@ -52,7 +52,8 @@ void traverse_bitmap_commit_list(struct bitmap_index *,
void test_bitmap_walk(struct rev_info *revs);
int test_bitmap_commits(struct repository *r);
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
struct list_objects_filter_options *filter);
struct list_objects_filter_options *filter,
int filter_provided_objects);
int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
struct packed_git **packfile,
uint32_t *entries,

View File

@ -223,7 +223,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
cp.progress = progress;
cp.count = 0;
bitmap_git = prepare_bitmap_walk(revs, NULL);
bitmap_git = prepare_bitmap_walk(revs, NULL, 0);
if (bitmap_git) {
traverse_bitmap_commit_list(bitmap_git, revs, mark_object_seen);
free_bitmap_index(bitmap_git);

View File

@ -1123,7 +1123,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
mark_parents_uninteresting(p);
if (p->object.flags & SEEN)
continue;
p->object.flags |= SEEN;
p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (list)
commit_list_insert_by_date(p, list);
if (queue)
@ -1165,7 +1165,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
}
p->object.flags |= left_flag;
if (!(p->object.flags & SEEN)) {
p->object.flags |= SEEN;
p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (list)
commit_list_insert_by_date(p, list);
if (queue)

View File

@ -44,9 +44,6 @@
/*
* Indicates object was reached by traversal. i.e. not given by user on
* command-line or stdin.
* NEEDSWORK: NOT_USER_GIVEN doesn't apply to commits because we only support
* filtering trees and blobs, but it may be useful to support filtering commits
* in the future.
*/
#define NOT_USER_GIVEN (1u<<25)
#define TRACK_LINEAR (1u<<26)

View File

@ -159,6 +159,78 @@ test_expect_success 'verify blob:limit=1m' '
test_must_be_empty observed
'
# Test object:type=<type> filter.
test_expect_success 'setup object-type' '
test_create_repo object-type &&
test_commit --no-tag -C object-type message blob &&
git -C object-type tag tag -m tag-message
'
test_expect_success 'verify object:type= fails with invalid type' '
test_must_fail git -C object-type rev-list --objects --filter=object:type= HEAD &&
test_must_fail git -C object-type rev-list --objects --filter=object:type=invalid HEAD
'
test_expect_success 'verify object:type=blob prints blob and commit' '
git -C object-type rev-parse HEAD >expected &&
printf "%s blob\n" $(git -C object-type rev-parse HEAD:blob) >>expected &&
git -C object-type rev-list --objects --filter=object:type=blob HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=tree prints tree and commit' '
(
git -C object-type rev-parse HEAD &&
printf "%s \n" $(git -C object-type rev-parse HEAD^{tree})
) >expected &&
git -C object-type rev-list --objects --filter=object:type=tree HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=commit prints commit' '
git -C object-type rev-parse HEAD >expected &&
git -C object-type rev-list --objects --filter=object:type=commit HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=tag prints tag' '
(
git -C object-type rev-parse HEAD &&
printf "%s tag\n" $(git -C object-type rev-parse tag)
) >expected &&
git -C object-type rev-list --objects --filter=object:type=tag tag >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=blob prints only blob with --filter-provided-objects' '
printf "%s blob\n" $(git -C object-type rev-parse HEAD:blob) >expected &&
git -C object-type rev-list --objects \
--filter=object:type=blob --filter-provided-objects HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=tree prints only tree with --filter-provided-objects' '
printf "%s \n" $(git -C object-type rev-parse HEAD^{tree}) >expected &&
git -C object-type rev-list --objects \
--filter=object:type=tree HEAD --filter-provided-objects >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=commit prints only commit with --filter-provided-objects' '
git -C object-type rev-parse HEAD >expected &&
git -C object-type rev-list --objects \
--filter=object:type=commit --filter-provided-objects HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'verify object:type=tag prints only tag with --filter-provided-objects' '
printf "%s tag\n" $(git -C object-type rev-parse tag) >expected &&
git -C object-type rev-list --objects \
--filter=object:type=tag --filter-provided-objects tag >actual &&
test_cmp expected actual
'
# Test sparse:path=<path> filter.
# !!!!
# NOTE: sparse:path filter support has been dropped for security reasons,

View File

@ -10,7 +10,8 @@ test_expect_success 'set up bitmapped repo' '
test_commit much-larger-blob-one &&
git repack -adb &&
test_commit two &&
test_commit much-larger-blob-two
test_commit much-larger-blob-two &&
git tag tag
'
test_expect_success 'filters fallback to non-bitmap traversal' '
@ -75,4 +76,69 @@ test_expect_success 'tree:1 filter' '
test_cmp expect actual
'
test_expect_success 'object:type filter' '
git rev-list --objects --filter=object:type=tag tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter=object:type=tag tag >actual &&
test_cmp expect actual &&
git rev-list --objects --filter=object:type=commit tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter=object:type=commit tag >actual &&
test_bitmap_traversal expect actual &&
git rev-list --objects --filter=object:type=tree tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter=object:type=tree tag >actual &&
test_bitmap_traversal expect actual &&
git rev-list --objects --filter=object:type=blob tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter=object:type=blob tag >actual &&
test_bitmap_traversal expect actual
'
test_expect_success 'object:type filter with --filter-provided-objects' '
git rev-list --objects --filter-provided-objects --filter=object:type=tag tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter-provided-objects --filter=object:type=tag tag >actual &&
test_cmp expect actual &&
git rev-list --objects --filter-provided-objects --filter=object:type=commit tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter-provided-objects --filter=object:type=commit tag >actual &&
test_bitmap_traversal expect actual &&
git rev-list --objects --filter-provided-objects --filter=object:type=tree tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter-provided-objects --filter=object:type=tree tag >actual &&
test_bitmap_traversal expect actual &&
git rev-list --objects --filter-provided-objects --filter=object:type=blob tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter-provided-objects --filter=object:type=blob tag >actual &&
test_bitmap_traversal expect actual
'
test_expect_success 'combine filter' '
git rev-list --objects --filter=blob:limit=1000 --filter=object:type=blob tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter=blob:limit=1000 --filter=object:type=blob tag >actual &&
test_bitmap_traversal expect actual
'
test_expect_success 'combine filter with --filter-provided-objects' '
git rev-list --objects --filter-provided-objects --filter=blob:limit=1000 --filter=object:type=blob tag >expect &&
git rev-list --use-bitmap-index \
--objects --filter-provided-objects --filter=blob:limit=1000 --filter=object:type=blob tag >actual &&
test_bitmap_traversal expect actual &&
git cat-file --batch-check="%(objecttype) %(objectsize)" <actual >objects &&
while read objecttype objectsize
do
test "$objecttype" = blob || return 1
test "$objectsize" -le 1000 || return 1
done <objects
'
test_done