neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 9afd81512b0ccded5499635d28dd50d065298ff0
parent d2ca90d87e3246b5b827ed08f8c0dfc8ad6ae4c8
Author: Justin M. Keyes <justinkz@gmail.com>
Date:   Tue, 13 Jan 2026 00:07:09 -0500

Merge #37377 from echasnovski/pack-fix-checkout


Diffstat:
Mruntime/lua/vim/pack.lua | 34+++++++++++++++++++---------------
Mtest/functional/plugin/pack_spec.lua | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 102 insertions(+), 16 deletions(-)

diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua @@ -264,17 +264,12 @@ end --- @param url string --- @param path string local function git_clone(url, path) - local cmd = { 'clone', '--quiet', '--no-checkout', '--recurse-submodules' } + local cmd = { 'clone', '--quiet', '--no-checkout' } if vim.startswith(url, 'file://') then cmd[#cmd + 1] = '--no-hardlinks' - else - if git_version >= vim.version.parse('2.36') then - cmd[#cmd + 1] = '--filter=blob:none' - cmd[#cmd + 1] = '--also-filter-submodules' - elseif git_version >= vim.version.parse('2.27') then - cmd[#cmd + 1] = '--filter=blob:none' - end + elseif git_version >= vim.version.parse('2.27') then + cmd[#cmd + 1] = '--filter=blob:none' end vim.list_extend(cmd, { '--origin', 'origin', url, path }) @@ -654,18 +649,27 @@ end --- @async --- @param p vim.pack.Plug --- @param timestamp string -local function checkout(p, timestamp) +--- @param skip_stash? boolean +local function checkout(p, timestamp, skip_stash) infer_revisions(p) - local stash_cmd = { 'stash', '--quiet' } - if git_version > vim.version.parse('2.13') then - stash_cmd[#stash_cmd + 1] = '--message' - stash_cmd[#stash_cmd + 1] = ('vim.pack: %s Stash before checkout'):format(timestamp) + if not skip_stash then + local stash_cmd = { 'stash', '--quiet' } + if git_version > vim.version.parse('2.13') then + stash_cmd[#stash_cmd + 1] = '--message' + stash_cmd[#stash_cmd + 1] = ('vim.pack: %s Stash before checkout'):format(timestamp) + end + git_cmd(stash_cmd, p.path) end - git_cmd(stash_cmd, p.path) git_cmd({ 'checkout', '--quiet', p.info.sha_target }, p.path) + local submodule_cmd = { 'submodule', 'update', '--init', '--recursive' } + if git_version >= vim.version.parse('2.36') then + submodule_cmd[#submodule_cmd + 1] = '--filter=blob:none' + end + git_cmd(submodule_cmd, p.path) + plugin_lock.plugins[p.spec.name].rev = p.info.sha_target -- (Re)Generate help tags according to the current help files. @@ -691,7 +695,7 @@ local function install_list(plug_list, confirm) -- Prefer revision from the lockfile instead of using `version` p.info.sha_target = (plugin_lock.plugins[p.spec.name] or {}).rev - checkout(p, timestamp) + checkout(p, timestamp, true) p.info.installed = true trigger_event(p, 'PackChanged', 'install') diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua @@ -221,6 +221,34 @@ function repos_setup.semver() add_tag('v1.0.0') end +function repos_setup.with_subs() + -- To-be-submodule repo + init_test_repo('sub') + + repo_write_file('sub', 'sub.lua', 'return "sub init"') + git_add_commit('Initial commit for "sub"', 'sub') + + -- With-submodules repo with submodule recorded at its initial commit + init_test_repo('with_subs') + + repo_write_file('with_subs', 'lua/with_subs.lua', 'return "with_subs init"') + local sub_src = 'file://' .. repo_get_path('sub') + git_cmd({ '-c', 'protocol.file.allow=always', 'submodule', 'add', sub_src, 'sub' }, 'with_subs') + git_add_commit('Initial commit for "with_subs"', 'with_subs') + git_cmd({ 'tag', 'init-commit' }, 'with_subs') + + -- Advance both submodule and with-submodules repos by one commit + repo_write_file('sub', 'sub.lua', 'return "sub main"') + git_add_commit('Second commit for "sub"', 'sub') + + repo_write_file('with_subs', 'lua/with_subs.lua', 'return "with_subs main"') + git_cmd( + { '-c', 'protocol.file.allow=always', 'submodule', 'update', '--remote', 'sub' }, + 'with_subs' + ) + git_add_commit('Second commit for "with_subs"', 'with_subs') +end + -- Utility -------------------------------------------------------------------- local function watch_events(event) @@ -319,6 +347,25 @@ local function mock_confirm(output_value) end) end +local function mock_git_file_transport() + -- HACK: mock `vim.system()` to have `git` commands be executed + -- with temporarily set 'protocol.file.allow=always' option. + -- Otherwise performing `git` operations with submodules from `vim.pack` + -- itself will fail with `fatal: transport 'file' not allowed`. + -- Directly adding `-c protocol.file.allow=always` to `git_cmd` in `vim.pack` + -- itself is too much and might be bad for security. + exec_lua(function() + local vim_system_orig = vim.system + vim.system = function(cmd, opts, on_exit) + if cmd[1] == 'git' then + table.insert(cmd, 2, '-c') + table.insert(cmd, 3, 'protocol.file.allow=always') + end + return vim_system_orig(cmd, opts, on_exit) + end + end) +end + local function is_jit() return exec_lua('return package.loaded.jit ~= nil') end @@ -369,6 +416,11 @@ describe('vim.pack', function() vim.pack.add({ repos_src.basic }) end) eq(exec_lua('return #_G.event_log'), 0) + + -- Should not create redundant stash entry + local basic_path = pack_get_plug_path('basic') + local stash_list = system_sync({ 'git', 'stash', 'list' }, { cwd = basic_path }).stdout or '' + eq('', stash_list) end) it('passes `data` field through to `opts.load`', function() @@ -782,6 +834,16 @@ describe('vim.pack', function() eq(false, vim.tbl_contains(rtp, after_dir)) end) + it('installs with submodules', function() + mock_git_file_transport() + exec_lua(function() + vim.pack.add({ repos_src.with_subs }) + end) + + local sub_lua_file = vim.fs.joinpath(pack_get_plug_path('with_subs'), 'sub', 'sub.lua') + eq('return "sub main"', fn.readblob(sub_lua_file)) + end) + it('does not install on bad `version`', function() local err = pcall_err(exec_lua, function() vim.pack.add({ { src = repos_src.basic, version = 'not-exist' } }) @@ -823,7 +885,8 @@ describe('vim.pack', function() local function assert_works() -- Should auto-install but wait before executing code after it n.clear({ args_rm = { '-u' } }) - n.exec_lua('vim.wait(500, function() return _G.done end, 50)') + vim.uv.sleep(2000) + eq(true, exec_lua('return _G.done')) assert_loaded() -- Should only `:packadd!`/`:packadd` already installed plugin @@ -1656,6 +1719,25 @@ describe('vim.pack', function() eq('return "fetch new 2"', fn.readblob(fetch_lua_file)) end) + it('works with submodules', function() + mock_git_file_transport() + exec_lua(function() + vim.pack.add({ { src = repos_src.with_subs, version = 'init-commit' } }) + end) + + local sub_lua_file = vim.fs.joinpath(pack_get_plug_path('with_subs'), 'sub', 'sub.lua') + eq('return "sub init"', fn.readblob(sub_lua_file)) + + n.clear() + mock_git_file_transport() + exec_lua(function() + vim.pack.add({ repos_src.with_subs }) + vim.pack.update({ 'with_subs' }) + end) + n.exec('write') + eq('return "sub main"', fn.readblob(sub_lua_file)) + end) + it('can force update', function() exec_lua(function() vim.pack.add({ repos_src.fetch })