Configuration and provenance
Before interpreting settings, a tool has to decide which configuration sources it trusts and reads. girt keeps three operations separate: parse supplied bytes, resolve explicit sources and includes, and edit one direct file while holding its lock. An effective snapshot can contain many files; it cannot safely be written back as though it were one source document.
Select sources and publish a local override
Section titled “Select sources and publish a local override”In a Cargo binary project, run cargo add girt tempfile and put this program in src/main.rs. It
supplies a disposable global source, opens a repository with it, writes a local override, and
explains the winning source:
use girt::config::{ConfigFile, ConfigInputs, ConfigScope};use girt::{InitKind, ObjectFormat, Repository};
fn main() -> Result<(), Box<dyn std::error::Error>> { let directory = tempfile::tempdir()?; let repo = Repository::init( ObjectFormat::Sha1, directory.path().join("project.git"), InitKind::Bare, )?;
let global = directory.path().join("global"); std::fs::write(&global, b"[core]\neditor = user-editor\n")?; let inputs = ConfigInputs::default().with_files(vec![ConfigFile::new(global, ConfigScope::Global)]);
let opened = Repository::open_with_config(repo.git_dir(), &inputs)?; assert_eq!( opened.config().string("core", None, "editor"), Some(&b"user-editor"[..]) );
let mut edit = opened.edit_config(4096)?; edit.document_mut() .append("core", None, "editor", b"project-editor")?; edit.commit()?; // Publication does not mutate a previously opened configuration snapshot. assert_eq!( opened.config().string("core", None, "editor"), Some(&b"user-editor"[..]) );
let refreshed = Repository::open_with_config(repo.git_dir(), &inputs)?; assert_eq!( refreshed.config().string("core", None, "editor"), Some(&b"project-editor"[..]) );
let winning = refreshed .config() .entries() .iter() .rev() .find(|entry| { entry.section.eq_ignore_ascii_case(b"core") && entry.subsection.is_none() && entry.name.eq_ignore_ascii_case(b"editor") }) .and_then(|entry| entry.origin.as_ref()) .ok_or("origin missing")?; assert_eq!(winning.scope, ConfigScope::Local); assert_eq!( winning.location.path.as_deref(), Some(refreshed.common_dir().join("config").as_path()) );
Ok(())}Run it with cargo run. Everything is removed with the temporary directory. The example treats an
editor setting as data; it does not execute it. An application that launches configured commands
must decide for itself which values to trust and how to run them.
Appending a local occurrence makes it the scalar winner while preserving earlier occurrences. Use
Document::set_value to change a particular existing occurrence identified in the current draft.
Explain precedence before editing
Section titled “Explain precedence before editing”File scopes run from system and global through local and enabled worktree configuration, followed by supplied environment and command overrides. Within a scope, caller order remains significant; select XDG configuration before the home file. Ordinary includes expand in place. Repository format bootstrap stays separate: an override cannot reinterpret SHA-1 storage as SHA-256.
Use entries() and each entry’s origin to explain where occurrences came from. Scalar lookup takes
the last value, but each consumer of a repeated variable has its own rules for combining
occurrences. Let the URL and refspec APIs interpret those values instead of flattening them
yourself. Scope describes precedence, not permission to execute or trust a value.
An absent setting, an implicit assignment, and an explicit empty value are distinct. Boolean lookup interprets implicit as true and empty as false; string lookup treats implicit as absent. A successful parse does not establish that a value is a valid boolean, integer, URL, or safe command. Propagate a typed lookup error instead of silently substituting a default.
Supply environment and include context explicitly
Section titled “Supply environment and include context explicitly”To see the configuration git would see, pass an environment snapshot such as
workflow::Environment::from_process() to ConfigInputs::git_default, then open with
Repository::discover_with_config or Repository::open_with_config. It selects the system file,
the XDG and home global files, the GIT_CONFIG_* file selectors and the runtime pairs, in Git’s
order. Use ConfigInputs::from_environment instead to choose the system file and pair budget
yourself. Use Config::from_environment only to decode GIT_CONFIG_COUNT and numbered
GIT_CONFIG_KEY_n / GIT_CONFIG_VALUE_n pairs, where n starts at zero. Neither reads or mutates
process-global state itself.
Conditional includes can depend on branch names, logical/canonical Git-directory aliases, or remote
URLs. Expanding home and prefix paths needs that context too. Supply the facts the application
knows, then resolve again when those facts change. Default resolution matches ordinary include
placement. An adapter that requires each top-level source’s URL conditions to ignore other top-level
sources can select IncludeConditionVisibility::RootSnapshot; that mode can include different files
than ordinary resolution. See the
configuration module for both modes and their
limits.
Edit the intended source and recover deliberately
Section titled “Edit the intended source and recover deliberately”The repository edit entry point selects its common local config. To edit an approved global or other
direct file, use ConfigEdit::open on that path. Includes are not followed for editing. Document
retains comments and unrelated syntax; its occurrence indices and section ordinals refer to the
current draft. Reacquire them after edits. Use require_source when publication should depend on
the exact bytes or absence observed earlier.
A lock conflict does not prove the lock is stale. A changed-source error means the file changed
since you read it: read it again and decide whether the edit still makes sense. Fix syntax errors in
the source or the draft. If the source or the edited output exceeds the byte budget, decide whether
you trust it enough to raise the budget. If cleanup fails, inspect the lock file your edit left
behind. Call abort to see cleanup failures; dropping the edit only cleans up on a best-effort
basis. Publication does not promise power-loss durability or an atomic transaction across several
files.
After a successful commit, reopen or re-resolve before constructing dependent values. Existing repository, configuration, and remote values remain snapshots. The ConfigEdit contract describes the filesystem assumptions and error evidence that a retry must preserve.