Two bugs in the untracked-skipped warning, both surfaced by review. 1. Wrong scope for subdir inputs. The helper accepted any path the caller had (sourcePath, which may be a user-supplied subdirectory) and ran `git -C <path> ls-files --others --exclude-standard`. Git scopes that output to the cwd, so pointing `vm run ./repo/sub` at a subdir silently underreported untracked files living elsewhere in the repo — exactly the files the warning exists to surface. Fix: resolve sourcePath to the repo root inside the helper via `rev-parse --show-toplevel` before counting. 2. Inconsistent failure handling. The comment said the helper should be silent when the count can't be determined; the body returned the error. vm_run.go treated the error as non-fatal (logged a warning, continued); workspace prepare and --dry-run aborted the whole operation on the same helper failure. A courtesy notice shouldn't kill the operation. Fix: make the helper best-effort in signature and body — no error return, swallows rev-parse + count failures, emits nothing when there's nothing to say. All three callers lose their error branches. Regression tests: - subdir input reports the root-level untracked file (the bug case) - non-repo path produces silence, not a fatal error - inspector whose runner errors on every call produces silence Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
2.5 KiB
Go
61 lines
2.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// runWorkspaceDryRun inspects the local repo at resolvedPath and
|
|
// prints the file list that `vm run` / `workspace prepare` would ship
|
|
// into the guest. Runs on the CLI side (no daemon RPC needed) since
|
|
// the daemon is always local and the workspace inspection is a pure
|
|
// git read. Git calls go through d.repoInspector so tests inject a
|
|
// stub Runner via the deps struct instead of touching package globals.
|
|
func (d *deps) runWorkspaceDryRun(ctx context.Context, out io.Writer, resolvedPath, branchName, fromRef string, includeUntracked bool) error {
|
|
spec, err := d.repoInspector.InspectRepo(ctx, resolvedPath, branchName, fromRef, includeUntracked)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(out, "dry-run: %d file(s) would be copied to guest\n", len(spec.OverlayPaths))
|
|
fmt.Fprintf(out, "repo: %s\n", spec.RepoRoot)
|
|
if includeUntracked {
|
|
fmt.Fprintln(out, "mode: tracked + untracked non-ignored (--include-untracked)")
|
|
} else {
|
|
fmt.Fprintln(out, "mode: tracked only (re-run with --include-untracked to also copy untracked non-ignored files)")
|
|
}
|
|
fmt.Fprintln(out, "---")
|
|
for _, path := range spec.OverlayPaths {
|
|
fmt.Fprintln(out, path)
|
|
}
|
|
if !includeUntracked {
|
|
d.noteUntrackedSkipped(ctx, out, spec.RepoRoot)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// noteUntrackedSkipped prints a one-line notice when the repo holds
|
|
// untracked non-ignored files that will NOT be copied because
|
|
// --include-untracked was not passed.
|
|
//
|
|
// Best-effort: if sourcePath isn't inside a git repo, or git errors,
|
|
// or there are no untracked files, the helper stays silent. The
|
|
// notice is a courtesy — failing the whole operation over a courtesy
|
|
// would be worse than the notice being missing.
|
|
//
|
|
// Resolves sourcePath to the repo root internally via `git rev-parse
|
|
// --show-toplevel` so callers can pass whatever path the user typed.
|
|
// Before this helper normalised, subdir inputs ran `ls-files
|
|
// --others` scoped to the subdir, which silently underreported the
|
|
// skipped files the user needed to know about.
|
|
func (d *deps) noteUntrackedSkipped(ctx context.Context, out io.Writer, sourcePath string) {
|
|
repoRoot, err := d.repoInspector.GitTrimmedOutput(ctx, sourcePath, "rev-parse", "--show-toplevel")
|
|
if err != nil || repoRoot == "" {
|
|
return
|
|
}
|
|
count, err := d.repoInspector.CountUntrackedPaths(ctx, repoRoot)
|
|
if err != nil || count == 0 {
|
|
return
|
|
}
|
|
fmt.Fprintf(out, "---\nnote: %d untracked non-ignored file(s) were NOT copied (git-tracked files only by default — pass --include-untracked to include them)\n", count)
|
|
}
|