diff --git a/lua/git/cmd.lua b/lua/git/cmd.lua index 302c64a..e5bf137 100644 --- a/lua/git/cmd.lua +++ b/lua/git/cmd.lua @@ -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 diff --git a/lua/git/log_view.lua b/lua/git/log_view.lua index a2e2a9d..80a91d6 100644 --- a/lua/git/log_view.lua +++ b/lua/git/log_view.lua @@ -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 -- worktree -> max_count diff --git a/lua/git/object.lua b/lua/git/object.lua index f9c09a1..34b3bda 100644 --- a/lua/git/object.lua +++ b/lua/git/object.lua @@ -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 diff --git a/lua/git/repo.lua b/lua/git/repo.lua index 8241bca..482ca81 100644 --- a/lua/git/repo.lua +++ b/lua/git/repo.lua @@ -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 "" diff --git a/lua/git/status_view.lua b/lua/git/status_view.lua index 0bec220..2e1b223 100644 --- a/lua/git/status_view.lua +++ b/lua/git/status_view.lua @@ -460,17 +460,19 @@ 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 "")) + 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, + }) end local function action_unstage() @@ -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 diff --git a/lua/git/util.lua b/lua/git/util.lua index 02888b6..4725b1a 100644 --- a/lua/git/util.lua +++ b/lua/git/util.lua @@ -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,30 +198,29 @@ function M.exec(cmd, opts) opts = opts or {} local sys_opts = { cwd = opts.cwd, stdin = opts.stdin, text = true } - local function handle(result) - if result.code ~= 0 then - if not opts.silent then - 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 - end - 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) - ) + if opts.on_exit then + vim.system(cmd, sys_opts, vim.schedule_wrap(opts.on_exit)) return nil end - return handle(vim.system(cmd, sys_opts):wait()) + + 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 "?" + M.error("%s failed: %s", label, vim.trim(result.stderr or "")) + end + return nil + end + return result.stdout or "" +end + +---@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