5

In my vimrc I have:

set backupdir=~/.cache/vim/backup |call mkdir(&backupdir, 'p', 0o700)
set undodir=~/.cache/vim/undo     |call mkdir(&undodir,   'p', 0o700)

This stores a backup file in ~/.cache/vim/backup on every write, which is useful if I accidentally remove something, or remove something that I ended up wanting to have later anyway.

The problem is that the filename is just the "basename", that is, both ~/one/README.md and ~/two/README.md get written as ~/.cache/vim/backup/README.md, overwriting any previous file.

This problem doesn't exist for undodir, which stores it as two files, with the full pathname:

~/.vim/cache/undo/%home%martin%one%README.md
~/.vim/cache/undo/%home%martin%two%README.md

How can I keep a unique copy for every pathname, similar to what undodir does by default?

2 Answers 2

6

If you look into :h 'backupdir', you will find in the description:

    - For Unix and Win32, if a directory ends in two path separators "//",
      the backup file name will be built from the complete path to the
      file with all path separators changed to percent '%' signs. This
      will ensure file name uniqueness in the backup directory.
      On Win32, it is also possible to end with "\\".  However, When a
      separating comma is following, you must use "//", since "\\" will
      include the comma in the file name. Therefore it is recommended to
      use '//', instead of '\\'.

Thus, if you add // at the end of the path, it should behave the same as 'undodir':

set backupdir=~/.cache/vim/backup// |call mkdir(&backupdir, 'p', 0o700)

One example of setting up directories:

vim9script

g:vimdata = $'{has("win32") ? expand("$APPDATA") : expand("~/.config")}/vim-data'
if !isdirectory(g:vimdata) | mkdir(g:vimdata, "p") | endif

&directory = expand($'{g:vimdata}/swap/')
&backupdir = expand($'{g:vimdata}/backup//')
&undodir = expand($'{g:vimdata}/undo//')
if !isdirectory(&undodir)   | mkdir(&undodir, "p")   | endif
if !isdirectory(&backupdir) | mkdir(&backupdir, "p") | endif
if !isdirectory(&directory) | mkdir(&directory, "p") | endif

set backup
set undofile

Here, I have edited 2 readme.md files, one in ~/temp/hello/ and another in ~/temp/world/ directories:

enter image description here

1
  • Brilliant; this is much better than my hack! Looks like this was added in 8.1.0251 Sep 21, 2022 at 10:43
0

The best way I could figure out is (abusing) the backupext setting for this:

au BufWritePre * &backupext = '-' .. substitute(expand('<afile>:p'), '[^a-zA-Z0-9_-]', '%', 'g')

This sets it to the dirname before every write, and you end up with two files in ~/.cache/vim/backup:

~/.cache/vim/backup/README.md-%home%martin%one%README%md
~/.cache/vim/backup/README.md-%home%martin%two%README%md

It's a bit uglier than undodir, but still workable enough.

2
  • 1. In this case there's no need to reset extension on every write; could make use of some different autocmd to set it once per buffer lifetime 2. You can apply hash function, e.g. :h sha256(), to path string and to get unique numeric extension (actually only few last digits should be enough) instead of that ugly and lengthy thing.
    – Matt
    Sep 21, 2022 at 6:14
  • The problem with sha256() is that it'll be hard to know which file belongs to which path @Matt; you'll end up with 20 README.md-<some-hash>. Either way, Maxim's solution is much better. Sep 21, 2022 at 10:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.