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
+50 -50
View File
@@ -1,7 +1,9 @@
local log = require("log")
local util = require("util")
local UNMERGED = {
local M = {}
M.UNMERGED = {
DD = true,
AU = true,
UD = true,
@@ -14,7 +16,7 @@ local UNMERGED = {
---@param code string porcelain v1 XY code
---@return string? char
---@return string? hl_group
local function indicator(code)
function M.indicator(code)
if code == "" then
return nil
end
@@ -24,7 +26,7 @@ local function indicator(code)
if code == "!!" then
return "!", "GitIgnored"
end
if UNMERGED[code] then
if M.UNMERGED[code] then
return "U", "GitUnmerged"
end
local x, y = code:sub(1, 1), code:sub(2, 2)
@@ -43,7 +45,7 @@ end
---@param code string
---@return string?
local function format(code)
local char, hl = indicator(code)
local char, hl = M.indicator(code)
if not char then
return nil
end
@@ -53,7 +55,7 @@ end
---@param path string
---@return string? gitdir
---@return string? worktree
local function resolve(path)
function M.resolve(path)
local found = vim.fs.find(".git", { upward = true, path = path })[1]
if not found then
return nil
@@ -88,12 +90,12 @@ end
---both when not inside a git repo.
---@return string? gitdir
---@return string? worktree
local function resolve_cwd()
function M.resolve_cwd()
local path = vim.api.nvim_buf_get_name(0)
if path == "" then
path = vim.fn.getcwd()
end
return resolve(path)
return M.resolve(path)
end
---@class ow.Git.Repo
@@ -152,15 +154,17 @@ local function do_refresh(repo)
-- path lines, so it ignores `##` lines below. Running with `--branch`
-- lets the sidebar reuse this single subprocess via the GitRefresh
-- data payload instead of spawning its own.
vim.system({
"git",
"-c",
"core.quotePath=false",
"status",
"--porcelain=v1",
"--branch",
}, { cwd = repo.worktree, text = true }, function(obj)
vim.schedule(function()
vim.system(
{
"git",
"-c",
"core.quotePath=false",
"status",
"--porcelain=v1",
"--branch",
},
{ cwd = repo.worktree, text = true },
vim.schedule_wrap(function(obj)
local statuses = {}
if obj.code == 0 then
for line in (obj.stdout or ""):gmatch("[^\r\n]+") do
@@ -210,7 +214,7 @@ local function do_refresh(repo)
},
})
end)
end)
)
end
---@param gitdir string
@@ -244,7 +248,7 @@ local function register(buf)
if path == "" then
return nil
end
local gitdir, worktree = resolve(path)
local gitdir, worktree = M.resolve(path)
if not gitdir or not worktree then
return nil
end
@@ -259,7 +263,7 @@ local function register(buf)
end
---@param buf integer
local function unregister(buf)
function M.unregister(buf)
local repo = repo_by_buf[buf]
if not repo then
return
@@ -273,7 +277,7 @@ local function unregister(buf)
end
---@param buf integer
local function refresh_buf(buf)
function M.refresh_buf(buf)
if not vim.api.nvim_buf_is_valid(buf) or vim.bo[buf].buftype ~= "" then
return
end
@@ -285,7 +289,7 @@ local function refresh_buf(buf)
repo:refresh()
end
local function stop_all()
function M.stop_all()
for _, repo in pairs(repo_by_gitdir) do
repo:stop_watcher()
end
@@ -293,8 +297,8 @@ end
---@param path string
---@return string?
local function head(path)
local gitdir = resolve(path)
function M.head(path)
local gitdir = M.resolve(path)
if not gitdir then
return nil
end
@@ -318,39 +322,35 @@ local function head(path)
return nil
end
---Resolve a git revision to its object SHA. Returns nil if the ref can't be
---resolved (root-commit's `^`, blob's `^`, malformed ref, etc.). When `short`
---is true, the result is abbreviated via `core.abbrev` (auto-extended by git
---to keep the prefix unique in the current repo).
---Resolve a git revision to its object SHA. Calls `callback(sha?)` on the
---main loop with nil if the ref can't be parsed (root-commit's `^`, blob's
---`^`, malformed ref, etc.). When `short` is true, the result is abbreviated
---via `core.abbrev` (auto-extended by git to keep the prefix unique in the
---current repo).
---@param worktree string
---@param ref string
---@param short? boolean
---@return string?
local function rev_parse(worktree, ref, short)
---@param short boolean
---@param callback fun(sha: string?)
function M.rev_parse(worktree, ref, short, callback)
local cmd = { "git", "rev-parse", "--verify", "--quiet" }
if short then
table.insert(cmd, "--short")
end
table.insert(cmd, ref)
local result = vim.system(cmd, { cwd = worktree, text = true }):wait()
if result.code ~= 0 then
return nil
end
local sha = vim.trim(result.stdout or "")
if sha == "" then
return nil
end
return sha
vim.system(
cmd,
{ cwd = worktree, text = true },
vim.schedule_wrap(function(result)
local sha
if result.code == 0 then
local trimmed = vim.trim(result.stdout or "")
if trimmed ~= "" then
sha = trimmed
end
end
callback(sha)
end)
)
end
return {
UNMERGED = UNMERGED,
head = head,
indicator = indicator,
refresh_buf = refresh_buf,
resolve = resolve,
resolve_cwd = resolve_cwd,
rev_parse = rev_parse,
stop_all = stop_all,
unregister = unregister,
}
return M