perf(git/repo): cache ref/file lookups, invalidate on status refresh
This commit is contained in:
+54
-31
@@ -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,18 +185,20 @@ end
|
||||
|
||||
---@return string[]
|
||||
function Repo:list_refs()
|
||||
local out = util.exec({
|
||||
"git",
|
||||
"for-each-ref",
|
||||
"--format=%(refname:short)",
|
||||
"refs/heads",
|
||||
"refs/tags",
|
||||
"refs/remotes",
|
||||
}, { cwd = self.worktree, silent = true })
|
||||
if not out then
|
||||
return {}
|
||||
end
|
||||
return util.split_lines(out)
|
||||
return self:get_cached("refs", function(self)
|
||||
local out = util.exec({
|
||||
"git",
|
||||
"for-each-ref",
|
||||
"--format=%(refname:short)",
|
||||
"refs/heads",
|
||||
"refs/tags",
|
||||
"refs/remotes",
|
||||
}, { cwd = self.worktree, silent = true })
|
||||
if not out then
|
||||
return {}
|
||||
end
|
||||
return util.split_lines(out)
|
||||
end)
|
||||
end
|
||||
|
||||
local PSEUDO_REFS = {
|
||||
@@ -194,31 +213,35 @@ local PSEUDO_REFS = {
|
||||
|
||||
---@return string[]
|
||||
function Repo:list_pseudo_refs()
|
||||
local refs = {}
|
||||
for _, name in ipairs(PSEUDO_REFS) do
|
||||
if name == "HEAD" or vim.uv.fs_stat(self.gitdir .. "/" .. name) then
|
||||
table.insert(refs, name)
|
||||
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
|
||||
table.insert(refs, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
return refs
|
||||
return refs
|
||||
end)
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function Repo:list_stash_refs()
|
||||
if not vim.uv.fs_stat(self.gitdir .. "/refs/stash") then
|
||||
return {}
|
||||
end
|
||||
local refs = { "stash" }
|
||||
local out = util.exec(
|
||||
{ "git", "stash", "list", "--pretty=format:%gd" },
|
||||
{ cwd = self.worktree, silent = true }
|
||||
)
|
||||
if out then
|
||||
for _, entry in ipairs(util.split_lines(out)) do
|
||||
table.insert(refs, entry)
|
||||
return self:get_cached("stash_refs", function(self)
|
||||
if not vim.uv.fs_stat(self.gitdir .. "/refs/stash") then
|
||||
return {}
|
||||
end
|
||||
end
|
||||
return refs
|
||||
local refs = { "stash" }
|
||||
local out = util.exec(
|
||||
{ "git", "stash", "list", "--pretty=format:%gd" },
|
||||
{ cwd = self.worktree, silent = true }
|
||||
)
|
||||
if out then
|
||||
for _, entry in ipairs(util.split_lines(out)) do
|
||||
table.insert(refs, entry)
|
||||
end
|
||||
end
|
||||
return refs
|
||||
end)
|
||||
end
|
||||
|
||||
---@param rev string
|
||||
|
||||
Reference in New Issue
Block a user