1 #define USE_THE_REPOSITORY_VARIABLE
3 #include "git-compat-util.h"
6 #include "list-objects-filter-options.h"
7 #include "promisor-remote.h"
10 #include "parse-options.h"
12 static int parse_combine_filter(
13 struct list_objects_filter_options
*filter_options
,
15 struct strbuf
*errbuf
);
17 const char *list_object_filter_config_name(enum list_objects_filter_choice c
)
21 /* we have no name for "no filter at all" */
31 case LOFC_OBJECT_TYPE
:
36 /* not a real filter type; just the count of all filters */
39 BUG("list_object_filter_config_name: invalid argument '%d'", c
);
42 int gently_parse_list_objects_filter(
43 struct list_objects_filter_options
*filter_options
,
45 struct strbuf
*errbuf
)
52 if (filter_options
->choice
)
53 BUG("filter_options already populated");
55 if (!strcmp(arg
, "blob:none")) {
56 filter_options
->choice
= LOFC_BLOB_NONE
;
59 } else if (skip_prefix(arg
, "blob:limit=", &v0
)) {
60 if (git_parse_ulong(v0
, &filter_options
->blob_limit_value
)) {
61 filter_options
->choice
= LOFC_BLOB_LIMIT
;
65 } else if (skip_prefix(arg
, "tree:", &v0
)) {
66 if (!git_parse_ulong(v0
, &filter_options
->tree_exclude_depth
)) {
67 strbuf_addstr(errbuf
, _("expected 'tree:<depth>'"));
70 filter_options
->choice
= LOFC_TREE_DEPTH
;
73 } else if (skip_prefix(arg
, "sparse:oid=", &v0
)) {
74 filter_options
->sparse_oid_name
= xstrdup(v0
);
75 filter_options
->choice
= LOFC_SPARSE_OID
;
78 } else if (skip_prefix(arg
, "sparse:path=", &v0
)) {
82 _("sparse:path filters support has been dropped"));
86 } else if (skip_prefix(arg
, "object:type=", &v0
)) {
87 int type
= type_from_string_gently(v0
, strlen(v0
), 1);
89 strbuf_addf(errbuf
, _("'%s' for 'object:type=<type>' is "
90 "not a valid object type"), v0
);
94 filter_options
->object_type
= type
;
95 filter_options
->choice
= LOFC_OBJECT_TYPE
;
99 } else if (skip_prefix(arg
, "combine:", &v0
)) {
100 return parse_combine_filter(filter_options
, v0
, errbuf
);
104 * Please update _git_fetch() in git-completion.bash when you
108 strbuf_addf(errbuf
, _("invalid filter-spec '%s'"), arg
);
110 list_objects_filter_init(filter_options
);
114 static const char *RESERVED_NON_WS
= "~`!@#$^&*()[]{}\\;'\",<>?";
116 static int has_reserved_character(
117 struct strbuf
*sub_spec
, struct strbuf
*errbuf
)
119 const char *c
= sub_spec
->buf
;
121 if (*c
<= ' ' || strchr(RESERVED_NON_WS
, *c
)) {
124 _("must escape char in sub-filter-spec: '%c'"),
134 static int parse_combine_subfilter(
135 struct list_objects_filter_options
*filter_options
,
136 struct strbuf
*subspec
,
137 struct strbuf
*errbuf
)
139 size_t new_index
= filter_options
->sub_nr
;
143 ALLOC_GROW_BY(filter_options
->sub
, filter_options
->sub_nr
, 1,
144 filter_options
->sub_alloc
);
145 list_objects_filter_init(&filter_options
->sub
[new_index
]);
147 decoded
= url_percent_decode(subspec
->buf
);
149 result
= has_reserved_character(subspec
, errbuf
) ||
150 gently_parse_list_objects_filter(
151 &filter_options
->sub
[new_index
], decoded
, errbuf
);
157 static int parse_combine_filter(
158 struct list_objects_filter_options
*filter_options
,
160 struct strbuf
*errbuf
)
162 struct strbuf
**subspecs
= strbuf_split_str(arg
, '+', 0);
167 strbuf_addstr(errbuf
, _("expected something after combine:"));
172 for (sub
= 0; subspecs
[sub
] && !result
; sub
++) {
173 if (subspecs
[sub
+ 1]) {
175 * This is not the last subspec. Remove trailing "+" so
178 size_t last
= subspecs
[sub
]->len
- 1;
179 assert(subspecs
[sub
]->buf
[last
] == '+');
180 strbuf_remove(subspecs
[sub
], last
, 1);
182 result
= parse_combine_subfilter(
183 filter_options
, subspecs
[sub
], errbuf
);
186 filter_options
->choice
= LOFC_COMBINE
;
189 strbuf_list_free(subspecs
);
191 list_objects_filter_release(filter_options
);
195 static int allow_unencoded(char ch
)
197 if (ch
<= ' ' || ch
== '%' || ch
== '+')
199 return !strchr(RESERVED_NON_WS
, ch
);
202 static void filter_spec_append_urlencode(
203 struct list_objects_filter_options
*filter
, const char *raw
)
205 size_t orig_len
= filter
->filter_spec
.len
;
206 strbuf_addstr_urlencode(&filter
->filter_spec
, raw
, allow_unencoded
);
207 trace_printf("Add to combine filter-spec: %s\n",
208 filter
->filter_spec
.buf
+ orig_len
);
212 * Changes filter_options into an equivalent LOFC_COMBINE filter options
213 * instance. Does not do anything if filter_options is already LOFC_COMBINE.
215 static void transform_to_combine_type(
216 struct list_objects_filter_options
*filter_options
)
218 assert(filter_options
->choice
);
219 if (filter_options
->choice
== LOFC_COMBINE
)
222 const int initial_sub_alloc
= 2;
223 struct list_objects_filter_options
*sub_array
=
224 xcalloc(initial_sub_alloc
, sizeof(*sub_array
));
225 sub_array
[0] = *filter_options
;
226 list_objects_filter_init(filter_options
);
227 filter_options
->sub
= sub_array
;
228 filter_options
->sub_alloc
= initial_sub_alloc
;
230 filter_options
->sub_nr
= 1;
231 filter_options
->choice
= LOFC_COMBINE
;
232 strbuf_addstr(&filter_options
->filter_spec
, "combine:");
233 filter_spec_append_urlencode(
235 list_objects_filter_spec(&filter_options
->sub
[0]));
237 * We don't need the filter_spec strings for subfilter specs, only the
240 strbuf_release(&filter_options
->sub
[0].filter_spec
);
243 void list_objects_filter_die_if_populated(
244 struct list_objects_filter_options
*filter_options
)
246 if (filter_options
->choice
)
247 die(_("multiple filter-specs cannot be combined"));
250 void parse_list_objects_filter(
251 struct list_objects_filter_options
*filter_options
,
254 struct strbuf errbuf
= STRBUF_INIT
;
256 if (!filter_options
->filter_spec
.buf
)
257 BUG("filter_options not properly initialized");
259 if (!filter_options
->choice
) {
260 if (gently_parse_list_objects_filter(filter_options
, arg
, &errbuf
))
261 die("%s", errbuf
.buf
);
262 strbuf_addstr(&filter_options
->filter_spec
, arg
);
264 struct list_objects_filter_options
*sub
;
267 * Make filter_options an LOFC_COMBINE spec so we can trivially
268 * add subspecs to it.
270 transform_to_combine_type(filter_options
);
272 ALLOC_GROW_BY(filter_options
->sub
, filter_options
->sub_nr
, 1,
273 filter_options
->sub_alloc
);
274 sub
= &filter_options
->sub
[filter_options
->sub_nr
- 1];
276 list_objects_filter_init(sub
);
277 if (gently_parse_list_objects_filter(sub
, arg
, &errbuf
))
278 die("%s", errbuf
.buf
);
280 strbuf_addch(&filter_options
->filter_spec
, '+');
281 filter_spec_append_urlencode(filter_options
, arg
);
285 int opt_parse_list_objects_filter(const struct option
*opt
,
286 const char *arg
, int unset
)
288 struct list_objects_filter_options
*filter_options
= opt
->value
;
291 list_objects_filter_set_no_filter(filter_options
);
293 parse_list_objects_filter(filter_options
, arg
);
297 const char *list_objects_filter_spec(struct list_objects_filter_options
*filter
)
299 if (!filter
->filter_spec
.len
)
300 BUG("no filter_spec available for this filter");
301 return filter
->filter_spec
.buf
;
304 const char *expand_list_objects_filter_spec(
305 struct list_objects_filter_options
*filter
)
307 if (filter
->choice
== LOFC_BLOB_LIMIT
) {
308 strbuf_release(&filter
->filter_spec
);
309 strbuf_addf(&filter
->filter_spec
, "blob:limit=%lu",
310 filter
->blob_limit_value
);
313 return list_objects_filter_spec(filter
);
316 void list_objects_filter_release(
317 struct list_objects_filter_options
*filter_options
)
323 strbuf_release(&filter_options
->filter_spec
);
324 free(filter_options
->sparse_oid_name
);
325 for (sub
= 0; sub
< filter_options
->sub_nr
; sub
++)
326 list_objects_filter_release(&filter_options
->sub
[sub
]);
327 free(filter_options
->sub
);
328 list_objects_filter_init(filter_options
);
331 void partial_clone_register(
333 struct list_objects_filter_options
*filter_options
)
335 struct promisor_remote
*promisor_remote
;
339 /* Check if it is already registered */
340 if ((promisor_remote
= repo_promisor_remote_find(the_repository
, remote
))) {
341 if (promisor_remote
->partial_clone_filter
)
343 * Remote is already registered and a filter is already
344 * set, so we don't need to do anything here.
348 if (upgrade_repository_format(1) < 0)
349 die(_("unable to upgrade repository format to support partial clone"));
351 /* Add promisor config for the remote */
352 cfg_name
= xstrfmt("remote.%s.promisor", remote
);
353 git_config_set(cfg_name
, "true");
358 * Record the initial filter-spec in the config as
359 * the default for subsequent fetches from this remote.
361 filter_name
= xstrfmt("remote.%s.partialclonefilter", remote
);
362 /* NEEDSWORK: 'expand' result leaking??? */
363 git_config_set(filter_name
,
364 expand_list_objects_filter_spec(filter_options
));
367 /* Make sure the config info are reset */
368 repo_promisor_remote_reinit(the_repository
);
371 void partial_clone_get_default_filter_spec(
372 struct list_objects_filter_options
*filter_options
,
375 struct promisor_remote
*promisor
= repo_promisor_remote_find(the_repository
,
377 struct strbuf errbuf
= STRBUF_INIT
;
380 * Parse default value, but silently ignore it if it is invalid.
382 if (!promisor
|| !promisor
->partial_clone_filter
)
385 strbuf_addstr(&filter_options
->filter_spec
,
386 promisor
->partial_clone_filter
);
387 gently_parse_list_objects_filter(filter_options
,
388 promisor
->partial_clone_filter
,
390 strbuf_release(&errbuf
);
393 void list_objects_filter_copy(
394 struct list_objects_filter_options
*dest
,
395 const struct list_objects_filter_options
*src
)
399 /* Copy everything. We will overwrite the pointers shortly. */
400 memcpy(dest
, src
, sizeof(struct list_objects_filter_options
));
402 strbuf_init(&dest
->filter_spec
, 0);
403 strbuf_addbuf(&dest
->filter_spec
, &src
->filter_spec
);
404 dest
->sparse_oid_name
= xstrdup_or_null(src
->sparse_oid_name
);
406 ALLOC_ARRAY(dest
->sub
, dest
->sub_alloc
);
407 for (i
= 0; i
< src
->sub_nr
; i
++)
408 list_objects_filter_copy(&dest
->sub
[i
], &src
->sub
[i
]);
411 void list_objects_filter_init(struct list_objects_filter_options
*filter_options
)
413 struct list_objects_filter_options blank
= LIST_OBJECTS_FILTER_INIT
;
414 memcpy(filter_options
, &blank
, sizeof(*filter_options
));