fix(polecat): detect when shell is cd'd into worktree before nuke (#969)

When nuking a polecat while your shell is cd'd into its worktree,
the shell becomes completely broken (even `echo hello` fails).

Add a check before deletion that detects if the current working
directory is inside the polecat's worktree. If so, return an error
with helpful instructions to cd elsewhere first.

The check runs ALWAYS, even with --force, because this is about
shell safety not data loss. You can't bypass it - just cd out first.

Fixes #942
This commit is contained in:
Bo
2026-01-25 21:01:24 -05:00
committed by GitHub
parent 7926d7b3e8
commit 212f08ad03

View File

@@ -22,10 +22,11 @@ import (
// Common errors
var (
ErrPolecatExists = errors.New("polecat already exists")
ErrPolecatNotFound = errors.New("polecat not found")
ErrHasChanges = errors.New("polecat has uncommitted changes")
ErrPolecatExists = errors.New("polecat already exists")
ErrPolecatNotFound = errors.New("polecat not found")
ErrHasChanges = errors.New("polecat has uncommitted changes")
ErrHasUncommittedWork = errors.New("polecat has uncommitted work")
ErrShellInWorktree = errors.New("shell working directory is inside polecat worktree")
)
// UncommittedWorkError provides details about uncommitted work.
@@ -528,6 +529,23 @@ func (m *Manager) RemoveWithOptions(name string, force, nuclear bool) error {
}
}
// Check if user's shell is cd'd into the worktree (prevents broken shell)
// This check ALWAYS runs, even with --force/nuclear, because deleting your
// shell's cwd breaks the shell completely (all commands fail with exit 1).
// See: https://github.com/steveyegge/gastown/issues/942
cwd, cwdErr := os.Getwd()
if cwdErr == nil {
// Normalize paths for comparison
cwdAbs, _ := filepath.Abs(cwd)
cloneAbs, _ := filepath.Abs(clonePath)
polecatAbs, _ := filepath.Abs(polecatDir)
if strings.HasPrefix(cwdAbs, cloneAbs) || strings.HasPrefix(cwdAbs, polecatAbs) {
return fmt.Errorf("%w: your shell is in %s\n\nPlease cd elsewhere first, then retry:\n cd ~/gt\n gt polecat nuke %s/%s --force",
ErrShellInWorktree, cwd, m.rig.Name, name)
}
}
// Get repo base to remove the worktree properly
repoGit, err := m.repoBase()
if err != nil {