perf(git/repo): cache ref/file lookups, invalidate on status refresh

This commit is contained in:
2026-05-07 16:25:32 +02:00
parent 9b1ada8d97
commit 104479187c
2 changed files with 71 additions and 43 deletions
+8 -3
View File
@@ -317,22 +317,26 @@ end
---@param dir string
---@return string[]
local function list_files(r, dir)
return r:get_cached("files:" .. dir, function(self)
local cmd = { "git", "ls-files" }
if dir ~= "" then
table.insert(cmd, dir)
end
local out = util.exec(cmd, { cwd = r.worktree, silent = true })
local out = util.exec(cmd, { cwd = self.worktree, silent = true })
return out and util.split_lines(out) or {}
end)
end
---@param r ow.Git.Repo
---@return string[]
local function list_remotes(r)
return r:get_cached("remotes", function(self)
local out = util.exec(
{ "git", "remote" },
{ cwd = r.worktree, silent = true }
{ cwd = self.worktree, silent = true }
)
return out and util.split_lines(out) or {}
end)
end
---@type table<string, string[]>
@@ -469,7 +473,8 @@ function M.complete_rev(arg_lead)
local colon = arg_lead:find(":", 1, true)
if not colon then
local refs = r:list_refs()
local refs = {}
vim.list_extend(refs, r:list_refs())
vim.list_extend(refs, r:list_pseudo_refs())
vim.list_extend(refs, r:list_stash_refs())
return prefix_filter(refs, arg_lead)
+23
View File
@@ -35,6 +35,7 @@ local global = util.Emitter.new()
---@field private _watcher? uv.uv_fs_event_t
---@field private _schedule_refresh fun(self: ow.Git.Repo)
---@field private _refresh_handle ow.Git.Util.DebounceHandle
---@field private _cache table<string, any>
local Repo = {}
Repo.__index = Repo
@@ -56,6 +57,7 @@ function Repo:_fetch_status()
STATUS_CMD,
{ cwd = self.worktree, text = true },
vim.schedule_wrap(function(obj)
self._cache = {}
if obj.code ~= 0 then
util.error("git status failed: %s", vim.trim(obj.stderr or ""))
return
@@ -82,6 +84,7 @@ function Repo.new(gitdir, worktree)
tabs = {},
status = status.parse(""),
_events = util.Emitter.new(),
_cache = {},
}, Repo)
self._schedule_refresh, self._refresh_handle =
util.debounce(Repo._fetch_status, 50)
@@ -90,6 +93,20 @@ function Repo.new(gitdir, worktree)
return self
end
---@generic T
---@param key string
---@param compute fun(self: ow.Git.Repo): T
---@return T
function Repo:get_cached(key, compute)
local hit = self._cache[key]
if hit ~= nil then
return hit
end
local value = compute(self)
self._cache[key] = value
return value
end
function Repo:start_watcher()
local watcher, err = vim.uv.new_fs_event()
if not watcher then
@@ -168,6 +185,7 @@ end
---@return string[]
function Repo:list_refs()
return self:get_cached("refs", function(self)
local out = util.exec({
"git",
"for-each-ref",
@@ -180,6 +198,7 @@ function Repo:list_refs()
return {}
end
return util.split_lines(out)
end)
end
local PSEUDO_REFS = {
@@ -194,6 +213,7 @@ local PSEUDO_REFS = {
---@return string[]
function Repo:list_pseudo_refs()
return self:get_cached("pseudo_refs", function(self)
local refs = {}
for _, name in ipairs(PSEUDO_REFS) do
if name == "HEAD" or vim.uv.fs_stat(self.gitdir .. "/" .. name) then
@@ -201,10 +221,12 @@ function Repo:list_pseudo_refs()
end
end
return refs
end)
end
---@return string[]
function Repo:list_stash_refs()
return self:get_cached("stash_refs", function(self)
if not vim.uv.fs_stat(self.gitdir .. "/refs/stash") then
return {}
end
@@ -219,6 +241,7 @@ function Repo:list_stash_refs()
end
end
return refs
end)
end
---@param rev string