Artifact a0e9374f297b8d64949f19d120ffca9f64b01035801ff618e2237c2ddd05b1e4:
- File
internal/checkout/revert_undo.go
— part of check-in
[80764da743]
at
2026-10-19 20:09:26
on branch feat/submanifest-reader
— fix(checkout): revert renames onto taken names, saving undo first
Checkout.Revert refused to undo a rename when another file held the old name: a swap, a chain, a new add or an untracked file there. Like fossil's revert it now saves each file it writes or deletes for undo, then reverts in one transaction, so Undo or fossil undo brings back what it overwrote, and a failed revert puts the files back.
Undoing a rename takes back the old name from whatever row holds it: a row renamed onto it is reverted to its own old name, and any other row is dropped, as fossil's UPDATE OR REPLACE drops it. Every committed file ends under its committed name; fossil's own revert leaves a swap renamed and stops tracking the second file of a chain.
Tracked in #248. (user: [email protected], size: 1021) [annotate] [blame] [check-ins using]
//go:build !js package checkout import ( "database/sql" "github.com/danmestas/go-libfossil/internal/undo" ) // revertUndo is the undo state Revert saves: the files its steps write or // delete, as they were before. type revertUndo struct { files []undo.File } // readRevertUndo reads the file under every name steps write or delete, as // fossil's revert calls undo_save before it touches one. func (c *Checkout) readRevertUndo(steps []revertStep) (revertUndo, error) { var u revertUndo for _, name := range revertNames(steps) { f, err := undo.ReadFile(c.env.Storage, c.dir, name) if err != nil { return revertUndo{}, err } u.files = append(u.files, f) } return u, nil } // save records u as the undo state within tx, before vfile changes. func (u revertUndo) save(tx *sql.Tx) error { return undo.SaveTx(tx, u.files) } // rollBackRevert puts u's files back after a revert failed partway. func (c *Checkout) rollBackRevert(u revertUndo) error { return undo.RestoreFiles(c.env, c.dir, u.files) }