Vim Undo Tips and Tricks
The Vim editor always reminds me of a coelacanth – a survivor of a past age that looks impossibly clumsy yet somehow works. At times, it seems to have more commands than anyone could memorize or use, some with obsolete names (for instance, "pull" for "paste"). Yet if you learn even a fraction of its functionality, you can work faster and more efficiently than with any GUI editor. Take, for example, Vim's undo and redo features, parts of which seem counterproductively complicated, yet which are more powerful than most equivalents in other text editors.
The basics are simple enough. The undo buffer is called a tree in Vim, and a set of editing alterations is called a leaf or a change (the notifications and documentation are inconsistent).
A leaf is defined as either every change to a line from the time you enter insert mode to the time that you leave it, or a single command in command mode. A leaf can be the result of most kinds of editing, including spell checker corrections, copy and paste, and search and replace, as well as manual edits. However, leaves are not created for the actions of some plugins, such as the folds Vim Outliner makes when collapsing and expanding trees.
To undo a leaf, press Esc to switch to command mode and press u. All changes you made during your last session in insert mode will be deleted. You can also press U to undo only the changes to the last line that you edited.
In either case, you leave only a message behind at the bottom of the screen, summarizing the changes. For example, the message might read:
1 change; before #9 11:57:30
This tells you that a single change was made before the ninth item in the undo buffer, and that the change was made shortly before noon. This information can help you revert to a specific change when you are using some of the undo feature's sophisticated tools, as we'll see in a moment.
If you decide you shouldn't have undone a change, you have two choices: either type Ctrl-r or enter
redo as a command. Both restore the last change that you undid.
This simple undo and redo functionality is all that many users ever need. However, Vim offers more sophisticated maneuvres for the cognoscenti.
Moving Through Trees and Branches
While in command mode, you can type
g- to search up and down the undo tree. This is by far the easiest way to revert to the leaf you want, since you move one step at a time backward or forward.
Alternatively, you can run
:undolist in command mode to see the number of changes in the tree, then move to a change directly using
:later, and specifying an interval in seconds (s), minutes (m), hours (h), or even days (d). The first movement – which, naturally, must use
:earlier – is relative to the current time. Vim will move to the leaf closest to the time you specify. To get the very latest change, choose a very large interval, such as
:later 10d. Any additional uses are relative to the time of the leaf you are currently at, which is why Vim's messages let you know what time each change was made.
All changes continue to be preserved in the undo buffer, even when you revert to an earlier leaf. As a result, if you get lost, you can use either redo command to change to the previous leaf that you were at. If necessary you can continue entering a redo command until you are back where you started and the message "Already at latest change" appears when you try another redo.
If you save frequently, you can also use
:later to move between saves. For instance,
:earlier 2f returns the file to the state of two saves ago. This option has the advantage of being precise, although you still need to remember what changes you made before each save.
The Challenge of Branching
Navigating the undo history is relatively simple when you have a single undo tree, but complications quickly set in if you have branches. A branch is a point on the tree to which you have reverted, then gone on to make additional changes of any sort recorded in the undo buffer. The original branch as well as new branch are preserved, but what is confusing is that changes are recorded consecutively, so that the ninth change in the undo buffer can be in one branch, the tenth in a second, and the eleventh back in the first branch. The online Vim documentation gives a thorough illustration you can recreate for yourself to see how branches work.
Why would you want to create a branch? The truth is, you have no choice. Vim simply creates them automatically when you revert to an earlier leaf and start to edit from that point. That means you have to keep close track of what you are doing, or else you may accidentally create a branch and become lost in your own undo buffer.
However, if you create a branch and remember where it was located, you can experiment, making an entire series of changes and then returning to the original if you decide that the experiment was unsuccessful.
Still, branches tend to make undo buffer navigation confusing. The only help you can get is Vim's message as you make each change, plus an overview in the information produced by
:undolist. For example, suppose you enter
:undolist and receive a summary like this one:
number changes time
9 9 11:57:36
11 6 12:15:19
17 3 1:03:56
From this information you know that the undo tree has three branches: one that branched off at the ninth change, another at the eleventh, and a third at the seventeenth. You can also see the number of changes in each branch, and the time that the last change was made on each branch. Armed with this information, you can navigate somewhat more easily. For example, by keeping count of your use of
g-, you would know which branch you are in with each reversion. Similarly, using
:earlier, you could revert to the second branch by chosing a time somewhere between 11:57:36 and 12:15:19 (assuming, that is, that you weren't jumping around between branches during this period).
Better Undo Through Plugins
Because undo trees with branches can be so confusing, the most effective way to work with them is to install a plugin that allows you to more easily keep track of the undo tree and all its branches.
Undo_tags gives users the ability to add a tag and optional description to a leaf while in command mode via the command
:UTMark. When you want to move to a leaf, you can view a list of tags with
:UTList. This command shows the same information as
:undolist, but adds the tags and descriptions. To jump to a command, use
:UTRestore, and jump to a particular leaf with another command. If necessary, you can also delete tags.
If you choose carefully selected tags and descriptions, undo_tags can greatly reduce your need to have a good memory to use Vim undo, and provide another method of navigation as well. It is especially useful if you use a common naming pattern for all leaves in the same branch.
By far the most popular undo plugin is Gundo, which displays a tree – what it calls a "graph" – and a preview pane on the left side of your terminal window. Your present leaf is marked by an at-sign (@), and you can move up and down the graph by pressing the j and k keys. If you want to revert to a leaf, all you need to do is move to it, then press Enter.
[caption id="attachment_184226" align="alignnone" width="300" caption="Gundo"]
As you move through Gundo's graph, the preview pane shows the changes made for the currently selected leaf. The preview pane can also show the difference between the present leaf and the one selected on the graph, or replay all the changes made to the selected leaf.
Even more than undo_tags, Gundo allows you to take full advantage of Vim's undo features with a minimum of effort. Its main weakness is that branches are crammed into the available space in a way that can make them hard to read.
Before Vim 7.3, undo history was lost when you closed a file, but now persistent undo is a standard Vim feature. As the name suggest, persistent undo saves the undo history between work sessions – as long as the undo buffer contains fewer than 10,000 lines. Since by default Vim stores only 1,000 levels, generally one per line, that means you can usually rely on retaining the undo history between work sessions. If you need to, you can use the command
set ul=NUMBER to double or triple the number of levels of storage, at a cost of an overhead so slight that on modern systems you are unlikely to notice the change.
Persistent undo includes the option of storing the undo history in a separate file in the same directory as your original document. The undo history file has the same name as the document, but with a .un~ extension. When you open the original document again, the undo history is automatically loaded with the document with which it is associated.
To create a separate undo file, run the command
:set undofile. Should you prefer to store the undo file in a directory other than the default, use the undodir variable in your .vimrc file to set it. You can then save the undo history as you work with the command
:wundo, or reload it with
:rundo, which can be useful for a quick recovery if you find yourself hopelessly lost in the branches of the undo tree.
Persistent undo is simply the latest of Vim's undo features. Arcane, unwieldly, and not well-coordinated with each other, these features can be frustrating to learn, and may not always do exactly what you want. However, if you learn how to make the undo tools work for you, you'll understand better why Vim remains many users' editor of choice. It may be ancient, but its power and versatility ensure that it is still far from obsolete.
This work is licensed under a Creative Commons Attribution 3.0 Unported License
This work is licensed under a Creative Commons Attribution 3.0 Unported License