diff options
Diffstat (limited to 'internal/engine/apply.go')
| -rw-r--r-- | internal/engine/apply.go | 19 |
1 files changed, 17 insertions, 2 deletions
diff --git a/internal/engine/apply.go b/internal/engine/apply.go index 94d65e4..e109aaf 100644 --- a/internal/engine/apply.go +++ b/internal/engine/apply.go @@ -343,6 +343,14 @@ func (e *Engine) PlanUndo(runID string) (*UndoPlan, error) { return nil, fmt.Errorf("engine: plan undo: run %s is itself an undo and cannot be undone", runID) } + // Reversals an earlier undo of this same run already completed are not + // offered again (review M10): an undo that stopped part way can be + // finished by undoing the run once more. + reversed, err := journal.ReversedSteps(e.Config.LogFile(), runID) + if err != nil { + return nil, fmt.Errorf("engine: plan undo: %w", err) + } + // Entries are grouped by directory and file together (review M7): one run // spans every directory, and two directories can each hold a file of the // same name. @@ -362,7 +370,7 @@ func (e *Engine) PlanUndo(runID string) (*UndoPlan, error) { up := &UndoPlan{Run: runID} for _, k := range order { - uf := planUndoFile(k.dir, k.file, byFile[k]) + uf := planUndoFile(k.dir, k.file, byFile[k], reversed) // Critical finding, Task 8's review: a file every one of whose // entries has Status != "ok" (declined by the ORIGINAL run's own // review, or skipped, or failed before anything happened) yields @@ -464,7 +472,7 @@ func isFileAffecting(action string) bool { // two steps of one file that touch the same path, and every un-contended // check keeps behaving exactly as before, since the projection only ever // overrides a real occupant that this same chain is itself about to clear. -func planUndoFile(dir, file string, ents []journal.Entry) UndoFile { +func planUndoFile(dir, file string, ents []journal.Entry, reversed map[journal.ReversedKey]int) UndoFile { uf := UndoFile{File: file, Dir: dir} proj := newUndoProjection() for i := len(ents) - 1; i >= 0; i-- { @@ -483,6 +491,13 @@ func planUndoFile(dir, file string, ents []journal.Entry) UndoFile { break } step := reverseStep(en) + if k := (journal.ReversedKey{Dir: dir, File: file, Action: step.Action, Src: step.Src}); reversed[k] > 0 { + // An earlier undo of this run already reversed this step: the + // disk already shows it, and it is not offered again. + reversed[k]-- + proj.record(step) + continue + } if (en.Action == "move" || en.Action == "rename") && proj.occupied[en.Dst] { // A reversal already queued for this same file puts it back at // en.Dst before this one runs, and that reversal was checked |
