Skip to content

feat(config): local-context-first config via git local config#23

Open
bezhermoso wants to merge 6 commits intoblock:mainfrom
bezhermoso:bezalel/local-context
Open

feat(config): local-context-first config via git local config#23
bezhermoso wants to merge 6 commits intoblock:mainfrom
bezhermoso:bezalel/local-context

Conversation

@bezhermoso
Copy link
Contributor

@bezhermoso bezhermoso commented Feb 25, 2026

Summary

Adds support for reading wt configuration directly from a repo's .git/config, enabling local-context-first semantics: a repo can carry its own wt configuration without requiring ~/.wt/repos/<name>.conf or wt context add setup.

This means a user can configure a repo once:

git config --local wt.enabled true
git config --local wt.worktreesBase ~/src/monorepo-worktrees
git config --local wt.ideaFilesBase ~/src/monorepo-idea
git config --local wt.baseBranch main

...and all wt commands Just Work from inside that repo or any of its worktrees, with no global context setup needed.

How it works

A new wt_read_git_config() function in lib/wt-common is called at source-time, before the existing wt_read_config(). It checks for the wt.enabled=true gate, reads wt.* keys from the repo's local git config via git config --local --get-regexp '^wt\.', validates them, and sets the corresponding WT_* shell variables.

Config precedence (highest to lowest)

Priority Source When it wins
1 Git local config (wt.* in .git/config) CWD is inside a repo/worktree with wt.enabled=true and all required keys
2 Environment variable Exported before wt-common sourced
3 Context .conf file ~/.wt/repos/<name>.conf has the value
4 Hardcoded default Nothing else provided it

Git config keys

Git config key Shell variable Required?
wt.enabled (gate — must be true) Yes
wt.worktreesBase WT_WORKTREES_BASE Yes
wt.ideaFilesBase WT_IDEA_FILES_BASE Yes
wt.baseBranch WT_BASE_BRANCH Yes
wt.activeWorktree WT_ACTIVE_WORKTREE No (optional)
wt.metadataPatterns WT_METADATA_PATTERNS No (optional)

WT_MAIN_REPO_ROOT is always auto-derived from git rev-parse --git-common-dir and is not a configurable key. This eliminates a potential source of conflict between what git says the repo root is and what the user configured.

All-or-nothing validation

All three required keys must be present and non-empty. If any is missing, no git config values are applied and a diagnostic warning is printed to stderr naming the missing keys.

Key design choices

  • Explicit opt-in: wt.enabled=true must be set — repos don't accidentally pick up local config
  • Worktree-transparent: git config --local in a worktree reads from the main repo's .git/config automatically, so config set once in the main repo is visible from all worktrees
  • mainRepoRoot is not configurable: always derived from git-common-dir to avoid stale/conflicting values
  • Read-only: wt never writes to git config; users manage it with standard git config commands
  • Bash 3.2 compatible: uses tr for case-insensitive key matching (macOS ships bash 3.2)
  • No interference with existing workflows: repos without wt.enabled=true are silently skipped; the context system and env vars continue to work exactly as before
  • Scoped reload: only wt help calls wt_read_git_config after a force-reload — context switching and completions intentionally reload from .conf only

What's NOT changing

  • No wt config subcommand (users use git config directly)
  • No changes to wt context add / install.sh setup flows
  • No git global config support (local only)
  • No changes to the .conf file format or context system

Test plan

  • 40 unit tests covering: gate behavior, core config loading, validation, optional keys, case insensitivity, worktree support, auto-derivation, and precedence
  • All existing integration/e2e tests still pass (1 pre-existing failure in wt-remove --merged is unrelated)
  • Manual: git config --local wt.enabled true + set 3 required keys, wt help shows values
  • Manual: cd into a worktree of that repo — same result
  • Manual: set wt.enabled false, run wt help — falls back to .conf/defaults
  • Manual: set only wt.baseBranch (incomplete), run wt help — warning on stderr
  • Manual: outside any git repo, wt help shows .conf/default values, no warning

🤖 Generated with Claude Code

…mantics

Add wt_read_git_config() to lib/wt-common, enabling repos to carry their
own wt configuration via standard `git config --local wt.*` keys. This
removes the hard dependency on ~/.wt/repos/<name>.conf for repos that
prefer self-contained configuration.

New four-tier config precedence (highest to lowest):
  1. Git local config (wt.* keys in .git/config)
  2. Environment variables
  3. Context .conf file (~/.wt/repos/<name>.conf)
  4. Hardcoded defaults

The function reads wt.* keys atomically: all four required keys
(wt.mainRepoRoot, wt.worktreesBase, wt.ideaFilesBase, wt.baseBranch)
must be present or the entire git config source is ignored with a
diagnostic warning. This prevents confusing partial-config states.

Key design choices:
- All-or-nothing validation prevents half-configured repos
- Works transparently from worktrees (git config --local reads from
  the main repo's .git/config automatically)
- Case-insensitive key matching per git convention
- Bash 3.2 compatible (uses tr instead of ${key,,})
- Read-only: wt never writes to git config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bezhermoso and others added 4 commits February 24, 2026 20:47
wt.mainRepoRoot is redundant when using git local config — the main
repo root is always derivable via `git rev-parse --git-common-dir`
(its parent directory), which works correctly from both the main repo
and any worktree.

Reduce required git config keys from 4 to 3:
  - wt.worktreesBase (required)
  - wt.ideaFilesBase (required)
  - wt.baseBranch (required)
  - wt.mainRepoRoot (optional override, auto-derived if absent)

This means the minimal setup is now:
  git config --local wt.worktreesBase ~/src/repo-worktrees
  git config --local wt.ideaFilesBase ~/src/repo-idea
  git config --local wt.baseBranch main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mainRepoRoot is always derivable from git — there's no reason to let
users set it, and doing so just creates a point of conflict between
what git says and what the config says.

Now always derived via git-common-dir; the key is no longer recognized
even if present in .git/config (silently ignored).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In zsh, `local var` on an already-declared variable prints its current
value to stdout. Declaring `lkey` inside the while loop caused it to
print on every iteration after the first.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
wt_show_help() force-reloads config from .conf to pick up context
changes, but this clears the git local config values. Add
wt_read_git_config after the reload so help displays the actual
effective values.

Only wt-help gets this treatment — context switching and completions
intentionally reload from .conf only, since they serve the context
system (not the local repo).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bezhermoso bezhermoso changed the title feat: local-context-first config via git local config feat(config): local-context-first config via git local config Feb 25, 2026
Add an explicit opt-in gate so repos don't accidentally pick up
local config. Users must set `git config --local wt.enabled true`
before any other wt.* keys take effect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Collaborator

@guodong-sq guodong-sq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do the values set in wt_read_git_config need to go through _wt_expand_path()?

# are present.
#
# Sets variables unconditionally — git local config has highest priority.
wt_read_git_config() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be called by wt_load_context_config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seem to me that wt_load_context_config's purpose is to load config from a context's *.conf files. I think it's wrong to override that with git local configs. I see these two as the two possible sources to pick from.

Perhaps what we need is a wt_load_config --from=(git|context|ordered)?

  • --from git load from local git config only
  • --from context load from current context only
  • --from ordered try loading from git first, then context fallback (the default behavior to achieve local-context-first)

}

# Parse config file into shell variables (no sourcing)
# Only sets variables that aren't already set in the environment
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer true with the added wt_read_git_config

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