summaryrefslogtreecommitdiff
path: root/refs/reftable-backend.c
AgeCommit message (Collapse)AuthorFilesLines
7 daysMerge branch 'kn/reftable-optimize-reloading'Junio C Hamano1-14/+8
The reftable code has been optimized to avoid an unnecessary stat/reload of the stack when an addition already holds the list_file lock, reducing the number of newfstatat syscalls from linear to constant when writing refs. * kn/reftable-optimize-reloading: reftable/stack: avoid reloading the stack when already locked reftable/stack: move list lock to `struct reftable_stack` reftable/stack: rename reftable_stack_new_addition() reftable/stack: remove `REFTABLE_STACK_NEW_ADDITION_RELOAD`
2026-08-25Merge branch 'ch/chdir-notify-drop-name'Junio C Hamano1-4/+3
The unused name parameter in 'struct chdir_notify_entry' has been removed from chdir_notify_register(), chdir_notify_unregister(), and related callback signatures across several subsystems, simplifying the API now that trace output no longer uses it. * ch/chdir-notify-drop-name: chdir-notify.h: Removed unused param 'name'
2026-08-24reftable/stack: rename reftable_stack_new_addition()Karthik Nayak1-2/+2
Rename the function `reftable_stack_new_addition()` to `reftable_stack_addition_new()` to be more inline with our naming scheme. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-24reftable/stack: remove `REFTABLE_STACK_NEW_ADDITION_RELOAD`Karthik Nayak1-12/+6
In 80e7342ea8 (reftable/stack: allow locking of outdated stacks, 2024-09-24), the `REFTABLE_STACK_NEW_ADDITION_RELOAD` was introduced so that callers of `reftable_stack_init_addition()` can also reload the stack if there was a concurrent update made before the lock was obtained. Then 16684b6fae (refs/reftable: always reload stacks when creating lock, 2025-08-12) updated all of the remaining call-sites to propagate this flag to ensure that we always reload the stack whenever there was a concurrent update. As all calls to `reftable_stack_init_addition()` inevitably propagate the flag, it is safe to remove the flag and its associated code and make the reloading of the stack the default flow. This makes it easier to follow the flow and simplifies the logic. The only exceptions are: 1. Unit tests, where we explicitly do not propagate the flag. These tests are now modified with the new status quo. 2. `reftable_stack_clean()`, which was propagating 0 to `reftable_stack_new_addition()` but was then manually reloading the stack after. Here the new flow will achieve the same, while also allowing us to remove the manual reload. This also makes two checks for 'REFTABLE_OUTDATED_ERROR' redundant, so remove them also. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-14chdir-notify.h: Removed unused param 'name'Colin Hinton1-4/+3
The `name` parameter in `chdir_notify_entry` was only ever used by chdir_notify_reparent() to produce trace output. That function was removed in 5bf546755c (chdir-notify: drop unused `chdir_notify_reparent()`, 2026-06-25), which left `name` with no remaining consumers. Prior to that removal, most callers had already stopped passing a meaningful name, switching to NULL in 1f43ff2c7e (refs: unregister reference stores from "chdir_notify", 2026-06-25) and 0de2467e6c (odb/source-packed: start converting to a proper `struct odb_source`, 2026-06-17). Since no caller has populated `name` with real data for some time, and its last consumer is gone, drop it from chdir_notify_register(), chdir_notify_unregister(), and the callback signature to simplify the API. Signed-off-by: Colin Hinton <colinlewishinton@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-10reftable: fix quadratic behavior in the presence of tombstonesKristofer Karlsson1-11/+43
When many tombstones are present in a reftable, operations that need to look up or iterate over refs exhibit quadratic behavior. With 8000 refs deleted and re-created, update-ref takes ~15s, quadrupling for each doubling of input size. The root cause is the merged iterator's suppress_deletions flag. When set, merged_iter_next_void() silently consumes tombstone records in a tight internal loop before returning to the caller. This prevents higher-level code from checking iteration bounds (such as prefix or refname comparisons) until after all tombstones have been scanned. This affects any code path that seeks into a range containing tombstones, including: - refs_verify_refnames_available() seeks to "refs/tags/foo-1/" to check for D/F conflicts and must scan through all subsequent tombstones before the caller can see that they are past the prefix of interest. - reftable_backend_read_ref() seeks to a specific refname and must scan through all subsequent tombstones before returning "not found", because the merged iterator skips the matching tombstone and searches for the next live record. Fix this by making suppress_deletions configurable via reftable_stack_options instead of unconditionally enabling it. Git no longer sets the flag, so tombstones are now returned to callers in the reftable backend, which skip them after their existing bounds checks. This allows iteration to terminate as soon as a tombstone past the relevant bound is encountered. Downstream users of the reftable library (e.g. libgit2) can still enable suppress_deletions through the stack options to retain the previous behavior. This also requires adding deletion checks to the log iteration paths, since suppress_deletions applied to both ref and log iterators. Both tests in p1401 go from ~13s to ~0.2s with this change. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-06Merge branch 'ps/refs-onbranch-fixes'Junio C Hamano1-65/+112
Reference backend configuration has been updated to load lazily to avoid recursive calls during repository initialization when 'onbranch' configuration conditions are evaluated. This has also fixed a memory leak and allowed the unused `chdir_notify_reparent()` machinery to be dropped. * ps/refs-onbranch-fixes: refs: protect against chicken-and-egg recursion refs/reftable: lazy-load configuration to fix chicken-and-egg reftable: split up write options refs/files: lazy-load configuration to fix chicken-and-egg refs: move parsing of "core.logAllRefUpdates" back into ref stores repository: free main reference database chdir-notify: drop unused `chdir_notify_reparent()` refs: unregister reference stores from "chdir_notify" setup: don't apply "GIT_REFERENCE_BACKEND" without a repository setup: stop applying repository format twice setup: inline `check_and_apply_repository_format()`
2026-06-26refs/reftable: lazy-load configuration to fix chicken-and-eggPatrick Steinhardt1-61/+85
Same as with the "files" backend, the "reftable" backend also has a chicken-and-egg problem with "onbranch" conditions. Fix this issue the same as we did with the "files" backend by lazy-loading configuration. Now that both the "files" and the "reftable" backend handle this properly, add a generic test to t1400 that verifies that the user can configure "core.logAllRefUpdates" via an "onbranch" condition. This is mostly a nonsensical thing to do in the first place, but it serves as a good sanity check. Note that we had to move `should_write_log()` around so that it can access the new `reftable_be_write_options()` function. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-26reftable: split up write optionsPatrick Steinhardt1-11/+18
When initializing the reftable stack the caller may optionally pass some write options. These write options mix up two different concerns though: - Of course, they allow the caller to configure how new reftables are being written. - But they also allow the caller to configure the stack itself, like its hash ID and the `on_reload` callback. This is somewhat awkward, as it doesn't easily give the caller the flexibility to for example write multiple reftables with different options. Furthermore, this requires us to eagerly parse relevant configuration when initializing the reftable backend. Refactor the code by splitting out those options that configure the stack itself. Creating a new stack will thus only require this limited set of options, whereas the caller is expected to pass write options to all functions that end up writing tables. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-26refs: move parsing of "core.logAllRefUpdates" back into ref storesPatrick Steinhardt1-9/+11
In cc42c88945 (refs: extract out reflog config to generic layer, 2026-05-04) we have refactored how we parse "core.logAllRefUpdates" so that it happens in the generic layer. Unfortunately, this has worsened a preexisting issue where we may recurse when creating the reference store because of a chicken-and-egg problem between parsing the configuration and evaluating "onbranch" conditions. Prepare for a fix by essentially reverting that change so that we handle this setting in the respective backends again. The backends are already parsing other configuration anyway, so by moving the logic back in there we can ensure that all backend configuration is parsed the same way. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-26refs: unregister reference stores from "chdir_notify"Patrick Steinhardt1-1/+15
When creating reference stores we register them with the "chdir_notify" subsystem. This is required because some of the paths we track may be relative paths, so we have to reparent them in case the current working directory changes. But while we register the reference stores, we never unregister them. This can have multiple outcomes: - For a repository's main reference database we essentially keep the pointer alive. We never free that database, either, and our leak checker doesn't notice because it's still registered. - For submodule and worktree reference databases we do eventually free them in `repo_clear()`, so we may keep pointers to free'd memory registered. We never notice though as we don't tend to chdir around in the middle of the process. We never noticed either of these symptoms, but they are obviously bad. Partially fix those issues by unregistering the reference stores when releasing them. The leak of the main reference database will be fixed in a subsequent commit. Note that this requires us to use `chdir_notify_register()` instead of `chdir_notify_reparent()`, as there is no infrastructure to unregister the latter. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-11treewide: drop USE_THE_REPOSITORY_VARIABLEPatrick Steinhardt1-3/+1
Adapt a couple of trivial callers of `is_bare_repository()` to instead use a repository available via the caller's context so that we can drop the `USE_THE_REPOSITORY_VARIABLE` macro. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-11environment: stop using `the_repository` in `is_bare_repository()`Patrick Steinhardt1-1/+1
Refactor `is_bare_repository()` to take in a repository parameter so that we no longer depend on `the_repository`. Adjust callers accordingly. Furthermore, move the function outside of the declarations that are only available when `USE_THE_REPOSITORY_VARIABLE` is set, as it no longer depends on that variable. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-21Merge branch 'kn/refs-generic-helpers'Junio C Hamano1-32/+8
Refactor service routines in the ref subsystem backends. * kn/refs-generic-helpers: refs: use peeled tag values in reference backends refs: add peeled object ID to the `ref_update` struct refs: move object parsing to the generic layer update-ref: handle rejections while adding updates update-ref: move `print_rejected_refs()` up refs: return `ref_transaction_error` from `ref_transaction_update()` refs: extract out reflog config to generic layer refs: introduce `ref_store_init_options` refs: remove unused typedef 'ref_transaction_commit_fn'
2026-05-11Merge branch 'sp/refs-reduce-the-repository'Junio C Hamano1-3/+3
Code clean-up to use the right instance of a repository instance in calls inside refs subsystem. * sp/refs-reduce-the-repository: refs/reftable-backend: drop uses of the_repository refs: remove the_hash_algo global state refs: add struct repository parameter in get_files_ref_lock_timeout_ms()
2026-05-05refs: use peeled tag values in reference backendsKarthik Nayak1-7/+2
The reference backends peel tag objects when storing references to them. This is to provide optimized reads which avoids hitting the odb. The previous commits ensures that the peeled value is now propagated via the generic layer. So modify the packed and reftable backend to directly use this value instead of calling `peel_object()` independently. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-05refs: add peeled object ID to the `ref_update` structKarthik Nayak1-3/+3
Certain reference backends {packed, reftable}, have the ability to also store the peeled object ID for a reference pointing to a tag object. This has the added benefit that during retrieval of such references, we also obtain the peeled object ID without having to use the ODB. To provide this functionality, each backend independently calls the ODB to obtain the peeled OID. To move this functionality to the generic layer, there must be support infrastructure to pass in a peeled OID for reference updates. Add a `peeled` field to the `ref_update` structure and modify `ref_transaction_add_update()` to receive and copy this object ID to the `ref_update` structure. Finally, modify `ref_transaction_update()` to peel tag objects and pass the peeled OID to `ref_transaction_add_update()`. Update all callers of these functions with the new function parameters. Callers which only add reflog updates, need to only pass in NULL, since for reflogs, we don't store peeled OIDs. Reference deletions also only need to pass in NULL. For others, pass along the peeled OID if available. In a following commit, we'll modify the backends to use this peeled OID instead of parsing it themselves. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-05refs: move object parsing to the generic layerKarthik Nayak1-19/+0
Regular reference updates made via reference transactions validate that the provided object ID exists in the object database, which is done by calling 'parse_object()'. This check is done independently by the backends which leads to duplicated logic. Let's move this to the generic layer, ensuring the backends only have to care about reference storage and not about validation of the object IDs. With this also remove the 'REF_TRANSACTION_ERROR_INVALID_NEW_VALUE' error type as its no longer used. Since we don't iterate over individual references in `ref_transaction_prepare()`, we add this check to `ref_transaction_update()`. This means that the validation is done as soon as an update is queued, without needing to prepare the transaction. It can be argued that this is more ideal, since this validation has no dependency on the reference transaction being prepared. It must be noted that the change in behavior means that this error cannot be ignored even with usage of batched updates, since this happens when the update is being added to the transaction. But since the caller gets specific error codes, they can either abort the transaction or continue adding other updates to the transaction. Modify 'builtin/receive-pack.c' to now capture the error type so that the error propagated to the client stays the same. Also remove two of the tests which validates batch-updates with invalid new_oid. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-05refs: extract out reflog config to generic layerKarthik Nayak1-1/+1
The reference backends need to know when to create reflog entries, this is dictated by the 'core.logallrefupdates' config. Instead of relying on the backends to call `repo_settings_get_log_all_ref_updates()` to obtain this config value, let's do this in the generic layer and pass down the value to the backends. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-05refs: introduce `ref_store_init_options`Karthik Nayak1-2/+2
Reference backends are initiated via the `init()` function. When initiating the function, the backend is also provided flags which denote the access levels of the initiator. Create a new structure `ref_store_init_options` to house such options and move the access flags to this structure. This allows easier extension of providing further options to the backends. In the following commit, we'll also provide config around reflog creation to the backends via the same structure. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-04-08refs/reftable-backend: drop uses of the_repositoryShreyansh Paliwal1-3/+3
reftable_be_init() and reftable_be_create_on_disk() use the_repository even though a repository instance is already available, either directly or via struct ref_store. Replace these uses with the appropriate local repository instance (repo or ref_store->repo) to avoid relying on global state. Note that USE_THE_REPOSITORY_VARIABLE cannot be removed yet, as is_bare_repository() is still there in the file. Signed-off-by: Shreyansh Paliwal <shreyanshpaliwalcmsmn@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-04-02reftable/stack: provide fsync(3p) via system headerPatrick Steinhardt1-6/+0
Users of the reftable library are expected to provide their own function callback in cases they want to sync(3p) data to disk via the reftable write options. But if no such function was provided we end up calling fsync(3p) directly, which may not even be available on some systems. While dropping the explicit call to fsync(3p) would work, it would lead to an unsafe default behaviour where a project may have forgotten to set up the callback function, and that could lead to potential data loss. So this is not a great solution. Instead, drop the callback function and make it mandatory for the project to define fsync(3p). In the case of Git, we can then easily inject our custom implementation via the "reftable-system.h" header so that we continue to use `fsync_component()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-09Merge branch 'ps/refs-for-each'Junio C Hamano1-5/+5
Code refactoring around refs-for-each-* API functions. * ps/refs-for-each: refs: replace `refs_for_each_fullref_in()` refs: replace `refs_for_each_namespaced_ref()` refs: replace `refs_for_each_glob_ref()` refs: replace `refs_for_each_glob_ref_in()` refs: replace `refs_for_each_rawref_in()` refs: replace `refs_for_each_rawref()` refs: replace `refs_for_each_ref_in()` refs: improve verification for-each-ref options refs: generalize `refs_for_each_fullref_in_prefixes()` refs: generalize `refs_for_each_namespaced_ref()` refs: speed up `refs_for_each_glob_ref_in()` refs: introduce `refs_for_each_ref_ext` refs: rename `each_ref_fn` refs: rename `do_for_each_ref_flags` refs: move `do_for_each_ref_flags` further up refs: move `refs_head_ref_namespaced()` refs: remove unused `refs_for_each_include_root_ref()`
2026-03-04Merge branch 'kn/ref-location'Junio C Hamano1-47/+14
Allow the directory in which reference backends store their data to be specified. * kn/ref-location: refs: add GIT_REFERENCE_BACKEND to specify reference backend refs: allow reference location in refstorage config refs: receive and use the reference storage payload refs: move out stub modification to generic layer refs: extract out `refs_create_refdir_stubs()` setup: don't modify repo in `create_reference_database()`
2026-02-25refs: receive and use the reference storage payloadKarthik Nayak1-10/+14
An upcoming commit will add support for providing an URI via the 'extensions.refStorage' config. The URI will contain the reference backend and a corresponding payload. The payload can be then used for providing an alternate locations for the reference backend. To prepare for this, modify the existing backends to accept such an argument when initializing via the 'init()' function. Both the files and reftable backends will parse the information to be filesystem paths to store references. Given that no callers pass any payload yet this is essentially a no-op change for now. To enable this, provide a 'refs_compute_filesystem_location()' function which will parse the current 'gitdir' and the 'payload' to provide the final reference directory and common reference directory (if working in a linked worktree). The documentation and tests will be added alongside the extension of the config variable. 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>
2026-02-25refs: move out stub modification to generic layerKarthik Nayak1-27/+0
When creating the reftable reference backend on disk, we create stubs to ensure that the directory can be recognized as a Git repository. This is done by calling `refs_create_refdir_stubs()`. Move this to the generic layer as this is needed for all backends excluding from the files backends. In an upcoming commit where we introduce alternate reference backend locations, we'll have to also create stubs in the $GIT_DIR irrespective of the backend being used. This commit builds the base to add that logic. Similarly, move the logic for deletion of stubs to the generic layer. The files backend recursively calls the remove function of the 'packed-backend', here skip calling the generic function since that would try to delete stubs. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-02-25refs: extract out `refs_create_refdir_stubs()`Karthik Nayak1-12/+2
For Git to recognize a directory as a Git directory, it requires the directory to contain: 1. 'HEAD' file 2. 'objects/' directory 3. 'refs/' directory Here, #1 and #3 are part of the reference storage mechanism, specifically the files backend. Since then, newer backends such as the reftable backend have moved to using their own path ('reftable/') for storing references. But to ensure Git still recognizes the directory as a Git directory, we create stubs. There are two locations where we create stubs: - In 'refs/reftable-backend.c' when creating the reftable backend. - In 'clone.c' before spawning transport helpers. In a following commit, we'll add another instance. So instead of repeating the code, let's extract out this code to `refs_create_refdir_stubs()` and use it. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-02-23refs: rename `do_for_each_ref_flags`Patrick Steinhardt1-5/+5
The enum `do_for_each_ref_flags` and its individual values don't match to our current best practices when it comes to naming things. Rename it to `refs_for_each_flag`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-02-09Merge branch 'kn/ref-batch-output-error-reporting-fix'Junio C Hamano1-3/+2
A handful of code paths that started using batched ref update API (after Git 2.51 or so) lost detailed error output, which have been corrected. * kn/ref-batch-output-error-reporting-fix: fetch: delay user information post committing of transaction receive-pack: utilize rejected ref error details fetch: utilize rejected ref error details update-ref: utilize rejected error details if available refs: add rejection detail to the callback function refs: skip to next ref when current ref is rejected
2026-01-25refs: skip to next ref when current ref is rejectedKarthik Nayak1-3/+2
In `refs_verify_refnames_available()` we have two nested loops: the outer loop iterates over all references to check, while the inner loop checks for filesystem conflicts for a given ref by breaking down its path. With batched updates, when we detect a filesystem conflict, we mark the update as rejected and execute 'continue'. However, this only skips to the next iteration of the inner loop, not the outer loop as intended. This causes the same reference to be repeatedly rejected. Fix this by using a goto statement to skip to the next reference in the outer loop. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12refs/reftable: introduce generic checks for refsPatrick Steinhardt1-6/+76
In a preceding commit we have extracted generic checks for both direct and symbolic refs that apply for all backends. Wire up those checks for the "reftable" backend. Note that this is done by iterating through all refs manually with the low-level reftable ref iterator. We explicitly don't want to use the higher-level iterator that is exposed to users of the reftable backend as that iterator may swallow for example broken refs. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12refs/reftable: fix consistency checks with worktreesPatrick Steinhardt1-15/+14
The ref consistency checks are driven via `cmd_refs_verify()`. That function loops through all worktrees (including the main worktree) and then checks the ref store for each of them individually. It follows that the backend is expected to only verify refs that belong to the specified worktree. While the "files" backend handles this correctly, the "reftable" backend doesn't. In fact, it completely ignores the passed worktree and instead verifies refs of _all_ worktrees. The consequence is that we'll end up every ref store N times, where N is the number of worktrees. Or rather, that would be the case if we actually iterated through the worktree reftable stacks correctly. But we use `strmap_for_each_entry()` to iterate through the stacks, but the map is in fact not even properly populated. So instead of checking stacks N^2 times, we actually only end up checking the reftable stack of the main worktree. Fix this bug by only verifying the stack of the passed-in worktree and constructing the backends via `backend_for_worktree()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12refs/reftable: extract function to retrieve backend for worktreePatrick Steinhardt1-27/+43
Pull out the logic to retrieve a backend for a given worktree. This function will be used in a subsequent commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12refs/reftable: adapt includes to become consistentPatrick Steinhardt1-2/+2
Adapt the includes to be sorted and to use include paths that are relative to the "refs/" directory. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-21Merge branch 'kn/maintenance-is-needed'Junio C Hamano1-0/+25
"git maintenance" command learned "is-needed" subcommand to tell if it is necessary to perform various maintenance tasks. * kn/maintenance-is-needed: maintenance: add 'is-needed' subcommand maintenance: add checking logic in `pack_refs_condition()` refs: add a `optimize_required` field to `struct ref_storage_be` reftable/stack: add function to check if optimization is required reftable/stack: return stack segments directly
2025-11-19Merge branch 'ps/ref-peeled-tags-fixes'Junio C Hamano1-1/+1
Another fix-up to "peeled-tags" topic. * ps/ref-peeled-tags-fixes: object: fix performance regression when peeling tags
2025-11-19Merge branch 'kn/refs-optim-cleanup'Junio C Hamano1-11/+4
Code clean-up. * kn/refs-optim-cleanup: t/pack-refs-tests: move the 'test_done' to callees refs: rename 'pack_refs_opts' to 'refs_optimize_opts' refs: move to using the '.optimize' functions
2025-11-19Merge branch 'ps/ref-peeled-tags'Junio C Hamano1-32/+15
Some ref backend storage can hold not just the object name of an annotated tag, but the object name of the object the tag points at. The code to handle this information has been streamlined. * ps/ref-peeled-tags: t7004: do not chdir around in the main process ref-filter: fix stale parsed objects ref-filter: parse objects on demand ref-filter: detect broken tags when dereferencing them refs: don't store peeled object IDs for invalid tags object: add flag to `peel_object()` to verify object type refs: drop infrastructure to peel via iterators refs: drop `current_ref_iter` hack builtin/show-ref: convert to use `reference_get_peeled_oid()` ref-filter: propagate peeled object ID upload-pack: convert to use `reference_get_peeled_oid()` refs: expose peeled object ID via the iterator refs: refactor reference status flags refs: fully reset `struct ref_iterator::ref` on iteration refs: introduce `.ref` field for the base iterator refs: introduce wrapper struct for `each_ref_fn`
2025-11-10refs: add a `optimize_required` field to `struct ref_storage_be`Karthik Nayak1-0/+25
To allow users of the refs namespace to check if the reference backend requires optimization, add a new field `optimize_required` field to `struct ref_storage_be`. This field is of type `optimize_required_fn` which is also introduced in this commit. Modify the debug, files, packed and reftable backend to implement this field. A following commit will expose this via 'git pack-refs' and 'git refs optimize'. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-06Merge branch 'pk/reflog-migrate-message-fix'Junio C Hamano1-1/+1
Message fix. * pk/reflog-migrate-message-fix: refs: add missing space in messages
2025-11-06object: fix performance regression when peeling tagsPatrick Steinhardt1-1/+1
Our Bencher dashboards [1] have recently alerted us about a bunch of performance regressions when writing references, specifically with the reftable backend. There is a 3x regression when writing many refs with preexisting refs in the reftable format, and a 10x regression when migrating refs between backends in either of the formats. Bisecting the issue lands us at 6ec4c0b45b (refs: don't store peeled object IDs for invalid tags, 2025-10-23). The gist of the commit is that we may end up storing peeled objects in both reftables and packed-refs for corrupted tags, where the claimed tagged object type is different than the actual tagged object type. This will then cause us to create the `struct object *` with a wrong type, as well, and obviously nothing good comes out of that. The fix for this issue was to introduce a new flag to `peel_object()` that causes us to verify the tagged object's type before writing it into the refdb -- if the tag is corrupt, we skip writing the peeled value. To verify whether the peeled value is correct we have to look up the object type via the ODB and compare the actual type with the claimed type, and that additional object lookup is costly. This also explains why we see the regression only when writing refs with the reftable backend, but we see the regression with both backends when migrating refs: - The reftable backend knows to store peeled values in the new table immediately, so it has to try and peel each ref it's about to write to the transaction. So the performance regression is visible for all writes. - The files backend only stores peeled values when writing the packed-refs file, so it wouldn't hit the performance regression for normal writes. But on ref migrations we know to write all new values into the packed-refs file immediately, and that's why we see the regression for both backends there. Taking a step back though reveals an oddity in the new verification logic: we not only verify the _tagged_ object's type, but we also verify the type of the tag itself. But this isn't really needed, as we wouldn't hit the bug in such a case anyway, as we only hit the issue with corrupt tags claiming an invalid type for the tagged object. The consequence of this is that we now started to look up the target object of every single reference we're about to write, regardless of whether it even is a tag or not. And that is of course quite costly. Fix the issue by only verifying the type of the tagged objects. This means that we of course still have a performance hit for actual tags. But this only happens for writes anyway, and I'd claim it's preferable to not store corrupted data in the refdb than to be fast here. Rename the flag accordingly to clarify that we only verify the tagged object's type. This fix brings performance back to previous levels: Benchmark 1: baseline Time (mean ± σ): 46.0 ms ± 0.4 ms [User: 40.0 ms, System: 5.7 ms] Range (min … max): 45.0 ms … 47.1 ms 54 runs Benchmark 2: regression Time (mean ± σ): 140.2 ms ± 1.3 ms [User: 77.5 ms, System: 60.5 ms] Range (min … max): 138.0 ms … 142.7 ms 20 runs Benchmark 3: fix Time (mean ± σ): 46.2 ms ± 0.4 ms [User: 40.2 ms, System: 5.7 ms] Range (min … max): 45.0 ms … 47.3 ms 55 runs Summary update-ref: baseline 1.00 ± 0.01 times faster than fix 3.05 ± 0.04 times faster than regression [1]: https://bencher.dev/perf/git/plots Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-05refs: add missing space in messagesPeter Krefting1-1/+1
Signed-off-by: Peter Krefting <peter@softwolves.pp.se> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: rename 'pack_refs_opts' to 'refs_optimize_opts'Karthik Nayak1-2/+2
The previous commit removed all references to 'pack_refs()' within the refs subsystem. Continue this cleanup by also renaming 'pack_refs_opts' to 'refs_optimize_opts' and the respective flags accordingly. Keeping the naming consistent will make the code easier to maintain. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: move to using the '.optimize' functionsKarthik Nayak1-10/+3
The `struct ref_store` variable exposes two ways to optimize a reftable backend: 1. pack_refs 2. optimize The former was specific to the 'files' + 'packed' refs backend. The latter is more generic and covers all backends. While the naming is different, both of these functions perform the same functionality. Consolidate this code to only maintain the 'optimize' functions. Do this by modifying the backends so that they exclusively implement the `optimize` callback, only. All users of the refs subsystem already use the 'optimize' function so there is no changes needed on the callee side. Finally, cleanup all references to the 'pack_refs' field of the structure and code around it. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: don't store peeled object IDs for invalid tagsPatrick Steinhardt1-1/+2
Both the "files" and "reftable" backend store peeled object IDs for references that point to tags: - The "files" backend stores the value when packing refs, where each peeled object ID is prefixed with "^". - The "reftable" backend stores the value whenever writing a new reference that points to a tag via a special ref record type. Both of these backends use `peel_object()` to find the peeled object ID. But as explained in the preceding commit, that function does not detect the case where the tag's tagged object and its claimed type mismatch. The consequence of storing these bogus peeled object IDs is that we're less likely to detect such corruption in other parts of Git. git-for-each-ref(1) for example does not notice anymore that the tag is broken when using "--format=%(*objectname)" to dereference tags. One could claim that this is good, because it still allows us to mostly use the tag as intended. But the biggest problem here is that we now have different behaviour for such a broken tag depending on whether or not we have its peeled value in the refdb. Fix the issue by verifying the object type when peeling the object. If that verification fails we simply skip storing the peeled value in either of the reference formats. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04object: add flag to `peel_object()` to verify object typePatrick Steinhardt1-2/+2
When peeling a tag to a non-tag object we repeatedly call `parse_object()` on the tagged object until we find the first object that isn't a tag. While this feels sensible at first, there is a big catch here: `parse_object()` doesn't actually verify the type of the tagged object. The relevant code path here eventually ends up in `parse_tag_buffer()`. Here, we parse the various fields of the tag, including the "type". Once we've figured out the type and the tagged object ID, we call one of the `lookup_${type}()` functions for whatever type we have found. There is two possible outcomes in the successful case: 1. The object is already part of our cached objects. In that case we double-check whether the type we're trying to look up matches the type that was cached. 2. The object is _not_ part of our cached objects. In that case, we simply create a new object with the expected type, but we don't parse that object. In the first case we might notice type mismatches, but only in the case where our cache has the object with the correct type. In the second case, we'll blindly assume that the type is correct and then go with it. We'll only notice that the type might be wrong when we try to parse the object at a later point. Now arguably, we could change `parse_tag_buffer()` to verify the tagged object's type for us. But that would have the effect that such a tag cannot be parsed at all anymore, and we have a small bunch of tests for exactly this case that assert we still can open such tags. So this change does not feel like something we can retroactively tighten, even though one shouldn't ever hit such corrupted tags. Instead, add a new `flags` field to `peel_object()` that allows the caller to opt in to strict object verification. This will be wired up at a subset of callsites over the next few commits. Note that this change also inlines `deref_tag_noverify()`. There's only been two callsites of that function, the one we're changing and one in our test helpers. The latter callsite can trivially use `deref_tag()` instead, so by inlining the function we avoid having to pass down the flag. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: drop infrastructure to peel via iteratorsPatrick Steinhardt1-24/+0
Now that the peeled object ID gets propagated via the `struct reference` there is no need anymore to call into the reference iterator itself to dereference an object. Remove this infrastructure. Most of the changes are straight-forward deletions of code. There is one exception though in `refs/packed-backend.c::write_with_updates()`. Here we stop peeling the iterator and instead just pass the peeled object ID of that iterator directly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: expose peeled object ID via the iteratorPatrick Steinhardt1-0/+5
Both the "files" and "reftable" backend are able to store peeled values for tags in the respective formats. This allows for a more efficient lookup of the target object of such a tag without having to manually peel via the object database. The infrastructure to access these peeled object IDs is somewhat funky though. When iterating through objects, we store a pointer reference to the current iterator in a global variable. The callbacks invoked by that iterator are then expected to call `peel_iterated_oid()`, which checks whether the globally-stored iterator's current reference refers to the one handed into that function. If so, we ask the iterator to peel the object, otherwise we manually peel the object via the object database. Depending on global state like this is somewhat weird and also quite fragile. Introduce a new `struct reference::peeled_oid` field that can be populated by the reference backends. This field can be accessed via a new function `reference_get_peeled_oid()` that either uses that value, if set, or alternatively peels via the ODB. With this change we don't have to rely on global state anymore, but make the peeled object ID available to the callback functions directly. Adjust trivial callers that already have a `struct reference` available. Remaining callers will be adjusted in subsequent commits. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: fully reset `struct ref_iterator::ref` on iterationPatrick Steinhardt1-0/+1
With the introduction of the `struct ref_iterator::ref` field it now is a whole lot easier to introduce new fields that become accessible to the caller without having to adapt every single callsite. But there's a downside: when a new field is introduced we always have to adapt all backends to set that field. This isn't something we can avoid in the general case: when the new field is expected to be populated by all backends we of course cannot avoid doing so. But new fields may be entirely optional, in which case we'd still have such churn. And furthermore, it is very easy right now to leak state from a previous iteration into the next iteration. Address this issue by ensuring that the reference backends all fully reset the field on every single iteration. This ensures that no state from previous iterations can leak into the next one. And it ensures that any newly introduced fields will be zeroed out by default. Note that we don't have to explicitly adapt the "files" backend, as it uses the `cache_ref_iterator` internally. Furthermore, other "wrapping" iterators like for example the `prefix_ref_iterator` copy around the whole reference, so these don't need to be adapted either. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-11-04refs: introduce `.ref` field for the base iteratorPatrick Steinhardt1-6/+6
The base iterator has a couple of fields that tracks the name, target, object ID and flags for the current reference. Due to this design we have to create a new `struct reference` whenever we want to hand over that reference to the callback function, which is tedious and not very efficient. Convert the structure to instead contain a `struct reference` as member. This member is expected to be populated by the implementations of the iterator and is handed over to the callback directly. While at it, simplify `should_pack_ref()` to take a `struct reference` directly instead of passing its respective fields. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>