Thinking about what you do with undo
In thinking more about undo issues, I think that the fundamental misconception of designing an undo system is to think of it as purely operation based, instead of something more complex (I don't think that it's purely time based either). Undo is only operation based over the very short term, where you are undo-ing an immediate mistake.
I've been trying to think about what things you do with undo, in general. So far, what I have come up with is:
- fix an immediate mistake (the classical reason for simple undo).
- go back to how things were at some point in the past, either to
look at it and then go back to 'now' or to restart work from that
(In a time-based view of undo, restarting from a point in the past should not overwrite everything after that; the past should be immutable because it is, after all, the past. This creates a very basic version of branching.)
- experiment with something, then revert to a previous version if it
doesn't work out.
- fix a mistake that you didn't notice until some time later. I don't
think that this is as simple as rewinding time and starting again,
because you probably have work done since the mistake that you want
(In software where you already view things as a sequence of edits, for example a non-destructive photo editor, this is a relatively natural and hopefully simple thing. Otherwise, not so much.)
One fundamental difference between an operation based view and other views of undo is how you handle changes after a series of undo operations. Such changes effectively create a branch in the history; on one side is all of the changes that were undone, and on the other is your just-made changes. In an operation based view it is sensible to immediately throw away the first branch when the second is created, but it is my strong belief that in practice this is going to be very frustrating for users; among other things, it makes an accidental edit while you are deep down in an undo sequence very damaging, since you just lost a bunch of work.
(If you adopt the principle that undo should never cause users to lose work, you are basically forced to at least a time based view of undo so that the user can always return to any previous state of their work.)