Git fork

refs: selectively set prefix in the seek functions

The ref iterator exposes a `ref_iterator_seek()` function. The name
suggests that this would seek the iterator to a specific reference in
some ways similar to how `fseek()` works for the filesystem.

However, the function actually sets the prefix for refs iteration. So
further iteration would only yield references which match the particular
prefix. This is a bit confusing.

Let's add a 'flags' field to the function, which when set with the
'REF_ITERATOR_SEEK_SET_PREFIX' flag, will set the prefix for the
iteration in-line with the existing behavior. Otherwise, the reference
backends will simply seek to the specified reference and clears any
previously set prefix. This allows users to start iteration from a
specific reference.

In the packed and reftable backend, since references are available in a
sorted list, the changes are simply setting the prefix if needed. The
changes on the files-backend are a little more involved, since the files
backend uses the 'ref-cache' mechanism. We move out the existing logic
within `cache_ref_iterator_seek()` to `cache_ref_iterator_set_prefix()`
which is called when the 'REF_ITERATOR_SEEK_SET_PREFIX' flag is set. We
then parse the provided seek string and set the required levels and
their indexes to ensure that seeking is possible.

Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Karthik Nayak and committed by
Junio C Hamano
2b4648b9 883a7ea0

+152 -50
+3 -3
refs.c
··· 2666 if (!initial_transaction) { 2667 int ok; 2668 2669 - if (!iter) { 2670 iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0, 2671 DO_FOR_EACH_INCLUDE_BROKEN); 2672 - } else if (ref_iterator_seek(iter, dirname.buf) < 0) { 2673 goto cleanup; 2674 - } 2675 2676 while ((ok = ref_iterator_advance(iter)) == ITER_OK) { 2677 if (skip &&
··· 2666 if (!initial_transaction) { 2667 int ok; 2668 2669 + if (!iter) 2670 iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0, 2671 DO_FOR_EACH_INCLUDE_BROKEN); 2672 + else if (ref_iterator_seek(iter, dirname.buf, 2673 + REF_ITERATOR_SEEK_SET_PREFIX) < 0) 2674 goto cleanup; 2675 2676 while ((ok = ref_iterator_advance(iter)) == ITER_OK) { 2677 if (skip &&
+17 -9
refs.h
··· 1299 */ 1300 int ref_iterator_advance(struct ref_iterator *ref_iterator); 1301 1302 /* 1303 - * Seek the iterator to the first reference with the given prefix. 1304 - * The prefix is matched as a literal string, without regard for path 1305 - * separators. If prefix is NULL or the empty string, seek the iterator to the 1306 * first reference again. 1307 * 1308 - * This function is expected to behave as if a new ref iterator with the same 1309 - * prefix had been created, but allows reuse of iterators and thus may allow 1310 - * the backend to optimize. Parameters other than the prefix that have been 1311 - * passed when creating the iterator will remain unchanged. 1312 * 1313 * Returns 0 on success, a negative error code otherwise. 1314 */ 1315 - int ref_iterator_seek(struct ref_iterator *ref_iterator, 1316 - const char *prefix); 1317 1318 /* 1319 * If possible, peel the reference currently being viewed by the
··· 1299 */ 1300 int ref_iterator_advance(struct ref_iterator *ref_iterator); 1301 1302 + enum ref_iterator_seek_flag { 1303 + /* 1304 + * When the REF_ITERATOR_SEEK_SET_PREFIX flag is set, the iterator's prefix is 1305 + * updated to match the provided string, affecting all subsequent iterations. If 1306 + * not, the iterator seeks to the specified reference and clears any previously 1307 + * set prefix. 1308 + */ 1309 + REF_ITERATOR_SEEK_SET_PREFIX = (1 << 0), 1310 + }; 1311 + 1312 /* 1313 + * Seek the iterator to the first reference matching the given seek string. 1314 + * The seek string is matched as a literal string, without regard for path 1315 + * separators. If seek is NULL or the empty string, seek the iterator to the 1316 * first reference again. 1317 * 1318 + * This function is expected to behave as if a new ref iterator has been 1319 + * created, but allows reuse of existing iterators for optimization. 1320 * 1321 * Returns 0 on success, a negative error code otherwise. 1322 */ 1323 + int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname, 1324 + unsigned int flags); 1325 1326 /* 1327 * If possible, peel the reference currently being viewed by the
+4 -3
refs/debug.c
··· 170 } 171 172 static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator, 173 - const char *prefix) 174 { 175 struct debug_ref_iterator *diter = 176 (struct debug_ref_iterator *)ref_iterator; 177 - int res = diter->iter->vtable->seek(diter->iter, prefix); 178 - trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res); 179 return res; 180 } 181
··· 170 } 171 172 static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator, 173 + const char *refname, unsigned int flags) 174 { 175 struct debug_ref_iterator *diter = 176 (struct debug_ref_iterator *)ref_iterator; 177 + int res = diter->iter->vtable->seek(diter->iter, refname, flags); 178 + trace_printf_key(&trace_refs, "iterator_seek: %s flags: %d: %d\n", 179 + refname ? refname : "", flags, res); 180 return res; 181 } 182
+4 -3
refs/files-backend.c
··· 929 } 930 931 static int files_ref_iterator_seek(struct ref_iterator *ref_iterator, 932 - const char *prefix) 933 { 934 struct files_ref_iterator *iter = 935 (struct files_ref_iterator *)ref_iterator; 936 - return ref_iterator_seek(iter->iter0, prefix); 937 } 938 939 static int files_ref_iterator_peel(struct ref_iterator *ref_iterator, ··· 2316 } 2317 2318 static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 2319 - const char *prefix UNUSED) 2320 { 2321 BUG("ref_iterator_seek() called for reflog_iterator"); 2322 }
··· 929 } 930 931 static int files_ref_iterator_seek(struct ref_iterator *ref_iterator, 932 + const char *refname, unsigned int flags) 933 { 934 struct files_ref_iterator *iter = 935 (struct files_ref_iterator *)ref_iterator; 936 + return ref_iterator_seek(iter->iter0, refname, flags); 937 } 938 939 static int files_ref_iterator_peel(struct ref_iterator *ref_iterator, ··· 2316 } 2317 2318 static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 2319 + const char *refname UNUSED, 2320 + unsigned int flags UNUSED) 2321 { 2322 BUG("ref_iterator_seek() called for reflog_iterator"); 2323 }
+15 -11
refs/iterator.c
··· 15 return ref_iterator->vtable->advance(ref_iterator); 16 } 17 18 - int ref_iterator_seek(struct ref_iterator *ref_iterator, 19 - const char *prefix) 20 { 21 - return ref_iterator->vtable->seek(ref_iterator, prefix); 22 } 23 24 int ref_iterator_peel(struct ref_iterator *ref_iterator, ··· 57 } 58 59 static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 60 - const char *prefix UNUSED) 61 { 62 return 0; 63 } ··· 224 } 225 226 static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator, 227 - const char *prefix) 228 { 229 struct merge_ref_iterator *iter = 230 (struct merge_ref_iterator *)ref_iterator; ··· 234 iter->iter0 = iter->iter0_owned; 235 iter->iter1 = iter->iter1_owned; 236 237 - ret = ref_iterator_seek(iter->iter0, prefix); 238 if (ret < 0) 239 return ret; 240 241 - ret = ref_iterator_seek(iter->iter1, prefix); 242 if (ret < 0) 243 return ret; 244 ··· 407 } 408 409 static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator, 410 - const char *prefix) 411 { 412 struct prefix_ref_iterator *iter = 413 (struct prefix_ref_iterator *)ref_iterator; 414 - free(iter->prefix); 415 - iter->prefix = xstrdup_or_null(prefix); 416 - return ref_iterator_seek(iter->iter0, prefix); 417 } 418 419 static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
··· 15 return ref_iterator->vtable->advance(ref_iterator); 16 } 17 18 + int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname, 19 + unsigned int flags) 20 { 21 + return ref_iterator->vtable->seek(ref_iterator, refname, flags); 22 } 23 24 int ref_iterator_peel(struct ref_iterator *ref_iterator, ··· 57 } 58 59 static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 60 + const char *refname UNUSED, 61 + unsigned int flags UNUSED) 62 { 63 return 0; 64 } ··· 225 } 226 227 static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator, 228 + const char *refname, unsigned int flags) 229 { 230 struct merge_ref_iterator *iter = 231 (struct merge_ref_iterator *)ref_iterator; ··· 235 iter->iter0 = iter->iter0_owned; 236 iter->iter1 = iter->iter1_owned; 237 238 + ret = ref_iterator_seek(iter->iter0, refname, flags); 239 if (ret < 0) 240 return ret; 241 242 + ret = ref_iterator_seek(iter->iter1, refname, flags); 243 if (ret < 0) 244 return ret; 245 ··· 408 } 409 410 static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator, 411 + const char *refname, unsigned int flags) 412 { 413 struct prefix_ref_iterator *iter = 414 (struct prefix_ref_iterator *)ref_iterator; 415 + 416 + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { 417 + free(iter->prefix); 418 + iter->prefix = xstrdup_or_null(refname); 419 + } 420 + return ref_iterator_seek(iter->iter0, refname, flags); 421 } 422 423 static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
+11 -6
refs/packed-backend.c
··· 1004 } 1005 1006 static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator, 1007 - const char *prefix) 1008 { 1009 struct packed_ref_iterator *iter = 1010 (struct packed_ref_iterator *)ref_iterator; 1011 const char *start; 1012 1013 - if (prefix && *prefix) 1014 - start = find_reference_location(iter->snapshot, prefix, 0); 1015 else 1016 start = iter->snapshot->start; 1017 1018 - free(iter->prefix); 1019 - iter->prefix = xstrdup_or_null(prefix); 1020 iter->pos = start; 1021 iter->eof = iter->snapshot->eof; 1022 ··· 1194 iter->repo = ref_store->repo; 1195 iter->flags = flags; 1196 1197 - if (packed_ref_iterator_seek(&iter->base, prefix) < 0) { 1198 ref_iterator_free(&iter->base); 1199 return NULL; 1200 }
··· 1004 } 1005 1006 static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator, 1007 + const char *refname, unsigned int flags) 1008 { 1009 struct packed_ref_iterator *iter = 1010 (struct packed_ref_iterator *)ref_iterator; 1011 const char *start; 1012 1013 + if (refname && *refname) 1014 + start = find_reference_location(iter->snapshot, refname, 0); 1015 else 1016 start = iter->snapshot->start; 1017 1018 + /* Unset any previously set prefix */ 1019 + FREE_AND_NULL(iter->prefix); 1020 + 1021 + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) 1022 + iter->prefix = xstrdup_or_null(refname); 1023 + 1024 iter->pos = start; 1025 iter->eof = iter->snapshot->eof; 1026 ··· 1198 iter->repo = ref_store->repo; 1199 iter->flags = flags; 1200 1201 + if (packed_ref_iterator_seek(&iter->base, prefix, 1202 + REF_ITERATOR_SEEK_SET_PREFIX) < 0) { 1203 ref_iterator_free(&iter->base); 1204 return NULL; 1205 }
+80 -5
refs/ref-cache.c
··· 434 } 435 } 436 437 - static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, 438 - const char *prefix) 439 { 440 - struct cache_ref_iterator *iter = 441 - (struct cache_ref_iterator *)ref_iterator; 442 struct cache_ref_iterator_level *level; 443 struct ref_dir *dir; 444 ··· 469 return 0; 470 } 471 472 static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, 473 struct object_id *peeled) 474 { ··· 509 iter->cache = cache; 510 iter->prime_dir = prime_dir; 511 512 - if (cache_ref_iterator_seek(&iter->base, prefix) < 0) { 513 ref_iterator_free(&iter->base); 514 return NULL; 515 }
··· 434 } 435 } 436 437 + static int cache_ref_iterator_set_prefix(struct cache_ref_iterator *iter, 438 + const char *prefix) 439 { 440 struct cache_ref_iterator_level *level; 441 struct ref_dir *dir; 442 ··· 467 return 0; 468 } 469 470 + static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, 471 + const char *refname, unsigned int flags) 472 + { 473 + struct cache_ref_iterator *iter = 474 + (struct cache_ref_iterator *)ref_iterator; 475 + 476 + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { 477 + return cache_ref_iterator_set_prefix(iter, refname); 478 + } else if (refname && *refname) { 479 + struct cache_ref_iterator_level *level; 480 + const char *slash = refname; 481 + struct ref_dir *dir; 482 + 483 + dir = get_ref_dir(iter->cache->root); 484 + 485 + if (iter->prime_dir) 486 + prime_ref_dir(dir, refname); 487 + 488 + iter->levels_nr = 1; 489 + level = &iter->levels[0]; 490 + level->index = -1; 491 + level->dir = dir; 492 + 493 + /* Unset any previously set prefix */ 494 + FREE_AND_NULL(iter->prefix); 495 + 496 + /* 497 + * Breakdown the provided seek path and assign the correct 498 + * indexing to each level as needed. 499 + */ 500 + do { 501 + int len, idx; 502 + int cmp = 0; 503 + 504 + sort_ref_dir(dir); 505 + 506 + slash = strchr(slash, '/'); 507 + len = slash ? slash - refname : (int)strlen(refname); 508 + 509 + for (idx = 0; idx < dir->nr; idx++) { 510 + cmp = strncmp(refname, dir->entries[idx]->name, len); 511 + if (cmp <= 0) 512 + break; 513 + } 514 + /* don't overflow the index */ 515 + idx = idx >= dir->nr ? dir->nr - 1 : idx; 516 + 517 + if (slash) 518 + slash = slash + 1; 519 + 520 + level->index = idx; 521 + if (dir->entries[idx]->flag & REF_DIR) { 522 + /* push down a level */ 523 + dir = get_ref_dir(dir->entries[idx]); 524 + 525 + ALLOC_GROW(iter->levels, iter->levels_nr + 1, 526 + iter->levels_alloc); 527 + level = &iter->levels[iter->levels_nr++]; 528 + level->dir = dir; 529 + level->index = -1; 530 + } else { 531 + /* reduce the index so the leaf node is iterated over */ 532 + if (cmp <= 0 && !slash) 533 + level->index = idx - 1; 534 + /* 535 + * while the seek path may not be exhausted, our 536 + * match is exhausted at a leaf node. 537 + */ 538 + break; 539 + } 540 + } while (slash); 541 + } 542 + 543 + return 0; 544 + } 545 + 546 static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, 547 struct object_id *peeled) 548 { ··· 583 iter->cache = cache; 584 iter->prime_dir = prime_dir; 585 586 + if (cache_ref_iterator_seek(&iter->base, prefix, 587 + REF_ITERATOR_SEEK_SET_PREFIX) < 0) { 588 ref_iterator_free(&iter->base); 589 return NULL; 590 }
+4 -3
refs/refs-internal.h
··· 353 typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); 354 355 /* 356 - * Seek the iterator to the first reference matching the given prefix. Should 357 - * behave the same as if a new iterator was created with the same prefix. 358 */ 359 typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator, 360 - const char *prefix); 361 362 /* 363 * Peels the current ref, returning 0 for success or -1 for failure.
··· 353 typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); 354 355 /* 356 + * Seek the iterator to the first matching reference. If the 357 + * REF_ITERATOR_SEEK_SET_PREFIX flag is set, it would behave the same as if a 358 + * new iterator was created with the provided refname as prefix. 359 */ 360 typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator, 361 + const char *refname, unsigned int flags); 362 363 /* 364 * Peels the current ref, returning 0 for success or -1 for failure.
+14 -7
refs/reftable-backend.c
··· 719 } 720 721 static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator, 722 - const char *prefix) 723 { 724 struct reftable_ref_iterator *iter = 725 (struct reftable_ref_iterator *)ref_iterator; 726 727 - free(iter->prefix); 728 - iter->prefix = xstrdup_or_null(prefix); 729 - iter->prefix_len = prefix ? strlen(prefix) : 0; 730 - iter->err = reftable_iterator_seek_ref(&iter->iter, prefix); 731 732 return iter->err; 733 } ··· 839 if (ret) 840 goto done; 841 842 - ret = reftable_ref_iterator_seek(&iter->base, prefix); 843 if (ret) 844 goto done; 845 ··· 2042 } 2043 2044 static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 2045 - const char *prefix UNUSED) 2046 { 2047 BUG("reftable reflog iterator cannot be seeked"); 2048 return -1;
··· 719 } 720 721 static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator, 722 + const char *refname, unsigned int flags) 723 { 724 struct reftable_ref_iterator *iter = 725 (struct reftable_ref_iterator *)ref_iterator; 726 727 + /* Unset any previously set prefix */ 728 + FREE_AND_NULL(iter->prefix); 729 + iter->prefix_len = 0; 730 + 731 + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { 732 + iter->prefix = xstrdup_or_null(refname); 733 + iter->prefix_len = refname ? strlen(refname) : 0; 734 + } 735 + iter->err = reftable_iterator_seek_ref(&iter->iter, refname); 736 737 return iter->err; 738 } ··· 844 if (ret) 845 goto done; 846 847 + ret = reftable_ref_iterator_seek(&iter->base, prefix, 848 + REF_ITERATOR_SEEK_SET_PREFIX); 849 if (ret) 850 goto done; 851 ··· 2048 } 2049 2050 static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, 2051 + const char *refname UNUSED, 2052 + unsigned int flags UNUSED) 2053 { 2054 BUG("reftable reflog iterator cannot be seeked"); 2055 return -1;