Skip to content

Commit b0c42a5

Browse files
pks-tgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9a2a4f9 commit b0c42a5

File tree

6 files changed

+144
-3
lines changed

6 files changed

+144
-3
lines changed

Documentation/config/uploadpack.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ uploadpackfilter.allow::
6666
uploadpackfilter.<filter>.allow::
6767
Explicitly allow or ban the object filter corresponding to
6868
`<filter>`, where `<filter>` may be one of: `blob:none`,
69-
`blob:limit`, `tree`, `sparse:oid`, or `combine`. If using
70-
combined filters, both `combine` and all of the nested filter
71-
kinds must be allowed. Defaults to `uploadpackfilter.allow`.
69+
`blob:limit`, `object:type`, `tree`, `sparse:oid`, or `combine`.
70+
If using combined filters, both `combine` and all of the nested
71+
filter kinds must be allowed. Defaults to `uploadpackfilter.allow`.
7272

7373
uploadpackfilter.tree.maxDepth::
7474
Only allow `--filter=tree:<n>` when `<n>` is no more than the value of

Documentation/rev-list-options.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,9 @@ or units. n may be zero. The suffixes k, m, and g can be used to name
892892
units in KiB, MiB, or GiB. For example, 'blob:limit=1k' is the same
893893
as 'blob:limit=1024'.
894894
+
895+
The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
896+
which are not of the requested type.
897+
+
895898
The form '--filter=sparse:oid=<blob-ish>' uses a sparse-checkout
896899
specification contained in the blob (or blob-expression) '<blob-ish>'
897900
to omit blobs that would not be not required for a sparse checkout on

list-objects-filter-options.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ const char *list_object_filter_config_name(enum list_objects_filter_choice c)
2929
return "tree";
3030
case LOFC_SPARSE_OID:
3131
return "sparse:oid";
32+
case LOFC_OBJECT_TYPE:
33+
return "object:type";
3234
case LOFC_COMBINE:
3335
return "combine";
3436
case LOFC__COUNT:
@@ -97,6 +99,19 @@ static int gently_parse_list_objects_filter(
9799
}
98100
return 1;
99101

