gastownhall/beads · error

target git directory changed

Error message

target git directory changed

What it means

Revalidation compares the target worktree's resolved git admin directory (gitDir) with the one recorded at plan time. A change means the worktree's .git link was rewritten or the worktree was recreated at the same path but pointing at different git metadata — bd's plan no longer describes reality, so it aborts with this error and marks GitAdminDirectory as Changed.

Source

Thrown at cmd/bd/worktree_cmd.go:1413

	if worktreeRegistryIdentity(currentEntry) != plan.target.registryID {
		facts.Registration = worktreeremove.InvariantChanged
		if currentEntry.locked != plan.target.locked || currentEntry.lockReason != plan.target.lockReason ||
			currentEntry.prunable != plan.target.prunable || currentEntry.pruneReason != plan.target.pruneReason {
			facts.LockPrune = worktreeremove.InvariantChanged
		}
		return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("registered target identity changed")}
	}
	facts.Registration = worktreeremove.InvariantStable
	facts.LockPrune = worktreeremove.InvariantStable
	facts.TargetPath = worktreeremove.InvariantStable

	currentTarget, err := inspectWorktreeTarget(ctx, plan.git, currentEntry)
	if err != nil {
		return worktreeRevalidationObservation{facts: facts, err: err}
	}
	if !sameWorktreePath(currentTarget.gitDir, plan.target.gitDir) {
		facts.GitAdminDirectory = worktreeremove.InvariantChanged
		return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target git directory changed")}
	}
	facts.GitAdminDirectory = worktreeremove.InvariantStable
	if !sameWorktreePath(currentTarget.commonDir, plan.commonDir) {
		facts.CommonDirectory = worktreeremove.InvariantChanged
		return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target common git directory changed")}
	}
	facts.CommonDirectory = worktreeremove.InvariantStable
	if currentTarget.headOID != plan.target.headOID {
		facts.Head = worktreeremove.InvariantChanged
		return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target HEAD changed from %s to %s", plan.target.headOID, currentTarget.headOID)}
	}
	facts.Head = worktreeremove.InvariantStable
	if currentTarget.status != plan.target.status {
		if (currentTarget.status == "") != (plan.target.status == "") {
			facts.Cleanliness = worktreeremove.InvariantChanged
		} else {
			facts.Cleanliness = worktreeremove.InvariantStable
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Discard the stale plan and re-run `bd worktree remove` so the plan captures the new gitDir
  2. Inspect the target's `.git` file to see where it now points; if the worktree was recreated, this is expected
  3. Avoid recreating worktrees while a removal is pending; serialize git worktree operations

Example fix

// before: worktree recreated at same path between plan and execute
$ bd worktree remove feature
// error: target git directory changed
// after
$ bd worktree remove feature   # fresh plan against the new worktree
Defensive patterns

Strategy: retry

Validate before calling

cur=$(git -C /wt/feature rev-parse --path-format=absolute --git-dir)
[ "$cur" = "$RECORDED_GITDIR" ] || { echo 'worktree git dir changed; rebuild plan'; exit 1; }

Type guard

func gitDirStable(before, after string) bool {
	return filepath.Clean(before) == filepath.Clean(after)
}

Try / catch

obs := revalidateWorktreePlan(ctx, plan)
if obs.err != nil && strings.Contains(obs.err.Error(), "git directory changed") {
	plan, err = buildWorktreeRemovalPlan(ctx, git, name, nil) // fresh plan, then execute
}

Prevention

When it happens

Trigger: Executing a removal plan after the target's `.git` file was rewritten (worktree deleted and re-added at the same path), a `git worktree repair`/`prune`+re-add cycle, or manual edits to the `.git` pointer file between plan and execute.

Common situations: Scripts that recreate worktrees at fixed paths; IDE worktree sync features; manual copying of worktree directories; running a stale bd plan against a rebuilt checkout.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d48b91f1ab59c6b7. Report an issue: GitHub.