diff --git a/runtime/doc/pack.txt b/runtime/doc/pack.txt index eaac5d7b0b..eb1faecc3d 100644 --- a/runtime/doc/pack.txt +++ b/runtime/doc/pack.txt @@ -309,16 +309,27 @@ Unfreeze plugin to start receiving updates ~ • |:restart|. Revert plugin after an update ~ -• Locate plugin's revision at working state. For example: - • If there is a previous version of |vim.pack-lockfile| (like from version - control history), use it to get plugin's `rev` field. - • If there is a log file ("nvim-pack.log" at "log" |stdpath()|), open it and - navigate to latest updates (at the bottom). Locate lines about plugin - update details and use revision from "State before". -• Freeze plugin to target revision (set `version` and |:restart|). -• Run `vim.pack.update({ 'plugin-name' }, { force = true })` to make plugin - state on disk follow target revision. |:restart|. -• When ready to deal with updating plugin, unfreeze it. +• Revert the |vim.pack-lockfile| to the state before the update: + • If Git tracked: `git checkout HEAD -- nvim-pack-lock.json` + • If not tracked: examine log file ("nvim-pack.log" at "log" |stdpath()|), + locate the revisions before the latest update, and (carefully) adjust + current lockfile to have those revisions. +• |:restart|. +• `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. Read and confirm. + +Synchronize config across machines ~ +• On main machine: + • Add |vim.pack-lockfile| to VCS. + • Push to the remote server. +• On secondary machine: + • Pull from the server. + • |:restart|. New plugins (not present locally, but present in the lockfile) + are installed at proper revision. + • `vim.pack.update(nil, { target = 'lockfile' })`. Read and confirm. + • Manually delete outdated plugins (present locally, but were not present in + the lockfile prior to restart) with `vim.pack.del( { 'plugin' })`. They + can be located by examining the VCS difference of the lockfile + (`git diff -- nvim-pack-lock.json` for Git). Remove plugins from disk ~ • Use |vim.pack.del()| with a list of plugin names to remove. Make sure their @@ -480,6 +491,12 @@ update({names}, {opts}) *vim.pack.update()* • {opts} (`table?`) A table with the following fields: • {force}? (`boolean`) Whether to skip confirmation and make updates immediately. Default `false`. + • {target}? (`string`) How to compute a new plugin revision. + One of: + • "version" (default) - use latest revision matching + `version` from plugin specification. + • "lockfile" - use revision from the lockfile. Useful for + reverting or performing controlled update. vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index 736e78335a..a808ce8683 100644 --- a/runtime/lua/vim/pack.lua +++ b/runtime/lua/vim/pack.lua @@ -113,16 +113,28 @@ --- ---Revert plugin after an update ~ --- ----- Locate plugin's revision at working state. For example: ---- - If there is a previous version of |vim.pack-lockfile| (like from version ---- control history), use it to get plugin's `rev` field. ---- - If there is a log file ("nvim-pack.log" at "log" |stdpath()|), open it ---- and navigate to latest updates (at the bottom). Locate lines about plugin ---- update details and use revision from "State before". ----- Freeze plugin to target revision (set `version` and |:restart|). ----- Run `vim.pack.update({ 'plugin-name' }, { force = true })` to make plugin ---- state on disk follow target revision. |:restart|. ----- When ready to deal with updating plugin, unfreeze it. +---- Revert the |vim.pack-lockfile| to the state before the update: +--- - If Git tracked: `git checkout HEAD -- nvim-pack-lock.json` +--- - If not tracked: examine log file ("nvim-pack.log" at "log" |stdpath()|), +--- locate the revisions before the latest update, and (carefully) adjust +--- current lockfile to have those revisions. +---- |:restart|. +---- `vim.pack.update({ 'plugin' }, { target = 'lockfile' })`. Read and confirm. +--- +---Synchronize config across machines ~ +--- +---- On main machine: +--- - Add |vim.pack-lockfile| to VCS. +--- - Push to the remote server. +---- On secondary machine: +--- - Pull from the server. +--- - |:restart|. New plugins (not present locally, but present in the lockfile) +--- are installed at proper revision. +--- - `vim.pack.update(nil, { target = 'lockfile' })`. Read and confirm. +--- - Manually delete outdated plugins (present locally, but were not present +--- in the lockfile prior to restart) with `vim.pack.del( { 'plugin' })`. +--- They can be located by examining the VCS difference of the lockfile +--- (`git diff -- nvim-pack-lock.json` for Git). --- ---Remove plugins from disk ~ --- @@ -1139,6 +1151,12 @@ end --- @class vim.pack.keyset.update --- @inlinedoc --- @field force? boolean Whether to skip confirmation and make updates immediately. Default `false`. +--- +--- How to compute a new plugin revision. One of: +--- - "version" (default) - use latest revision matching `version` from plugin specification. +--- - "lockfile" - use revision from the lockfile. Useful for reverting or performing controlled +--- update. +--- @field target? string --- Update plugins --- @@ -1173,7 +1191,7 @@ end --- @param opts? vim.pack.keyset.update function M.update(names, opts) vim.validate('names', names, vim.islist, true, 'list') - opts = vim.tbl_extend('force', { force = false }, opts or {}) + opts = vim.tbl_extend('force', { force = false, target = 'version' }, opts or {}) local plug_list = plug_list_from_names(names) if #plug_list == 0 then @@ -1190,8 +1208,9 @@ function M.update(names, opts) --- @async --- @param p vim.pack.Plug local function do_update(p) + local l_data = plugin_lock.plugins[p.spec.name] -- Ensure proper `origin` if needed - if plugin_lock.plugins[p.spec.name].src ~= p.spec.src then + if l_data.src ~= p.spec.src then git_cmd({ 'remote', 'set-url', 'origin', p.spec.src }, p.path) plugin_lock.plugins[p.spec.name].src = p.spec.src needs_lock_write = true @@ -1205,6 +1224,10 @@ function M.update(names, opts) end -- Compute change info: changelog if any, new tags if nothing to update + if opts.target == 'lockfile' then + p.info.version_str = '*lockfile*' + p.info.sha_target = l_data.rev + end infer_update_details(p) -- Checkout immediately if no need to confirm diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua index 6c84f31796..ac5367a649 100644 --- a/test/functional/plugin/pack_spec.lua +++ b/test/functional/plugin/pack_spec.lua @@ -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