Side note: this is one area where functional programming languages can really shine. In a pure functional language, all data structures are "persistent", meaning that all previous versions hang around as long as you keep a reference to them. All you need to do is push them on a stack and you have built-in undo support for any operation in the language. If functional languages supported SoftReferences (to my knowledge, none of the pure ones do), you could even have the garbage collector eat the bottom of the undo stack transparently.
In Haskell you could hide this in a monad: it'd act like the State monad, but with an additional action to "undo" that just pops the stack and restores the state. You could also put some special processing in the get/set actions to handle things like folding transparently.