Skip to content

feat: add per-project config overrides#749

Merged
max-sixty merged 16 commits intomainfrom
project-specific-config
Jan 20, 2026
Merged

feat: add per-project config overrides#749
max-sixty merged 16 commits intomainfrom
project-specific-config

Conversation

@max-sixty
Copy link
Owner

Summary

  • Add per-project user config for worktree-path, commit-generation, list, commit, and merge settings
  • Project-specific settings merge with global settings (project takes precedence for fields that are set)
  • Config keyed by project identifier (e.g., github.com/user/repo)

Example:

[projects."github.com/user/repo"]
approved-commands = ["npm ci", "npm test"]
worktree-path = ".worktrees/{{ branch | sanitize }}"
list.full = true
merge.squash = false

Test plan

  • Unit tests for config merging (test_effective_* in user.rs)
  • Integration tests pass (895 tests)
  • Snapshot tests updated for help text changes
  • Manual verification of wt config create template

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

max-sixty and others added 16 commits January 19, 2026 13:03
Allow users to specify different worktree path templates for different
projects in their user config, enabling portable dotfiles across
machines with different project structures.

Config format:
```toml
[projects."github.com/user/repo"]
worktree-path = ".worktrees/{{ branch | sanitize }}"
approved-commands = ["npm ci"]
```

Per-project worktree-path takes precedence over global, which falls
back to the default template.

Closes #596

Co-Authored-By: Claude <noreply@anthropic.com>
… merge

Extend the per-project user config to support overriding more settings:
- [projects."...".commit-generation] - LLM model, args, template
- [projects."...".list] - branches, remotes, full defaults
- [projects."...".commit] - stage mode
- [projects."...".merge] - squash, commit, rebase, remove, verify

Config precedence: CLI arg > project config > global config > default

Implementation:
- Add Merge trait for field-wise config merging (other takes precedence)
- Add effective_* methods to WorktrunkConfig for merged config access
- Add helper methods on CommandEnv/CommandContext for easy access
- Update all commands to use effective configs instead of direct access
- Fix approval clearing to preserve other per-project settings

The approval clearing fix prevents data loss: previously clearing approvals
would wipe all per-project settings (worktree-path, commit-generation, etc).
Now it only clears approved-commands and removes project entries only if
completely empty.

Co-Authored-By: Claude <noreply@anthropic.com>
DRY improvement: the effective_list, effective_commit, and effective_merge
methods all had identical 4-way match logic. Extract to a generic helper
function that handles the merge pattern.

Co-Authored-By: Claude <noreply@anthropic.com>
- Clarify that per-project settings contain approvals + optional overrides
- Put approved-commands first (primary use case)
- Use TOML dotted keys for compact examples (list.full, merge.squash)
- Remove verbose examples that repeated the section header

Co-Authored-By: Claude <noreply@anthropic.com>
The `project: Option<&str>` parameter already indicates merging behavior.
Renamed methods:
- WorktrunkConfig: commit_generation(), list(), commit(), merge()
- CommandEnv/CommandContext: commit_generation(), commit(), merge()

Co-Authored-By: Claude <noreply@anthropic.com>
Resolved conflicts from WorktrunkConfig → UserConfig rename.
Kept per-project config logic with updated type name.

Co-Authored-By: Claude <noreply@anthropic.com>
Rustdoc interprets `[text]` as intra-doc links. Use backticks to make
them code spans instead.

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	src/commands/merge.rs
#	tests/snapshots/integration__integration_tests__help__help_config_create.snap
#	tests/snapshots/integration__integration_tests__help__help_config_long.snap
Updated config help snapshots to reflect per-project config documentation.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for config validation:
- Empty worktree-path (global and per-project)
- Absolute worktree-path (global and per-project)
- Mutual exclusivity of template/template-file options

Extracted validation into a separate `validate()` method to avoid
code duplication between `load()` and test helper.

Co-Authored-By: Claude <noreply@anthropic.com>
On Windows, `/absolute/path` is not considered absolute - Windows uses
`C:\path` style. Use cfg!(windows) to conditionally set the test path.

Co-Authored-By: Claude <noreply@anthropic.com>
The doc comments incorrectly stated that project_id() returns None when
there's no remote URL. The actual implementation falls back to the
canonical repository path, only returning None for non-UTF-8 paths.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add "(Experimental)" to per-project settings section heading
- Clarify that setting overrides (worktree-path, list, commit, merge) are
  new while approved-commands is stable
- Replace manual PartialEq impl with derive for CommitGenerationConfig

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace .to_string().as_str() with &.to_string()
- Replace .is_some() + .unwrap() with tuple pattern match

Co-Authored-By: Claude <noreply@anthropic.com>
Move opening brace to same line as if-let condition to match code style.
@max-sixty max-sixty enabled auto-merge (squash) January 20, 2026 05:56
@max-sixty max-sixty merged commit 41b2411 into main Jan 20, 2026
21 checks passed
@max-sixty max-sixty deleted the project-specific-config branch January 20, 2026 05:57
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.

1 participant