fix(git/repo): recurse subdirs in fs watcher

This commit is contained in:
2026-05-07 16:27:31 +02:00
parent 104479187c
commit c07e9c8de3
+85 -28
View File
@@ -32,7 +32,7 @@ local global = util.Emitter.new()
---@field tabs table<integer, true> ---@field tabs table<integer, true>
---@field status ow.Git.Status ---@field status ow.Git.Status
---@field private _events ow.Git.Util.Emitter<ow.Git.Repo.Event> ---@field private _events ow.Git.Util.Emitter<ow.Git.Repo.Event>
---@field private _watcher? uv.uv_fs_event_t ---@field private _watchers table<string, uv.uv_fs_event_t>
---@field private _schedule_refresh fun(self: ow.Git.Repo) ---@field private _schedule_refresh fun(self: ow.Git.Repo)
---@field private _refresh_handle ow.Git.Util.DebounceHandle ---@field private _refresh_handle ow.Git.Util.DebounceHandle
---@field private _cache table<string, any> ---@field private _cache table<string, any>
@@ -107,44 +107,101 @@ function Repo:get_cached(key, compute)
return value return value
end end
function Repo:start_watcher() ---@param path string
local watcher, err = vim.uv.new_fs_event() ---@param on_event fun(filename: string?)
---@return uv.uv_fs_event_t?
local function start_fs_event(path, on_event)
local watcher = vim.uv.new_fs_event()
if not watcher then if not watcher then
util.error( return nil
"git: failed to create fs_event for %s: %s",
self.gitdir,
err
)
return
end end
local ok, start_err = watcher:start( local ok = watcher:start(path, {}, function(err, filename)
self.gitdir, if err then
{ recursive = true }, return
function(err_, filename)
if
err_
or filename:match("^objects/")
or filename:match("^logs/")
then
return
end
self:refresh()
end end
) on_event(filename)
end)
if not ok then if not ok then
util.error("failed to watch %s: %s", self.gitdir, tostring(start_err))
watcher:close() watcher:close()
return nil
end
return watcher
end
---@private
---@param path string
---@param on_change fun()
function Repo:_watch_tree(path, on_change)
if self._watchers[path] then
return return
end end
self._watcher = watcher local stat = vim.uv.fs_stat(path)
if not stat or stat.type ~= "directory" then
return
end
local watcher = start_fs_event(path, function(filename)
if not vim.uv.fs_stat(path) then
local w = self._watchers[path] --[[@as uv.uv_fs_event_t?]]
if w then
w:stop()
w:close()
self._watchers[path] = nil
end
return
end
on_change()
if filename then
vim.schedule(function()
self:_watch_tree(vim.fs.joinpath(path, filename), on_change)
end)
end
end)
if not watcher then
return
end
self._watchers[path] = watcher
local handle = vim.uv.fs_scandir(path)
if not handle then
return
end
while true do
local name, typ = vim.uv.fs_scandir_next(handle)
if not name then
break
end
if typ == "directory" then
self:_watch_tree(vim.fs.joinpath(path, name), on_change)
end
end
end
function Repo:start_watcher()
self._watchers = {}
local top = start_fs_event(self.gitdir, function(filename)
if
filename
and (filename:match("^objects") or filename:match("^logs"))
then
return
end
self:refresh()
end)
if not top then
util.error("git: failed to watch %s", self.gitdir)
return
end
self._watchers[self.gitdir] = top
self:_watch_tree(vim.fs.joinpath(self.gitdir, "refs"), function()
self:refresh()
end)
end end
function Repo:close() function Repo:close()
if self._watcher then for _, watcher in pairs(self._watchers) do
self._watcher:stop() watcher:stop()
self._watcher:close() watcher:close()
self._watcher = nil
end end
self._watchers = {}
self._refresh_handle.close() self._refresh_handle.close()
end end