Undo tree

Vim Motions tracks branching undo history in a shadow tree parallel to CM6’s linear undo stacks. When you undo and make a new edit, the old “future” becomes an alternative branch — accessible via g-/g+ or :earlier/:later. Inspired by Neovim’s undotree.

Chronological (g-/g+)

g- moves to the chronologically older state across ALL branches. g+ moves to the chronologically newer state. The buffer content changes — you see the document revert or advance.

Unlike u/Ctrl-R which only navigate the current branch, g-/g+ traverse every state in the order it was created, regardless of which branch it’s on.

By count (:earlier/:later)

CommandEffect
:earlier NGo back N changes
:later NGo forward N changes
:earlier NsGo to state from N seconds ago
:earlier NmGo to state from N minutes ago
:earlier NhGo to state from N hours ago
:earlier NdGo to state from N days ago
:earlier NfGo to Nth previous save point
:later NfGo to Nth next save point

Undo list (:undolist)

:undolist opens a modal showing all undo tree nodes with their sequence number, timestamp, change summary, save status, and branch count. The current node is marked with >.

:UndoTreeToggle (or :UndoTreeShow/:UndoTreeHide) opens a sidebar panel showing the undo tree as a visual hierarchy.

Keyboard navigation

KeyAction
jSelect next node
kSelect previous node
EnterNavigate to selected node
qClose sidebar

Click any node to navigate to it.

Features

  • Relative timestamps — “3m ago”, “1h ago”
  • Change summary — “+7 chars, -3 chars”
  • Branch indicators — shows when a node has multiple children
  • Collapse/expand — click the toggle to collapse alternate branches
  • Current node highlight — the active state is visually distinct
  • Saved markers — nodes where the file was saved are flagged

Lua API

vim.fn.undotree() returns a Neovim-compatible dictionary:

local tree = vim.fn.undotree()
print(tree.seq_last)   -- highest sequence number
print(tree.seq_cur)    -- current position
print(tree.time_cur)   -- timestamp of current state (Unix seconds)
print(#tree.entries)   -- number of entries on the main branch

Settings

Configure via Settings → Vim Motions → Undo tree or vimrc/Lua:

SettingDefaultDescription
enableUndoTreetrueEnable undo tree tracking
undoTreeMaxNodes1000Maximum nodes per file (oldest branches pruned)
undoTreePositionrightSidebar position (left/right)
undoTreeAutoOpenfalseAuto-open sidebar on branch creation
undoFilefalsePersist undo tree across sessions

Memory management

Undo trees are automatically evicted from memory when all editors for a file are closed. When undoFile is enabled, dirty trees are persisted before eviction. Reopening a file restores from persistence or starts fresh.

External modification detection

When undoFile is enabled and a file was modified outside Obsidian between sessions, a notification is shown indicating the undo tree is stale. The tree structure is preserved for :undolist display, but navigation is disabled for that session.

Vimrc

set undotree          " enable (default)
set noundotree        " disable
set undofile          " persist across sessions
set noundofile        " session-only (default)

Lua

vim.opt.undotree = true
vim.opt.undofile = true
vim.opt.undotreemaxnodes = 500