aboutsummaryrefslogtreecommitdiff
path: root/cmd/krino/undo.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/krino/undo.go')
-rw-r--r--cmd/krino/undo.go67
1 files changed, 32 insertions, 35 deletions
diff --git a/cmd/krino/undo.go b/cmd/krino/undo.go
index 5a7db4b..85b74b5 100644
--- a/cmd/krino/undo.go
+++ b/cmd/krino/undo.go
@@ -26,9 +26,9 @@ func init() { commands["undo"] = cmdUndo }
// the most recent one otherwise. Since the newest run in the log can never
// itself be marked Undone - that would require a still-later run to have
// reversed it - "the most recent run" and "the most recent run that has not
-// been undone" are the same run in every case, including the one this
-// task's own test exercises: undoing an undo run a second time with no RUN
-// argument targets that very undo run, which PlanUndo then refuses by name.
+// been undone" are the same run in every case, including the case tested
+// below: undoing an undo run a second time with no RUN argument targets
+// that very undo run, which PlanUndo then refuses by name.
//
// Undo builds a plan like any other, shown and approved the same way (spec
// §10) - reviewUndoDir/-Files/-PerFile below are undo's own counterpart to
@@ -74,10 +74,9 @@ func cmdUndo(g *globals, args []string, stdout, stderr io.Writer) int {
}
// Installed here, before any paging or review, not just around
- // ApplyUndo: Ruling 5 (Task 7) is that SIGTERM landing between
- // keystrokes or during the pager needs the terminal restored, and that
- // window starts as soon as this command might show something on a
- // terminal.
+ // ApplyUndo: SIGTERM landing between keystrokes or during the pager
+ // needs the terminal restored, and that window starts as soon as this
+ // command might show something on a terminal.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
stopSignals := installSignalHandler(cancel)
@@ -101,9 +100,9 @@ func cmdUndo(g *globals, args []string, stdout, stderr io.Writer) int {
return 0
}
runID = runs[0].ID
- // The most recent run is itself an undo: continue it, by planning the
- // run it undid again - reversals it completed are not offered twice
- // (review M10). Naming an undo run explicitly is still refused.
+ // The most recent run is itself an undo: continue it, by planning
+ // the run it undid again - reversals it completed are not offered
+ // twice. Naming an undo run explicitly is still refused.
if runs[0].UndoOf != "" {
runID = runs[0].UndoOf
}
@@ -128,9 +127,8 @@ func cmdUndo(g *globals, args []string, stdout, stderr io.Writer) int {
return 1
}
- // Fix round 2026-09-12 (widened per the coordinator's follow-up
- // ruling): an undo moves files just as an apply does, so it needs
- // cmdSort's same per-directory guard (spec §11: a second krino on the
+ // An undo moves files just as an apply does, so it needs cmdSort's
+ // same per-directory guard (spec §11: a second krino on the
// same directory waits for the lock, or fails immediately with -y),
// held across the SAME window cmdSort holds its own lock across - the
// plan display and the review, not just the apply. Failing before the
@@ -165,8 +163,8 @@ func cmdUndo(g *globals, args []string, stdout, stderr io.Writer) int {
var buf bytes.Buffer
printUndoPlan(&buf, up)
text := colourRefused(buf.String(), p)
- // Ruling 6 (Task 7), carried over: the plan goes through tui.Page for
- // -n as much as for -y and the interactive path.
+ // The plan goes through tui.Page for -n as much as for -y and the
+ // interactive path.
if err := show(g, stdout, text); err != nil {
fmt.Fprintf(stderr, "krino: %v\n", err)
return 1
@@ -204,9 +202,9 @@ func cmdUndo(g *globals, args []string, stdout, stderr io.Writer) int {
toApply, notReviewed := finalizeUndoPlan(up, approved, action)
- // Ruling 2 (Task 7), carried over: journal.Open creates the state
- // directory and the log file as a side effect of merely being called,
- // so the session opens it only now, when something will actually be
+ // journal.Open creates the state directory and the log file as a side
+ // effect of merely being called, so the session opens it only now,
+ // when something will actually be
// applied - never for -n (returned above), and not merely because -y
// or a review session ran, unlike cmdSort, which opens before it knows
// whether anything is actionable (a difference forced by cmdSort not
@@ -281,18 +279,17 @@ func releaseUndoLocks(locks []*lock.Lock) []error {
// finalizeUndoPlan builds the *engine.UndoPlan ApplyUndo actually runs,
// preserving up.Files' own order: every refused file rides along unchanged
// (ApplyUndo declines these itself, silently, exactly as it already does
-// when handed the unfiltered plan - spec §10's refusal is not this task's
-// to make noisier); every actionable file approved marks true rides along
+// when handed the unfiltered plan - spec §10's refusal is not made any
+// noisier here); every actionable file approved marks true rides along
// unchanged too. A file the user said no to, or left unmarked when [d] or
-// [q] cut a per-file review short, is not dropped - fix round 2026-09-12,
-// item 2 of Task 8's review: spec §9 says a declined file is logged even
-// though nothing happens to it, the same as the forward path already does,
-// so it is kept with Declined set, which tells ApplyUndo to log its steps
-// as declined rather than reverse them.
+// [q] cut a per-file review short, is not dropped: spec §9 says a
+// declined file is logged even though nothing happens to it, the same as
+// the forward path already does, so it is kept with Declined set, which
+// tells ApplyUndo to log its steps as declined rather than reverse them.
//
// After [w] (action 'w'), a reversible file the review never reached is
// left out of the plan entirely, as review's [w] leaves a forward file
-// unlogged, and counted in notReviewed (review cli F2).
+// unlogged, and counted in notReviewed.
func finalizeUndoPlan(up *engine.UndoPlan, approved map[int]bool, action rune) (plan *engine.UndoPlan, notReviewed int) {
out := &engine.UndoPlan{Run: up.Run, Cleanup: up.Cleanup}
for i, f := range up.Files {
@@ -374,15 +371,15 @@ func reviewUndoFiles(in io.Reader, out io.Writer, files []engine.UndoFile, p pal
// its reason and reverses nothing of it regardless of anything chosen here
// - but it still gets its own [i/N] line, so the numbering accounts for
// every file in the plan, not just the reversible ones. It behaves as
-// review.go's reviewPerFile does (review cli F2): a no is recorded as false,
-// each choice is echoed in red, [w] ends with 'w' leaving unreached files
-// out of approved, and [q] ends with 'q'.
+// review.go's reviewPerFile does: a no is recorded as false, each choice is
+// echoed in red, [w] ends with 'w' leaving unreached files out of
+// approved, and [q] ends with 'q'.
func reviewUndoPerFile(in io.Reader, out io.Writer, files []engine.UndoFile, p palette) (approved map[int]bool, end rune, err error) {
approved = map[int]bool{}
yesRest := false
for i, f := range files {
// Heading and steps wrap past the action column, so a long name or
- // path cannot pass for a step line (plan 11 review L4).
+ // path cannot pass for a step line.
width := widthPolicy(out)
fmt.Fprintln(out)
for _, l := range wrapped("", fmt.Sprintf("[%d/%d] %s/%s", i+1, len(files), display(f.Dir), display(f.File)), 7+undoStepWidth+2, width, plainText) {
@@ -468,11 +465,11 @@ const undoStepWidth = len("undo-displace")
// one (a sibling directory not yet empty for undo-mkdir - spec §10's one
// case where a step's own failure does not refuse its whole file), the
// directory removed for undo-mkdir (no destination to show), the file being
-// trashed for undo-copy (fix wave item 3: its Dst is deliberately empty -
-// trash.Put only chooses the entry name at execution time - so this is the
-// one action with no path to point an arrow at; before this fix the cell
-// rendered as a bare "undo-copy → ", the plan's one row that said
-// nothing about what it would do to the user's file), or an arrow to where
+// trashed for undo-copy (its Dst is deliberately empty - trash.Put only
+// chooses the entry name at execution time - so this is the one action
+// with no path to point an arrow at; rendering it as a bare "undo-copy
+// → " would be the plan's one row that says nothing about what it would do
+// to the user's file), or an arrow to where
// the step puts the file back, ~-abbreviated - undo has no single root the
// way a sort plan does (one run can span several directories), so there is
// no root-relative form to render here the way actionCell has.