refactor(git): route every git invocation through util.git

This commit is contained in:
2026-05-08 03:44:23 +02:00
parent ebfcaef240
commit 01a543c12f
6 changed files with 115 additions and 132 deletions
+33 -38
View File
@@ -22,17 +22,12 @@ local function git_cmds()
if cached_cmds then
return cached_cmds
end
local result = vim.system(
{ "git", "--list-cmds=main,others,alias" },
{ text = true }
)
:wait()
if result.code ~= 0 then
util.error("git --list-cmds failed: %s", vim.trim(result.stderr or ""))
local out = util.git({ "--list-cmds=main,others,alias" })
if not out then
return {}
end
cached_cmds = {}
for line in (result.stdout or ""):gmatch("[^\r\n]+") do
for line in out:gmatch("[^\r\n]+") do
if line ~= "" then
table.insert(cached_cmds, line)
end
@@ -191,10 +186,16 @@ end
local function run_in_split(r, args, conf)
util.git(args, {
cwd = r.worktree,
on_done = function(stdout)
if not stdout then
on_exit = function(result)
if result.code ~= 0 then
util.error(
"git %s failed: %s",
args[1] or "?",
vim.trim(result.stderr or "")
)
return
end
local stdout = result.stdout or ""
local buf = place_split("[Git " .. table.concat(args, " ") .. "]")
repo.bind(buf, r)
object.attach_dispatch(buf)
@@ -216,14 +217,11 @@ end
---@param r ow.Git.Repo
---@param args string[]
local function run_to_messages(r, args)
local cmd = { "git" }
vim.list_extend(cmd, args)
vim.system(
cmd,
{ cwd = r.worktree, text = true },
vim.schedule_wrap(function(obj)
local out = vim.trim(obj.stdout or "")
local err = vim.trim(obj.stderr or "")
util.git(args, {
cwd = r.worktree,
on_exit = function(result)
local out = vim.trim(result.stdout or "")
local err = vim.trim(result.stderr or "")
local chunks = {}
if out ~= "" then
table.insert(chunks, { out })
@@ -234,17 +232,17 @@ local function run_to_messages(r, args)
end
table.insert(chunks, { err, "ErrorMsg" })
end
if #chunks == 0 and obj.code ~= 0 then
if #chunks == 0 and result.code ~= 0 then
table.insert(
chunks,
{ "git exited " .. tostring(obj.code), "ErrorMsg" }
{ "git exited " .. tostring(result.code), "ErrorMsg" }
)
end
if #chunks > 0 then
vim.api.nvim_echo(chunks, true, {})
end
end)
)
end,
})
end
---@param args string[]
@@ -337,11 +335,11 @@ end
---@return string[]
local function list_files(r, dir)
return r:get_cached("files:" .. dir, function(self)
local cmd = { "git", "ls-files" }
local args = { "ls-files" }
if dir ~= "" then
table.insert(cmd, dir)
table.insert(args, dir)
end
local out = util.exec(cmd, { cwd = self.worktree, silent = true })
local out = util.git(args, { cwd = self.worktree, silent = true })
return out and util.split_lines(out) or {}
end)
end
@@ -350,8 +348,8 @@ end
---@return string[]
local function list_remotes(r)
return r:get_cached("remotes", function(self)
local out = util.exec(
{ "git", "remote" },
local out = util.git(
{ "remote" },
{ cwd = self.worktree, silent = true }
)
return out and util.split_lines(out) or {}
@@ -382,13 +380,10 @@ local function fetch_completions(sub)
if cached_completions[sub] then
return cached_completions[sub]
end
local out = util.exec(
{ "git", sub, "--git-completion-helper-all" },
local out = util.git(
{ sub, "--git-completion-helper-all" },
{ silent = true }
) or util.exec(
{ "git", sub, "--git-completion-helper" },
{ silent = true }
)
) or util.git({ sub, "--git-completion-helper" }, { silent = true })
local items = {}
if out then
for tok in out:gmatch("%S+") do
@@ -469,8 +464,8 @@ function M.complete_rev(arg_lead)
local stage, stage_path_lead = arg_lead:match("^:([0-3]):(.*)$")
if stage then
local out = util.exec(
{ "git", "ls-files", "--stage" },
local out = util.git(
{ "ls-files", "--stage" },
{ cwd = r.worktree, silent = true }
)
if not out then
@@ -506,11 +501,11 @@ function M.complete_rev(arg_lead)
name_lead = name_lead or path_lead
if rev ~= "" then
local cmd = { "git", "ls-tree", rev }
local args = { "ls-tree", rev }
if dir ~= "" then
table.insert(cmd, dir)
table.insert(args, dir)
end
local out = util.exec(cmd, { cwd = r.worktree, silent = true })
local out = util.git(args, { cwd = r.worktree, silent = true })
if not out then
return {}
end
+3 -4
View File
@@ -32,8 +32,7 @@ end
---@param max_count integer?
---@return string?
local function fetch(worktree, max_count)
local cmd = {
"git",
local args = {
"log",
"--graph",
"--all",
@@ -42,9 +41,9 @@ local function fetch(worktree, max_count)
"--format=format:" .. LOG_FORMAT,
}
if max_count then
table.insert(cmd, "--max-count=" .. max_count)
table.insert(args, "--max-count=" .. max_count)
end
return util.exec(cmd, { cwd = worktree })
return util.git(args, { cwd = worktree })
end
---@type table<string, integer> -- worktree -> max_count
+7 -8
View File
@@ -96,8 +96,8 @@ local function attach_index_writer(buf, r, path)
vim.api.nvim_buf_get_lines(buf, 0, -1, false),
"\n"
) .. "\n"
local hash_stdout = util.exec(
{ "git", "hash-object", "-w", "--stdin" },
local hash_stdout = util.git(
{ "hash-object", "-w", "--stdin" },
{ cwd = r.worktree, stdin = body }
)
if not hash_stdout then
@@ -108,8 +108,8 @@ local function attach_index_writer(buf, r, path)
local mode = state and state.index_mode
if not mode then
mode = "100644"
local ls = util.exec(
{ "git", "ls-files", "-s", "--", path },
local ls = util.git(
{ "ls-files", "-s", "--", path },
{ cwd = r.worktree, silent = true }
)
if ls then
@@ -123,8 +123,7 @@ local function attach_index_writer(buf, r, path)
end
end
if
not util.exec({
"git",
not util.git({
"update-index",
"--cacheinfo",
mode,
@@ -171,8 +170,8 @@ end
---@return boolean ok
local function populate(buf, r, rev, state, rev_sha)
local rev_str = rev:format()
local stdout = util.exec(
{ "git", "cat-file", "-p", rev_str },
local stdout = util.git(
{ "cat-file", "-p", rev_str },
{ cwd = r.worktree }
)
if not stdout then
+21 -21
View File
@@ -38,8 +38,7 @@ local global = util.Emitter.new()
local Repo = {}
Repo.__index = Repo
local STATUS_CMD = {
"git",
local STATUS_ARGS = {
"--no-optional-locks",
"-c",
"core.quotePath=false",
@@ -52,20 +51,22 @@ local STATUS_CMD = {
---@private
function Repo:_fetch_status()
vim.system(
STATUS_CMD,
{ cwd = self.worktree, text = true },
vim.schedule_wrap(function(obj)
util.git(STATUS_ARGS, {
cwd = self.worktree,
on_exit = function(result)
self._cache = {}
if obj.code ~= 0 then
util.error("git status failed: %s", vim.trim(obj.stderr or ""))
if result.code ~= 0 then
util.error(
"git status failed: %s",
vim.trim(result.stderr or "")
)
return
end
self.status = status.parse(obj.stdout or "")
self.status = status.parse(result.stdout or "")
self._events:emit("refresh", self.status)
global:emit("refresh", self, self.status)
end)
)
end,
})
end
function Repo:refresh()
@@ -242,8 +243,7 @@ end
---@return string[]
function Repo:list_refs()
return self:get_cached("refs", function(self)
local out = util.exec({
"git",
local out = util.git({
"for-each-ref",
"--format=%(refname:short)",
"refs/heads",
@@ -287,8 +287,8 @@ function Repo:list_stash_refs()
return {}
end
local refs = { "stash" }
local out = util.exec(
{ "git", "stash", "list", "--pretty=format:%gd" },
local out = util.git(
{ "stash", "list", "--pretty=format:%gd" },
{ cwd = self.worktree, silent = true }
)
if out then
@@ -304,12 +304,12 @@ end
---@param short boolean
---@return string?
function Repo:rev_parse(rev, short)
local cmd = { "git", "rev-parse", "--verify", "--quiet" }
local args = { "rev-parse", "--verify", "--quiet" }
if short then
table.insert(cmd, "--short")
table.insert(args, "--short")
end
table.insert(cmd, rev)
local stdout = util.exec(cmd, { cwd = self.worktree, silent = true })
table.insert(args, rev)
local stdout = util.git(args, { cwd = self.worktree, silent = true })
local trimmed = stdout and vim.trim(stdout) or ""
return trimmed ~= "" and trimmed or nil
end
@@ -321,8 +321,8 @@ end
---@return ow.Git.Repo.ResolveStatus
function Repo:resolve_sha(prefix)
local result = self:get_cached("resolve:" .. prefix, function(self)
local out = util.exec(
{ "git", "rev-parse", "--disambiguate=" .. prefix },
local out = util.git(
{ "rev-parse", "--disambiguate=" .. prefix },
{ cwd = self.worktree, silent = true }
)
local trimmed = out and vim.trim(out) or ""
+29 -29
View File
@@ -460,18 +460,20 @@ local function action_stage()
if #paths == 0 then
return
end
local cmd = { "git", "add", "--" }
vim.list_extend(cmd, paths)
vim.system(
cmd,
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.error("git add failed: %s", vim.trim(obj.stderr or ""))
end
end)
local args = { "add", "--" }
vim.list_extend(args, paths)
util.git(args, {
cwd = s.repo.worktree,
on_exit = function(result)
if result.code ~= 0 then
util.error(
"git add failed: %s",
vim.trim(result.stderr or "")
)
end
end,
})
end
local function action_unstage()
local s, item = current_entry(vim.api.nvim_get_current_buf())
@@ -481,7 +483,7 @@ local function action_unstage()
if item.kind ~= "staged" then
return
end
local cmd = { "git", "restore", "--staged", "--" }
local args = { "restore", "--staged", "--" }
local entries
if item.is_header then
entries = s.repo.status:by_kind("staged")
@@ -494,22 +496,21 @@ local function action_unstage()
end
for _, e in ipairs(entries) do
if e.orig then
table.insert(cmd, e.orig)
table.insert(args, e.orig)
end
table.insert(cmd, e.path)
table.insert(args, e.path)
end
vim.system(
cmd,
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.git(args, {
cwd = s.repo.worktree,
on_exit = function(result)
if result.code ~= 0 then
util.error(
"git restore --staged failed: %s",
vim.trim(obj.stderr or "")
vim.trim(result.stderr or "")
)
end
end)
)
end,
})
end
local function action_discard()
@@ -542,18 +543,17 @@ local function action_discard()
elseif item.kind == "unstaged" then
prompt = string.format("Discard changes to %s?", item.path)
action = function()
vim.system(
{ "git", "checkout", "--", item.path },
{ cwd = s.repo.worktree },
vim.schedule_wrap(function(obj)
if obj.code ~= 0 then
util.git({ "checkout", "--", item.path }, {
cwd = s.repo.worktree,
on_exit = function(result)
if result.code ~= 0 then
util.error(
"git checkout failed: %s",
vim.trim(obj.stderr or "")
vim.trim(result.stderr or "")
)
end
end)
)
end,
})
end
else
return
+15 -25
View File
@@ -189,16 +189,7 @@ end
---@field cwd string?
---@field stdin string?
---@field silent boolean?
---@field on_done fun(stdout: string?)?
---@param args string[]
---@param opts ow.Git.Util.ExecOpts?
---@return string?
function M.git(args, opts)
local cmd = { "git" }
vim.list_extend(cmd, args)
return M.exec(cmd, opts)
end
---@field on_exit fun(result: vim.SystemCompleted)?
---@param cmd string[]
---@param opts ow.Git.Util.ExecOpts?
@@ -207,12 +198,15 @@ function M.exec(cmd, opts)
opts = opts or {}
local sys_opts = { cwd = opts.cwd, stdin = opts.stdin, text = true }
local function handle(result)
if opts.on_exit then
vim.system(cmd, sys_opts, vim.schedule_wrap(opts.on_exit))
return nil
end
local result = vim.system(cmd, sys_opts):wait()
if result.code ~= 0 then
if not opts.silent then
local label = cmd[2] and (cmd[1] .. " " .. cmd[2])
or cmd[1]
or "?"
local label = cmd[2] and (cmd[1] .. " " .. cmd[2]) or cmd[1] or "?"
M.error("%s failed: %s", label, vim.trim(result.stderr or ""))
end
return nil
@@ -220,17 +214,13 @@ function M.exec(cmd, opts)
return result.stdout or ""
end
if opts.on_done then
vim.system(
cmd,
sys_opts,
vim.schedule_wrap(function(result)
opts.on_done(handle(result))
end)
)
return nil
end
return handle(vim.system(cmd, sys_opts):wait())
---@param args string[]
---@param opts ow.Git.Util.ExecOpts?
---@return string?
function M.git(args, opts)
local cmd = { "git" }
vim.list_extend(cmd, args)
return M.exec(cmd, opts)
end
---@class ow.Git.Util.Emitter<T>