Skip to content

Relax destructive-change rules when running inside a git worktree #44

@don4of4

Description

@don4of4

Problem

When Claude Code (or any agent) runs inside a git worktree rather than the main working tree, several built-in rules produce false positives. Worktrees are designed as disposable, isolated workspaces — discarding changes in a worktree doesn't risk the main branch's uncommitted work.

Commands that Safety Net currently blocks in worktrees:

Command Rule Why it's safe in a worktree
git restore <file> analyzeGitRestore Worktree is isolated; main tree is unaffected
git restore --worktree <file> analyzeGitRestore (--worktree flag) Same — the worktree IS the disposable workspace
git checkout -- <file> analyzeGitCheckout Discards changes only in the worktree
git reset --hard analyzeGitReset Resets only the worktree, not the main tree
git clean -f analyzeGitClean Cleans only the worktree

These are routine operations when using worktree-based workflows like worktrunk or Claude Code's native --worktree flag, where the typical flow is: create worktree, make changes, merge or discard, remove worktree.

Impact

The blocking behavior forces the agent to either:

  1. Ask the user for manual intervention (defeats the purpose of autonomous agents)
  2. Work around the block with more complex alternatives
  3. Fail the task entirely

This is especially disruptive for parallel agent sessions where multiple worktrees are used simultaneously — the whole point is that each worktree is isolated and disposable.

Bonus irony: Filing this very issue was blocked by Safety Net because the issue body contains the text git restore as a string literal — the same class of false positive reported in #41.

Suggested fix

Detect whether the current working directory is inside a worktree (not the main working tree) and relax the change-discard rules accordingly.

Detection is straightforward:

# Returns "true" if inside a linked worktree (not the main working tree)
git rev-parse --is-inside-work-tree && [ "$(git rev-parse --git-common-dir)" != "$(git rev-parse --git-dir)" ]

Or in Node.js, check if .git is a file (not a directory) — linked worktrees have a .git file containing gitdir: /path/to/main/.git/worktrees/<name>:

import { statSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

function isLinkedWorktree(cwd: string): boolean {
  try {
    const dotGit = join(cwd, '.git');
    const stat = statSync(dotGit);
    if (stat.isFile()) {
      const content = readFileSync(dotGit, 'utf-8');
      return content.trimStart().startsWith('gitdir:');
    }
    return false;
  } catch {
    return false;
  }
}

Rules to relax in worktrees

These rules protect against losing uncommitted work. In a linked worktree, that work is isolated by design:

  • git restore (with or without --worktree)
  • git checkout -- <files>
  • git reset --hard
  • git clean -f

Rules to keep in worktrees

These have effects beyond the local working tree and should remain blocked:

  • git push --force (affects remote)
  • git branch -D (affects shared branch refs)
  • git stash drop/clear (stash is shared across worktrees)
  • git worktree remove --force (could delete another worktree)

Environment

  • Safety Net v0.5.1 (via cc-marketplace plugin)
  • macOS (Darwin 25.3.0)
  • Worktree workflow: worktrunk + Claude Code --worktree

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions