New branches grow beneath the brush; look back, and seek old traces.
See the code you undid.
Undo a change in Neovim, write something else, and the first version leaves the screen. With 'undofile' on, Neovim may still keep it on disk, on a branch of the undo tree that never reached a file or git. xunhen reads that history and shows every state it kept, without changing anything.
xunhen browse shows it. Node 2 is an experiment that was undone and never saved. Select it, then compare it with node 3, the version that was saved.The example, step by step
- The last line of
retry.gobecame// common. Node 1. - That line became an experiment,
func experiment() int { return 42 }. Node 2. - The experiment was undone before the file was ever saved.
- A different fix,
func chosen() int { return 1 }, was written and saved. Node 3.
Git could only ever have seen node 3. The experiment exists nowhere but the undo file, and xunhen rebuilds it from there.
xunhen show retry.go --node 2
package sample
func experiment() int { return 42 }Commands
Every command takes the source file and finds its history in the undo directories you name, once in your shell or with --undo-dir:
export XUNHEN_UNDO_DIR="$HOME/.local/state/nvim/undo"xunhen inspect retry.go- Lists the retained states, their branches, and when each was recorded.
xunhen show retry.go --node 2- Rebuilds one state as text. With --raw, it writes the exact text for a file.
xunhen diff retry.go --from 2 --to 3- Compares two states as a unified diff.
xunhen browse retry.go- Does all three in the terminal, as above.
:echo &undodir in Neovim prints where your undo files are. The usage guide walks through a complete recovery and lists every flag.
xunhen diff retry.go --from 2 --to 3
--- node 2
+++ node 3
@@ -1,3 +1,3 @@
package sample
-func experiment() int { return 42 }
+func chosen() int { return 1 }What survives
- Only history Neovim wrote to disk. With
'undofile'on, Neovim writes the undo file each time you save. - Edits after the last save, and states older than
'undolevels'keeps, are gone. - Rebuilding text needs the source as it was at the last save. The undo file stores changes, not copies of the file.
- Undo format 3, as Neovim 0.12.5 writes it on Linux x86-64, is the one tested. Other producers are unverified.
- Text must be UTF-8 with LF line endings. Anything else is refused with the reason.
The troubleshooting guide explains what to do when a state is missing.
Turn on persistent undo
Neovim keeps undo history on disk only with 'undofile' set. Add this to your init.lua:
vim.o.undofile = trueFrom then on, every save writes the history xunhen reads.
Install
No release has been published yet. Until the first one, build xunhen from a clone with Go 1.27.1:
git clone https://github.com/nuggocto/xunhen.git
cd xunhen
CGO_ENABLED=0 go build -trimpath -o bin/xunhen ./cmd/xunhenThe executable is bin/xunhen. Copy it anywhere on your PATH.
With the first release
The install guide covers each of these, with upgrades and removal:
- Release archive, with checksums, into
~/.local/binwithout root - Nix flake, for a user profile or a NixOS configuration
- AUR package, built with
makepkg - Go source, with
go install