From 9d171806d3847e04d90f2e8225038ce2eed07d1b Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Mon, 27 Apr 2026 14:51:01 -0700 Subject: [PATCH] fix(copy): honor configured directory patterns --- lib/commands/copy.sh | 16 ++++++++----- lib/copy.sh | 16 +++++++++++-- tests/cmd_copy.bats | 56 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/lib/commands/copy.sh b/lib/commands/copy.sh index 84e6748..13281c1 100644 --- a/lib/commands/copy.sh +++ b/lib/commands/copy.sh @@ -43,17 +43,19 @@ cmd_copy() { src_path="$_ctx_worktree_path" # Get patterns (flag > config + .worktreeinclude) - local excludes + local excludes dir_includes="" dir_excludes="" if [ -z "$patterns" ]; then merge_copy_patterns "$repo_root" patterns="$_ctx_copy_includes" excludes="$_ctx_copy_excludes" + dir_includes=$(cfg_get_all gtr.copy.includeDirs copy.includeDirs) + dir_excludes=$(cfg_get_all gtr.copy.excludeDirs copy.excludeDirs) else excludes=$(cfg_get_all gtr.copy.exclude copy.exclude) fi - if [ -z "$patterns" ]; then - log_error "No patterns specified. Use '-- ...' or configure gtr.copy.include" + if [ -z "$patterns" ] && [ -z "$dir_includes" ]; then + log_error "No patterns specified. Use '-- ...' or configure gtr.copy.include or gtr.copy.includeDirs" exit 1 fi @@ -85,10 +87,12 @@ cmd_copy() { if [ "$dry_run" -eq 1 ]; then log_step "[dry-run] Would copy to: $dst_branch" - copy_patterns "$src_path" "$dst_path" "$patterns" "$excludes" "true" "true" + [ -n "$patterns" ] && copy_patterns "$src_path" "$dst_path" "$patterns" "$excludes" "true" "true" + [ -n "$dir_includes" ] && copy_directories "$src_path" "$dst_path" "$dir_includes" "$dir_excludes" "true" else log_step "Copying to: $dst_branch" - copy_patterns "$src_path" "$dst_path" "$patterns" "$excludes" "true" + [ -n "$patterns" ] && copy_patterns "$src_path" "$dst_path" "$patterns" "$excludes" "true" + [ -n "$dir_includes" ] && copy_directories "$src_path" "$dst_path" "$dir_includes" "$dir_excludes" fi copied_any=1 done @@ -96,4 +100,4 @@ cmd_copy() { if [ "$copied_any" -eq 0 ]; then log_warn "No files copied (source and target may be the same)" fi -} \ No newline at end of file +} diff --git a/lib/copy.sh b/lib/copy.sh index 7c335db..3bb6e80 100644 --- a/lib/copy.sh +++ b/lib/copy.sh @@ -319,7 +319,7 @@ EOF } # Copy directories matching patterns (typically git-ignored directories like node_modules) -# Usage: copy_directories src_root dst_root dir_patterns excludes +# Usage: copy_directories src_root dst_root dir_patterns excludes [dry_run] # dir_patterns: newline-separated directory names to copy (e.g., "node_modules", ".venv") # excludes: newline-separated directory patterns to exclude (supports globs like "node_modules/.cache") # WARNING: This copies entire directories including potentially sensitive files. @@ -329,6 +329,7 @@ copy_directories() { local dst_root="$2" local dir_patterns="$3" local excludes="$4" + local dry_run="${5:-false}" if [ -z "$dir_patterns" ]; then return 0 @@ -372,6 +373,13 @@ copy_directories() { local dest_dir="$dst_root/$dir_path" local dest_parent dest_parent=$(dirname "$dest_dir") + + if [ "$dry_run" = "true" ]; then + log_info "[dry-run] Would copy directory $dir_path" + copied_count=$((copied_count + 1)) + continue + fi + mkdir -p "$dest_parent" # Copy directory using CoW when available (preserves symlinks as symlinks) @@ -392,7 +400,11 @@ EOF cd "$old_pwd" || return 1 if [ "$copied_count" -gt 0 ]; then - log_info "Copied $copied_count directories" + if [ "$dry_run" = "true" ]; then + log_info "[dry-run] Would copy $copied_count directories" + else + log_info "Copied $copied_count directories" + fi fi return 0 diff --git a/tests/cmd_copy.bats b/tests/cmd_copy.bats index dcdf813..192f5d0 100644 --- a/tests/cmd_copy.bats +++ b/tests/cmd_copy.bats @@ -37,6 +37,39 @@ teardown() { [ -f "$TEST_WORKTREES_DIR/copy-target/config.json" ] } +@test "cmd_copy copies configured includeDirs" { + mkdir -p "$TEST_REPO/.zed" + echo "settings" > "$TEST_REPO/.zed/settings.json" + git config --add gtr.copy.includeDirs ".zed" + + run cmd_copy copy-target + [ "$status" -eq 0 ] + [ -f "$TEST_WORKTREES_DIR/copy-target/.zed/settings.json" ] +} + +@test "cmd_copy dry-run does not copy configured includeDirs" { + mkdir -p "$TEST_REPO/.zed" + echo "settings" > "$TEST_REPO/.zed/settings.json" + git config --add gtr.copy.includeDirs ".zed" + + run cmd_copy copy-target --dry-run + [ "$status" -eq 0 ] + [ ! -d "$TEST_WORKTREES_DIR/copy-target/.zed" ] +} + +@test "cmd_copy applies configured excludeDirs" { + mkdir -p "$TEST_REPO/.zed/cache" + echo "settings" > "$TEST_REPO/.zed/settings.json" + echo "token" > "$TEST_REPO/.zed/cache/token" + git config --add gtr.copy.includeDirs ".zed" + git config --add gtr.copy.excludeDirs ".zed/cache" + + run cmd_copy copy-target + [ "$status" -eq 0 ] + [ -f "$TEST_WORKTREES_DIR/copy-target/.zed/settings.json" ] + [ ! -e "$TEST_WORKTREES_DIR/copy-target/.zed/cache/token" ] +} + # ── --all flag ─────────────────────────────────────────────────────────────── @test "cmd_copy --all copies to all worktrees" { @@ -47,6 +80,29 @@ teardown() { [ -f "$TEST_WORKTREES_DIR/copy-target-2/.env" ] } +@test "cmd_copy --all copies configured includeDirs to all worktrees" { + create_test_worktree "copy-target-2" + mkdir -p "$TEST_REPO/.zed" + echo "settings" > "$TEST_REPO/.zed/settings.json" + git config --add gtr.copy.includeDirs ".zed" + + run cmd_copy --all + [ "$status" -eq 0 ] + [ -f "$TEST_WORKTREES_DIR/copy-target/.zed/settings.json" ] + [ -f "$TEST_WORKTREES_DIR/copy-target-2/.zed/settings.json" ] +} + +@test "cmd_copy --from copies configured includeDirs from source worktree" { + create_test_worktree "copy-source" + mkdir -p "$TEST_WORKTREES_DIR/copy-source/.idea" + echo "workspace" > "$TEST_WORKTREES_DIR/copy-source/.idea/workspace.xml" + git config --add gtr.copy.includeDirs ".idea" + + run cmd_copy copy-target --from copy-source + [ "$status" -eq 0 ] + [ -f "$TEST_WORKTREES_DIR/copy-target/.idea/workspace.xml" ] +} + # ── Error cases ────────────────────────────────────────────────────────────── @test "cmd_copy fails with no arguments" {