From a812789c264b1cabd8e5a22b410a9004781a81c9 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 9 Apr 2021 13:27:52 +0200 Subject: uploadpack.txt: document implication of `uploadpackfilter.allow` When `uploadpackfilter.allow` is set to `true`, it means that filters are enabled by default except in the case where a filter is explicitly disabled via `uploadpackilter..allow`. This option will not only enable the currently supported set of filters, but also any filters which get added in the future. As such, an admin which wants to have tight control over which filters are allowed and which aren't probably shouldn't ever set `uploadpackfilter.allow=true`. Amend the documentation to make the ramifications more explicit so that admins are aware of this. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt index b0d7612..6729a07 100644 --- a/Documentation/config/uploadpack.txt +++ b/Documentation/config/uploadpack.txt @@ -59,7 +59,8 @@ 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..allow:: -- cgit v0.10.2-6-g49f6 From b2025da38be94568bf046c2b8520fe87bcbb5c3d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 9 Apr 2021 13:27:57 +0200 Subject: revision: mark commit parents as NOT_USER_GIVEN The NOT_USER_GIVEN flag of an object marks whether a flag was explicitly provided by the user or not. The most important use case for this is when filtering objects: only objects that were not explicitly requested will get filtered. The flag is currently only set for blobs and trees, which has been fine given that there are no filters for tags or commits currently. We're about to extend filtering capabilities to add object type filter though, which requires us to set up the NOT_USER_GIVEN flag correctly -- if it's not set, the object wouldn't get filtered at all. Mark unseen commit parents as NOT_USER_GIVEN when processing parents. Like this, explicitly provided parents stay user-given and thus unfiltered, while parents which get loaded as part of the graph walk can be filtered. This commit shouldn't have any user-visible impact yet as there is no logic to filter commits yet. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/revision.c b/revision.c index 553c0fa..fd34c75 100644 --- a/revision.c +++ b/revision.c @@ -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) diff --git a/revision.h b/revision.h index a24f72d..93aa012 100644 --- a/revision.h +++ b/revision.h @@ -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) -- cgit v0.10.2-6-g49f6 From 628d81be6c007de5aa0fa352b912b89f74a5cfa7 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 9 Apr 2021 13:28:02 +0200 Subject: list-objects: move tag processing into its own function Move processing of tags into its own function to make the logic easier to extend when we're going to implement filtering for tags. No change in behaviour is expected from this commit. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/list-objects.c b/list-objects.c index e19589b..a5a6030 100644 --- a/list-objects.c +++ b/list-objects.c @@ -213,6 +213,14 @@ 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) +{ + tag->object.flags |= SEEN; + 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 +342,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) -- cgit v0.10.2-6-g49f6 From 9a2a4f95448890d138a800c8a55c5d5dcfe16082 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 12 Apr 2021 15:37:35 +0200 Subject: list-objects: support filtering by tag and commit Object filters currently only support filtering blobs or trees based on some criteria. This commit lays the foundation to also allow filtering of tags and commits. No change in behaviour is expected from this commit given that there are no filters yet for those object types. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/list-objects-filter.c b/list-objects-filter.c index 39e2f15..0ebfa52 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -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; diff --git a/list-objects-filter.h b/list-objects-filter.h index cfd784e..9e98814 100644 --- a/list-objects-filter.h +++ b/list-objects-filter.h @@ -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 diff --git a/list-objects.c b/list-objects.c index a5a6030..7f40467 100644 --- a/list-objects.c +++ b/list-objects.c @@ -217,8 +217,15 @@ static void process_tag(struct traversal_context *ctx, struct tag *tag, const char *name) { - tag->object.flags |= SEEN; - ctx->show_object(&tag->object, name, ctx->show_data); + 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, @@ -368,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 @@ -382,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) /* -- cgit v0.10.2-6-g49f6 From b0c42a53c9d36ea69f4d2650001f05e98eb347cb Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 19 Apr 2021 13:46:53 +0200 Subject: list-objects: implement object type filter While it already is possible to filter objects by some criteria in git-rev-list(1), it is not yet possible to filter out only a specific type of objects. This makes some filters less useful. The `blob:limit` filter for example filters blobs such that only those which are smaller than the given limit are returned. But it is unfit to ask only for these smallish blobs, given that git-rev-list(1) will continue to print tags, commits and trees. Now that we have the infrastructure in place to also filter tags and commits, we can improve this situation by implementing a new filter which selects objects based on their type. Above query can thus trivially be implemented with the following command: $ git rev-list --objects --filter=object:type=blob \ --filter=blob:limit=200 Furthermore, this filter allows to optimize for certain other cases: if for example only tags or commits have been selected, there is no need to walk down trees. The new filter is not yet supported in bitmaps. This is going to be implemented in a subsequent commit. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt index 6729a07..32fad5b 100644 --- a/Documentation/config/uploadpack.txt +++ b/Documentation/config/uploadpack.txt @@ -66,9 +66,9 @@ uploadpackfilter.allow:: uploadpackfilter..allow:: Explicitly allow or ban the object filter corresponding to ``, where `` 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:` when `` is no more than the value of diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index b1c8f86..3afa8ff 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -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=' uses a sparse-checkout specification contained in the blob (or blob-expression) '' to omit blobs that would not be not required for a sparse checkout on diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index d2d1c81..96a605c 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -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=' 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); diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index 01767c3..da5b673 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -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 */ diff --git a/list-objects-filter.c b/list-objects-filter.c index 0ebfa52..1c1ee3d 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -545,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; @@ -691,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, }; diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index 31457d1..c810a62 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -159,6 +159,50 @@ test_expect_success 'verify blob:limit=1m' ' test_must_be_empty observed ' +# Test object: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 sparse:path= filter. # !!!! # NOTE: sparse:path filter support has been dropped for security reasons, -- cgit v0.10.2-6-g49f6 From 7ab6aafa582b9c537885d4a6ef2c837323c5014d Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 19 Apr 2021 13:46:58 +0200 Subject: pack-bitmap: implement object type filter The preceding commit has added a new object filter for git-rev-list(1) which allows to filter objects by type. Implement the equivalent filter for packfile bitmaps so that we can answer these queries fast. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/pack-bitmap.c b/pack-bitmap.c index b4513f8..cd3f5c4 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -779,9 +779,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, @@ -911,6 +908,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, @@ -943,6 +958,14 @@ 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; + } + /* filter choice not handled */ return -1; } diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh index 3f88994..fb66735 100755 --- a/t/t6113-rev-list-bitmap-filters.sh +++ b/t/t6113-rev-list-bitmap-filters.sh @@ -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,26 @@ 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_done -- cgit v0.10.2-6-g49f6 From 169a15ebd66dd23bf8c379ad33bced1578c848ef Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 19 Apr 2021 13:47:02 +0200 Subject: pack-bitmap: implement combined filter When the user has multiple objects filters specified, then this is internally represented by having a "combined" filter. These combined filters aren't yet supported by bitmap indices and can thus not be accelerated. Fix this by implementing support for these combined filters. The implementation is quite trivial: when there's a combined filter, we simply recurse into `filter_bitmap()` for all of the sub-filters. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/pack-bitmap.c b/pack-bitmap.c index cd3f5c4..7ce3ede 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -966,6 +966,16 @@ static int filter_bitmap(struct bitmap_index *bitmap_git, 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; } diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh index fb66735..cb9db7d 100755 --- a/t/t6113-rev-list-bitmap-filters.sh +++ b/t/t6113-rev-list-bitmap-filters.sh @@ -98,4 +98,11 @@ test_expect_success 'object:type filter' ' 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_done -- cgit v0.10.2-6-g49f6 From 9cf68b27d50c29a0370bd61581d350b0b0a18e85 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Mon, 19 Apr 2021 13:47:06 +0200 Subject: rev-list: allow filtering of provided items When providing an object filter, it is currently impossible to also filter provided items. E.g. when executing `git rev-list HEAD` , the commit this reference points to will be treated as user-provided and is thus excluded from the filtering mechanism. This makes it harder than necessary to properly use the new `--filter=object:type` filter given that even if the user wants to only see blobs, he'll still see commits of provided references. Improve this by introducing a new `--filter-provided-objects` option to the git-rev-parse(1) command. If given, then all user-provided references will be subject to filtering. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 3afa8ff..5bf2a85 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -933,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. diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 525c2d8..2f2026d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -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() && diff --git a/builtin/rev-list.c b/builtin/rev-list.c index b4d8ea0..7677b1a 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -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) diff --git a/pack-bitmap.c b/pack-bitmap.c index 7ce3ede..6b790a8 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -986,7 +986,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; @@ -1081,7 +1082,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; diff --git a/pack-bitmap.h b/pack-bitmap.h index 36d9993..bb45217 100644 --- a/pack-bitmap.h +++ b/pack-bitmap.h @@ -50,7 +50,8 @@ void traverse_bitmap_commit_list(struct bitmap_index *, show_reachable_fn show_reachable); void test_bitmap_walk(struct rev_info *revs); 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, diff --git a/reachable.c b/reachable.c index 77a60c7..fc833ca 100644 --- a/reachable.c +++ b/reachable.c @@ -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); diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index c810a62..4ade105 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -203,6 +203,34 @@ test_expect_success 'verify object:type=tag prints tag' ' 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= filter. # !!!! # NOTE: sparse:path filter support has been dropped for security reasons, diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh index cb9db7d..4d8e091 100755 --- a/t/t6113-rev-list-bitmap-filters.sh +++ b/t/t6113-rev-list-bitmap-filters.sh @@ -98,6 +98,28 @@ test_expect_success 'object:type filter' ' 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 \ @@ -105,4 +127,18 @@ test_expect_success 'combine filter' ' 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)" objects && + while read objecttype objectsize + do + test "$objecttype" = blob || return 1 + test "$objectsize" -le 1000 || return 1 + done