Skip to content

gix config refresh and writing - #2929

Open
Sebastian Thiel (Byron) wants to merge 5 commits into
mainfrom
config-refresh
Open

gix config refresh and writing#2929
Sebastian Thiel (Byron) wants to merge 5 commits into
mainfrom
config-refresh

Conversation

@Byron

@Byron Sebastian Thiel (Byron) commented Aug 18, 2026

Copy link
Copy Markdown
Member

Tasks

This section is for Byron only. Models continuing this PR must not add, remove, check, uncheck, rename, or reorder checkboxes here.

  • refackiew

Everything below this line was generated by Codex GPT-5.

Created by Codex on behalf of Byron. Byron will review before this is ready to merge.

Summary

  • add Repository::config() and config_mut() accessors that refresh changed configuration files while leaving snapshot-only access free of freshness I/O
  • preserve runtime API sections across reloads without duplicating open::Options::config_overrides()
  • add SnapshotMut::commit_to_file() to atomically write one exact file designation with lock-first mtime validation, permission preservation, and deleted-file recreation
  • reject stale files, forged designations, override-only sources, and invalid snapshots before changing disk or in-memory state

Commits

  • 4c759f742e — refresh repository configuration on access, including the platform-specific size guard
  • 9bacd89ae1 — write mutable configuration snapshots to disk

Git baseline

Git's config.c clears and reloads repository configuration with includes, and its file mutation path locks <path>.lock, preserves the file mode, then commits by atomic rename. This change adapts those behaviors to long-lived gix::Repository snapshots and adds the requested mtime stale-write guard.

Validation

  • GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix --test gix repository::config::config_snapshot
  • GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix --test gix repository::config::config_snapshot::commit_to_file
  • GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix --test gix repository::size_in_memory -- --exact
  • GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix --test gix --features async-network-client repository::size_in_memory -- --exact
  • cargo clippy -p gix --all-targets -- -D warnings
  • cargo deny --workspace --all-features check bans licenses sources
  • env GIX_TEST_IGNORE_ARCHIVES=1 just ci-test
  • GitHub CI before the Tix-only history cleanup: all 32 checks passed; the resulting tree is unchanged

The required one-time codex review --commit invocation was attempted for each original pre-squash commit, but the review service rejected all four because the account usage limit was exhausted. Independent agent audits were completed for both feature commits and the Windows CI fix.

Reported issue

Original report

While gix::Repository::index() checks if the index file has to be reloaded, the git configuration has no such mechanism. `config_snapshot_mut()` is intentionally a snapshot, and refreshing it is currently impossible.
It would be a start if there was a `config()` or `config_mut()` that returns a snapshot (just like the current accessors), but after checking that none of the included configuration files changed. This information is readily present in section metadata. If any of these files changed according to the modification date, then reload the configuration just like it happens when opening (or reloading) so side-effects are handled.
Make sure that open::Options::config_overrides() aren't duplicated, and that API-only sections aren't lost either.

Write-back follow-up

In a separate commit, let's also add a way to write back changes to any file, based on the in-memory file. This will never write API or env overrides, but writes back any section that matches the desigation metadata. This entails a way to select which single file to write probably by passing section metadata identifying the file, to obtaina lock, and to write changes back to the file if it's not stale based on the mtime should should have. A deleted file is not considered stale.
If the file to write changed after the snapshot was obtained, fail. In that case, the user has to retry with a refreshed file.

Clarification

>  - Capture original file designations and mtime baselines when creating the mutable snapshot.

No, this information is part of what `repo.cache` holds, and the `config_snapshot_mut()` call for instance won't do additional IO. This is only done by `config()` and `config_mut()` respectively.


> - Resolve symlinks, acquire the target’s gix_lock::File, then compare mtimes while holding the lock.

Don't do that, only the local configuration files are (likely) writable and support locks there, and system wide ones probably aren't. But yeah, hold the lock before comparing the mtime of the file to write.

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@Byron Sebastian Thiel (Byron) changed the title feat(gix): refresh and persist repository configuration snapshots gix config refresh and writing Aug 19, 2026
@Byron
Sebastian Thiel (Byron) force-pushed the config-refresh branch 5 times, most recently from b1e8570 to cbdb3b0 Compare August 27, 2026 12:10
Add opt-in final-symlink resolution for callers that need Git default lockfile behavior while retaining the existing no-follow API for loose references. Resolve again before each retry and retain the target whose lock was actually acquired.

Append the lock suffix directly to OsString values so non-UTF-8 resource names remain byte-exact. Retain each original resource path instead of reconstructing it from the lock filename, avoiding panics for dotfile locks.

Allow symlink-aware callers to adjust lock permissions after the process umask has been applied, so shared-repository modes can grant the intended access.
Add Repository::config_file_mut() as a transaction over one physical configuration file. It acquires a symlink-aware lock before reading, parses without expanding includes, preserves formatting and existing permissions, and resolves relative paths against the opening CWD.

Lock acquisition honors the discoverable core.configLockTimeout key with Git-compatible parsing and a 1000 ms default. New files also honor core.sharedRepository after the process umask, including named, boolean, compatibility, and explicit octal modes.

Committing only writes the file atomically. Repository state changes through an explicit full reload, which retains normal Git-compatible validation and rebuilds include- and bootstrap-dependent state without a second partial-refresh path.
The tempfile crate normalizes a temporary file attributes before persistence, which drops permissions copied from the resource being updated. Windows also refuses to replace a read-only destination directly, causing config commits either to fail or to become writable.

Persist with MoveFileExW on Windows after removing only the temporary attribute and temporarily clearing a read-only destination. Restore both paths attributes after failed attempts so the existing retry and recovery guarantees remain intact.

Cover direct tempfile replacement and repository config mutation with Windows regression tests.
… Windows regression tests.

Assisted-by: GPT 5.6
Co-authored-by: GPT 5.6 <codex@openai.com>
Windows canonicalization can produce verbatim paths with a `\\?\` prefix while included configuration metadata uses an ordinary drive path. Comparing those representations directly prevents an explicitly safe config file from being promoted to full trust, including after repository reloads.

Canonicalize both the path under test and configured safe-directory paths through the filesystem before exact or wildcard comparison. Retain the existing lexical realpath fallback for missing paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants