Skip to content

Commit 073312b

Browse files
committed
Merge branch 'js/filter-options-should-use-plain-int'
Update the protocol message specification to allow only the limited use of scaled quantities. This is ensure potential compatibility issues will not go out of hand. * js/filter-options-should-use-plain-int: filter-options: expand scaled numbers tree:<depth>: skip some trees even when collecting omits list-objects-filter: teach tree:# how to handle >0
2 parents b99a579 + 87c2d9d commit 073312b

11 files changed

+329
-44
lines changed

Documentation/rev-list-options.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,8 +730,13 @@ specification contained in <path>.
730730
+
731731
The form '--filter=tree:<depth>' omits all blobs and trees whose depth
732732
from the root tree is >= <depth> (minimum depth if an object is located
733-
at multiple depths in the commits traversed). Currently, only <depth>=0
734-
is supported, which omits all blobs and trees.
733+
at multiple depths in the commits traversed). <depth>=0 will not include
734+
any trees or blobs unless included explicitly in the command-line (or
735+
standard input when --stdin is used). <depth>=1 will include only the
736+
tree and blobs which are referenced directly by a commit reachable from
737+
<commit> or an explicitly-given object. <depth>=2 is like <depth>=1
738+
while also including trees and blobs one more level removed from an
739+
explicitly-given commit or tree.
735740

736741
--no-filter::
737742
Turn off any previous `--filter=` argument.

Documentation/technical/protocol-v2.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,13 @@ included in the client's request:
296296
Request that various objects from the packfile be omitted
297297
using one of several filtering techniques. These are intended
298298
for use with partial clone and partial fetch operations. See
299-
`rev-list` for possible "filter-spec" values.
299+
`rev-list` for possible "filter-spec" values. When communicating
300+
with other processes, senders SHOULD translate scaled integers
301+
(e.g. "1k") into a fully-expanded form (e.g. "1024") to aid
302+
interoperability with older receivers that may not understand
303+
newly-invented scaling suffixes. However, receivers SHOULD
304+
accept the following suffixes: 'k', 'm', and 'g' for 1024,
305+
1048576, and 1073741824, respectively.
300306

301307
If the 'ref-in-want' feature is advertised, the following argument can
302308
be included in the client's request as well as the potential addition of

builtin/clone.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,9 +1136,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11361136
option_upload_pack);
11371137

11381138
if (filter_options.choice) {
1139+
struct strbuf expanded_filter_spec = STRBUF_INIT;
1140+
expand_list_objects_filter_spec(&filter_options,
1141+
&expanded_filter_spec);
11391142
transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
1140-
filter_options.filter_spec);
1143+
expanded_filter_spec.buf);
11411144
transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
1145+
strbuf_release(&expanded_filter_spec);
11421146
}
11431147

11441148
if (transport->smart_options && !deepen && !filter_options.choice)

builtin/fetch.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
11651165
static struct transport *prepare_transport(struct remote *remote, int deepen)
11661166
{
11671167
struct transport *transport;
1168+
11681169
transport = transport_get(remote, NULL);
11691170
transport_set_verbosity(transport, verbosity, progress);
11701171
transport->family = family;
@@ -1184,9 +1185,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
11841185
if (update_shallow)
11851186
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
11861187
if (filter_options.choice) {
1188+
struct strbuf expanded_filter_spec = STRBUF_INIT;
1189+
expand_list_objects_filter_spec(&filter_options,
1190+
&expanded_filter_spec);
11871191
set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
1188-
filter_options.filter_spec);
1192+
expanded_filter_spec.buf);
11891193
set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
1194+
strbuf_release(&expanded_filter_spec);
11901195
}
11911196
if (negotiation_tip.nr) {
11921197
if (transport->smart_options)

fetch-pack.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,14 @@ static int find_common(struct fetch_negotiator *negotiator,
329329
packet_buf_write(&req_buf, "deepen-not %s", s->string);
330330
}
331331
}
332-
if (server_supports_filtering && args->filter_options.choice)
332+
if (server_supports_filtering && args->filter_options.choice) {
333+
struct strbuf expanded_filter_spec = STRBUF_INIT;
334+
expand_list_objects_filter_spec(&args->filter_options,
335+
&expanded_filter_spec);
333336
packet_buf_write(&req_buf, "filter %s",
334-
args->filter_options.filter_spec);
337+
expanded_filter_spec.buf);
338+
strbuf_release(&expanded_filter_spec);
339+
}
335340
packet_buf_flush(&req_buf);
336341
state_len = req_buf.len;
337342

@@ -1122,9 +1127,13 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
11221127
/* Add filter */
11231128
if (server_supports_feature("fetch", "filter", 0) &&
11241129
args->filter_options.choice) {
1130+
struct strbuf expanded_filter_spec = STRBUF_INIT;
11251131
print_verbose(args, _("Server supports filter"));
1132+
expand_list_objects_filter_spec(&args->filter_options,
1133+
&expanded_filter_spec);
11261134
packet_buf_write(&req_buf, "filter %s",
1127-
args->filter_options.filter_spec);
1135+
expanded_filter_spec.buf);
1136+
strbuf_release(&expanded_filter_spec);
11281137
} else if (args->filter_options.choice) {
11291138
warning("filtering not recognized by server, ignoring");
11301139
}

list-objects-filter-options.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
* See Documentation/rev-list-options.txt for allowed values for <arg>.
1919
*
2020
* Capture the given arg as the "filter_spec". This can be forwarded to
21-
* subordinate commands when necessary. We also "intern" the arg for
22-
* the convenience of the current command.
21+
* subordinate commands when necessary (although it's better to pass it through
22+
* expand_list_objects_filter_spec() first). We also "intern" the arg for the
23+
* convenience of the current command.
2324
*/
2425
static int gently_parse_list_objects_filter(
2526
struct list_objects_filter_options *filter_options,
@@ -50,16 +51,15 @@ static int gently_parse_list_objects_filter(
5051
}
5152

5253
} else if (skip_prefix(arg, "tree:", &v0)) {
53-
unsigned long depth;
54-
if (!git_parse_ulong(v0, &depth) || depth != 0) {
54+
if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) {
5555
if (errbuf) {
5656
strbuf_addstr(
5757
errbuf,
58-
_("only 'tree:0' is supported"));
58+
_("expected 'tree:<depth>'"));
5959
}
6060
return 1;
6161
}
62-
filter_options->choice = LOFC_TREE_NONE;
62+
filter_options->choice = LOFC_TREE_DEPTH;
6363
return 0;
6464

