1#include"cache.h" 2#include"dir.h" 3#include"tag.h" 4#include"commit.h" 5#include"tree.h" 6#include"blob.h" 7#include"diff.h" 8#include"tree-walk.h" 9#include"revision.h" 10#include"list-objects.h" 11#include"list-objects-filter.h" 12#include"list-objects-filter-options.h" 13#include"oidmap.h" 14#include"oidset.h" 15#include"object-store.h" 16 17/* Remember to update object flag allocation in object.h */ 18/* 19 * FILTER_SHOWN_BUT_REVISIT -- we set this bit on tree objects 20 * that have been shown, but should be revisited if they appear 21 * in the traversal (until we mark it SEEN). This is a way to 22 * let us silently de-dup calls to show() in the caller. This 23 * is subtly different from the "revision.h:SHOWN" and the 24 * "sha1-name.c:ONELINE_SEEN" bits. And also different from 25 * the non-de-dup usage in pack-bitmap.c 26 */ 27#define FILTER_SHOWN_BUT_REVISIT (1<<21) 28 29/* 30 * A filter for list-objects to omit ALL blobs from the traversal. 31 * And to OPTIONALLY collect a list of the omitted OIDs. 32 */ 33struct filter_blobs_none_data { 34struct oidset *omits; 35}; 36 37static enum list_objects_filter_result filter_blobs_none( 38struct repository *r, 39enum list_objects_filter_situation filter_situation, 40struct object *obj, 41const char*pathname, 42const char*filename, 43void*filter_data_) 44{ 45struct filter_blobs_none_data *filter_data = filter_data_; 46 47switch(filter_situation) { 48default: 49BUG("unknown filter_situation:%d", filter_situation); 50 51case LOFS_BEGIN_TREE: 52assert(obj->type == OBJ_TREE); 53/* always include all tree objects */ 54return LOFR_MARK_SEEN | LOFR_DO_SHOW; 55 56case LOFS_END_TREE: 57assert(obj->type == OBJ_TREE); 58return LOFR_ZERO; 59 60case LOFS_BLOB: 61assert(obj->type == OBJ_BLOB); 62assert((obj->flags & SEEN) ==0); 63 64if(filter_data->omits) 65oidset_insert(filter_data->omits, &obj->oid); 66return LOFR_MARK_SEEN;/* but not LOFR_DO_SHOW (hard omit) */ 67} 68} 69 70static void*filter_blobs_none__init( 71struct oidset *omitted, 72struct list_objects_filter_options *filter_options, 73 filter_object_fn *filter_fn, 74 filter_free_fn *filter_free_fn) 75{ 76struct filter_blobs_none_data *d =xcalloc(1,sizeof(*d)); 77 d->omits = omitted; 78 79*filter_fn = filter_blobs_none; 80*filter_free_fn = free; 81return d; 82} 83 84/* 85 * A filter for list-objects to omit ALL trees and blobs from the traversal. 86 * Can OPTIONALLY collect a list of the omitted OIDs. 87 */ 88struct filter_trees_depth_data { 89struct 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 */ 99struct oidmap seen_at_depth; 100 101unsigned long exclude_depth; 102unsigned long current_depth; 103}; 104 105struct seen_map_entry { 106struct oidmap_entry base; 107size_t depth; 108}; 109 110/* Returns 1 if the oid was in the omits set before it was invoked. */ 111static intfilter_trees_update_omits( 112struct object *obj, 113struct filter_trees_depth_data *filter_data, 114int include_it) 115{ 116if(!filter_data->omits) 117return0; 118 119if(include_it) 120returnoidset_remove(filter_data->omits, &obj->oid); 121else 122returnoidset_insert(filter_data->omits, &obj->oid); 123} 124 125static enum list_objects_filter_result filter_trees_depth( 126struct repository *r, 127enum list_objects_filter_situation filter_situation, 128struct object *obj, 129const char*pathname, 130const char*filename, 131void*filter_data_) 132{ 133struct filter_trees_depth_data *filter_data = filter_data_; 134struct seen_map_entry *seen_info; 135int include_it = filter_data->current_depth < 136 filter_data->exclude_depth; 137int filter_res; 138int 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 */ 144 145switch(filter_situation) { 146default: 147BUG("unknown filter_situation:%d", filter_situation); 148 149case LOFS_END_TREE: 150assert(obj->type == OBJ_TREE); 151 filter_data->current_depth--; 152return LOFR_ZERO; 153 154case LOFS_BLOB: 155filter_trees_update_omits(obj, filter_data, include_it); 156return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO; 157 158case LOFS_BEGIN_TREE: 159 seen_info =oidmap_get( 160&filter_data->seen_at_depth, &obj->oid); 161if(!seen_info) { 162 seen_info =xcalloc(1,sizeof(*seen_info)); 163oidcpy(&seen_info->base.oid, &obj->oid); 164 seen_info->depth = filter_data->current_depth; 165oidmap_put(&filter_data->seen_at_depth, seen_info); 166 already_seen =0; 167}else{ 168 already_seen = 169 filter_data->current_depth >= seen_info->depth; 170} 171 172if(already_seen) { 173 filter_res = LOFR_SKIP_TREE; 174}else{ 175int been_omitted =filter_trees_update_omits( 176 obj, filter_data, include_it); 177 seen_info->depth = filter_data->current_depth; 178 179if(include_it) 180 filter_res = LOFR_DO_SHOW; 181else 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; 187else 188 filter_res = LOFR_SKIP_TREE; 189} 190 191 filter_data->current_depth++; 192return filter_res; 193} 194} 195 196static voidfilter_trees_free(void*filter_data) { 197struct filter_trees_depth_data *d = filter_data; 198if(!d) 199return; 200oidmap_free(&d->seen_at_depth,1); 201free(d); 202} 203 204static void*filter_trees_depth__init( 205struct oidset *omitted, 206struct list_objects_filter_options *filter_options, 207 filter_object_fn *filter_fn, 208 filter_free_fn *filter_free_fn) 209{ 210struct filter_trees_depth_data *d =xcalloc(1,sizeof(*d)); 211 d->omits = omitted; 212oidmap_init(&d->seen_at_depth,0); 213 d->exclude_depth = filter_options->tree_exclude_depth; 214 d->current_depth =0; 215 216*filter_fn = filter_trees_depth; 217*filter_free_fn = filter_trees_free; 218return d; 219} 220 221/* 222 * A filter for list-objects to omit large blobs. 223 * And to OPTIONALLY collect a list of the omitted OIDs. 224 */ 225struct filter_blobs_limit_data { 226struct oidset *omits; 227unsigned long max_bytes; 228}; 229 230static enum list_objects_filter_result filter_blobs_limit( 231struct repository *r, 232enum list_objects_filter_situation filter_situation, 233struct object *obj, 234const char*pathname, 235const char*filename, 236void*filter_data_) 237{ 238struct filter_blobs_limit_data *filter_data = filter_data_; 239unsigned long object_length; 240enum object_type t; 241 242switch(filter_situation) { 243default: 244BUG("unknown filter_situation:%d", filter_situation); 245 246case LOFS_BEGIN_TREE: 247assert(obj->type == OBJ_TREE); 248/* always include all tree objects */ 249return LOFR_MARK_SEEN | LOFR_DO_SHOW; 250 251case LOFS_END_TREE: 252assert(obj->type == OBJ_TREE); 253return LOFR_ZERO; 254 255case LOFS_BLOB: 256assert(obj->type == OBJ_BLOB); 257assert((obj->flags & SEEN) ==0); 258 259 t =oid_object_info(r, &obj->oid, &object_length); 260if(t != OBJ_BLOB) {/* probably OBJ_NONE */ 261/* 262 * We DO NOT have the blob locally, so we cannot 263 * apply the size filter criteria. Be conservative 264 * and force show it (and let the caller deal with 265 * the ambiguity). 266 */ 267goto include_it; 268} 269 270if(object_length < filter_data->max_bytes) 271goto include_it; 272 273if(filter_data->omits) 274oidset_insert(filter_data->omits, &obj->oid); 275return LOFR_MARK_SEEN;/* but not LOFR_DO_SHOW (hard omit) */ 276} 277 278include_it: 279if(filter_data->omits) 280oidset_remove(filter_data->omits, &obj->oid); 281return LOFR_MARK_SEEN | LOFR_DO_SHOW; 282} 283 284static void*filter_blobs_limit__init( 285struct oidset *omitted, 286struct list_objects_filter_options *filter_options, 287 filter_object_fn *filter_fn, 288 filter_free_fn *filter_free_fn) 289{ 290struct filter_blobs_limit_data *d =xcalloc(1,sizeof(*d)); 291 d->omits = omitted; 292 d->max_bytes = filter_options->blob_limit_value; 293 294*filter_fn = filter_blobs_limit; 295*filter_free_fn = free; 296return d; 297} 298 299/* 300 * A filter driven by a sparse-checkout specification to only 301 * include blobs that a sparse checkout would populate. 302 * 303 * The sparse-checkout spec can be loaded from a blob with the 304 * given OID or from a local pathname. We allow an OID because 305 * the repo may be bare or we may be doing the filtering on the 306 * server. 307 */ 308struct frame { 309/* 310 * defval is the usual default include/exclude value that 311 * should be inherited as we recurse into directories based 312 * upon pattern matching of the directory itself or of a 313 * containing directory. 314 */ 315int defval; 316 317/* 318 * 1 if the directory (recursively) contains any provisionally 319 * omitted objects. 320 * 321 * 0 if everything (recursively) contained in this directory 322 * has been explicitly included (SHOWN) in the result and 323 * the directory may be short-cut later in the traversal. 324 */ 325unsigned child_prov_omit :1; 326}; 327 328struct filter_sparse_data { 329struct oidset *omits; 330struct exclude_list el; 331 332size_t nr, alloc; 333struct frame *array_frame; 334}; 335 336static enum list_objects_filter_result filter_sparse( 337struct repository *r, 338enum list_objects_filter_situation filter_situation, 339struct object *obj, 340const char*pathname, 341const char*filename, 342void*filter_data_) 343{ 344struct filter_sparse_data *filter_data = filter_data_; 345int val, dtype; 346struct frame *frame; 347 348switch(filter_situation) { 349default: 350BUG("unknown filter_situation:%d", filter_situation); 351 352case LOFS_BEGIN_TREE: 353assert(obj->type == OBJ_TREE); 354 dtype = DT_DIR; 355 val =is_excluded_from_list(pathname,strlen(pathname), 356 filename, &dtype, &filter_data->el, 357 r->index); 358if(val <0) 359 val = filter_data->array_frame[filter_data->nr].defval; 360 361ALLOC_GROW(filter_data->array_frame, filter_data->nr +1, 362 filter_data->alloc); 363 filter_data->nr++; 364 filter_data->array_frame[filter_data->nr].defval = val; 365 filter_data->array_frame[filter_data->nr].child_prov_omit =0; 366 367/* 368 * A directory with this tree OID may appear in multiple 369 * places in the tree. (Think of a directory move or copy, 370 * with no other changes, so the OID is the same, but the 371 * full pathnames of objects within this directory are new 372 * and may match is_excluded() patterns differently.) 373 * So we cannot mark this directory as SEEN (yet), since 374 * that will prevent process_tree() from revisiting this 375 * tree object with other pathname prefixes. 376 * 377 * Only _DO_SHOW the tree object the first time we visit 378 * this tree object. 379 * 380 * We always show all tree objects. A future optimization 381 * may want to attempt to narrow this. 382 */ 383if(obj->flags & FILTER_SHOWN_BUT_REVISIT) 384return LOFR_ZERO; 385 obj->flags |= FILTER_SHOWN_BUT_REVISIT; 386return LOFR_DO_SHOW; 387 388case LOFS_END_TREE: 389assert(obj->type == OBJ_TREE); 390assert(filter_data->nr >0); 391 392 frame = &filter_data->array_frame[filter_data->nr]; 393 filter_data->nr--; 394 395/* 396 * Tell our parent directory if any of our children were 397 * provisionally omitted. 398 */ 399 filter_data->array_frame[filter_data->nr].child_prov_omit |= 400 frame->child_prov_omit; 401 402/* 403 * If there are NO provisionally omitted child objects (ALL child 404 * objects in this folder were INCLUDED), then we can mark the 405 * folder as SEEN (so we will not have to revisit it again). 406 */ 407if(!frame->child_prov_omit) 408return LOFR_MARK_SEEN; 409return LOFR_ZERO; 410 411case LOFS_BLOB: 412assert(obj->type == OBJ_BLOB); 413assert((obj->flags & SEEN) ==0); 414 415 frame = &filter_data->array_frame[filter_data->nr]; 416 417 dtype = DT_REG; 418 val =is_excluded_from_list(pathname,strlen(pathname), 419 filename, &dtype, &filter_data->el, 420 r->index); 421if(val <0) 422 val = frame->defval; 423if(val >0) { 424if(filter_data->omits) 425oidset_remove(filter_data->omits, &obj->oid); 426return LOFR_MARK_SEEN | LOFR_DO_SHOW; 427} 428 429/* 430 * Provisionally omit it. We've already established that 431 * this pathname is not in the sparse-checkout specification 432 * with the CURRENT pathname, so we *WANT* to omit this blob. 433 * 434 * However, a pathname elsewhere in the tree may also 435 * reference this same blob, so we cannot reject it yet. 436 * Leave the LOFR_ bits unset so that if the blob appears 437 * again in the traversal, we will be asked again. 438 */ 439if(filter_data->omits) 440oidset_insert(filter_data->omits, &obj->oid); 441 442/* 443 * Remember that at least 1 blob in this tree was 444 * provisionally omitted. This prevents us from short 445 * cutting the tree in future iterations. 446 */ 447 frame->child_prov_omit =1; 448return LOFR_ZERO; 449} 450} 451 452 453static voidfilter_sparse_free(void*filter_data) 454{ 455struct filter_sparse_data *d = filter_data; 456/* TODO free contents of 'd' */ 457free(d); 458} 459 460static void*filter_sparse_oid__init( 461struct oidset *omitted, 462struct list_objects_filter_options *filter_options, 463 filter_object_fn *filter_fn, 464 filter_free_fn *filter_free_fn) 465{ 466struct filter_sparse_data *d =xcalloc(1,sizeof(*d)); 467 d->omits = omitted; 468if(add_excludes_from_blob_to_list(filter_options->sparse_oid_value, 469 NULL,0, &d->el) <0) 470die("could not load filter specification"); 471 472ALLOC_GROW(d->array_frame, d->nr +1, d->alloc); 473 d->array_frame[d->nr].defval =0;/* default to include */ 474 d->array_frame[d->nr].child_prov_omit =0; 475 476*filter_fn = filter_sparse; 477*filter_free_fn = filter_sparse_free; 478return d; 479} 480 481typedefvoid*(*filter_init_fn)( 482struct oidset *omitted, 483struct list_objects_filter_options *filter_options, 484 filter_object_fn *filter_fn, 485 filter_free_fn *filter_free_fn); 486 487/* 488 * Must match "enum list_objects_filter_choice". 489 */ 490static filter_init_fn s_filters[] = { 491 NULL, 492 filter_blobs_none__init, 493 filter_blobs_limit__init, 494 filter_trees_depth__init, 495 filter_sparse_oid__init, 496}; 497 498void*list_objects_filter__init( 499struct oidset *omitted, 500struct list_objects_filter_options *filter_options, 501 filter_object_fn *filter_fn, 502 filter_free_fn *filter_free_fn) 503{ 504 filter_init_fn init_fn; 505 506assert((sizeof(s_filters) /sizeof(s_filters[0])) == LOFC__COUNT); 507 508if(filter_options->choice >= LOFC__COUNT) 509BUG("invalid list-objects filter choice:%d", 510 filter_options->choice); 511 512 init_fn = s_filters[filter_options->choice]; 513if(init_fn) 514returninit_fn(omitted, filter_options, 515 filter_fn, filter_free_fn); 516*filter_fn = NULL; 517*filter_free_fn = NULL; 518return NULL; 519}