@@ -397,6 +397,27 @@ static int cmp_packed_ref_entries(const void *v1, const void *v2)
397397 }
398398}
399399
400+ /*
401+ * Compare a packed-refs record pointed to by `rec` to the specified
402+ * NUL-terminated refname.
403+ */
404+ static int cmp_entry_to_refname (const char * rec , const char * refname )
405+ {
406+ const char * r1 = rec + GIT_SHA1_HEXSZ + 1 ;
407+ const char * r2 = refname ;
408+
409+ while (1 ) {
410+ if (* r1 == '\n' )
411+ return * r2 ? -1 : 0 ;
412+ if (!* r2 )
413+ return 1 ;
414+ if (* r1 != * r2 )
415+ return (unsigned char )* r1 < (unsigned char )* r2 ? -1 : +1 ;
416+ r1 ++ ;
417+ r2 ++ ;
418+ }
419+ }
420+
400421/*
401422 * `packed_refs->buf` is not known to be sorted. Check whether it is,
402423 * and if not, sort it into new memory and munmap/free the old
@@ -503,6 +524,17 @@ static const char *find_start_of_record(const char *buf, const char *p)
503524 return p ;
504525}
505526
527+ /*
528+ * Return a pointer to the start of the record following the record
529+ * that contains `*p`. If none is found before `end`, return `end`.
530+ */
531+ static const char * find_end_of_record (const char * p , const char * end )
532+ {
533+ while (++ p < end && (p [-1 ] != '\n' || p [0 ] == '^' ))
534+ ;
535+ return p ;
536+ }
537+
506538/*
507539 * We want to be able to compare mmapped reference records quickly,
508540 * without totally parsing them. We can do so because the records are
@@ -592,6 +624,65 @@ static int load_contents(struct packed_ref_cache *packed_refs)
592624 return 1 ;
593625}
594626
627+ /*
628+ * Find the place in `cache->buf` where the start of the record for
629+ * `refname` starts. If `mustexist` is true and the reference doesn't
630+ * exist, then return NULL. If `mustexist` is false and the reference
631+ * doesn't exist, then return the point where that reference would be
632+ * inserted. In the latter mode, `refname` doesn't have to be a proper
633+ * reference name; for example, one could search for "refs/replace/"
634+ * to find the start of any replace references.
635+ *
636+ * The record is sought using a binary search, so `cache->buf` must be
637+ * sorted.
638+ */
639+ static const char * find_reference_location (struct packed_ref_cache * cache ,
640+ const char * refname , int mustexist )
641+ {
642+ /*
643+ * This is not *quite* a garden-variety binary search, because
644+ * the data we're searching is made up of records, and we
645+ * always need to find the beginning of a record to do a
646+ * comparison. A "record" here is one line for the reference
647+ * itself and zero or one peel lines that start with '^'. Our
648+ * loop invariant is described in the next two comments.
649+ */
650+
651+ /*
652+ * A pointer to the character at the start of a record whose
653+ * preceding records all have reference names that come
654+ * *before* `refname`.
655+ */
656+ const char * lo = cache -> buf + cache -> header_len ;
657+
658+ /*
659+ * A pointer to a the first character of a record whose
660+ * reference name comes *after* `refname`.
661+ */
662+ const char * hi = cache -> eof ;
663+
664+ while (lo < hi ) {
665+ const char * mid , * rec ;
666+ int cmp ;
667+
668+ mid = lo + (hi - lo ) / 2 ;
669+ rec = find_start_of_record (lo , mid );
670+ cmp = cmp_entry_to_refname (rec , refname );
671+ if (cmp < 0 ) {
672+ lo = find_end_of_record (mid , hi );
673+ } else if (cmp > 0 ) {
674+ hi = rec ;
675+ } else {
676+ return rec ;
677+ }
678+ }
679+
680+ if (mustexist )
681+ return NULL ;
682+ else
683+ return lo ;
684+ }
685+
595686/*
596687 * Read from the `packed-refs` file into a newly-allocated
597688 * `packed_ref_cache` and return it. The return value will already
@@ -888,6 +979,8 @@ static struct ref_iterator *packed_ref_iterator_begin(
888979 const char * prefix , unsigned int flags )
889980{
890981 struct packed_ref_store * refs ;
982+ struct packed_ref_cache * packed_refs ;
983+ const char * start ;
891984 struct packed_ref_iterator * iter ;
892985 struct ref_iterator * ref_iterator ;
893986 unsigned int required_flags = REF_STORE_READ ;
@@ -905,13 +998,23 @@ static struct ref_iterator *packed_ref_iterator_begin(
905998 * the packed-ref cache is up to date with what is on disk,
906999 * and re-reads it if not.
9071000 */
1001+ iter -> cache = packed_refs = get_packed_ref_cache (refs );
1002+ acquire_packed_ref_cache (packed_refs );
9081003
909- iter -> cache = get_packed_ref_cache (refs );
910- acquire_packed_ref_cache (iter -> cache );
911- iter -> iter0 = cache_ref_iterator_begin (iter -> cache -> cache , prefix , 0 );
1004+ if (prefix && * prefix )
1005+ start = find_reference_location (packed_refs , prefix , 0 );
1006+ else
1007+ start = packed_refs -> buf + packed_refs -> header_len ;
1008+
1009+ iter -> iter0 = mmapped_ref_iterator_begin (
1010+ packed_refs , start , packed_refs -> eof );
9121011
9131012 iter -> flags = flags ;
9141013
1014+ if (prefix && * prefix )
1015+ /* Stop iteration after we've gone *past* prefix: */
1016+ ref_iterator = prefix_ref_iterator_begin (ref_iterator , prefix , 0 );
1017+
9151018 return ref_iterator ;
9161019}
9171020
0 commit comments