diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index bdcb470308..fb56f69ed5 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -192,6 +192,7 @@ LUA • |vim.pos| can now convert between positions and buffer offsets. • |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 b17f9c1d79..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: diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index 7eddacc3e2..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
@@ -1443,10 +1455,13 @@ end
 
 --- @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
@@ -1457,6 +1472,10 @@ local function add_p_data_info(p_data_list)
       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
@@ -1471,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()`.
@@ -1520,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 3963234642..c6bb3121a1 100644
--- a/test/functional/plugin/pack_spec.lua
+++ b/test/functional/plugin/pack_spec.lua
@@ -2193,6 +2193,53 @@ describe('vim.pack', function()
       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' } },