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