refactor(git): convert blocking subprocess calls to async

This commit is contained in:
2026-04-27 16:02:14 +02:00
parent 00eae8dbb9
commit 6a86a75ed5
8 changed files with 338 additions and 253 deletions
+48 -31
View File
@@ -59,47 +59,62 @@ local function attach_index_writer(buf, worktree, path)
})
end
---Run `git show <revspec>` asynchronously and call `callback(lines?)` on the
---main loop. `lines` is nil on failure (the error is logged).
---@param worktree string
---@param revspec string anything `git show` accepts (e.g. `HEAD:foo`, `:foo`, blob SHA)
---@return string[]
local function read_show(worktree, revspec)
local result = vim.system(
---@param callback fun(lines: string[]?)
local function read_show_async(worktree, revspec, callback)
vim.system(
{ "git", "show", revspec },
{ cwd = worktree, text = true }
{ cwd = worktree, text = true },
vim.schedule_wrap(function(result)
if result.code ~= 0 then
log.error(
"git show %s failed: %s",
revspec,
vim.trim(result.stderr or "")
)
callback(nil)
return
end
callback(util.split_lines(result.stdout or ""))
end)
)
:wait()
if result.code ~= 0 then
log.error(
"git show %s failed: %s",
revspec,
vim.trim(result.stderr or "")
)
return {}
end
return util.split_lines(result.stdout or "")
end
---Internal builder: run `git show <revspec>`, drop the result into a fresh
---scratch buffer, and (when `is_index` is true) wire up the BufWriteCmd
---that writes back to the git index for `index_path`.
---Internal builder: create a scratch buffer immediately and asynchronously
---fill it with the content of `git show <revspec>`. Returning the buffer
---synchronously lets callers wire up windows / `:diffthis` right away; the
---diff updates when the content arrives. The buffer starts non-modifiable
---and the index `BufWriteCmd` is only attached after a successful load, so
---a premature `:w` can't blow away the index entry with empty content.
---@param worktree string
---@param revspec string
---@param is_index boolean
---@param index_path string? required when is_index is true
---@return integer
local function build_show_buf(worktree, revspec, is_index, index_path)
local lines = read_show(worktree, revspec)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].buftype = is_index and "acwrite" or "nofile"
vim.bo[buf].buftype = "nofile"
vim.bo[buf].bufhidden = "wipe"
vim.bo[buf].swapfile = false
if is_index then
attach_index_writer(buf, worktree, assert(index_path))
else
vim.bo[buf].modifiable = false
end
vim.bo[buf].modifiable = false
vim.bo[buf].modified = false
read_show_async(worktree, revspec, function(lines)
if not vim.api.nvim_buf_is_valid(buf) then
return
end
vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines or {})
if is_index then
vim.bo[buf].buftype = "acwrite"
attach_index_writer(buf, worktree, assert(index_path))
else
vim.bo[buf].modifiable = false
end
vim.bo[buf].modified = false
end)
return buf
end
@@ -192,12 +207,14 @@ function M.split(opts)
local label = is_index and "index" or opts.ref
M.set_buf_name_and_filetype(other, "git://" .. label .. "/" .. rel)
local split_cmd = opts.vertical and "leftabove vertical sbuffer "
or "leftabove sbuffer "
vim.cmd(split_cmd .. other)
vim.cmd("diffthis")
vim.cmd("wincmd p")
vim.cmd("diffthis")
local cur_win = vim.api.nvim_get_current_win()
local other_win = vim.api.nvim_open_win(other, true, {
split = opts.vertical and "left" or "above",
win = cur_win,
})
vim.wo[other_win].diff = true
vim.api.nvim_set_current_win(cur_win)
vim.wo[cur_win].diff = true
end
return M