diff --git a/lib/wt-context-setup b/lib/wt-context-setup index cc666a3..05c893b 100644 --- a/lib/wt-context-setup +++ b/lib/wt-context-setup @@ -288,6 +288,29 @@ _wt_select_metadata_patterns() { fi } +# Write wt.* keys to git local config for plugin compatibility +# Args: $1 = repo root, $2 = context name +# Keys match the JetBrains plugin's writeConfig() for bidirectional parity +_wt_write_git_config() { + local repo_root="$1" context_name="$2" + if [[ ! -d "$repo_root/.git" ]] && [[ ! -f "$repo_root/.git" ]]; then + warn "Skipping git local config — no .git found at $repo_root" + return 0 + fi + info "Writing git local config..." + git -C "$repo_root" config --local wt.enabled true + git -C "$repo_root" config --local wt.contextName "$context_name" + git -C "$repo_root" config --local wt.worktreesBase "$WT_WORKTREES_BASE" + git -C "$repo_root" config --local wt.ideaFilesBase "$WT_IDEA_FILES_BASE" + git -C "$repo_root" config --local wt.baseBranch "$WT_BASE_BRANCH" + git -C "$repo_root" config --local wt.activeWorktree "$WT_ACTIVE_WORKTREE" + if [[ -n "${WT_METADATA_PATTERNS:-}" ]]; then + git -C "$repo_root" config --local wt.metadataPatterns "$WT_METADATA_PATTERNS" + else + git -C "$repo_root" config --local --unset wt.metadataPatterns 2>/dev/null || true + fi +} + # ───────────────────────────────────────────────────────────────────────────── # Main setup functions # ───────────────────────────────────────────────────────────────────────────── @@ -820,6 +843,9 @@ EOF echo " ✓ Created context config: $config_path" + # Write git local config (mirrors .conf for plugin compatibility) + _wt_write_git_config "$repo_path" "$context_name" + # Write context name to current file - config is read from .conf at runtime mkdir -p "$(dirname "$current_file")" echo "$context_name" > "$current_file" diff --git a/test/unit/wt-context-setup.bats b/test/unit/wt-context-setup.bats index e568125..e21d4ea 100644 --- a/test/unit/wt-context-setup.bats +++ b/test/unit/wt-context-setup.bats @@ -413,3 +413,126 @@ teardown() { assert_equal "$content_unadopted" "gitdir: ${new_repo}/.git/worktrees/wt-all-unadopted" } +# ============================================================================= +# Tests for _wt_write_git_config() +# ============================================================================= + +@test "_wt_write_git_config writes all expected keys to git local config" { + local repo + repo=$(create_mock_repo) + + export WT_WORKTREES_BASE="$TEST_HOME/.wt/repos/myctx/worktrees" + export WT_IDEA_FILES_BASE="$TEST_HOME/.wt/repos/myctx/idea-files" + export WT_BASE_BRANCH="main" + export WT_ACTIVE_WORKTREE="$TEST_HOME/active" + export WT_METADATA_PATTERNS=".idea .vscode" + + _wt_write_git_config "$repo" "myctx" + + assert_equal "$(git -C "$repo" config --local wt.enabled)" "true" + assert_equal "$(git -C "$repo" config --local wt.contextName)" "myctx" + assert_equal "$(git -C "$repo" config --local wt.worktreesBase)" "$WT_WORKTREES_BASE" + assert_equal "$(git -C "$repo" config --local wt.ideaFilesBase)" "$WT_IDEA_FILES_BASE" + assert_equal "$(git -C "$repo" config --local wt.baseBranch)" "main" + assert_equal "$(git -C "$repo" config --local wt.activeWorktree)" "$WT_ACTIVE_WORKTREE" + assert_equal "$(git -C "$repo" config --local wt.metadataPatterns)" ".idea .vscode" +} + +@test "_wt_write_git_config values match what the .conf file would contain" { + local repo + repo=$(create_mock_repo) + + export WT_WORKTREES_BASE="$TEST_HOME/.wt/repos/testctx/worktrees" + export WT_IDEA_FILES_BASE="$TEST_HOME/.wt/repos/testctx/idea-files" + export WT_BASE_BRANCH="develop" + export WT_ACTIVE_WORKTREE="$TEST_HOME/dev/myrepo" + export WT_METADATA_PATTERNS=".idea" + + _wt_write_git_config "$repo" "testctx" + + # Simulate what the .conf file would have and compare + assert_equal "$(git -C "$repo" config --local wt.worktreesBase)" "$WT_WORKTREES_BASE" + assert_equal "$(git -C "$repo" config --local wt.ideaFilesBase)" "$WT_IDEA_FILES_BASE" + assert_equal "$(git -C "$repo" config --local wt.baseBranch)" "$WT_BASE_BRANCH" + assert_equal "$(git -C "$repo" config --local wt.activeWorktree)" "$WT_ACTIVE_WORKTREE" + assert_equal "$(git -C "$repo" config --local wt.metadataPatterns)" "$WT_METADATA_PATTERNS" +} + +@test "_wt_write_git_config keys are readable by wt_read_git_config" { + local repo + repo=$(create_mock_repo) + + export WT_WORKTREES_BASE="$TEST_HOME/.wt/repos/roundtrip/worktrees" + export WT_IDEA_FILES_BASE="$TEST_HOME/.wt/repos/roundtrip/idea-files" + export WT_BASE_BRANCH="main" + export WT_ACTIVE_WORKTREE="$TEST_HOME/active" + export WT_METADATA_PATTERNS=".idea .ijwb" + + _wt_write_git_config "$repo" "roundtrip" + + # Clear the variables so wt_read_git_config has to populate them + unset WT_MAIN_REPO_ROOT WT_WORKTREES_BASE WT_IDEA_FILES_BASE WT_BASE_BRANCH + unset WT_ACTIVE_WORKTREE WT_METADATA_PATTERNS WT_CONTEXT_NAME + unset _WT_SKIP_GIT_CONFIG + + cd "$repo" + wt_read_git_config + + assert_equal "$WT_MAIN_REPO_ROOT" "$repo" + assert_equal "$WT_WORKTREES_BASE" "$TEST_HOME/.wt/repos/roundtrip/worktrees" + assert_equal "$WT_IDEA_FILES_BASE" "$TEST_HOME/.wt/repos/roundtrip/idea-files" + assert_equal "$WT_BASE_BRANCH" "main" + assert_equal "$WT_ACTIVE_WORKTREE" "$TEST_HOME/active" + assert_equal "$WT_METADATA_PATTERNS" ".idea .ijwb" + assert_equal "$WT_CONTEXT_NAME" "roundtrip" +} + +@test "_wt_write_git_config warns and returns 0 when no .git exists" { + local non_git_dir="$BATS_TEST_TMPDIR/no-git" + mkdir -p "$non_git_dir" + + export WT_WORKTREES_BASE="/tmp/wt" WT_IDEA_FILES_BASE="/tmp/idea" + export WT_BASE_BRANCH="main" WT_ACTIVE_WORKTREE="/tmp/active" + export WT_METADATA_PATTERNS="" + + run _wt_write_git_config "$non_git_dir" "test" + assert_success + assert_output --partial "Skipping git local config" +} + +@test "_wt_write_git_config unsets metadataPatterns when empty" { + local repo + repo=$(create_mock_repo) + + export WT_WORKTREES_BASE="/tmp/wt" WT_IDEA_FILES_BASE="/tmp/idea" + export WT_BASE_BRANCH="main" WT_ACTIVE_WORKTREE="/tmp/active" + + # First write with patterns + export WT_METADATA_PATTERNS=".idea" + _wt_write_git_config "$repo" "ctx" + assert_equal "$(git -C "$repo" config --local wt.metadataPatterns)" ".idea" + + # Then write with empty patterns — key should be unset + export WT_METADATA_PATTERNS="" + _wt_write_git_config "$repo" "ctx" + run git -C "$repo" config --local wt.metadataPatterns + assert_failure +} + +@test "_wt_write_git_config writes all 7 plugin-compatible keys" { + local repo + repo=$(create_mock_repo) + + export WT_WORKTREES_BASE="/wt" WT_IDEA_FILES_BASE="/idea" + export WT_BASE_BRANCH="main" WT_ACTIVE_WORKTREE="/active" + export WT_METADATA_PATTERNS=".idea" + + _wt_write_git_config "$repo" "ctx" + + # Verify all 7 keys the plugin expects are present + local keys + keys=$(git -C "$repo" config --local --get-regexp '^wt\.' | awk '{print $1}' | sort) + local expected + expected=$(printf '%s\n' wt.activeworktree wt.basebranch wt.contextname wt.enabled wt.ideafilesbase wt.metadatapatterns wt.worktreesbase | sort) + assert_equal "$keys" "$expected" +} diff --git a/wt-jetbrains-plugin/src/main/kotlin/com/block/wt/git/GitConfigHelper.kt b/wt-jetbrains-plugin/src/main/kotlin/com/block/wt/git/GitConfigHelper.kt index 32636f4..40e11e7 100644 --- a/wt-jetbrains-plugin/src/main/kotlin/com/block/wt/git/GitConfigHelper.kt +++ b/wt-jetbrains-plugin/src/main/kotlin/com/block/wt/git/GitConfigHelper.kt @@ -67,7 +67,8 @@ object GitConfigHelper { /** * Writes wt.* config to git local config (.git/config). * Uses individual `git config --local wt. ` calls. - * Note: the shell CLI never writes to git config (read-only); only the plugin writes. + * Both the CLI (via _wt_write_git_config in lib/wt-context-setup) and the plugin + * write wt.* keys to git local config. The key names and semantics are shared. */ fun writeConfig(repoPath: Path, config: ContextConfig) { val mainGitDir = GitDirResolver.resolveMainGitDir(repoPath)