99#include "hashmap.h"
1010#include "hex.h"
1111#include "list-objects.h"
12+ #include "list-objects-filter-options.h"
13+ #include "object-name.h"
14+ #include "odb.h"
1215#include "object.h"
1316#include "oid-array.h"
1417#include "path.h"
@@ -178,11 +181,6 @@ static int add_tree_entries(struct path_walk_context *ctx,
178181 return -1 ;
179182 }
180183
181- /* Skip this object if already seen. */
182- if (o -> flags & SEEN )
183- continue ;
184- o -> flags |= SEEN ;
185-
186184 strbuf_setlen (& path , base_len );
187185 strbuf_add (& path , entry .path , entry .pathlen );
188186
@@ -193,6 +191,40 @@ static int add_tree_entries(struct path_walk_context *ctx,
193191 if (type == OBJ_TREE )
194192 strbuf_addch (& path , '/' );
195193
194+ if (o -> flags & SEEN ) {
195+ /*
196+ * A tree with a shared OID may appear at multiple
197+ * paths. Even though we already added this tree to
198+ * the output at some other path, we still need to
199+ * walk into it at this in-cone path to discover
200+ * blobs that were not found at the earlier
201+ * out-of-cone path.
202+ *
203+ * Only do this for paths not yet in our map, to
204+ * avoid duplicate entries when the same tree OID
205+ * appears at the same path across multiple commits.
206+ */
207+ if (type == OBJ_TREE && ctx -> info -> pl &&
208+ ctx -> info -> pl -> use_cone_patterns &&
209+ !ctx -> info -> pl_sparse_trees &&
210+ !strmap_contains (& ctx -> paths_to_lists , path .buf )) {
211+ int dtype ;
212+ enum pattern_match_result m ;
213+ m = path_matches_pattern_list (path .buf , path .len ,
214+ path .buf + base_len ,
215+ & dtype ,
216+ ctx -> info -> pl ,
217+ ctx -> repo -> index );
218+ if (m != NOT_MATCHED ) {
219+ add_path_to_list (ctx , path .buf , type ,
220+ & entry .oid ,
221+ !(o -> flags & UNINTERESTING ));
222+ push_to_stack (ctx , path .buf );
223+ }
224+ }
225+ continue ;
226+ }
227+
196228 if (ctx -> info -> pl ) {
197229 int dtype ;
198230 enum pattern_match_result match ;
@@ -202,7 +234,8 @@ static int add_tree_entries(struct path_walk_context *ctx,
202234 ctx -> repo -> index );
203235
204236 if (ctx -> info -> pl -> use_cone_patterns &&
205- match == NOT_MATCHED )
237+ match == NOT_MATCHED &&
238+ (type == OBJ_BLOB || ctx -> info -> pl_sparse_trees ))
206239 continue ;
207240 else if (!ctx -> info -> pl -> use_cone_patterns &&
208241 type == OBJ_BLOB &&
@@ -237,6 +270,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
237270 continue ;
238271 }
239272
273+ o -> flags |= SEEN ;
240274 add_path_to_list (ctx , path .buf , type , & entry .oid ,
241275 !(o -> flags & UNINTERESTING ));
242276
@@ -314,9 +348,29 @@ static int walk_path(struct path_walk_context *ctx,
314348 /* Evaluate function pointer on this data, if requested. */
315349 if ((list -> type == OBJ_TREE && ctx -> info -> trees ) ||
316350 (list -> type == OBJ_BLOB && ctx -> info -> blobs ) ||
317- (list -> type == OBJ_TAG && ctx -> info -> tags ))
318- ret = ctx -> info -> path_fn (path , & list -> oids , list -> type ,
319- ctx -> info -> path_fn_data );
351+ (list -> type == OBJ_TAG && ctx -> info -> tags )) {
352+ struct oid_array * oids = & list -> oids ;
353+ struct oid_array filtered = OID_ARRAY_INIT ;
354+
355+ if (list -> type == OBJ_BLOB && ctx -> info -> blob_limit ) {
356+ for (size_t i = 0 ; i < list -> oids .nr ; i ++ ) {
357+ unsigned long size ;
358+
359+ if (odb_read_object_info (ctx -> repo -> objects ,
360+ & list -> oids .oid [i ],
361+ & size ) != OBJ_BLOB ||
362+ size < ctx -> info -> blob_limit )
363+ oid_array_append (& filtered ,
364+ & list -> oids .oid [i ]);
365+ }
366+ oids = & filtered ;
367+ }
368+
369+ if (oids -> nr )
370+ ret = ctx -> info -> path_fn (path , oids , list -> type ,
371+ ctx -> info -> path_fn_data );
372+ oid_array_clear (& filtered );
373+ }
320374
321375 /* Expand data for children. */
322376 if (list -> type == OBJ_TREE ) {
@@ -485,6 +539,85 @@ static int setup_pending_objects(struct path_walk_info *info,
485539 return 0 ;
486540}
487541
542+ static int prepare_filters (struct path_walk_info * info ,
543+ struct list_objects_filter_options * options )
544+ {
545+ switch (options -> choice ) {
546+ case LOFC_DISABLED :
547+ return 1 ;
548+
549+ case LOFC_BLOB_NONE :
550+ if (info ) {
551+ info -> blobs = 0 ;
552+ list_objects_filter_release (options );
553+ }
554+ return 1 ;
555+
556+ case LOFC_BLOB_LIMIT :
557+ if (info ) {
558+ if (!options -> blob_limit_value ) {
559+ info -> blobs = 0 ;
560+ } else {
561+ info -> blob_limit = options -> blob_limit_value ;
562+ }
563+ list_objects_filter_release (options );
564+ }
565+ return 1 ;
566+
567+ case LOFC_SPARSE_OID :
568+ if (info ) {
569+ struct object_id sparse_oid ;
570+ struct repository * repo = info -> revs -> repo ;
571+
572+ if (info -> pl ) {
573+ warning (_ ("sparse filter cannot be combined with existing sparse patterns" ));
574+ return 0 ;
575+ }
576+
577+ if (repo_get_oid_with_flags (repo ,
578+ options -> sparse_oid_name ,
579+ & sparse_oid ,
580+ GET_OID_BLOB )) {
581+ error (_ ("unable to access sparse blob in '%s'" ),
582+ options -> sparse_oid_name );
583+ return 0 ;
584+ }
585+
586+ CALLOC_ARRAY (info -> pl , 1 );
587+ info -> pl -> use_cone_patterns = 1 ;
588+
589+ if (add_patterns_from_blob_to_list (& sparse_oid , "" , 0 ,
590+ info -> pl ) < 0 ) {
591+ clear_pattern_list (info -> pl );
592+ FREE_AND_NULL (info -> pl );
593+ error (_ ("unable to parse sparse filter data in '%s'" ),
594+ oid_to_hex (& sparse_oid ));
595+ return 0 ;
596+ }
597+
598+ if (!info -> pl -> use_cone_patterns ) {
599+ clear_pattern_list (info -> pl );
600+ FREE_AND_NULL (info -> pl );
601+ warning (_ ("sparse filter is not cone-mode compatible" ));
602+ return 0 ;
603+ }
604+
605+ list_objects_filter_release (options );
606+ }
607+ return 1 ;
608+
609+ default :
610+ error (_ ("object filter '%s' not supported by the path-walk API" ),
611+ list_objects_filter_spec (options ));
612+ return 0 ;
613+ }
614+ }
615+
616+ int path_walk_filter_compatible (struct list_objects_filter_options * options )
617+ {
618+ return prepare_filters (NULL , options );
619+ }
620+
488621/**
489622 * Given the configuration of 'info', walk the commits based on 'info->revs' and
490623 * call 'info->path_fn' on each discovered path.
@@ -512,6 +645,9 @@ int walk_objects_by_path(struct path_walk_info *info)
512645
513646 trace2_region_enter ("path-walk" , "commit-walk" , info -> revs -> repo );
514647
648+ if (!prepare_filters (info , & info -> revs -> filter ))
649+ return -1 ;
650+
515651 CALLOC_ARRAY (commit_list , 1 );
516652 commit_list -> type = OBJ_COMMIT ;
517653
0 commit comments