102+
} else if (skip_prefix(arg, "object:type=", &v0)) {
103+
int type = type_from_string_gently(v0, strlen(v0), 1);
104+
if (type < 0) {
105+
strbuf_addf(errbuf, _("'%s' for 'object:type=<type>' is"
106+
"not a valid object type"), v0);
107+
return 1;
108+
}
109+
110+
filter_options->object_type = type;
111+
filter_options->choice = LOFC_OBJECT_TYPE;
112+
113+
return 0;
114+
100115
} else if (skip_prefix(arg, "combine:", &v0)) {
101116
return parse_combine_filter(filter_options, v0, errbuf);
102117

list-objects-filter-options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef LIST_OBJECTS_FILTER_OPTIONS_H
22
#define LIST_OBJECTS_FILTER_OPTIONS_H
33

4+
#include "cache.h"
45
#include "parse-options.h"
56
#include "string-list.h"
67

@@ -13,6 +14,7 @@ enum list_objects_filter_choice {
1314
LOFC_BLOB_LIMIT,
1415
LOFC_TREE_DEPTH,
1516
LOFC_SPARSE_OID,
17+
LOFC_OBJECT_TYPE,
1618
LOFC_COMBINE,
1719
LOFC__COUNT /* must be last */
1820
};
@@ -54,6 +56,7 @@ struct list_objects_filter_options {
5456
char *sparse_oid_name;
5557
unsigned long blob_limit_value;
5658
unsigned long tree_exclude_depth;
59+
enum object_type object_type;
5760

5861
/* LOFC_COMBINE values */
5962

list-objects-filter.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,81 @@ static void filter_sparse_oid__init(
545545
filter->free_fn = filter_sparse_free;
546546
}
547547

548+
/*
549+
* A filter for list-objects to omit large blobs.
550+
* And to OPTIONALLY collect a list of the omitted OIDs.
551+
*/
552+
struct filter_object_type_data {
553+
enum object_type object_type;
554+
};
555+
556+
static enum list_objects_filter_result filter_object_type(
557+
struct repository *r,
558+
enum list_objects_filter_situation filter_situation,
559+
struct object *obj,
560+
const char *pathname,
561+
const char *filename,
562+
struct oidset *omits,
563+
void *filter_data_)
564+
{
565+
struct filter_object_type_data *filter_data = filter_data_;
566+
567+
switch (filter_situation) {
568+
default:
569+
BUG("unknown filter_situation: %d", filter_situation);
570+
571+
case LOFS_TAG:
572+
assert(obj->type == OBJ_TAG);
573+
if (filter_data->object_type == OBJ_TAG)
574+
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
575+
return LOFR_MARK_SEEN;
576+
577+
case LOFS_COMMIT:
578+
assert(obj->type == OBJ_COMMIT);
579+
if (filter_data->object_type == OBJ_COMMIT)
580+
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
581+
return LOFR_MARK_SEEN;
582+
583+
case LOFS_BEGIN_TREE:
584+
assert(obj->type == OBJ_TREE);
585+
586+
/*
587+
* If we only want to show commits or tags, then there is no
588+
* need to walk down trees.
589+
*/
590+
if (filter_data->object_type == OBJ_COMMIT ||
591+
filter_data->object_type == OBJ_TAG)
592+
return LOFR_SKIP_TREE;
593+
594+
if (filter_data->object_type == OBJ_TREE)
595+
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
596+
597+
return LOFR_MARK_SEEN;
598+
599+
case LOFS_BLOB:
600+
assert(obj->type == OBJ_BLOB);
601+
602+
if (filter_data->object_type == OBJ_BLOB)
603+
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
604+
return LOFR_MARK_SEEN;
605+
606+
case LOFS_END_TREE:
607+
return LOFR_ZERO;
608+
}
609+
}
610+
611+
static void filter_object_type__init(
612+
struct list_objects_filter_options *filter_options,
613+
struct filter *filter)
614+
{
615+
struct filter_object_type_data *d = xcalloc(1, sizeof(*d));
616+
d->object_type = filter_options->object_type;
617+
618+
filter->filter_data = d;
619+
filter->filter_object_fn = filter_object_type;
620+
filter->free_fn = free;
621+
}
622+
548623
/* A filter which only shows objects shown by all sub-filters. */
549624
struct combine_filter_data {
550625
struct subfilter *sub;
@@ -691,6 +766,7 @@ static filter_init_fn s_filters[] = {
691766
filter_blobs_limit__init,
692767
filter_trees_depth__init,
693768
filter_sparse_oid__init,
769+
filter_object_type__init,
694770
filter_combine__init,
695771
};
696772

t/t6112-rev-list-filters-objects.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,50 @@ test_expect_success 'verify blob:limit=1m' '
159159
test_must_be_empty observed
160160
'
161161

162+
# Test object:type=<type> filter.
163+
164+
test_expect_success 'setup object-type' '
165+
test_create_repo object-type &&
166+
test_commit --no-tag -C object-type message blob &&
167+
git -C object-type tag tag -m tag-message
168+
'
169+
170+
test_expect_success 'verify object:type= fails with invalid type' '
171+
test_must_fail git -C object-type rev-list --objects --filter=object:type= HEAD &&
172+
test_must_fail git -C object-type rev-list --objects --filter=object:type=invalid HEAD
173+
'
174+
175+
test_expect_success 'verify object:type=blob prints blob and commit' '
176+
git -C object-type rev-parse HEAD >expected &&
177+
printf "%s blob\n" $(git -C object-type rev-parse HEAD:blob) >>expected &&
178+
git -C object-type rev-list --objects --filter=object:type=blob HEAD >actual &&
179+
test_cmp expected actual
180+
'
181+
182+
test_expect_success 'verify object:type=tree prints tree and commit' '
183+
(
184+
git -C object-type rev-parse HEAD &&
185+
printf "%s \n" $(git -C object-type rev-parse HEAD^{tree})
186+
) >expected &&
187+
git -C object-type rev-list --objects --filter=object:type=tree HEAD >actual &&
188+
test_cmp expected actual
189+
'
190+
191+
test_expect_success 'verify object:type=commit prints commit' '
192+
git -C object-type rev-parse HEAD >expected &&
193+
git -C object-type rev-list --objects --filter=object:type=commit HEAD >actual &&
194+
test_cmp expected actual
195+
'
196+
197+
test_expect_success 'verify object:type=tag prints tag' '
198+
(
199+
git -C object-type rev-parse HEAD &&
200+
printf "%s tag\n" $(git -C object-type rev-parse tag)
201+
) >expected &&
202+
git -C object-type rev-list --objects --filter=object:type=tag tag >actual &&
203+
test_cmp expected actual
204+
'
205+
162206
# Test sparse:path=<path> filter.
163207
# !!!!
164208
# NOTE: sparse:path filter support has been dropped for security reasons,

0 commit comments

Comments
 (0)