107

One of the new features in Vim 7.3 is 'persistent undo', which allows for the undotree to be saved to a file when exiting a buffer.

Unfortunately, I haven't quite been able to get it properly enabled, or I must be using it wrong. Here's what I've tried so far:

I added the following to ~/.vimrc

set undofile                " Save undos after file closes
set undodir=$HOME/.vim/undo " where to save undo histories
set undolevels=1000         " How many undos
set undoreload=10000        " number of lines to save for undo

After this, I supposedly should be able to open any file, edit it, then save-close it, and when I open it again I should be able to undo/redo as if I'd never left. Unfortunately, this doesn't seem to be the case, as no undofile is ever written.

Notes:

  1. I'm on Win 7 using Vim 7.3 from the Vim without cream project. Persistent undo is baked-in.

  2. $HOME/.vim/undo exists on my file system

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
duckworthd
  • 14,679
  • 16
  • 53
  • 68
  • 43
    Just to stress, point 2) is **very** important. Vim will not create the directory for you and persistent undo will not work until you `mkdir ~/.vim/undo` – puk Jan 25 '12 at 19:57
  • 2
    +1. Sorry, on Linux it works... thanks for telling me about it! – Ciro Santilli OurBigBook.com Nov 20 '13 at 11:03
  • For any future visitors to this question: Do **NOT** put quotes around the value of undodir! I got stuck on this problem for a while - use an absolute paht, without quotes around it. – naiveai Nov 16 '18 at 16:09

4 Answers4

59

Put this in your .vimrc to create an undodir if it doesn't exist and enable persistent undo. Tested on both Windows and Linux.

" Put plugins and dictionaries in this dir (also on Windows)
let vimDir = '$HOME/.vim'

if stridx(&runtimepath, expand(vimDir)) == -1
  " vimDir is not on runtimepath, add it
  let &runtimepath.=','.vimDir
endif

" Keep undo history across sessions by storing it in a file
if has('persistent_undo')
    let myUndoDir = expand(vimDir . '/undodir')
    " Create dirs
    call system('mkdir ' . vimDir)
    call system('mkdir ' . myUndoDir)
    let &undodir = myUndoDir
    set undofile
endif
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
  • 5
    This may be preferable for creating the directory: `:silent call system('mkdir -p ' . &undodir)` – Kyle Strand Feb 17 '15 at 21:57
  • 1
    I use `&` because I `set` the `undodir` rather than `let`ting it. (In my mind this distinction between `let` and `set` is one of the many, many, many mind-bogglingly awful "features" of Vimscript, but YMMV.) – Kyle Strand Feb 17 '15 at 21:58
  • 1
    Thanks Kyle, it's a good idea to create `.vim` if it doesn't exist. Unfortunately the `-p` flag doesn't exist on Windows so I edited the answer to call `mkdir` twice to ensure Windows compatibility. – Matthias Braun Feb 18 '15 at 15:11
  • 2
    Ah. My primary reason for using the `-p` flag is actually to avoid an error when the directory exists. I have my command inside an `if has('win32')` block, so I just use `mkdir` without `-p` on Windows. – Kyle Strand Feb 18 '15 at 16:54
  • 5
    Why not use [`mkdir()`](http://vimhelp.appspot.com/eval.txt.html#mkdir%28%29)? Then this can become: `call mkdir(myUndoDir, 'p')`, which behaves like `mkdir -p`. – nelstrom May 25 '17 at 15:06
  • Thanks for the hint, @nelstrom. `call mkdir(myUndoDir, 'p')` gives me an error (probably because the directory already exists): `Cannot create directory: C:\Users\me\.vim\undodir` – Matthias Braun May 25 '17 at 15:15
  • 1
    @MatthiasBraun I just realised that the documentation for `mkdir()` is different in [Vim](http://vimhelp.appspot.com/eval.txt.html#mkdir%28%29) and [Neovim](https://neovim.io/doc/user/eval.html#mkdir%28%29). When I suggested this I had been looking at Neovim's documentation, which states that: "If you try to create an existing directory with {path} set to "p" mkdir() will silently exit.". Apparently this is not the case in Vim. My bad! – nelstrom May 26 '17 at 17:53
  • @nelstrom That's an excellent modification. Thanks. (The answer to your "why not" question is "because I didn't know that existed.") – Kyle Strand Sep 26 '17 at 17:09
  • 1
    Related: [How can I create a folder, if it doesn't exist, from .vimrc?](https://stackoverflow.com/q/1549263/1333025). – Petr Jul 16 '22 at 08:07
8

I tried this in my _gvimrc:

" Persistent undo
try 
    set undodir=C:\vim\undodir
    set undofile
catch
endtry

It started working as advertised when I deleted the try-catch bracket, thus:

" Persistent undo
set undodir=C:\vim\undodir
set undofile

I had to create the directory.

Juan Lanus
  • 2,293
  • 23
  • 18
3

I suppose $HOME doesn't work as advertised.

On my system, :echo $HOME shows H:\, but : e $HOME/ says: ~/ invalid filename.

You could try with an absolute path to see whether it cures it

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I could |:echo $HOME| and |:e $HOME/| without problems, but I tried changing things anyways. It seemed to work at times, and not at others. Particularly, it never saves backups when I'm editing my ~/.vimrc file. – duckworthd Apr 19 '11 at 20:50
  • On windows 7 I'm using `$TEMP`. When I look `set undodir?` it shows `~/AppData/Local/Temp`. Undo file is persisted and loads itself correctly. Maybe `$TEMP` will be more reliable? – slawek Dec 05 '13 at 10:00
1

This now works as expected: file.txt open in a Vim 7.4 buffer on Windows 7, :setlocal undofile, then save a change to the buffer, and the undofile .file.txt.un~ is created alongside because :set undodir? reports that "undodir=." by default - ie no need to specify this manually. You can also :set undofile in a modeline.

joharr
  • 403
  • 4
  • 12