2222#include "../repo-settings.h"
2323#include "../setup.h"
2424#include "../strmap.h"
25+ #include "../trace2.h"
2526#include "parse.h"
2627#include "refs-internal.h"
2728
@@ -451,10 +452,81 @@ struct reftable_ref_iterator {
451452
452453 const char * prefix ;
453454 size_t prefix_len ;
455+ char * * exclude_patterns ;
456+ size_t exclude_patterns_index ;
457+ size_t exclude_patterns_strlen ;
454458 unsigned int flags ;
455459 int err ;
456460};
457461
462+ /*
463+ * Handle exclude patterns. Returns either `1`, which tells the caller that the
464+ * current reference shall not be shown. Or `0`, which indicates that it should
465+ * be shown.
466+ */
467+ static int should_exclude_current_ref (struct reftable_ref_iterator * iter )
468+ {
469+ while (iter -> exclude_patterns [iter -> exclude_patterns_index ]) {
470+ const char * pattern = iter -> exclude_patterns [iter -> exclude_patterns_index ];
471+ char * ref_after_pattern ;
472+ int cmp ;
473+
474+ /*
475+ * Lazily cache the pattern length so that we don't have to
476+ * recompute it every time this function is called.
477+ */
478+ if (!iter -> exclude_patterns_strlen )
479+ iter -> exclude_patterns_strlen = strlen (pattern );
480+
481+ /*
482+ * When the reference name is lexicographically bigger than the
483+ * current exclude pattern we know that it won't ever match any
484+ * of the following references, either. We thus advance to the
485+ * next pattern and re-check whether it matches.
486+ *
487+ * Otherwise, if it's smaller, then we do not have a match and
488+ * thus want to show the current reference.
489+ */
490+ cmp = strncmp (iter -> ref .refname , pattern ,
491+ iter -> exclude_patterns_strlen );
492+ if (cmp > 0 ) {
493+ iter -> exclude_patterns_index ++ ;
494+ iter -> exclude_patterns_strlen = 0 ;
495+ continue ;
496+ }
497+ if (cmp < 0 )
498+ return 0 ;
499+
500+ /*
501+ * The reference shares a prefix with the exclude pattern and
502+ * shall thus be omitted. We skip all references that match the
503+ * pattern by seeking to the first reference after the block of
504+ * matches.
505+ *
506+ * This is done by appending the highest possible character to
507+ * the pattern. Consequently, all references that have the
508+ * pattern as prefix and whose suffix starts with anything in
509+ * the range [0x00, 0xfe] are skipped. And given that 0xff is a
510+ * non-printable character that shouldn't ever be in a ref name,
511+ * we'd not yield any such record, either.
512+ *
513+ * Note that the seeked-to reference may also be excluded. This
514+ * is not handled here though, but the caller is expected to
515+ * loop and re-verify the next reference for us.
516+ */
517+ ref_after_pattern = xstrfmt ("%s%c" , pattern , 0xff );
518+ iter -> err = reftable_iterator_seek_ref (& iter -> iter , ref_after_pattern );
519+ iter -> exclude_patterns_index ++ ;
520+ iter -> exclude_patterns_strlen = 0 ;
521+ trace2_counter_add (TRACE2_COUNTER_ID_REFTABLE_RESEEKS , 1 );
522+
523+ free (ref_after_pattern );
524+ return 1 ;
525+ }
526+
527+ return 0 ;
528+ }
529+
458530static int reftable_ref_iterator_advance (struct ref_iterator * ref_iterator )
459531{
460532 struct reftable_ref_iterator * iter =
@@ -485,6 +557,9 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
485557 break ;
486558 }
487559
560+ if (iter -> exclude_patterns && should_exclude_current_ref (iter ))
561+ continue ;
562+
488563 if (iter -> flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
489564 parse_worktree_ref (iter -> ref .refname , NULL , NULL , NULL ) !=
490565 REF_WORKTREE_CURRENT )
@@ -574,6 +649,11 @@ static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
574649 (struct reftable_ref_iterator * )ref_iterator ;
575650 reftable_ref_record_release (& iter -> ref );
576651 reftable_iterator_destroy (& iter -> iter );
652+ if (iter -> exclude_patterns ) {
653+ for (size_t i = 0 ; iter -> exclude_patterns [i ]; i ++ )
654+ free (iter -> exclude_patterns [i ]);
655+ free (iter -> exclude_patterns );
656+ }
577657 free (iter );
578658 return ITER_DONE ;
579659}
@@ -584,9 +664,53 @@ static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
584664 .abort = reftable_ref_iterator_abort
585665};
586666
667+ static int qsort_strcmp (const void * va , const void * vb )
668+ {
669+ const char * a = * (const char * * )va ;
670+ const char * b = * (const char * * )vb ;
671+ return strcmp (a , b );
672+ }
673+
674+ static char * * filter_exclude_patterns (const char * * exclude_patterns )
675+ {
676+ size_t filtered_size = 0 , filtered_alloc = 0 ;
677+ char * * filtered = NULL ;
678+
679+ if (!exclude_patterns )
680+ return NULL ;
681+
682+ for (size_t i = 0 ; ; i ++ ) {
683+ const char * exclude_pattern = exclude_patterns [i ];
684+ int has_glob = 0 ;
685+
686+ if (!exclude_pattern )
687+ break ;
688+
689+ for (const char * p = exclude_pattern ; * p ; p ++ ) {
690+ has_glob = is_glob_special (* p );
691+ if (has_glob )
692+ break ;
693+ }
694+ if (has_glob )
695+ continue ;
696+
697+ ALLOC_GROW (filtered , filtered_size + 1 , filtered_alloc );
698+ filtered [filtered_size ++ ] = xstrdup (exclude_pattern );
699+ }
700+
701+ if (filtered_size ) {
702+ QSORT (filtered , filtered_size , qsort_strcmp );
703+ ALLOC_GROW (filtered , filtered_size + 1 , filtered_alloc );
704+ filtered [filtered_size ++ ] = NULL ;
705+ }
706+
707+ return filtered ;
708+ }
709+
587710static struct reftable_ref_iterator * ref_iterator_for_stack (struct reftable_ref_store * refs ,
588711 struct reftable_stack * stack ,
589712 const char * prefix ,
713+ const char * * exclude_patterns ,
590714 int flags )
591715{
592716 struct reftable_ref_iterator * iter ;
@@ -599,6 +723,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
599723 iter -> base .oid = & iter -> oid ;
600724 iter -> flags = flags ;
601725 iter -> refs = refs ;
726+ iter -> exclude_patterns = filter_exclude_patterns (exclude_patterns );
602727
603728 ret = refs -> err ;
604729 if (ret )
@@ -620,7 +745,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
620745
621746static struct ref_iterator * reftable_be_iterator_begin (struct ref_store * ref_store ,
622747 const char * prefix ,
623- const char * * exclude_patterns UNUSED ,
748+ const char * * exclude_patterns ,
624749 unsigned int flags )
625750{
626751 struct reftable_ref_iterator * main_iter , * worktree_iter ;
@@ -631,7 +756,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
631756 required_flags |= REF_STORE_ODB ;
632757 refs = reftable_be_downcast (ref_store , required_flags , "ref_iterator_begin" );
633758
634- main_iter = ref_iterator_for_stack (refs , refs -> main_stack , prefix , flags );
759+ main_iter = ref_iterator_for_stack (refs , refs -> main_stack , prefix ,
760+ exclude_patterns , flags );
635761
636762 /*
637763 * The worktree stack is only set when we're in an actual worktree
@@ -645,7 +771,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto
645771 * Otherwise we merge both the common and the per-worktree refs into a
646772 * single iterator.
647773 */
648- worktree_iter = ref_iterator_for_stack (refs , refs -> worktree_stack , prefix , flags );
774+ worktree_iter = ref_iterator_for_stack (refs , refs -> worktree_stack , prefix ,
775+ exclude_patterns , flags );
649776 return merge_ref_iterator_begin (& worktree_iter -> base , & main_iter -> base ,
650777 ref_iterator_select , NULL );
651778}
0 commit comments