Skip to content

feat: repeatable -b flag for multi-branch clone#324

Merged
avihut merged 34 commits intomasterfrom
feat/clone-multiple-branches
Mar 25, 2026
Merged

feat: repeatable -b flag for multi-branch clone#324
avihut merged 34 commits intomasterfrom
feat/clone-multiple-branches

Conversation

@avihut
Copy link
Owner

@avihut avihut commented Mar 25, 2026

Summary

  • Make -b repeatable so users can clone multiple branches at once: git worktree clone <url> -b feat-a -b feat-b
  • Accept HEAD and @ as -b values meaning "the remote's default branch"
  • Unify -b / --all-branches under a BranchSource abstraction with Default, Single, Multiple, All variants
  • Extract shared OperationTable TUI component from sync/prune — all three commands now share the same table infrastructure
  • Add TUI table for multi-branch clone with per-worktree worktree-pre-create / worktree-post-create hook presentation
  • Add gitoxide fast paths for local ref validation after bare clone (zero network)
  • Add --columns flag to clone with defaults: Branch, Path, Base, Age, Commit
  • Change -v from bool to u8 count (no flag = TUI, -v = TUI with hooks, -vv = sequential)
  • Fire worktree-post-create for every worktree created during clone (previously skipped for bare layouts)
  • 6 new YAML test scenarios, shell completions for HEAD/@, docs updates

Test plan

  • mise run ci passes (fmt, clippy, unit tests, man page verification)
  • All 36 clone YAML scenarios pass (81 steps)
  • Manual test: git worktree clone <url> -b @ -b feat --layout contained
  • Manual test: git worktree clone <url> -b @ -b feat --layout sibling
  • Manual test: git worktree clone <url> -b @ -b feat --layout nested
  • Manual test: git worktree clone <url> -b @ -b feat --layout contained-classic
  • Manual test: single -b backward compatibility
  • Manual test: --all-branches unchanged behavior
  • Manual test: -v shows hook sub-rows, -vv shows sequential output

Fixes #321

🤖 Generated with Claude Code

