feat(config): discovery loader - Discover() (config phase B4) - #95
Merged
Conversation
Fourth slice of the config loader (#58), per docs/design-config.md. xff/config/loader.{h,cc}: Discover(opts, read) locates and parses the layers into ConfigInputs: - system /etc/xff.ini (always read; its [policy] is never skipped), - user config via UserConfigPath ($XFF_CONFIG, else $XDG_CONFIG_HOME/xff/config, else $HOME/.config/xff/config), - explicit --xffrc=FILE armed into the user layer, in order. The FileReader is injected (run.cc will supply a std::ifstream-backed reader), so discovery is unit-tested without touching /etc or the home dir. --no-config skips the user layer and explicit files (ResolveConfig also drops the system [defaults]) but the system file is still read so its policy is available to the phase-C gate. Project cascade is phase E; no run.cc wiring yet (next slice).
helly25
enabled auto-merge (squash)
June 23, 2026 08:12
helly25
added a commit
that referenced
this pull request
Jul 1, 2026
…, PR 4/4) (#201) * Add -g / --gitignore: respect per-directory .gitignore (ignore family, PR 4/4) Completes the core ignore family: -g / --gitignore makes xff respect a .gitignore in every directory, reusing the #199 per-directory stack (deeper overrides shallower). Off by default (find-compatible); --gitignore=off turns it back off, and -u / --no-ignore remains the master switch over every source. - run.cc: GitignoreEnabled(globals) (last occurrence wins, =off disables); ResolveIgnoreFileNames now builds the per-directory filename list from all sources, lowest precedence first: .gitignore (-g) < .ignore < .xffignore (--ignore-files), so within a directory the xff-specific file wins. -u/--no-ignore returns none. Self-documentation (same change): --gitignore (alias -g) GlobalFlag in the Ignore / filter group (flow into --help / --man / --markdown) + the kHelpText usage page. cli/ignore_gitignore_test.sh: off by default, -g respects recursively, =off and -u/--no-ignore disable, a nested .gitignore scopes to its subtree, --help topic. Scope note: bare -g means ON today. The design's bare-g=auto (respect iff in a git repo), .git/info/exclude, global core.excludesFile, and the -g+/-g- suffix-sign spelling need git-repo detection + toggle-parser machinery not built yet; tracked as a follow-up (#95). bazel test //... green (33 tests). * ignore_gitignore_test: assert exit code + surface output on help-topic failure The --help=-g assertion flaked once on the ubuntu clang-asan CI cell (intermittent, not reproducible on macOS-asan / ubuntu-default / locally) as a bare 'substring missing', hiding the xff subprocess's captured stderr. Assert the exit status and print the captured output on mismatch so a recurrence shows the real cause (e.g. a sanitizer abort) rather than masquerading. Diagnostic + stricter; no behavior change.
helly25
added a commit
that referenced
this pull request
Jul 3, 2026
…) (#222) New xff/repo module: FindRepoRoot walks up from a search root to the git working-tree root (the first ancestor with a .git dir or file), probed via the VFS (Stat), so a fake filesystem drives the unit tests. Wire it into the -g / --gitignore ternary: bare -g / --gitignore now selects AUTO (respect per-directory .gitignore only when a search root is inside a git repo, matching git's own behavior), while --gitignore=on forces it on regardless and --gitignore=off forces it off. Last occurrence wins; still off by default (find-compatible), and -u / --no-ignore still overrides. Previously bare -g meant unconditional on. Self-doc: --gitignore GlobalFlag summary + kHelpText line describe the ternary; design.md notes what shipped vs pending. Tests: repo_test (unit, fake fs) + ignore_gitignore_test.sh (e2e: auto-on in a repo, auto-off outside, =on forces outside, =off/-u disable). Follow-ups (still #95): -g+/-g- short spellings, .git/info/exclude, and core.excludesFile.
helly25
added a commit
that referenced
this pull request
Jul 3, 2026
Complete the -g ternary's short forms: -g+ == --gitignore=on (force on), -g- == --gitignore=off (force off), alongside the existing bare -g = auto. Both are recognized in ResolveGitignoreMode (last occurrence wins); no parser change is needed since leading dash tokens are already collected as globals verbatim. Self-doc: --gitignore display + summary and the kHelpText line show the -g[+|-] forms; design.md moves them from pending to shipped. Tests: ignore_gitignore_test.sh gains -g+ forces on outside a repo and -g- forces off inside a repo (overriding the auto-on). Remaining under #95: .git/info/exclude and core.excludesFile.
helly25
added a commit
that referenced
this pull request
Jul 3, 2026
When gitignore is on and a search root is inside a git repo, rebase the IgnoreFileCache ancestor walk on the REPO ROOT instead of the search root (git/rg/fd behavior): every directory from the entry up to the repo root is consulted for .gitignore -- including directories ABOVE the search root -- and the repo's .git/info/exclude is applied at the bottom, below every .gitignore (matched relative to the repo root). Off a repo, or with gitignore off, the walk stops at the search root as before, so existing behavior (and every existing test) is unchanged; the new reach only appears when running xff inside a repo subdirectory. IgnoreFileCache gains a per-root Scope (repo root + the root's absolute form for rebasing relative entries) and a RepoExcludeFor cache for .git/info/exclude; the walk resolves entries to absolute in repo scope so paths above the search root reconcile. AbsoluteDir moved above the class. Tests: ignore_gitignore_test.sh gains .git/info/exclude honored, and -g from a subdir honoring the repo-root .gitignore + .git/info/exclude above it. Remaining under #95: global core.excludesFile.
helly25
added a commit
that referenced
this pull request
Jul 3, 2026
Complete the .gitignore stack with git's global excludes, the lowest ignore layer. xff/repo gains GlobalExcludesPath: reads core.excludesFile from the git config files ($XDG_CONFIG_HOME/git/config then ~/.gitconfig, the latter winning), expands a leading ~, and falls back to git's default $XDG_CONFIG_HOME/git/ignore (~/.config/git/ignore) when unset. VFS-backed and env-injected (GitConfigEnv), so it is unit-tested hermetically with a fake fs. run.cc resolves the path once when gitignore is on (HOME/XDG_CONFIG_HOME from the environment, read through walk_fs so --dry-run still consults it), loads it into a PatternList, and hands it to IgnoreFileCache, which applies it in Decide below .git/info/exclude (matched relative to the repo root). Empty (a no-op) when gitignore is off or no file exists. Tests: repo_test covers core.excludesFile parsing (case-insensitive section/key, quotes, ~ expansion, XDG vs ~/.gitconfig precedence, default fallback, no-HOME). ignore_gitignore_test.sh drives the real binary with a throwaway HOME for both the default ~/.config/git/ignore and a core.excludesFile custom path. This finishes #95's gitignore refinements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth slice of the config loader (#58), per
docs/design-config.md.What
xff/config/loader.{h,cc}-Discover(opts, read)locates and parses the layers intoConfigInputs(ready forResolveConfig):/etc/xff.ini- always read; its[policy]is never skipped by--no-config.UserConfigPath:$XFF_CONFIG, else$XDG_CONFIG_HOME/xff/config, else$HOME/.config/xff/config.--xffrc=FILEarmed into the user layer, in order (naming the file is the consent).The
FileReaderis injected (anabsl::FunctionRef), so discovery is unit-tested with an in-memory fake fs - no touching/etcor~.run.ccwill supply astd::ifstream-backed reader.--no-configskips the user layer + explicit files (andResolveConfigdrops the system[defaults]), but the system file is still read so its policy reaches the phase-C gate.Tests
UserConfigPathprecedence + empty; system+user layering under an active--config; explicit--xffrcordering;--no-config(policy still parsed, resolution empty); missing files -> empty layers. Green on default +--config=clang --config=asan; clang-format + no-em-dash clean.Roadmap: B1, B2, B3, B4 (this) -> B5
run.ccwiring -> C policy gate -> D styles/features (#54) -> E argv[0]+cascade+explain (#59). Project cascade is phase E.