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
+62 -62
View File
@@ -1,4 +1,5 @@
local diff = require("git.diff")
local git = require("git")
local log = require("log")
local repo = require("git.repo")
@@ -240,13 +241,15 @@ local function enrich_with_log(worktree, branch, groups, callback)
end
local pending = #fetches
for _, f in ipairs(fetches) do
vim.system({
"git",
"log",
"--format=%h %s",
f.range,
}, { cwd = worktree, text = true }, function(log_obj)
vim.schedule(function()
vim.system(
{
"git",
"log",
"--format=%h %s",
f.range,
},
{ cwd = worktree, text = true },
vim.schedule_wrap(function(log_obj)
if log_obj.code == 0 then
for line in (log_obj.stdout or ""):gmatch("[^\r\n]+") do
local sha, subject = line:match("^(%S+)%s+(.+)$")
@@ -270,7 +273,7 @@ local function enrich_with_log(worktree, branch, groups, callback)
callback(branch, groups)
end
end)
end)
)
end
end
@@ -287,15 +290,17 @@ local function fetch_status(worktree, prefetched_stdout, callback)
enrich_with_log(worktree, branch, groups, callback)
return
end
vim.system({
"git",
"-c",
"core.quotePath=false",
"status",
"--porcelain=v1",
"--branch",
}, { cwd = worktree, text = true }, function(obj)
vim.schedule(function()
vim.system(
{
"git",
"-c",
"core.quotePath=false",
"status",
"--porcelain=v1",
"--branch",
},
{ cwd = worktree, text = true },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
log.error("git status failed: %s", vim.trim(obj.stderr or ""))
local branch = { ahead = 0, behind = 0 }
@@ -313,7 +318,7 @@ local function fetch_status(worktree, prefetched_stdout, callback)
local branch, groups = parse_porcelain(obj.stdout or "")
enrich_with_log(worktree, branch, groups, callback)
end)
end)
)
end
---@param bufnr integer
@@ -598,9 +603,7 @@ end
---@param win integer
---@param enabled boolean
local function set_diff(win, enabled)
vim.api.nvim_win_call(win, function()
vim.cmd(enabled and "diffthis" or "diffoff")
end)
vim.wo[win].diff = enabled
if enabled then
vim.wo[win].foldenable = true
vim.wo[win].foldlevel = 0
@@ -642,6 +645,21 @@ local function entry_key(entry)
return entry.section .. "|" .. entry.path .. "|" .. (entry.orig or "")
end
---Split `target_win` and put its buffer into the new window. Matches the
---semantics of `:set_current_win(target); :rightbelow/leftabove vertical
---split` (the new window inherits the target's buffer; caller swaps it
---afterwards via `nvim_win_set_buf`).
---@param target_win integer
---@param dir "left"|"right"
---@return integer
local function vsplit_at(target_win, dir)
return vim.api.nvim_open_win(
vim.api.nvim_win_get_buf(target_win),
true,
{ split = dir, win = target_win }
)
end
---@param s ow.Git.StatusState
---@param entry ow.Git.StatusEntry
---@param focus_left boolean
@@ -673,23 +691,17 @@ local function show_diff(s, entry, focus_left)
end
if left_win and not right_win then
vim.api.nvim_set_current_win(left_win)
vim.cmd("rightbelow vertical split")
right_win = vim.api.nvim_get_current_win()
right_win = vsplit_at(left_win, "right")
reset_diff_win(right_win)
elseif right_win and not left_win then
vim.api.nvim_set_current_win(right_win)
vim.cmd("leftabove vertical split")
left_win = vim.api.nvim_get_current_win()
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
elseif not (left_win or right_win) then
local default_main = find_default_main_win(sidebar_win)
if default_main then
right_win = default_main
reset_diff_win(right_win)
vim.api.nvim_set_current_win(default_main)
vim.cmd("leftabove vertical split")
left_win = vim.api.nvim_get_current_win()
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
else
-- No reusable default-empty window. Open the diff pair by
@@ -697,12 +709,9 @@ local function show_diff(s, entry, focus_left)
-- when there are other windows to absorb the split; if the
-- sidebar is the only window in the tab, the split has to take
-- from the sidebar itself, so restore the width explicitly.
vim.api.nvim_set_current_win(sidebar_win)
vim.cmd("rightbelow vertical split")
right_win = vim.api.nvim_get_current_win()
right_win = vsplit_at(sidebar_win, "right")
reset_diff_win(right_win)
vim.cmd("leftabove vertical split")
left_win = vim.api.nvim_get_current_win()
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
vim.api.nvim_win_set_width(sidebar_win, SIDEBAR_WIDTH)
end
@@ -756,13 +765,11 @@ local function action_stage()
vim.system(
{ "git", "add", "--", entry.path },
{ cwd = s.worktree },
function(obj)
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
vim.schedule(function()
log.error("git add failed: %s", vim.trim(obj.stderr or ""))
end)
log.error("git add failed: %s", vim.trim(obj.stderr or ""))
end
end
end)
)
end
@@ -780,16 +787,18 @@ local function action_unstage()
table.insert(cmd, entry.orig)
end
table.insert(cmd, entry.path)
vim.system(cmd, { cwd = s.worktree }, function(obj)
if obj.code ~= 0 then
vim.schedule(function()
vim.system(
cmd,
{ cwd = s.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
log.error(
"git restore --staged failed: %s",
vim.trim(obj.stderr or "")
)
end)
end
end)
end
end)
)
end
local function action_discard()
@@ -828,16 +837,14 @@ local function action_discard()
vim.system(
{ "git", "checkout", "--", entry.path },
{ cwd = s.worktree },
function(obj)
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
vim.schedule(function()
log.error(
"git checkout failed: %s",
vim.trim(obj.stderr or "")
)
end)
log.error(
"git checkout failed: %s",
vim.trim(obj.stderr or "")
)
end
end
end)
)
end
else
@@ -875,15 +882,8 @@ local function open(worktree)
end
local previous_win = vim.api.nvim_get_current_win()
vim.cmd("leftabove vertical new")
local win = vim.api.nvim_get_current_win()
local bufnr = vim.api.nvim_get_current_buf()
vim.bo[bufnr].buftype = "nofile"
vim.bo[bufnr].bufhidden = "wipe"
vim.bo[bufnr].swapfile = false
local bufnr, win = git.new_scratch({ split = "left", bufhidden = "wipe" })
vim.bo[bufnr].filetype = "gitstatus"
vim.bo[bufnr].modifiable = false
vim.wo[win].number = false
vim.wo[win].relativenumber = false