6565
} else if (skip_prefix(arg, "sparse:oid=", &v0)) {
@@ -112,6 +112,21 @@ int opt_parse_list_objects_filter(const struct option *opt,
112112
return parse_list_objects_filter(filter_options, arg);
113113
}
114114

115+
void expand_list_objects_filter_spec(
116+
const struct list_objects_filter_options *filter,
117+
struct strbuf *expanded_spec)
118+
{
119+
strbuf_init(expanded_spec, strlen(filter->filter_spec));
120+
if (filter->choice == LOFC_BLOB_LIMIT)
121+
strbuf_addf(expanded_spec, "blob:limit=%lu",
122+
filter->blob_limit_value);
123+
else if (filter->choice == LOFC_TREE_DEPTH)
124+
strbuf_addf(expanded_spec, "tree:%lu",
125+
filter->tree_exclude_depth);
126+
else
127+
strbuf_addstr(expanded_spec, filter->filter_spec);
128+
}
129+
115130
void list_objects_filter_release(
116131
struct list_objects_filter_options *filter_options)
117132
{

list-objects-filter-options.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define LIST_OBJECTS_FILTER_OPTIONS_H
33

44
#include "parse-options.h"
5+
#include "strbuf.h"
56

67
/*
78
* The list of defined filters for list-objects.
@@ -10,7 +11,7 @@ enum list_objects_filter_choice {
1011
LOFC_DISABLED = 0,
1112
LOFC_BLOB_NONE,
1213
LOFC_BLOB_LIMIT,
13-
LOFC_TREE_NONE,
14+
LOFC_TREE_DEPTH,
1415
LOFC_SPARSE_OID,
1516
LOFC_SPARSE_PATH,
1617
LOFC__COUNT /* must be last */
@@ -20,8 +21,9 @@ struct list_objects_filter_options {
2021
/*
2122
* 'filter_spec' is the raw argument value given on the command line
2223
* or protocol request. (The part after the "--keyword=".) For
23-
* commands that launch filtering sub-processes, this value should be
24-
* passed to them as received by the current process.
24+
* commands that launch filtering sub-processes, or for communication
25+
* over the network, don't use this value; use the result of
26+
* expand_list_objects_filter_spec() instead.
2527
*/
2628
char *filter_spec;
2729

@@ -44,6 +46,7 @@ struct list_objects_filter_options {
4446
struct object_id *sparse_oid_value;
4547
char *sparse_path_value;
4648
unsigned long blob_limit_value;
49+
unsigned long tree_exclude_depth;
4750
};
4851

4952
/* Normalized command line arguments */
@@ -61,6 +64,17 @@ int opt_parse_list_objects_filter(const struct option *opt,
6164
N_("object filtering"), 0, \
6265
opt_parse_list_objects_filter }
6366

67+
/*
68+
* Translates abbreviated numbers in the filter's filter_spec into their
69+
* fully-expanded forms (e.g., "limit:blob=1k" becomes "limit:blob=1024").
70+
*
71+
* This form should be used instead of the raw filter_spec field when
72+
* communicating with a remote process or subprocess.
73+
*/
74+
void expand_list_objects_filter_spec(
75+
const struct list_objects_filter_options *filter,
76+
struct strbuf *expanded_spec);
77+
6478
void list_objects_filter_release(
6579
struct list_objects_filter_options *filter_options);
6680

list-objects-filter.c

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "list-objects.h"
1111
#include "list-objects-filter.h"
1212
#include "list-objects-filter-options.h"
13+
#include "oidmap.h"
1314
#include "oidset.h"
1415
#include "object-store.h"
1516

@@ -84,55 +85,136 @@ static void *filter_blobs_none__init(
8485
* A filter for list-objects to omit ALL trees and blobs from the traversal.
8586
* Can OPTIONALLY collect a list of the omitted OIDs.
8687
*/
87-
struct filter_trees_none_data {
88+
struct filter_trees_depth_data {
8889
struct oidset *omits;
90+
91+
/*
92+
* Maps trees to the minimum depth at which they were seen. It is not
93+
* necessary to re-traverse a tree at deeper or equal depths than it has
94+
* already been traversed.
95+
*
96+
* We can't use LOFR_MARK_SEEN for tree objects since this will prevent
97+
* it from being traversed at shallower depths.
98+
*/
99+
struct oidmap seen_at_depth;
100+
101+
unsigned long exclude_depth;
102+
unsigned long current_depth;
89103
};
90104

91-
static enum list_objects_filter_result filter_trees_none(
105+
struct seen_map_entry {
106+
struct oidmap_entry base;
107+
size_t depth;
108+
};
109+
110+
/* Returns 1 if the oid was in the omits set before it was invoked. */
111+
static int filter_trees_update_omits(
112+
struct object *obj,
113+
struct filter_trees_depth_data *filter_data,
114+
int include_it)
115+
{
116+
if (!filter_data->omits)
117+
return 0;
118+
119+
if (include_it)
120+
return oidset_remove(filter_data->omits, &obj->oid);
121+
else
122+
return oidset_insert(filter_data->omits, &obj->oid);
123+
}
124+
125+
static enum list_objects_filter_result filter_trees_depth(
92126
struct repository *r,
93127
enum list_objects_filter_situation filter_situation,
94128
struct object *obj,
95129
const char *pathname,
96130
const char *filename,
97131
void *filter_data_)
98132
{
99-
struct filter_trees_none_data *filter_data = filter_data_;
133+
struct filter_trees_depth_data *filter_data = filter_data_;
134+
struct seen_map_entry *seen_info;
135+
int include_it = filter_data->current_depth <
136+
filter_data->exclude_depth;
137+
int filter_res;
138+
int already_seen;
139+
140+
/*
141+
* Note that we do not use _MARK_SEEN in order to allow re-traversal in
142+
* case we encounter a tree or blob again at a shallower depth.
143+
*/
100144

101145
switch (filter_situation) {
102146
default:
103147
BUG("unknown filter_situation: %d", filter_situation);
104148

105-
case LOFS_BEGIN_TREE:
149+
case LOFS_END_TREE:
150+
assert(obj->type == OBJ_TREE);
151+
filter_data->current_depth--;
152+
return LOFR_ZERO;
153+
106154
case LOFS_BLOB:
107-
if (filter_data->omits) {
108-
oidset_insert(filter_data->omits, &obj->oid);
109-
/* _MARK_SEEN but not _DO_SHOW (hard omit) */
110-
return LOFR_MARK_SEEN;
155+
filter_trees_update_omits(obj, filter_data, include_it);
156+
return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO;
157+
158+
case LOFS_BEGIN_TREE:
159+
seen_info = oidmap_get(
160+
&filter_data->seen_at_depth, &obj->oid);
161+
if (!seen_info) {
162+
seen_info = xcalloc(1, sizeof(*seen_info));
163+
oidcpy(&seen_info->base.oid, &obj->oid);
164+
seen_info->depth = filter_data->current_depth;
165+
oidmap_put(&filter_data->seen_at_depth, seen_info);
166+
already_seen = 0;
111167
} else {
112-
/*
113-
* Not collecting omits so no need to to traverse tree.
114-
*/
115-
return LOFR_SKIP_TREE | LOFR_MARK_SEEN;
168+
already_seen =
169+
filter_data->current_depth >= seen_info->depth;
116170
}
117171

118-
case LOFS_END_TREE:
119-
assert(obj->type == OBJ_TREE);
120-
return LOFR_ZERO;
172+
if (already_seen) {
173+
filter_res = LOFR_SKIP_TREE;
174+
} else {
175+
int been_omitted = filter_trees_update_omits(
176+
obj, filter_data, include_it);
177+
seen_info->depth = filter_data->current_depth;
178+
179+
if (include_it)
180+
filter_res = LOFR_DO_SHOW;
181+
else if (filter_data->omits && !been_omitted)
182+
/*
183+
* Must update omit information of children
184+
* recursively; they have not been omitted yet.
185+
*/
186+
filter_res = LOFR_ZERO;
187+
else
188+
filter_res = LOFR_SKIP_TREE;
189+
}
121190

191+
filter_data->current_depth++;
192+
return filter_res;
122193
}
123194
}
124195

125-
static void* filter_trees_none__init(
196+
static void filter_trees_free(void *filter_data) {
197+
struct filter_trees_depth_data *d = filter_data;
198+
if (!d)
199+
return;
200+
oidmap_free(&d->seen_at_depth, 1);
201+
free(d);
202+
}
203+
204+
static void *filter_trees_depth__init(
126205
struct oidset *omitted,
127206
struct list_objects_filter_options *filter_options,
128207
filter_object_fn *filter_fn,
129208
filter_free_fn *filter_free_fn)
130209
{
131-
struct filter_trees_none_data *d = xcalloc(1, sizeof(*d));
210+
struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d));
132211
d->omits = omitted;
212+
oidmap_init(&d->seen_at_depth, 0);
213+
d->exclude_depth = filter_options->tree_exclude_depth;
214+
d->current_depth = 0;
133215

134-
*filter_fn = filter_trees_none;
135-
*filter_free_fn = free;
216+
*filter_fn = filter_trees_depth;
217+
*filter_free_fn = filter_trees_free;
136218
return d;
137219
}
138220

@@ -430,7 +512,7 @@ static filter_init_fn s_filters[] = {
430512
NULL,
431513
filter_blobs_none__init,
432514
filter_blobs_limit__init,
433-
filter_trees_none__init,
515+
filter_trees_depth__init,
434516
filter_sparse_oid__init,
435517
filter_sparse_path__init,
436518
};

0 commit comments

Comments
 (0)