feat(prune): `--apply` — safe, worktree-aware branch/worktree removal
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 35/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bien especificado
- Estado de actividad
- Tranquilo
- Stack tecnológico
- git, github, go
- Área
- cli, developer-experience, devtools
Línea de trabajo
Comienza con la tarea seleccionada y ordenada por dependencias, empezando por report/guard.go y report/guard_test.go para los tipos Guard puros y las pruebas tabulares. Lee los archivos de paquete referenciados y ejecuta primero el comando de pruebas de Go indicado en la tarea. Se considera terminado cuando se cumplen los criterios de aceptación de la tarea sin cambiar el valor predeterminado de solo lectura; la funcionalidad completa también requiere la integración con cmd/prune.go, pruebas de extremo a extremo y actualizaciones de README/help.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
skiffs prune today classifies local branches read-only. This adds an opt-in executor,
skiffs prune --apply, that removes integrated/merged branches and the worktrees holding
them — behind a durability guard that never destroys unique work. Read-only stays the default
(a plan / apply split).
Design
Durability guard — block removal if any of:
- Working tree not clean — untracked / modified / staged files.
- Un-PR'd commit — a commit in
default..branchthat is not an ancestor of the default
branch, not patch-equivalent to one already in default (rebase-&-merge, detected via
git cherry), and not covered by a MERGED PR (squash-&-merge). A CLOSED-unmerged
PR does not make a branch safe. - Precious ignored content — gitignored-but-present files that a plain
git worktree removewould silently destroy. Opted in via--anti-gitignore <path>(file of verbatim
.gitignorekeys) or--no-ignore(blanket: every ignored+present file is precious).
Removal routing (per --git-common-dir):
- default → native
git worktree remove+git branch -D - worktrunk-managed repo (
.config/wt.tomlpresent or anyworktrunk.*key in
.git/config) → delegate towt remove(preserves itspre-removehooks) - worktrunk-managed but
wtnot onPATH→ skip + report, never native-fallback
(fail-closed)
Interaction:
- read-only by default (bare
skiffs prune= the report / "plan") --apply→ interactive[y/N/a/q](Enter=skip,a=all,q=quit);-y/--yes= all--applyrefuses when the scan spans more than one--git-common-dir(single-repo
safety boundary; the primary + all its worktrees still count as one repo)- the per-branch verdict is additive to the existing
PruneResultJSON —
safeToDelete/needsReviewunchanged
Tasks
Dependency-ordered. T1 unblocks the parallel block T2–T5. Each task ≤ ~5 files, test-first.
-
T1 — Guard verdict types + pure
Guard()- Acceptance:
report/guard.godefinesVerdict{Branch, Blocks []BlockReason, Route},
BlockReason(DirtyWorktree|UnPushedCommits|PreciousIgnored),Route
(NativeGit|WorktrunkDelegate|SkipWtUnavailable),BranchState,GuardConfig.
Guard(BranchState, GuardConfig) Verdict;Removable == len(Blocks)==0. Additive —
PruneResultslices untouched. - Verify:
go test ./report/— table tests: each block reason alone, combined, clean case. - Files:
report/guard.go,report/guard_test.go.
- Acceptance:
-
T2 —
gh: MERGED-only signal- Acceptance:
internal/ghaddsMergedPRBranches(ctx, dir, limit) (map[string]bool, error)
filteringstate == MERGEDonly. ExistingMergedClosedPRBranchesunchanged. - Verify:
go test ./internal/gh/— fixture asserts CLOSED excluded, MERGED included. - Files:
internal/gh/gh.go,internal/gh/gh_test.go.
- Acceptance:
-
T3 —
git: durability collectors- Acceptance:
WorktreeDirty(dir) (bool, FileStatus)(reuseGetState);
UnDurableCommits(ctx, dir, defaultRef, branch string, mergedPR bool) ([]string, error)
combiningmerge-base --is-ancestor,git cherry defaultRef branch(+= candidate,
-= upstream), and the MERGED-PR cover; ignores empty commits and in-range merges. - Verify:
go test ./internal/git/—t.TempDir()repos for merge-commit, rebase-&-merge,
squash (+MERGED), and CLOSED-unmerged; assert removable vs held per the matrix. - Files:
internal/git/prune.go,internal/git/prune_test.go.
- Acceptance:
-
T4 —
internal/ignore:.anti-gitignore- Acceptance: new package.
Parse(path) ([]string, error)(non-empty, non-#verbatim
keys);PreciousPresent(ctx, dir string, keys []string, blanket bool) ([]string, error)=
ignored+present files (git ls-files --others --ignored --exclude-standard) matched by a
listed key (verbatim, viadoublestar), or all whenblanket. Reports per-key match count. - Verify:
go test ./internal/ignore/— temp repo with a gitignored dir + noise; scoped key
matches only its dir, blanket matches all, a typo'd key → 0 (surfaced, not silent). - Files:
internal/ignore/ignore.go,internal/ignore/ignore_test.go.
- Acceptance: new package.
-
T5 —
internal/worktrunk: detect + route- Acceptance: new package.
IsManaged(ctx, dir) bool(.config/wt.tomlpresent or
git config --get-regexp '^worktrunk\.'non-empty).Available() bool
(exec.LookPath("wt")).Route(managed, available bool) report.Route:
managed+avail→WorktrunkDelegate; managed+!avail→SkipWtUnavailable; else→NativeGit. - Verify:
go test ./internal/worktrunk/— temp repos with/without each signal; assert
fail-closed route whenwtunavailable. - Files:
internal/worktrunk/worktrunk.go,internal/worktrunk/worktrunk_test.go.
- Acceptance: new package.
-
T6 — Executor interface (real + fake)
- Acceptance:
Executorinterface{ RemoveWorktree(dir) error; DeleteBranch(name) error; WtRemove(branch) error }. Real impl shellsgit worktree remove/git branch -D/
wt remove --foreground.fakeExecutorrecords calls without running them. - Verify:
go test ./internal/git/— integration (temp repo) for realRemoveWorktree+
DeleteBranch; fake used by later routing tests. - Files:
internal/git/executor.go,internal/git/executor_test.go.
- Acceptance:
-
T7 —
cmd/prune: per-branch Verdict in the read-only report- Acceptance:
runPrunecollects state (T2–T5), builds aBranchStateper candidate, runs
Guard, renders held reasons in the table + addsverdictto JSON additively. No
--applyyet → still fully read-only. - Verify:
go test ./cmd/— safety-regression: barepruneperforms zero mutations
(refs+worktrees unchanged); JSON still containssafeToDelete/needsReviewplusverdict. - Files:
cmd/prune.go,cmd/prune_test.go.
- Acceptance:
-
T8 —
cmd/prune:--apply(single-repo gate +[y/N/a/q]+ dispatch)- Acceptance: flags
--apply(bool),--no-ignore(bool),--anti-gitignore <path>,
-y/--yes. Single-repo gate: if--applyand discovered branches span >1
--git-common-dir, error before any prompt. Interactive loop matches[y/N/a/q]
(Enter=skip,y,a=all,q=quit);-y= all. OnlyRemovableverdicts offered; held
listed with reason. Each confirmed removal dispatched viaRoute→Executor. - Verify:
go test ./cmd/— (a) multi-common-dir--applyrefused; (b) interaction parity
with scripted stdin; (c)SkipWtUnavailablenever calls native executor (fake asserts);
(d) temp-repo end-to-end: one loose branch + one worktree removed. - Files:
cmd/prune.go,cmd/prune_test.go.
- Acceptance: flags
-
T9 — Docs + help text
- Acceptance: README documents the plan/apply model, the durability guard, and all flags;
cobra long-help forpruneupdated.gofmt -l .clean,go vet ./...clean. - Verify:
go run . prune --helpshows the new flags;go test ./...green. - Files:
README.md,cmd/prune.go.
- Acceptance: README documents the plan/apply model, the durability guard, and all flags;
Parallelization
T1 ─┬─ T2 ─┐
├─ T3 ─┼─ T6 ─┐
├─ T4 ─┤ ├─ T8 ─ T9
└─ T5 ─┘ T7 ─┘
(T7 needs T1–T5; T8 needs T6+T7)
Out of scope
- Touching
NeedsReview,CLOSED-unmerged branches, or the primary/current worktree. - Glob re-interpretation in
.anti-gitignore— keys match verbatim against.gitignore. - Making the bare
skiffs prunecommand destructive (read-only stays the default). - Non-GitHub remotes: branches whose squash-safety can't be proven via a MERGED PR are held,
never auto-removed.
- Lenguaje dominante
- Go
- Estrellas
- 5
- Forks
- 0
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Issues similares
-
Mend: dependency security vulnerability
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
kube-tarian/sigrun#202 ·
-
question raised-by:verifier
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
medici-finance/assay#4013 ·
Los mantenedores suelen responder en 1 día
-
TestRegistry_ConcurrentMixedOperations fetches from the network and can hang the short suiteAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
tsukumogami/tsuku#2775 ·
Los mantenedores suelen responder en 1 día
-
automation models
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 69/100
Los mantenedores suelen responder en 1 día