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
+66 -43
View File
@@ -1,4 +1,5 @@
local diff = require("git.diff")
local git = require("git")
local log = require("log")
local repo = require("git.repo")
local util = require("util")
@@ -111,8 +112,8 @@ end
---@param ref string
local function show_blob(worktree, blob, path, ref)
local buf = blob_buf(worktree, blob, path, ref)
vim.cmd("normal! m'")
vim.cmd("buffer " .. buf)
vim.cmd.normal({ "m'", bang = true })
vim.api.nvim_set_current_buf(buf)
end
---@param ctx ow.Git.ShowContext
@@ -127,53 +128,75 @@ local function show_diff(ctx, section)
blob_buf(ctx.worktree, section.pre_blob, section.pre_path, parent)
local right =
blob_buf(ctx.worktree, section.post_blob, section.post_path, ctx.ref)
vim.cmd("normal! m'")
vim.cmd("buffer " .. left)
vim.cmd("diffthis")
vim.cmd("rightbelow vertical sbuffer " .. right)
vim.cmd("diffthis")
vim.cmd("wincmd p")
vim.cmd.normal({ "m'", bang = true })
local left_win = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_buf(left)
vim.wo[left_win].diff = true
local right_win =
vim.api.nvim_open_win(right, true, { split = "right", win = left_win })
vim.wo[right_win].diff = true
vim.api.nvim_set_current_win(left_win)
end
---@param worktree string
---@param ref string
function M.open_commit(worktree, ref)
local sha = repo.rev_parse(worktree, ref, true) or ref
local name = "git://" .. sha .. "/"
-- Reuse a previously-opened buffer for the same commit; commit SHAs
-- are immutable so the content is stable.
local existing = vim.fn.bufnr(name)
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
vim.cmd("normal! m'")
vim.cmd("buffer " .. existing)
return
end
repo.rev_parse(worktree, ref, true, function(resolved)
local sha = resolved or ref
local name = "git://" .. sha .. "/"
-- Reuse a previously-opened buffer for the same commit; commit SHAs
-- are immutable so the content is stable.
local existing = vim.fn.bufnr(name)
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
vim.api.nvim_open_win(existing, true, {
split = vim.o.splitbelow and "below" or "above",
})
return
end
local result = vim.system(
{ "git", "show", ref },
{ cwd = worktree, text = true }
)
:wait()
if result.code ~= 0 then
log.error("git show %s failed: %s", ref, result.stderr or "")
return
end
local lines = util.split_lines(result.stdout or "")
local parent = repo.rev_parse(worktree, ref .. "^", true)
local buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].buftype = "nofile"
vim.bo[buf].bufhidden = "hide"
vim.bo[buf].swapfile = false
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].modifiable = false
vim.bo[buf].modified = false
pcall(vim.api.nvim_buf_set_name, buf, name)
vim.b[buf].git_worktree = worktree
vim.b[buf].git_ref = sha
vim.b[buf].git_parent_ref = parent
vim.bo[buf].filetype = "git"
vim.cmd("normal! m'")
vim.cmd("buffer " .. buf)
local buf, win = git.new_scratch({ name = name })
vim.b[buf].git_worktree = worktree
vim.b[buf].git_ref = sha
vim.system(
{ "git", "show", ref },
{ cwd = worktree, text = true },
vim.schedule_wrap(function(result)
if result.code ~= 0 then
log.error(
"git show %s failed: %s",
ref,
vim.trim(result.stderr or "")
)
-- Tear down the empty placeholder window+buffer so a
-- retry runs a fresh fetch instead of hitting the
-- cached-buffer branch and reopening an empty pane.
if vim.api.nvim_win_is_valid(win) then
pcall(vim.api.nvim_win_close, win, true)
end
if vim.api.nvim_buf_is_valid(buf) then
pcall(vim.api.nvim_buf_delete, buf, { force = true })
end
return
end
if not vim.api.nvim_buf_is_valid(buf) then
return
end
local lines = util.split_lines(result.stdout or "")
repo.rev_parse(worktree, ref .. "^", true, function(parent)
if not vim.api.nvim_buf_is_valid(buf) then
return
end
vim.b[buf].git_parent_ref = parent
vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].modifiable = false
vim.bo[buf].modified = false
vim.bo[buf].filetype = "git"
end)
end)
)
end)
end
---@return boolean dispatched true if the cursor was on an actionable line