Skip to content

[Feat] Add gmc wt share discover subcommand#58

Merged
samzong merged 1 commit intomainfrom
fix/share-discover
Mar 30, 2026
Merged

[Feat] Add gmc wt share discover subcommand#58
samzong merged 1 commit intomainfrom
fix/share-discover

Conversation

@samzong
Copy link
Copy Markdown
Owner

@samzong samzong commented Mar 30, 2026

What's changed?

  • Add gmc wt share discover subcommand that scans the main worktree for files that should be shared across worktrees
  • Built-in candidate lists for copy (.env*, .claude/*, .serena/*) and link (node_modules, .venv, vendor, __pycache__) strategies
  • Auto-detect main/master worktree, skip already-configured paths
  • --auto flag to batch-add discovered resources and sync in one shot
  • --dry-run flag (default) for preview mode
  • AddDiscoveredResources batch method — single config load/save cycle
  • 6 unit tests covering discovery, skip-existing, empty, all-candidates, and batch-add

Why

@samzong samzong merged commit 3ebd4e2 into main Mar 30, 2026
1 check passed
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new discover command to the worktree share utility, allowing users to automatically find and configure common files (like .env) or directories (like node_modules) for sharing across worktrees using either copy or symlink strategies. The implementation includes a discovery engine that scans the main worktree and a batch addition mechanism. Feedback focuses on improving the robustness of the CLI by removing a redundant --dry-run flag, refactoring duplicated logic in the discovery loops, ensuring deterministic behavior when selecting a worktree to scan, and improving error handling within the test suite.

{Path: ".env", Strategy: StrategyCopy},
},
}
data, _ := yaml.Marshal(&cfg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The error returned by yaml.Marshal is being ignored. In tests, it's important to check for all potential errors to ensure the test setup is correct and the test is reliable. Please handle this error, for example by using require.NoError(t, err).

Suggested change
data, _ := yaml.Marshal(&cfg)
data, err := yaml.Marshal(&cfg)
require.NoError(t, err)

Comment thread cmd/worktree_share.go
_ = wtShareAddCmd.RegisterFlagCompletionFunc("strategy", completeStrategies)

wtShareDiscoverCmd.Flags().BoolVar(&discoverAuto, "auto", false, "Actually add discovered items and sync")
wtShareDiscoverCmd.Flags().Bool("dry-run", true, "Preview mode (default behavior)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The --dry-run flag is defined here but its value is never used in the command's logic. The dry-run behavior is instead controlled by the absence of the --auto flag. This can be confusing for users who might expect --dry-run=false to have an effect.

Since the Long description for the command already clearly states that dry-run is the default behavior, consider removing this flag to avoid confusion and simplify the command's interface.

Comment on lines +57 to +81
for _, candidate := range copyCandidates {
if existing[candidate] {
continue
}
if _, statErr := os.Stat(filepath.Join(mainPath, candidate)); statErr == nil {
results = append(results, DiscoverResult{
Path: candidate,
Strategy: StrategyCopy,
Reason: "config/env file — isolated copy per worktree",
})
}
}

for _, candidate := range linkCandidates {
if existing[candidate] {
continue
}
if _, statErr := os.Stat(filepath.Join(mainPath, candidate)); statErr == nil {
results = append(results, DiscoverResult{
Path: candidate,
Strategy: StrategySymlink,
Reason: "dependency/cache directory — shared symlink saves disk",
})
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The loops for copyCandidates and linkCandidates are nearly identical, leading to code duplication. To improve maintainability, this can be refactored.

Consider creating a helper function that takes a list of candidates and a strategy, or restructuring the candidate data into a single slice of structs that includes the strategy, and then iterating over it once.

Comment on lines +133 to +138
for _, wt := range worktrees {
if !wt.IsBare && filepath.Base(wt.Path) != ".bare" {
fmt.Fprintf(os.Stderr, "Warning: no main/master branch found, using worktree %q\n", filepath.Base(wt.Path))
return wt.Path, nil
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The fallback logic for finding a worktree (when no main or master branch is found) is non-deterministic. If multiple non-bare worktrees exist, the one chosen depends on the order returned by c.List(), which is not guaranteed to be stable. This could lead to unpredictable behavior on different runs.

To make the behavior predictable, you could sort the worktrees slice by path before this loop.

Comment on lines +25 to +27
oldCwd, _ := os.Getwd()
require.NoError(t, os.Chdir(repoDir))
defer func() { _ = os.Chdir(oldCwd) }()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Errors from os.Getwd() and os.Chdir() in the deferred function are being ignored. This pattern is repeated in other tests in this file. It's a good practice to handle these errors to make tests more robust and prevent subtle failures.

Consider changing this to:

oldCwd, err := os.Getwd()
require.NoError(t, err)

require.NoError(t, os.Chdir(repoDir))
defer func() {
    require.NoError(t, os.Chdir(oldCwd))
}()
Suggested change
oldCwd, _ := os.Getwd()
require.NoError(t, os.Chdir(repoDir))
defer func() { _ = os.Chdir(oldCwd) }()
t.Cleanup(func() { require.NoError(t, os.Chdir(oldCwd)) })

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.

feat(worktree): add share discover subcommand

1 participant