feat(pack): allow choosing update target in update()

Problem: There are two fairly common workflows that involve lockfile and
  can be made more straightforward in `vim.pack`:

    1. Revert latest update. Like if it introduced unwanted behavior.

    2. Update to a revision in the lockfile. Like if already updated
       on another machine, verified that everything works, `git add` +
       `git commit` + `git push` the config, and want to have the same
       plugin states on current machine.

Solution: Make `update` allow `opts.target`. By default it uses
  `version` from a plugin specification (like a regular "get new changes
  from source" workflow). But it also allows `"lockfile"` value to
  indicate that target revision after update should be taken from the
  current lockfile verbatim.

  With this, the workflows are:

    1. Revert (somehow) to the lockfile before the update, restart, and
       `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. If Git
       tracked, revert with `git checkout HEAD -- nvim-pack-lock.json`.
       For non-VCS tracked lockfile, the revisions can be taken from the
       log file. It would be nicer if `update()` would backup a lockfile
       before doing an update, but that might require discussions.

    2. `git pull` + `:restart` +
       `vim.pack.update(nil, { target = 'lockfile' })`.
       The only caveats are for new and deleted plugins:
        - New plugins (not present locally but present in the lockfile)
          will be installed at lockfile revision during restart.
        - Deleted plugins (present locally but not present in the
          lockfile) will still be present: both locally *and* in the
          lockfile. They can be located by
          `git diff -- nvim-pack-lock.json` and require manual
          `vim.pack.del({ 'old-plugin1', 'old-plugin2' })`.
This commit is contained in:
Evgeni Chasnovski
2025-12-26 17:35:25 +02:00
parent 899ec829be
commit 8c28507fcf
3 changed files with 94 additions and 22 deletions

View File

@@ -1687,6 +1687,38 @@ describe('vim.pack', function()
eq(hashes.fetch_new, get_lock_tbl().plugins.fetch.rev)
end)
it('can use lockfile revision as a target', function()
exec_lua(function()
vim.pack.add({ repos_src.fetch })
end)
eq('return "fetch main"', fn.readblob(fetch_lua_file))
-- Mock "update -> revert lockfile -> revert plugin"
local lock_path = get_lock_path()
local lockfile_before = fn.readblob(lock_path)
hashes.fetch_new = git_get_hash('main', 'fetch')
-- - Update
exec_lua('vim.pack.update({ "fetch" }, { force = true })')
eq('return "fetch new 2"', fn.readblob(fetch_lua_file))
-- - Revert lockfile
fn.writefile(vim.split(lockfile_before, '\n'), lock_path)
n.clear()
-- - Revert plugin
eq('return "fetch new 2"', fn.readblob(fetch_lua_file))
exec_lua('vim.pack.update({ "fetch" }, { target = "lockfile" })')
local confirm_lines = api.nvim_buf_get_lines(0, 0, -1, false)
n.exec('write')
eq('return "fetch main"', fn.readblob(fetch_lua_file))
eq(hashes.fetch_head, get_lock_tbl().plugins.fetch.rev)
-- - Should mention that new revision comes from *lockfile*
eq(confirm_lines[6], ('Revision before: %s'):format(hashes.fetch_new))
eq(confirm_lines[7], ('Revision after: %s (*lockfile*)'):format(hashes.fetch_head))
end)
it('can change `src` of installed plugin', function()
local basic_src = repos_src.basic
local defbranch_src = repos_src.defbranch