diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index c53d13e738..819b4dc40d 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -201,6 +201,8 @@ LUA • |vim.pos| can now convert between positions and buffer offsets. • |vim.ui.input()| now allows setting input scope. • |vim.log| for easily creating loggers. +• |vim.pack.get()| output includes revision of a pending update. +• |vim.pack.get()| can fetch new updates before computing the output. OPTIONS diff --git a/runtime/doc/pack.txt b/runtime/doc/pack.txt index b5cb447d6f..721b39182b 100644 --- a/runtime/doc/pack.txt +++ b/runtime/doc/pack.txt @@ -350,6 +350,11 @@ Remove plugins from disk ~ • Use |:packdel| with plugin names to remove. Use `:packdel ++all` to delete all inactive plugins. +Check for pending updates ~ +• Run `vim.pack.get(nil, { offline = false })` and check the output for items + with different `rev` and `rev_to` fields. To not download new updates from + source, use plain `vim.pack.get()`. + Commands *vim.pack-commands* *E5807* @@ -486,8 +491,10 @@ get({names}, {opts}) *vim.pack.get()* • {names} (`string[]?`) List of plugin names. Default: all plugins managed by |vim.pack|. • {opts} (`table?`) A table with the following fields: - • {info} (`boolean`) Whether to include extra plugin info. + • {info}? (`boolean`) Whether to include extra plugin info. Default `true`. + • {offline}? (`boolean`) Whether to skip downloading new + updates. Requires `info=true`. Default: `true`. Return: ~ (`table[]`) A list of objects with the following fields: @@ -496,7 +503,11 @@ get({names}, {opts}) *vim.pack.get()* • {branches}? (`string[]`) Available Git branches (first is default). Missing if `info=false`. • {path} (`string`) Plugin's path on disk. - • {rev} (`string`) Current Git revision. + • {rev} (`string`) Current Git revision. Taken from + |vim.pack-lockfile| if `info=false`. + • {rev_to}? (`string`) Git revision of a pending update. The same as + used during |vim.pack.update()| and which points to a resolved + `spec.version`. Missing if `info=false`. • {spec} (`vim.pack.SpecResolved`) A |vim.pack.Spec| with resolved `name`. • {tags}? (`string[]`) Available Git tags. Missing if `info=false`. diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index bd693904cd..0d2472d082 100644 --- a/runtime/lua/vim/pack.lua +++ b/runtime/lua/vim/pack.lua @@ -156,6 +156,12 @@ ---- Use |:packdel| with plugin names to remove. Use `:packdel ++all` to delete --- all inactive plugins. --- +---Check for pending updates ~ +--- +---- Run `vim.pack.get(nil, { offline = false })` and check the output for items +--- with different `rev` and `rev_to` fields. To not download new updates +--- from source, use plain `vim.pack.get()`. +--- ---
help
--- Commands *vim.pack-commands* *E5807*
---
@@ -317,6 +323,14 @@ local function git_get_hash(ref, cwd)
return git_cmd({ 'rev-list', '-1', ref }, cwd)
end
+--- @async
+--- @param cwd string
+local function git_fetch(cwd)
+ -- Using '--tags --force' means conflicting tags will be synced with remote
+ local args = { 'fetch', '--quiet', '--tags', '--force', '--recurse-submodules=yes', 'origin' }
+ git_cmd(args, cwd)
+end
+
--- @async
--- @param cwd string
--- @return string
@@ -1313,9 +1327,7 @@ function M.update(names, opts)
-- Fetch
if not opts.offline then
- -- Using '--tags --force' means conflicting tags will be synced with remote
- local args = { 'fetch', '--quiet', '--tags', '--force', '--recurse-submodules=yes', 'origin' }
- git_cmd(args, p.path)
+ git_fetch(p.path)
end
-- Compute change info: changelog if any, new tags if nothing to update
@@ -1434,23 +1446,39 @@ end
--- @field active boolean Whether plugin was added via |vim.pack.add()| to current session.
--- @field branches? string[] Available Git branches (first is default). Missing if `info=false`.
--- @field path string Plugin's path on disk.
---- @field rev string Current Git revision.
+--- @field rev string Current Git revision. Taken from |vim.pack-lockfile| if `info=false`.
+--- Git revision of a pending update. The same as used during |vim.pack.update()| and which
+--- points to a resolved `spec.version`. Missing if `info=false`.
+--- @field rev_to? string
--- @field spec vim.pack.SpecResolved A |vim.pack.Spec| with resolved `name`.
--- @field tags? string[] Available Git tags. Missing if `info=false`.
--- @class vim.pack.keyset.get
--- @inlinedoc
---- @field info boolean Whether to include extra plugin info. Default `true`.
+--- @field info? boolean Whether to include extra plugin info. Default `true`.
+--- Whether to skip downloading new updates. Requires `info=true`. Default: `true`.
+--- @field offline? boolean
--- @param p_data_list vim.pack.PlugData[]
-local function add_p_data_info(p_data_list)
+--- @param offline boolean
+local function add_p_data_info(p_data_list, offline)
local funs = {} --- @type (async fun())[]
+ local plug_dir = get_plug_dir()
for i, p_data in ipairs(p_data_list) do
+ local plug = new_plug(p_data.spec, plug_dir)
local path = p_data.path
--- @async
funs[i] = function()
p_data.branches = git_get_branches(path)
p_data.tags = git_get_tags(path)
+
+ if not offline then
+ git_fetch(path)
+ end
+
+ infer_revisions(plug)
+ p_data.rev = plug.info.sha_head
+ p_data.rev_to = plug.info.sha_target
end
end
async_join_run_wait(funs)
@@ -1462,7 +1490,7 @@ end
--- @return vim.pack.PlugData[]
function M.get(names, opts)
vim.validate('names', names, vim.islist, true, 'list')
- opts = vim.tbl_extend('force', { info = true }, opts or {})
+ opts = vim.tbl_extend('force', { info = true, offline = true }, opts or {})
-- Process active plugins in order they were added. Take into account that
-- there might be "holes" after `vim.pack.del()`.
@@ -1511,7 +1539,7 @@ function M.get(names, opts)
if opts.info then
git_ensure_exec()
- add_p_data_info(res)
+ add_p_data_info(res, opts.offline)
end
return res
diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua
index 646456a555..c6bb3121a1 100644
--- a/test/functional/plugin/pack_spec.lua
+++ b/test/functional/plugin/pack_spec.lua
@@ -2073,11 +2073,14 @@ describe('vim.pack', function()
local function make_basic_data(active, info)
local spec = { name = 'basic', src = repos_src.basic, version = 'feat-branch' }
local path = pack_get_plug_path('basic')
- local rev = git_get_hash('feat-branch', 'basic')
- local res = { active = active, path = path, spec = spec, rev = rev }
+ local res = { active = active, path = path, spec = spec }
if info then
res.branches = { 'main', 'feat-branch' }
+ res.rev = git_get_hash('feat-branch', 'basic')
+ res.rev_to = res.rev
res.tags = { 'some-tag' }
+ else
+ res.rev = get_lock_tbl().plugins.basic.rev
end
return res
end
@@ -2085,11 +2088,14 @@ describe('vim.pack', function()
local function make_defbranch_data(active, info)
local spec = { name = 'defbranch', src = repos_src.defbranch }
local path = pack_get_plug_path('defbranch')
- local rev = git_get_hash('dev', 'defbranch')
- local res = { active = active, path = path, spec = spec, rev = rev }
+ local res = { active = active, path = path, spec = spec }
if info then
res.branches = { 'dev', 'main' }
+ res.rev = git_get_hash('dev', 'defbranch')
+ res.rev_to = res.rev
res.tags = {}
+ else
+ res.rev = get_lock_tbl().plugins.defbranch.rev
end
return res
end
@@ -2098,11 +2104,14 @@ describe('vim.pack', function()
local spec =
{ name = 'plugindirs', src = repos_src.plugindirs, version = vim.version.range('*') }
local path = pack_get_plug_path('plugindirs')
- local rev = git_get_hash('v0.0.1', 'plugindirs')
- local res = { active = active, path = path, spec = spec, rev = rev }
+ local res = { active = active, path = path, spec = spec }
if info then
res.branches = { 'main' }
+ res.rev = git_get_hash('v0.0.1', 'plugindirs')
+ res.rev_to = res.rev
res.tags = { 'v0.0.1' }
+ else
+ res.rev = get_lock_tbl().plugins.plugindirs.rev
end
return res
end
@@ -2124,8 +2133,15 @@ describe('vim.pack', function()
-- Should preserve order in which plugins were `vim.pack.add()`ed
eq({ defbranch_data, basic_data, plugindirs_data }, exec_lua('return vim.pack.get()'))
- -- Should also list non-active plugins
+ -- Should also list non-active plugins and use proper source for `rev`
+ local lock_tbl = get_lock_tbl()
+ lock_tbl.plugins.defbranch.rev = 'aaa'
+ lock_tbl.plugins.basic.rev = 'bbb'
+ lock_tbl.plugins.plugindirs.rev = 'ccc'
+ local lockfile_text = vim.json.encode(lock_tbl, { indent = ' ', sort_keys = true })
+ fn.writefile(vim.split(lockfile_text, '\n'), get_lock_path())
n.clear()
+
vim_pack_add({ repos_src.defbranch })
defbranch_data = make_defbranch_data(true, true)
basic_data = make_basic_data(false, true)
@@ -2155,6 +2171,75 @@ describe('vim.pack', function()
eq({ defbranch_data }, exec_lua('return vim.pack.get({ "defbranch" }, { info = false })'))
end)
+ it('reports potential revision after update', function()
+ -- Install and set up different version without running `vim.pack.update()`
+ vim_pack_add({ repos_src.defbranch, { src = repos_src.basic, version = 'feat-branch' } })
+ pack_assert_content('defbranch', 'return "defbranch dev"')
+ pack_assert_content('basic', 'return "basic feat-branch"')
+
+ n.clear()
+ vim_pack_add({ { src = repos_src.defbranch, version = 'main' }, repos_src.basic })
+ n.clear()
+
+ -- Should report correct `rev_to` with active and not active plugins
+ vim_pack_add({ { src = repos_src.defbranch, version = 'main' } })
+ local defbranch_data = make_defbranch_data(true, true)
+ defbranch_data.spec.version = 'main'
+ defbranch_data.rev_to = git_get_hash('main', 'defbranch')
+ local basic_data = make_basic_data(false, true)
+ basic_data.spec.version = nil
+ basic_data.rev_to = git_get_hash('main', 'basic')
+
+ eq({ defbranch_data, basic_data }, exec_lua('return vim.pack.get()'))
+ end)
+
+ describe('opts.offline', function()
+ after_each(function()
+ n.rmdir(repo_get_path('fetch'))
+ end)
+
+ it('can fetch new updates', function()
+ -- Create a dedicated clean repo for which "push changes" will be mocked
+ init_test_repo('fetch')
+
+ repo_write_file('fetch', 'lua/fetch.lua', 'return "fetch init"')
+ git_add_commit('Initial commit', 'fetch')
+
+ local fetch_head = git_get_hash('HEAD', 'fetch')
+
+ -- Install initial versions of tested plugins
+ vim_pack_add({ repos_src.fetch })
+
+ -- Mock remote repo update
+ -- - Force push
+ repo_write_file('fetch', 'lua/fetch.lua', 'return "fetch new"')
+ git_cmd({ 'add', '*' }, 'fetch')
+ git_cmd({ 'commit', '--amend', '-m', 'Second commit' }, 'fetch')
+ local fetch_new = git_get_hash('HEAD', 'fetch')
+ t.neq(fetch_head, fetch_new)
+
+ -- Should not fetch new data with `offline=true` (default)
+ -- or if `info=false`
+ local spec = { name = 'fetch', src = repos_src.fetch }
+ local path = pack_get_plug_path('fetch')
+ local fetch_data = { active = true, path = path, spec = spec }
+ fetch_data.branches = { 'main' }
+ fetch_data.tags = {}
+ fetch_data.rev = fetch_head
+ fetch_data.rev_to = fetch_head
+
+ exec_lua('vim.pack.get(nil, { info = false, offline = false })')
+ eq({ fetch_data }, exec_lua('return vim.pack.get()'))
+
+ -- Should fetch new data with `offline=false`
+ fetch_data.rev_to = fetch_new
+ eq({ fetch_data }, exec_lua('return vim.pack.get(nil, { offline = false })'))
+
+ -- Should keep using already fetched data with `offline=true`
+ eq({ fetch_data }, exec_lua('return vim.pack.get(nil, {})'))
+ end)
+ end)
+
it('respects `data` field', function()
vim_pack_add({
{ src = repos_src.basic, version = 'feat-branch', data = { test = 'value' } },
@@ -2182,9 +2267,9 @@ describe('vim.pack', function()
-- Should not include removed plugins immediately after they are removed,
-- while still returning list without holes
- exec_lua('vim.pack.del({ "defbranch" }, { force = true })')
local defbranch_data = make_defbranch_data(true, true)
local basic_data = make_basic_data(true, true)
+ exec_lua('vim.pack.del({ "defbranch" }, { force = true })')
eq({ { defbranch_data, basic_data }, { basic_data } }, exec_lua('return _G.get_log'))
end)