avihut and others added 30 commits March 24, 2026 09:28
Covers repeatable -b flag, BranchSource abstraction, shared OperationTable
TUI component, gitoxide integration, and layout-dependent behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Clarify cd target fallback to base worktree for non-bare layouts
- Add BranchSource::All fallback when default branch missing on remote
- Derive failures from FinalStatus instead of separate accumulation
- Add OperationPhase::Setup match site update list
- Map new TaskMessage variants to FinalStatus
- Specify --no-checkout and --remote interaction with multiple -b
- Note BareCloneParams.branch remains Option<String> internally
- Fix gitoxide: new local-ref functions, not existing network-based ones
- Fix config_snapshot_mut reference to use CLI config writes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Define cd target for BranchSource::All (base worktree)
- Specify TaskId::Setup(String) variant and concrete match sites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 tasks covering BranchSource types, DAG event extensions, gitoxide
local ref validation, OperationTable extraction, sync/prune refactor,
CLI changes, TUI wiring, completions, and YAML test scenarios.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix TableConfig to use verbosity: u8 (not show_hook_sub_rows bool)
- Map Created/BaseCreated to FinalStatus::Updated per spec (no new variant)
- Add check_phase_completion() to OperationPhase match update list
- Fix OperationTable to accept Vec<OperationPhase> and Vec<WorktreeInfo>
- Clarify Phase 2/3 execution order (layout before branch resolution)
- Add missing unit tests: empty repo, All with absent default branch
- Add --remote + multiple -b conflict YAML scenario
- Add Task 12: audit --all-branches hook firing
- Add Task 13: partial failure summary output
- Add Task 14: docs/cli reference page update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends TaskId, OperationPhase, and TaskMessage enums with new variants
needed by the clone command's TUI table, and adds unreachable arms in
sync/prune to keep all matches exhaustive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce `OperationTable`, `TableConfig`, and `CompletedTable` in
`src/output/tui/operation_table.rs` as a reusable wrapper around
`TuiState` + `TuiRenderer`.  sync, prune, and clone can consume this
component without each reimplementing the wiring.  `TableConfig.verbosity`
(u8) replaces the old `show_hook_sub_rows` bool, computed as
`verbosity >= 1` inside `OperationTable::run()`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace manual TuiState::new() + TuiRenderer::new() + renderer.run()
wiring in the sync command with the OperationTable abstraction. Update
check_tui_failures() to accept &[WorktreeRow] so both sync and prune
can use it with their respective result types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace manual TuiState::new() + TuiRenderer::new() wiring in the prune
TUI path with OperationTable + TableConfig, following the same pattern
established in the sync refactor (Task 5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change the clone command's -b/--branch flag from Option<String> to
Vec<String> with clap::ArgAction::Append, allowing users to specify
multiple branches (e.g. -b main -b feat-x). Construct BranchSource
from the args immediately after validation. Add a --remote + multiple
-b conflict check. Update man pages and CLI docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After layout resolution, resolve branches against the remote using
BranchPlan. For multi-branch clones (BranchSource::Multiple), Phase 4
creates the base worktree, then Phase 5 creates satellite worktrees
sequentially with spinner-based output. The cd_target is resolved from
the branch plan's preference, falling back to the base worktree.

Single-branch and default cases are entirely unchanged - the new code
path only activates for Multiple branch sources.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Canonicalize parent_dir from git_dir (already absolute) instead of
  using the relative path that Phase 4 may have cd'd away from
- Fix set_upstream to pass explicit branch name so it works regardless
  of cwd (git branch --set-upstream-to=origin/b b)
- Remove duplicate inline satellite loop in commands/clone.rs, delegate
  to clone::create_satellite_worktree() from core

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When cloning with multiple -b flags on an interactive terminal, the
satellite worktree creation phase now uses an OperationTable TUI that
shows per-worktree status and hook execution (worktree-pre-create /
worktree-post-create) in real time, matching the sync/prune pattern.

The TUI path activates when stderr is a TTY and --verbose is not set.
Non-TTY and verbose modes fall back to the existing sequential spinner
path. Two operation phases are shown: a pre-completed Fetch phase
(bare clone already ran) and an active Setup phase for worktree
creation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace silent `if let Ok` patterns for `HookExecutor::new()` in
`create_satellite_worktrees_tui()` with explicit `match` blocks that
send failure events through the DAG channel when initialization fails,
preventing worktrees from being silently created as if hooks passed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add HEAD and @ as static completion values for the -b/--branch flag
in the git-worktree-clone command across bash, zsh, and fish shells.
Regenerate CLI docs to reflect the updated clone command reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Six scenarios covering the -b flag with multiple values:
- contained layout (bare repo, multiple worktrees)
- sibling layout (default branch injected as base)
- HEAD/@ token resolution to default branch
- nonexistent branch warning with partial success
- post-clone hook fires for the cd_target worktree
- --remote conflicts with multiple -b (exit 1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the TUI completes, print a summary of failed satellite worktrees
(e.g. "Created 3 of 5 worktrees (2 failed)") with per-branch error
reasons. Failure reasons are now stored on WorktreeRow.failure_reason
and populated from TaskMessage::Failed in TuiState::apply_event.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…licate

Three bugs fixed in the multi-branch clone feature:

1. Base worktree now appears as a pre-completed row in the TUI table
   (sends TaskStarted + TaskCompleted with BaseCreated immediately).

2. worktree-post-create hook no longer fires twice — the CliPresenter
   banner after the TUI is skipped when the TUI path already ran
   per-satellite hooks.

3. Bare layout no longer attempts to create a duplicate worktree for
   the branch that Phase 4 already set up (filtered from satellites
   before passing to both TUI and sequential creation paths).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- (none): TUI table with hooks hidden
- -v: TUI table with hook sub-rows visible
- -vv: sequential mode, full output (no TUI)

Matches the sync/prune verbosity pattern. Passes verbosity through
to OperationTable's TableConfig for hook sub-row control.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the layout.needs_bare() guard that prevented worktree-post-create
from firing for contained/bare layouts during single-branch clone. Add
worktree-post-create hook execution for the base worktree in the TUI
multi-branch path. Add pre-create and post-create hook execution for
satellite worktrees in the sequential multi-branch path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clone TUI table defaults to showing Branch, Base, Age, and Commit
columns. Users can override with --columns (same syntax as sync/prune).

Adds CommandKind::Clone and ListColumn::clone_defaults() to the
column selection system.

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

Two fixes for the multi-branch clone TUI table:

1. Base, Age, and Commit columns now show actual data instead of being
   empty. After each worktree is created (both base and satellites),
   commit metadata is fetched via gitoxide and sent as updated_info in
   the TaskCompleted event.

2. Path column for bare layouts now shows repo-relative paths (e.g.,
   "repo/branch") instead of just the branch name, by using
   repo_path.join(branch) instead of PathBuf::from(branch).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Default clone columns: Branch, Path, Base, Age, Commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use parent of repo_path as display_root so paths render as
"tax-analyzer/feature" instead of just "feature".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
relative_display_path resolves paths relative to cwd first. Since cwd
was repo_path, paths like repo_path/feature rendered as just "feature".
Setting cwd to repo_path.parent() makes them render as "repo/feature".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The base worktree path was computed from layout templates, which gave
wrong results for non-contained layouts (nested, sibling). Now uses
base_result.worktree_dir — the real path Phase 4 created. This also
fixes hooks not firing for the base worktree in nested layout since
the hook context was receiving the wrong path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
avihut and others added 4 commits March 24, 2026 23:35
For contained-classic layout, git_dir moves into a branch subdirectory
(e.g., repo/master/.git), so git_dir.parent() gave repo/master instead
of repo. This caused satellites to be created under repo/master/feature
instead of repo/feature. Use base_result.parent_dir (the repo root)
which is correct for all layouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 4 (setup_wrapped_nonbare) moves .git into a branch subdirectory
and changes cwd. The relative parent_dir becomes unreachable after
that. Canonicalize it before Phase 4 runs and propagate the absolute
path to satellite creation functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After clone_bare_phase, cwd is inside the repo directory. Using
canonicalize on the relative parent_dir looked for repo/repo which
doesn't exist. current_dir() gives the correct absolute path since
we're already inside the repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For contained-classic, .git lives inside the base worktree (e.g.,
repo/master/.git). The repo root has no .git, so git worktree add
fails when run from there. Now cds to the base worktree directory
where git can discover the repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@avihut avihut added this to the Public Launch milestone Mar 25, 2026
@avihut avihut added the feat New feature label Mar 25, 2026
@avihut avihut self-assigned this Mar 25, 2026
@avihut avihut merged commit ca23d44 into master Mar 25, 2026
8 checks passed
@avihut avihut deleted the feat/clone-multiple-branches branch March 25, 2026 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant