feat(config): read [[context]] rules from global git config - #147
Merged
Conversation
#145 shipped context rules with the config file as their only source, which left them as the one setting that could not live in git config — awkward for a feature modelled on git's own includeIf, which is git config. git config --global wt.context.work.whenpath "~/dev/repos/work" git config --global --add wt.context.work.env "WT_CATEGORY=work" git config --global --add wt.context.work.env "WT_ORG=acme" The loader kept one value per key, which is right for every scalar setting but loses all but the last variable of a rule. It now returns entries in the order git listed them, and gitConfigValues collapses them to last-wins for the scalars, so their behaviour is unchanged. Order is kept because it is what decides how rules compose. The two sources compose rather than replace: git config rules are evaluated first, then the config file's, under the same later-definitions-win rule that already governs rules within one source. So the config file wins wherever both cover the same path — the documented precedence — while a git config rule for an unrelated tree keeps working instead of vanishing the moment a [[context]] block is added to the file. Global scope only. A rule scoped to one repository is redundant, since that repository could set wt.pattern directly, and keeping rules out of --local holds the same user-owned line that already keeps them out of a committed .wt.toml. The system scope is skipped for a duller reason: wt reads no system git config for any setting, and making this the exception would be worse than the gap. Closes #146
timvw
enabled auto-merge (squash)
August 20, 2026 11:23
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #147 +/- ##
==========================================
+ Coverage 43.70% 44.42% +0.72%
==========================================
Files 35 35
Lines 3647 3689 +42
==========================================
+ Hits 1594 1639 +45
+ Misses 2053 2050 -3
🚀 New features to boost your workflow:
|
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.
Closes #146. Follow-up to #145, which shipped
[[context]]rules with the config file as their only source.wt.context.<name>.<key>is a git subsection, so<name>is a handle that makes the rule removable withgit config --global --remove-section 'wt.context.work'.The loader change
gitConfigFnreturnedmap[string]string, keeping only the last value per key. That is correct for every setting that existed — all scalars — but a rule setsenvonce per variable with--add, so all but the last would be dropped. It now returns[]gitConfigEntryin the order git listed them, andgitConfigValuescollapses that to last-wins for the scalars, leaving their behaviour untouched.Order is retained because it is what decides composition: first appearance of a name fixes a rule's position, so a later entry for an earlier rule merges into it rather than moving it to the end.
Composition, not replacement
Git config rules are evaluated first, then the config file's, under the same "later definitions win per variable" rule that already governs rules within a single source.
The alternative — the config file replacing the git config rules wholesale — would have matched how scalars behave, but it makes adding one unrelated
[[context]]block toconfig.tomlsilently delete every rule in~/.gitconfig. Appending gets the documented precedence (the file wins wherever both cover the same path) out of the mechanism that was already there, with no second rule to learn.Scope
Global only. Not
--local: a path rule scoped to a single repository is redundant, since that repository could setwt.patterndirectly, and.git/configis shared by every worktree of a repo. Keeping rules out of it holds the same user-owned line that already keeps them out of a committed.wt.toml.Not
--systemeither, but for a duller reason than the issue implied:wtreads no system git config for any setting. Makingwt.context.*obey a scope thatwt.rootignores would be a worse inconsistency than the gap. Noted in the docs.Parsing details
envvalues areNAME=VALUE, split on the first=; name and value are trimmed. No=, or an empty name, is skipped rather than guessed at — there is no reading of a bareWT_CATEGORYthat is obviously right, and inventing an empty value would collapse a path segment.wt.context.acme.api.env); the split is on the last one.wt.context.prefix is matched case-insensitively while the rule's name keeps the case it was written with.Tests
TestContextRulesFromGitConfigcovers multi-valueenv, ordering, dotted names, subsection case, and the malformed-entry cases.TestContextRulesFromRealGitConfigruns the same thing through actualgit config --addin a scratch repo, so the documented key shape is verified against git rather than against my expectation of it.TestGlobalGitConfigLoadsContextRules,TestLocalGitConfigCannotSupplyContextRulesandTestConfigFileRulesComposeOverGitConfigRulescover the loader end to end.Both the append-vs-replace choice and the case-insensitive prefix were mutation-checked — reverting either makes a test fail with a message naming the behaviour.
Existing loader tests were updated for the new signature and given an explicit git config stub: several called
loadWorktreeConfig()without one, so a realwt.context.*in the developer's own~/.gitconfigwould have leaked into their assertions.