Git fork
at reftables-rust 413 lines 11 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2 3#include "git-compat-util.h" 4#include "config.h" 5#include "gettext.h" 6#include "list-objects-filter-options.h" 7#include "promisor-remote.h" 8#include "trace.h" 9#include "url.h" 10#include "parse-options.h" 11 12static int parse_combine_filter( 13 struct list_objects_filter_options *filter_options, 14 const char *arg, 15 struct strbuf *errbuf); 16 17const char *list_object_filter_config_name(enum list_objects_filter_choice c) 18{ 19 switch (c) { 20 case LOFC_DISABLED: 21 /* we have no name for "no filter at all" */ 22 break; 23 case LOFC_BLOB_NONE: 24 return "blob:none"; 25 case LOFC_BLOB_LIMIT: 26 return "blob:limit"; 27 case LOFC_TREE_DEPTH: 28 return "tree"; 29 case LOFC_SPARSE_OID: 30 return "sparse:oid"; 31 case LOFC_OBJECT_TYPE: 32 return "object:type"; 33 case LOFC_COMBINE: 34 return "combine"; 35 case LOFC__COUNT: 36 /* not a real filter type; just the count of all filters */ 37 break; 38 } 39 BUG("list_object_filter_config_name: invalid argument '%d'", c); 40} 41 42int gently_parse_list_objects_filter( 43 struct list_objects_filter_options *filter_options, 44 const char *arg, 45 struct strbuf *errbuf) 46{ 47 const char *v0; 48 49 if (!arg) 50 return 0; 51 52 if (filter_options->choice) 53 BUG("filter_options already populated"); 54 55 if (!strcmp(arg, "blob:none")) { 56 filter_options->choice = LOFC_BLOB_NONE; 57 return 0; 58 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; 62 return 0; 63 } 64 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>'")); 68 return 1; 69 } 70 filter_options->choice = LOFC_TREE_DEPTH; 71 return 0; 72 73 } else if (skip_prefix(arg, "sparse:oid=", &v0)) { 74 filter_options->sparse_oid_name = xstrdup(v0); 75 filter_options->choice = LOFC_SPARSE_OID; 76 return 0; 77 78 } else if (skip_prefix(arg, "sparse:path=", &v0)) { 79 if (errbuf) { 80 strbuf_addstr( 81 errbuf, 82 _("sparse:path filters support has been dropped")); 83 } 84 return 1; 85 86 } else if (skip_prefix(arg, "object:type=", &v0)) { 87 int type = type_from_string_gently(v0, strlen(v0), 1); 88 if (type < 0) { 89 strbuf_addf(errbuf, _("'%s' for 'object:type=<type>' is " 90 "not a valid object type"), v0); 91 return 1; 92 } 93 94 filter_options->object_type = type; 95 filter_options->choice = LOFC_OBJECT_TYPE; 96 97 return 0; 98 99 } else if (skip_prefix(arg, "combine:", &v0)) { 100 return parse_combine_filter(filter_options, v0, errbuf); 101 102 } 103 /* 104 * Please update _git_fetch() in git-completion.bash when you 105 * add new filters 106 */ 107 108 strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg); 109 110 list_objects_filter_init(filter_options); 111 return 1; 112} 113 114static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?"; 115 116static int has_reserved_character( 117 struct strbuf *sub_spec, struct strbuf *errbuf) 118{ 119 const char *c = sub_spec->buf; 120 while (*c) { 121 if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) { 122 strbuf_addf( 123 errbuf, 124 _("must escape char in sub-filter-spec: '%c'"), 125 *c); 126 return 1; 127 } 128 c++; 129 } 130 131 return 0; 132} 133 134static int parse_combine_subfilter( 135 struct list_objects_filter_options *filter_options, 136 struct strbuf *subspec, 137 struct strbuf *errbuf) 138{ 139 size_t new_index = filter_options->sub_nr; 140 char *decoded; 141 int result; 142 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]); 146 147 decoded = url_percent_decode(subspec->buf); 148 149 result = has_reserved_character(subspec, errbuf) || 150 gently_parse_list_objects_filter( 151 &filter_options->sub[new_index], decoded, errbuf); 152 153 free(decoded); 154 return result; 155} 156 157static int parse_combine_filter( 158 struct list_objects_filter_options *filter_options, 159 const char *arg, 160 struct strbuf *errbuf) 161{ 162 struct strbuf **subspecs = strbuf_split_str(arg, '+', 0); 163 size_t sub; 164 int result = 0; 165 166 if (!subspecs[0]) { 167 strbuf_addstr(errbuf, _("expected something after combine:")); 168 result = 1; 169 goto cleanup; 170 } 171 172 for (sub = 0; subspecs[sub] && !result; sub++) { 173 if (subspecs[sub + 1]) { 174 /* 175 * This is not the last subspec. Remove trailing "+" so 176 * we can parse it. 177 */ 178 size_t last = subspecs[sub]->len - 1; 179 assert(subspecs[sub]->buf[last] == '+'); 180 strbuf_remove(subspecs[sub], last, 1); 181 } 182 result = parse_combine_subfilter( 183 filter_options, subspecs[sub], errbuf); 184 } 185 186 filter_options->choice = LOFC_COMBINE; 187 188cleanup: 189 strbuf_list_free(subspecs); 190 if (result) 191 list_objects_filter_release(filter_options); 192 return result; 193} 194 195static int allow_unencoded(char ch) 196{ 197 if (ch <= ' ' || ch == '%' || ch == '+') 198 return 0; 199 return !strchr(RESERVED_NON_WS, ch); 200} 201 202static void filter_spec_append_urlencode( 203 struct list_objects_filter_options *filter, const char *raw) 204{ 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); 209} 210 211/* 212 * Changes filter_options into an equivalent LOFC_COMBINE filter options 213 * instance. Does not do anything if filter_options is already LOFC_COMBINE. 214 */ 215static void transform_to_combine_type( 216 struct list_objects_filter_options *filter_options) 217{ 218 assert(filter_options->choice); 219 if (filter_options->choice == LOFC_COMBINE) 220 return; 221 { 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; 229 } 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( 234 filter_options, 235 list_objects_filter_spec(&filter_options->sub[0])); 236 /* 237 * We don't need the filter_spec strings for subfilter specs, only the 238 * top level. 239 */ 240 strbuf_release(&filter_options->sub[0].filter_spec); 241} 242 243void list_objects_filter_die_if_populated( 244 struct list_objects_filter_options *filter_options) 245{ 246 if (filter_options->choice) 247 die(_("multiple filter-specs cannot be combined")); 248} 249 250void parse_list_objects_filter( 251 struct list_objects_filter_options *filter_options, 252 const char *arg) 253{ 254 struct strbuf errbuf = STRBUF_INIT; 255 256 if (!filter_options->filter_spec.buf) 257 BUG("filter_options not properly initialized"); 258 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); 263 } else { 264 struct list_objects_filter_options *sub; 265 266 /* 267 * Make filter_options an LOFC_COMBINE spec so we can trivially 268 * add subspecs to it. 269 */ 270 transform_to_combine_type(filter_options); 271 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]; 275 276 list_objects_filter_init(sub); 277 if (gently_parse_list_objects_filter(sub, arg, &errbuf)) 278 die("%s", errbuf.buf); 279 280 strbuf_addch(&filter_options->filter_spec, '+'); 281 filter_spec_append_urlencode(filter_options, arg); 282 } 283} 284 285int opt_parse_list_objects_filter(const struct option *opt, 286 const char *arg, int unset) 287{ 288 struct list_objects_filter_options *filter_options = opt->value; 289 290 if (unset || !arg) 291 list_objects_filter_set_no_filter(filter_options); 292 else 293 parse_list_objects_filter(filter_options, arg); 294 return 0; 295} 296 297const char *list_objects_filter_spec(struct list_objects_filter_options *filter) 298{ 299 if (!filter->filter_spec.len) 300 BUG("no filter_spec available for this filter"); 301 return filter->filter_spec.buf; 302} 303 304const char *expand_list_objects_filter_spec( 305 struct list_objects_filter_options *filter) 306{ 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); 311 } 312 313 return list_objects_filter_spec(filter); 314} 315 316void list_objects_filter_release( 317 struct list_objects_filter_options *filter_options) 318{ 319 size_t sub; 320 321 if (!filter_options) 322 return; 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); 329} 330 331void partial_clone_register( 332 const char *remote, 333 struct list_objects_filter_options *filter_options) 334{ 335 struct promisor_remote *promisor_remote; 336 char *cfg_name; 337 char *filter_name; 338 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) 342 /* 343 * Remote is already registered and a filter is already 344 * set, so we don't need to do anything here. 345 */ 346 return; 347 } else { 348 if (upgrade_repository_format(1) < 0) 349 die(_("unable to upgrade repository format to support partial clone")); 350 351 /* Add promisor config for the remote */ 352 cfg_name = xstrfmt("remote.%s.promisor", remote); 353 repo_config_set(the_repository, cfg_name, "true"); 354 free(cfg_name); 355 } 356 357 /* 358 * Record the initial filter-spec in the config as 359 * the default for subsequent fetches from this remote. 360 */ 361 filter_name = xstrfmt("remote.%s.partialclonefilter", remote); 362 /* NEEDSWORK: 'expand' result leaking??? */ 363 repo_config_set(the_repository, filter_name, 364 expand_list_objects_filter_spec(filter_options)); 365 free(filter_name); 366 367 /* Make sure the config info are reset */ 368 repo_promisor_remote_reinit(the_repository); 369} 370 371void partial_clone_get_default_filter_spec( 372 struct list_objects_filter_options *filter_options, 373 const char *remote) 374{ 375 struct promisor_remote *promisor = repo_promisor_remote_find(the_repository, 376 remote); 377 struct strbuf errbuf = STRBUF_INIT; 378 379 /* 380 * Parse default value, but silently ignore it if it is invalid. 381 */ 382 if (!promisor || !promisor->partial_clone_filter) 383 return; 384 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, 389 &errbuf); 390 strbuf_release(&errbuf); 391} 392 393void list_objects_filter_copy( 394 struct list_objects_filter_options *dest, 395 const struct list_objects_filter_options *src) 396{ 397 /* Copy everything. We will overwrite the pointers shortly. */ 398 memcpy(dest, src, sizeof(struct list_objects_filter_options)); 399 400 strbuf_init(&dest->filter_spec, 0); 401 strbuf_addbuf(&dest->filter_spec, &src->filter_spec); 402 dest->sparse_oid_name = xstrdup_or_null(src->sparse_oid_name); 403 404 ALLOC_ARRAY(dest->sub, dest->sub_alloc); 405 for (size_t i = 0; i < src->sub_nr; i++) 406 list_objects_filter_copy(&dest->sub[i], &src->sub[i]); 407} 408 409void list_objects_filter_init(struct list_objects_filter_options *filter_options) 410{ 411 struct list_objects_filter_options blank = LIST_OBJECTS_FILTER_INIT; 412 memcpy(filter_options, &blank, sizeof(*filter_options)); 413}