Skip to content

Conversation

@helizaga
Copy link
Collaborator

@helizaga helizaga commented Dec 10, 2025

Pull Request

Description

Motivation

Fixes # (issue)

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Other (please describe):

Testing

Manual Testing Checklist

Tested on:

  • macOS
  • Linux (specify distro: **_**)
  • Windows (Git Bash)

Core functionality tested:

  • git gtr new <branch> - Create worktree
  • git gtr go <branch> - Navigate to worktree
  • git gtr editor <branch> - Open in editor (if applicable)
  • git gtr ai <branch> - Start AI tool (if applicable)
  • git gtr rm <branch> - Remove worktree
  • git gtr list - List worktrees
  • git gtr config - Configuration commands (if applicable)
  • Other commands affected by this change: **__**

Test Steps

Expected behavior:

Actual behavior:

Breaking Changes

  • This PR introduces breaking changes
  • I have discussed this in an issue first
  • Migration guide is included in documentation

Checklist

Before submitting this PR, please check:

  • I have read CONTRIBUTING.md
  • My code follows the project's style guidelines
  • I have performed manual testing on at least one platform
  • I have updated documentation (README.md, CLAUDE.md, etc.) if needed
  • My changes work on multiple platforms (or I've noted platform-specific behavior)
  • I have added/updated shell completions (if adding new commands or flags)
  • I have tested with both git gtr (production) and ./bin/gtr (development)
  • No new external dependencies are introduced (Bash + git only)
  • All existing functionality still works

Additional Context


License Acknowledgment

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.

Summary by CodeRabbit

  • New Features
    • Added git gtr copy to copy files between worktrees with --from, -n/--dry-run preview, and -a/--all bulk-target support; per-target status and warnings if nothing copied.
  • Completion
    • Updated zsh, bash, and fish completions to provide flags and worktree name suggestions for the copy command.
  • Documentation
    • README and CLAUDE.md updated with usage examples and a test-copy workflow.

✏️ Tip: You can customize this high-level summary in your review settings.

@helizaga helizaga requested a review from NatoBoram as a code owner December 10, 2025 05:48
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds a new git-gtr "copy" subcommand to copy files between worktrees, with --from, -n/--dry-run, -a/--all options; implements per-target copying (dry-run supported), worktree listing helper, updated shell completions, and documentation/examples.

Changes

Cohort / File(s) Summary
Core command implementation
bin/gtr
Added cmd_copy() implementing git gtr copy: parses --from, -n/--dry-run, -a/--all, --, resolves source/destination paths and targets, collects include/exclude patterns (config + .worktreeinclude), invokes copy_patterns per target, emits per-target status, and wired dispatcher/help.
Library utilities
lib/copy.sh, lib/core.sh
copy_patterns() signature extended to accept a dry_run parameter and logs preview actions without performing copies when set; added list_worktree_branches(base_dir, prefix) to enumerate worktree branch names.
Shell completions
completions/_git-gtr, completions/gtr.bash, completions/gtr.fish
Added copy subcommand to zsh/bash/fish completions, declared flags -n/--dry-run, -a/--all, --from, and wired worktree/branch name completions for target arguments.
Docs / Examples
README.md, CLAUDE.md
Documented git gtr copy usage, options, examples, and added a test-copy workflow to CLAUDE.md.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as bin/gtr (cmd_copy)
    participant Core as lib/core.sh (list_worktree_branches)
    participant Copy as lib/copy.sh (copy_patterns)

    User->>CLI: git gtr copy --from <src> [-n] [-a] [targets...]
    CLI->>CLI: parse flags, load include/exclude patterns
    alt --all present
        CLI->>Core: list_worktree_branches(repo_root, prefix)
        Core-->>CLI: branch list
    else targets provided
        CLI->>CLI: validate/resolve targets
    end
    CLI->>CLI: build final target list
    loop for each target
        CLI->>Copy: copy_patterns(src, dst, patterns, dry_run?)
        Copy-->>CLI: per-target logs / copied_count
        CLI->>CLI: report per-target status
    end
    CLI-->>User: aggregated summary (and warnings if nothing copied)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Focus review on:
    • cmd_copy() flag parsing, path resolution, and target selection edge cases
    • copy_patterns() dry-run vs actual behavior and directory creation logic
    • list_worktree_branches() directory iteration and branch detection
    • Shell completion wiring for correctness across zsh, bash, and fish

Poem

🐰 I hopped through trees of work and wood,

I sniffed the source and planned it good,
A dry-run nod, then copies made,
Across the branches, file parade,
A rabbit's job — both calm and spry.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add copy command for syncing files between worktrees' clearly and directly summarizes the main change—a new copy command feature for synchronizing files across worktrees.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/copy-command

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f922c14 and b7d1ef0.

📒 Files selected for processing (2)
  • bin/gtr (3 hunks)
  • lib/core.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
lib/**/*.sh

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

lib/**/*.sh: Core libraries (lib/core.sh, lib/config.sh, lib/ui.sh, lib/copy.sh, lib/hooks.sh, lib/platform.sh) must be sourced at startup and implement specific functionality
Maintain backwards compatibility with Git <2.22 by using fallback rev-parse --abbrev-ref HEAD instead of branch --show-current

lib/**/*.sh: Maintain backwards compatibility with existing configs in shell scripts
Quote all paths to support spaces in directory names
Use log_error / log_info from lib/ui.sh for user messages
Implement Git version fallbacks (e.g., Git 2.22+ --show-current vs older rev-parse); check lib/core.sh:97-100 for example
Add new config keys with gtr.<name> prefix to avoid collisions
For performance-sensitive loops (e.g., directory scans), prefer built-ins (find, grep) with minimal subshells
For any new Git command, add fallback for older versions or guard with detection

Files:

  • lib/core.sh
**/*.sh

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.sh: Always quote paths to handle spaces and special characters; avoid unguarded globbing
Keep set -e active in shell scripts; ensure non-critical failures are guarded with command || true

**/*.sh: Use shebang #!/usr/bin/env bash (not /bin/bash or /bin/sh) for all Bash scripts
Use snake_case naming for functions in Bash scripts
Use snake_case for local variables and UPPER_CASE for constants/environment variables in Bash scripts
Use 2-space indentation in Bash scripts (no tabs)
Always quote variables and paths in Bash scripts to prevent word splitting
Check return codes in Bash scripts using || exit 1 or || return 1 for error handling
Use local keyword for function-scoped variables in Bash scripts
Target Bash 3.2+ for compatibility with older systems like older macOS versions
Use set -e for error handling in Bash scripts to ensure failures are caught
Include fallback code for different Git versions (Git 2.5-2.21 compatibility fallbacks for modern Git 2.22+ commands)

Files:

  • lib/core.sh
**/*.{bash,fish,sh}

📄 CodeRabbit inference engine (.github/instructions/sh.instructions.md)

**/*.{bash,fish,sh}: Bash 3.2+ compatible (macOS default), but 4.0+ features allowed where appropriate
Always quote variables: use "$var" not $var
Use function-scoped variables: local var="value"
Check return codes; functions return 1 on failure
Use snake_case for functions and variables, UPPER_CASE for constants
Prefer [ ] over [[ ]] for POSIX portability; use [[ only when needed
Always quote glob inputs; disable unintended globbing with set -f temporarily if required
Avoid associative arrays in shell scripts; use simple string/loop constructs for Bash 3.2+ compatibility
Avoid readarray and process substitution unsupported in older Bash
Debug with bash -x ./bin/gtr <cmd> or wrap suspicious blocks with set -x / set +x
Check function presence with declare -f create_worktree or declare -f resolve_target
Use stderr for variable inspection: echo "DEBUG var=$var" >&2 to keep stdout clean for command substitution
Keep dependencies minimal: only use git, sed, awk, find, grep; avoid jq/curl unless justified
Check availability of external commands before use when adding new tools
Use "${var}" for variable expansion; for line loops use while IFS= read -r line; do ... done to preserve spaces
Sanitize branch names via sanitize_branch_name function; do NOT duplicate logic elsewhere
Everything is sourced at startup with set -e enabled; functions call each other directly; no subshells except for hooks and AI tools

Files:

  • lib/core.sh
{bin/gtr,lib/**/*.sh,adapters/**/*.sh}

📄 CodeRabbit inference engine (.github/instructions/testing.instructions.md)

{bin/gtr,lib/**/*.sh,adapters/**/*.sh}: All commands must exit 0 (except intentional failures) and produce expected side-effects
No unquoted path errors; spaces must be handled in file paths
Hooks must run only once per creation/removal event

Files:

  • lib/core.sh
  • bin/gtr
bin/gtr

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

bin/gtr: Dispatch commands through cmd_* functions in bin/gtr (case block lines 36‑77)
Update GTR_VERSION on line 8 of bin/gtr when releasing; this affects gtr version / --version output

Global set -e in bin/gtr: guard non-critical commands with || true

list --porcelain output must remain stable for scripting purposes

Version number in bin/gtr (line 8) must be updated when releasing a new version

Files:

  • bin/gtr
🔇 Additional comments (4)
lib/core.sh (1)

440-455: LGTM! Clean worktree branch enumeration.

The function correctly handles the non-existent directory case, properly quotes paths, and filters to directories only. The glob pattern is safe, and the [ -d "$dir" ] check handles non-matching patterns gracefully.

bin/gtr (3)

81-83: LGTM! Dispatcher integration is consistent.

The copy command is properly integrated into the main dispatcher following the established pattern.


1192-1205: LGTM! Clear and comprehensive documentation.

The help text provides excellent examples covering the main use cases (explicit patterns, multiple targets, --all mode, dry-run), and clearly explains the pattern override behavior. The documentation follows the established help format.


473-595: Well-structured copy command with good error handling.

The implementation correctly handles argument parsing, pattern precedence (flags > config), target resolution, and provides appropriate user feedback. The --all mode integration with list_worktree_branches is clean, and the self-copy check prevents errors. The copy_patterns function calls on lines 584 and 587 are correct: the function signature uses optional positional parameters with defaults (preserve_paths="${5:-true}" and dry_run="${6:-false}"), so the dry-run call explicitly passes both parameters while the normal call omits the dry_run parameter to use its default value of false.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
lib/core.sh (1)

440-456: Minor cleanup in list_worktree_branches and note on semantics

  • repo_root is currently unused; either drop it from the signature or rename to _repo_root to avoid confusion / shellcheck noise.
  • The helper returns only branch names, which is fine given resolve_target is branch‑based, but it also means cmd_copy --all inherits the existing limitation that you can’t distinguish multiple worktrees sharing the same branch. If you ever add per‑worktree identifiers, this function will likely need to return paths (or path+branch) instead of just names.
bin/gtr (1)

81-83: cmd_copy implementation and wiring look solid

  • Dispatcher correctly adds copycmd_copy and the help text matches the implemented flags and examples.
  • Argument parsing in cmd_copy is consistent with existing commands:
    • --from, -n/--dry-run, -a/--all, and -- for patterns are handled clearly.
    • Targets are required unless --all is set, and repo context / source worktree resolution are guarded with || exit 1, which is good under global set -e.
  • Pattern resolution honors precedence as documented:
    • CLI -- <pattern>... wins over gtr.copy.include and .worktreeinclude.
    • Excludes are taken from gtr.copy.exclude.
  • --all is implemented via list_worktree_branches, then each target goes through resolve_target, with source==destination safely skipped and a final warning when nothing is processed.
  • Dry‑run is correctly plumbed through to copy_patterns via the extra boolean parameter, and the logs clearly distinguish real copies from previews.

If you later introduce a notion of multiple managed worktrees per branch, note that cmd_copy --all (like go/run/rm) is inherently branch‑centric via resolve_target and would need revisiting to operate per physical worktree instead of per branch.

Also applies to: 473-595, 1192-1205

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e1ad20c and a56de31.

📒 Files selected for processing (6)
  • bin/gtr (3 hunks)
  • completions/_git-gtr (3 hunks)
  • completions/gtr.bash (2 hunks)
  • completions/gtr.fish (3 hunks)
  • lib/copy.sh (4 hunks)
  • lib/core.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
lib/**/*.sh

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

lib/**/*.sh: Core libraries (lib/core.sh, lib/config.sh, lib/ui.sh, lib/copy.sh, lib/hooks.sh, lib/platform.sh) must be sourced at startup and implement specific functionality
Maintain backwards compatibility with Git <2.22 by using fallback rev-parse --abbrev-ref HEAD instead of branch --show-current

lib/**/*.sh: Maintain backwards compatibility with existing configs in shell scripts
Quote all paths to support spaces in directory names
Use log_error / log_info from lib/ui.sh for user messages
Implement Git version fallbacks (e.g., Git 2.22+ --show-current vs older rev-parse); check lib/core.sh:97-100 for example
Add new config keys with gtr.<name> prefix to avoid collisions
For performance-sensitive loops (e.g., directory scans), prefer built-ins (find, grep) with minimal subshells
For any new Git command, add fallback for older versions or guard with detection

Files:

  • lib/core.sh
  • lib/copy.sh
**/*.sh

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.sh: Always quote paths to handle spaces and special characters; avoid unguarded globbing
Keep set -e active in shell scripts; ensure non-critical failures are guarded with command || true

**/*.sh: Use shebang #!/usr/bin/env bash (not /bin/bash or /bin/sh) for all Bash scripts
Use snake_case naming for functions in Bash scripts
Use snake_case for local variables and UPPER_CASE for constants/environment variables in Bash scripts
Use 2-space indentation in Bash scripts (no tabs)
Always quote variables and paths in Bash scripts to prevent word splitting
Check return codes in Bash scripts using || exit 1 or || return 1 for error handling
Use local keyword for function-scoped variables in Bash scripts
Target Bash 3.2+ for compatibility with older systems like older macOS versions
Use set -e for error handling in Bash scripts to ensure failures are caught
Include fallback code for different Git versions (Git 2.5-2.21 compatibility fallbacks for modern Git 2.22+ commands)

Files:

  • lib/core.sh
  • lib/copy.sh
**/*.{bash,fish,sh}

📄 CodeRabbit inference engine (.github/instructions/sh.instructions.md)

**/*.{bash,fish,sh}: Bash 3.2+ compatible (macOS default), but 4.0+ features allowed where appropriate
Always quote variables: use "$var" not $var
Use function-scoped variables: local var="value"
Check return codes; functions return 1 on failure
Use snake_case for functions and variables, UPPER_CASE for constants
Prefer [ ] over [[ ]] for POSIX portability; use [[ only when needed
Always quote glob inputs; disable unintended globbing with set -f temporarily if required
Avoid associative arrays in shell scripts; use simple string/loop constructs for Bash 3.2+ compatibility
Avoid readarray and process substitution unsupported in older Bash
Debug with bash -x ./bin/gtr <cmd> or wrap suspicious blocks with set -x / set +x
Check function presence with declare -f create_worktree or declare -f resolve_target
Use stderr for variable inspection: echo "DEBUG var=$var" >&2 to keep stdout clean for command substitution
Keep dependencies minimal: only use git, sed, awk, find, grep; avoid jq/curl unless justified
Check availability of external commands before use when adding new tools
Use "${var}" for variable expansion; for line loops use while IFS= read -r line; do ... done to preserve spaces
Sanitize branch names via sanitize_branch_name function; do NOT duplicate logic elsewhere
Everything is sourced at startup with set -e enabled; functions call each other directly; no subshells except for hooks and AI tools

Files:

  • lib/core.sh
  • lib/copy.sh
  • completions/gtr.fish
  • completions/gtr.bash
{bin/gtr,lib/**/*.sh,adapters/**/*.sh}

📄 CodeRabbit inference engine (.github/instructions/testing.instructions.md)

{bin/gtr,lib/**/*.sh,adapters/**/*.sh}: All commands must exit 0 (except intentional failures) and produce expected side-effects
No unquoted path errors; spaces must be handled in file paths
Hooks must run only once per creation/removal event

Files:

  • lib/core.sh
  • lib/copy.sh
  • bin/gtr
bin/gtr

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

bin/gtr: Dispatch commands through cmd_* functions in bin/gtr (case block lines 36‑77)
Update GTR_VERSION on line 8 of bin/gtr when releasing; this affects gtr version / --version output

Global set -e in bin/gtr: guard non-critical commands with || true

list --porcelain output must remain stable for scripting purposes

Version number in bin/gtr (line 8) must be updated when releasing a new version

Files:

  • bin/gtr
completions/*

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Shell completion files must be updated for bash, zsh, and fish when commands or adapters are added

Files:

  • completions/gtr.fish
  • completions/gtr.bash
  • completions/_git-gtr
completions/{gtr.bash,_git-gtr,gtr.fish}

📄 CodeRabbit inference engine (.github/instructions/completions.instructions.md)

completions/{gtr.bash,_git-gtr,gtr.fish}: Always update all three completion files (gtr.bash, _git-gtr, gtr.fish) when adding new commands, flags, editors, or AI adapters
Implement command completion in all three completion files to support top-level commands (new, rm, editor, ai, list, etc.) with appropriate tab-completion
Implement flag completion in all three completion files to support command-specific flags (e.g., --from, --force, --editor) with appropriate tab-completion
Implement adapter name completion in all three completion files, listing available editor names (cursor, vscode, zed) and AI tool names (aider, claude, codex) for --editor and --ai flags

Files:

  • completions/gtr.fish
  • completions/gtr.bash
  • completions/_git-gtr
completions/gtr.fish

📄 CodeRabbit inference engine (.github/instructions/completions.instructions.md)

completions/gtr.fish: In completions/gtr.fish, use custom predicates (__fish_git_gtr_needs_command, __fish_git_gtr_using_command) to handle git subcommand context and detect 'git gtr' usage
In completions/gtr.fish, support dynamic branch completion using 'git branch --format='%(refname:short)' and include special ID '1' for main repo

Files:

  • completions/gtr.fish
completions/**

📄 CodeRabbit inference engine (CLAUDE.md)

Update all three shell completion files when adding new commands or flags: completions/gtr.bash, completions/_git-gtr (Zsh), and completions/gtr.fish (Fish)

Files:

  • completions/gtr.fish
  • completions/gtr.bash
  • completions/_git-gtr
completions/gtr.bash

📄 CodeRabbit inference engine (.github/instructions/completions.instructions.md)

completions/gtr.bash: In completions/gtr.bash, implement command completion using COMPREPLY array and compgen filtering for the current word ($cur)
In completions/gtr.bash, support dynamic branch completion using 'git branch --format='%(refname:short)' and include special ID '1' for main repo

Files:

  • completions/gtr.bash
completions/_git-gtr

📄 CodeRabbit inference engine (.github/instructions/completions.instructions.md)

completions/_git-gtr: In completions/_git-gtr, use Zsh completion framework with _arguments and _describe/_values for sophisticated completion logic with descriptions and grouping
In completions/_git-gtr, support dynamic branch completion using 'git branch --format='%(refname:short)' and include special ID '1' for main repo

Files:

  • completions/_git-gtr
🪛 Shellcheck (0.11.0)
completions/gtr.bash

[warning] 25-25: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)


[warning] 50-50: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)


[warning] 56-56: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)

🔇 Additional comments (4)
completions/gtr.bash (1)

25-25: Copy completions and config keys look consistent

The new copy subcommand is correctly wired into bash completion:

  • Top‑level command list now includes copy.
  • copy) case offers -n/--dry-run, -a/--all, and --from when completing flags, and 1 + branch names otherwise.
  • config key completion includes the new gtr.copy.* keys, matching the help text and config usage.

No issues from the ShellCheck SC2207 hints here; the existing COMPREPLY=($(compgen ...)) pattern is standard for bash completion in this project.

Also applies to: 48-58, 68-72

completions/gtr.fish (1)

38-48: Fish completion for copy matches CLI behavior

  • copy is added to the top‑level command list with an appropriate description.
  • Short/long flags for the copy command (-n/--dry-run, -a/--all, --from) are exposed with clear descriptions.
  • Branch completion for copy reuses __gtr_worktree_branches, giving 1 plus local branches, consistent with other commands.
  • gtr.copy.* config keys are present in the config completion block.

Looks good and aligned with the other shells.

Also applies to: 65-69, 86-92, 95-99

lib/copy.sh (1)

19-24: Dry‑run support in copy_patterns is backward‑compatible and side‑effect free

  • The extra [dry_run] parameter is optional and defaults to "false", so existing callers (like cmd_create) retain their behavior.
  • When dry_run is "true", the function:
    • Skips mkdir -p and cp entirely.
    • Logs [dry-run] Would copy: <file> per match and a final [dry-run] Would copy N file(s) summary.
  • Real copies still create parent directories and log per‑file success/failure, with the same counting and overall summary as before.
  • All new logic keeps paths quoted and respects the existing safety checks on patterns.

This is a clean extension and fits the new cmd_copy usage well.

Also applies to: 30-31, 106-123, 164-181, 192-197

completions/_git-gtr (1)

18-34: Zsh completion for copy is wired correctly

  • copy is added to the subcommand list with a clear description.
  • First positional argument after copy now completes to 1 plus branch names, matching other branch‑oriented commands (go, run, rm).
  • Subsequent arguments for copy use _arguments to expose:
    • -n/--dry-run, -a/--all, and --from (with worktree‑aware completion),
    • Positional targets completed from the same branch/1 set.

This matches the new CLI and stays consistent with the existing Zsh completion style.

Also applies to: 63-71, 97-107

@helizaga helizaga merged commit 9fd4e85 into main Dec 10, 2025
1 check passed
@helizaga helizaga deleted the feat/copy-command branch December 10, 2025 06:29
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