The idea: a request as an object

In the Command pattern a request, such as "type abc" or "make it bold", becomes an object. The object has two methods: execute() does the work, and undo() takes it back. Once requests are objects, you can keep them in a list, run them later, run them again, or reverse them. Undo and redo in almost every editor are built this way.

The toolbar (invoker) creates a command object with execute() and undo(), calls execute(), which changes the document (receiver), and pushes the command on the undo stack; Editor undo moves it to the redo stack
The invoker only knows execute() and undo(); the command knows how to change the document.

The canvas is a tiny text editor with three roles. The receiver is the document: up to 16 characters, each plain or bold. The invoker is the toolbar: it makes a command for each button, runs it, and keeps the history. The command sits between them. The page's Editor undo and Editor redo buttons are the editor's own; the Step Back button in the playback bar only rewinds the animation.

execute() and undo(): save what you need

A command can only undo what it remembers. So when it executes, it saves just enough to reverse itself. Each command saves something different:

commandexecute()savesundo()
Insert "abc"type "abc" at the endposition and lengthdelete that many characters there
Delete 2delete the last 2 charactersthe deleted text and its positioninsert the text back
Bold allmake every character boldthe old bold flags, e.g. --b--put the old flags back

Delete is the one that shows why saving matters: once "bc" is deleted, nothing in the document remembers it. Only the command does. (Demo 4: a new command clears redo runs a Delete; watch its card change to Delete 2 ยท saved "bc".)

interface Command { void execute(); void undo(); }

class DeleteCommand implements Command {
    Document doc; int count; int pos; String saved;
    void execute() { pos = doc.length() - count; saved = doc.cut(pos, count); }
    void undo()    { doc.insert(pos, saved); }
}

The undo stack and the redo stack

The invoker keeps two stacks. After a command executes, it is pushed on the undo stack. Editor undo pops the top command, calls its undo() and pushes it on the redo stack. Editor redo pops the top of the redo stack, calls execute() again and pushes it back on the undo stack. Both are stacks because undo goes back in time: the last change must be reversed first, or the saved positions would point at the wrong characters. (See the stack page for push and pop on their own.)

Demo 1: run three commands builds "abcde" in bold, one card per command. Demo 2: undo twice reverses Bold all, then Insert "de"; both cards end up on the redo stack, with Insert "de" on top. Demo 3: undo, then redo runs Insert "de" again.

Four snapshots of the undo and redo stacks: after three commands undo holds abc, de, bold; after two undos undo holds abc and redo holds bold, de; after a redo undo holds abc, de and redo holds bold; after a new Delete command undo holds abc, Delete and the redo stack is empty
Undo and redo move the same command objects between two stacks; a new command empties the redo stack.

Why a new command clears redo

A redo command was saved for one exact document. After two undos the document is "abc", and the redo stack holds Insert "de" and Bold all. If you now run Delete 2, the document becomes "a". The redo commands remember position 3 and five bold flags, which no longer fit a one-character document. So every new command clears the redo stack, and the undone work is gone for good. (Demo 4: a new command clears redo.) Some editors keep an undo tree instead, so no branch is lost; the page keeps the usual linear history.

The history also has a limit. On the page it is 6 commands; a 7th drops the oldest, which can then never be undone. Real editors limit history by count or by memory.

Macros: one command made of commands

A macro (a composite command) holds a list of child commands and is itself a command. Its execute() runs the children in order. Its undo() runs the children's undo() in reverse order, for the same reason the history is a stack. The page's macro is Insert "!" then Bold all. One Editor undo first restores the bold flags, then deletes the "!". (Demo 5: macro, undone in one step.) Editors use the same idea to group a burst of typing, or a "replace all", into one undo step.

Where you meet it

Java Swing has UndoableEdit with undo() and redo(), an UndoManager with a limit, and CompoundEdit for macros. Word processors, IDEs, drawing tools and spreadsheets keep a command history the same way. Commands also make queues possible: a request object can be put in a job queue, sent to another thread or machine, logged and replayed. A database log is related but different: write-ahead logging records before and after images so a crash can be redone or undone, much like the state a command saves. See also SOLID: the invoker depends only on the Command interface, so new commands don't change the toolbar.

In Event Sourcing and CQRS commands become stored events, and state is rebuilt by replaying them instead of by undo.

For history that branches and merges, see Git Three-Way Merge.

Git keeps a similar history of moves for its pointers: see Git branches and HEAD, where the reflog lets you undo a reset.

What the page leaves out

A real editor has a caret and a selection, so commands insert and delete in the middle of the text and save the selection too. Typing is usually merged into one command per word or per pause. Some editors use mementos (snapshots of the whole state) instead of per-command undo data, or an undo tree that keeps every branch. Collaborative editors must also transform commands against other users' edits (operational transformation, CRDTs). Commands that touch the outside world, such as sending an email, can't be undone at all and need a compensating action.