perf(git): run git log sync with bounded count

This commit is contained in:
2026-04-28 05:36:23 +02:00
parent 80a486719e
commit 4caa1b84df
2 changed files with 78 additions and 68 deletions
+28 -15
View File
@@ -6,19 +6,26 @@ local util = require("util")
local M = {} local M = {}
local LOG_FORMAT = "%h %ad {%an}%d %s" local LOG_FORMAT = "%h %ad {%an}%d %s"
local DEFAULT_MAX_COUNT = 1000
---@class ow.Git.LogOpts
---@field max_count integer? cap on commits to show. Nil uses the default, <= 0 means "all"
---@param opts ow.Git.LogOpts?
function M.show(opts)
opts = opts or {}
local max_count = opts.max_count
if max_count == nil then
max_count = DEFAULT_MAX_COUNT
end
function M.show()
local _, worktree = repo.resolve_cwd() local _, worktree = repo.resolve_cwd()
if not worktree then if not worktree then
log.warning("not in a git repository") log.warning("not in a git repository")
return return
end end
local buf = git.new_scratch() local cmd = {
vim.b[buf].git_worktree = worktree
vim.system(
{
"git", "git",
"log", "log",
"--graph", "--graph",
@@ -26,24 +33,30 @@ function M.show()
"--decorate", "--decorate",
"--date=short", "--date=short",
"--format=format:" .. LOG_FORMAT, "--format=format:" .. LOG_FORMAT,
}, }
{ cwd = worktree, text = true }, if max_count > 0 then
vim.schedule_wrap(function(result) table.insert(cmd, "--max-count=" .. max_count)
if not vim.api.nvim_buf_is_valid(buf) then
return
end end
local result = vim.system(cmd, { cwd = worktree, text = true }):wait()
if result.code ~= 0 then if result.code ~= 0 then
log.error("git log failed: %s", vim.trim(result.stderr or "")) log.error("git log failed: %s", vim.trim(result.stderr or ""))
return return
end end
local lines = util.split_lines(result.stdout or "")
local buf = git.new_scratch()
vim.b[buf].git_worktree = worktree
vim.bo[buf].modifiable = true vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) vim.api.nvim_buf_set_lines(
buf,
0,
-1,
false,
util.split_lines(result.stdout or "")
)
vim.bo[buf].modifiable = false vim.bo[buf].modifiable = false
vim.bo[buf].modified = false vim.bo[buf].modified = false
vim.bo[buf].filetype = "gitlog" vim.bo[buf].filetype = "gitlog"
end)
)
end end
return M return M
+26 -29
View File
@@ -214,14 +214,13 @@ local function parse_porcelain(stdout)
return branch, groups return branch, groups
end end
---Run the ahead/behind `git log` calls for any non-zero counters and call ---Fill in the Unpushed/Unpulled groups from `git log` for any non-zero
---`callback(branch, groups)` once they all finish (or immediately when ---ahead/behind counter. Capped at 200 commits per range so a wildly
---there's nothing to fetch). ---divergent branch can't blow the sidebar's render budget.
---@param worktree string ---@param worktree string
---@param branch ow.Git.BranchInfo ---@param branch ow.Git.BranchInfo
---@param groups table<string, ow.Git.SidebarEntry[]> ---@param groups table<string, ow.Git.SidebarEntry[]>
---@param callback fun(branch: ow.Git.BranchInfo, groups: table<string, ow.Git.SidebarEntry[]>) local function enrich_with_log(worktree, branch, groups)
local function enrich_with_log(worktree, branch, groups, callback)
local fetches = {} local fetches = {}
if branch.upstream and branch.ahead > 0 then if branch.upstream and branch.ahead > 0 then
table.insert( table.insert(
@@ -235,27 +234,29 @@ local function enrich_with_log(worktree, branch, groups, callback)
{ section = "Unpulled", range = "HEAD..@{upstream}" } { section = "Unpulled", range = "HEAD..@{upstream}" }
) )
end end
if #fetches == 0 then -- Submit both subprocesses before waiting so they run concurrently
callback(branch, groups) -- rather than sequentially. Total time = max, not sum.
return local pending = {}
end
local pending = #fetches
for _, f in ipairs(fetches) do for _, f in ipairs(fetches) do
vim.system( table.insert(pending, {
{ f = f,
sys = vim.system({
"git", "git",
"log", "log",
"--max-count=200",
"--format=%h %s", "--format=%h %s",
f.range, f.range,
}, }, { cwd = worktree, text = true }),
{ cwd = worktree, text = true }, })
vim.schedule_wrap(function(log_obj) end
if log_obj.code == 0 then for _, p in ipairs(pending) do
for line in (log_obj.stdout or ""):gmatch("[^\r\n]+") do local result = p.sys:wait()
if result.code == 0 then
for line in (result.stdout or ""):gmatch("[^\r\n]+") do
local sha, subject = line:match("^(%S+)%s+(.+)$") local sha, subject = line:match("^(%S+)%s+(.+)$")
if sha then if sha then
table.insert(groups[f.section], { table.insert(groups[p.f.section], {
section = f.section, section = p.f.section,
sha = sha, sha = sha,
subject = subject, subject = subject,
}) })
@@ -264,16 +265,10 @@ local function enrich_with_log(worktree, branch, groups, callback)
else else
log.error( log.error(
"git log %s failed: %s", "git log %s failed: %s",
f.range, p.f.range,
vim.trim(log_obj.stderr or "") vim.trim(result.stderr or "")
) )
end end
pending = pending - 1
if pending == 0 then
callback(branch, groups)
end
end)
)
end end
end end
@@ -287,7 +282,8 @@ end
local function fetch_status(worktree, prefetched_stdout, callback) local function fetch_status(worktree, prefetched_stdout, callback)
if prefetched_stdout then if prefetched_stdout then
local branch, groups = parse_porcelain(prefetched_stdout) local branch, groups = parse_porcelain(prefetched_stdout)
enrich_with_log(worktree, branch, groups, callback) enrich_with_log(worktree, branch, groups)
callback(branch, groups)
return return
end end
vim.system( vim.system(
@@ -316,7 +312,8 @@ local function fetch_status(worktree, prefetched_stdout, callback)
return return
end end
local branch, groups = parse_porcelain(obj.stdout or "") local branch, groups = parse_porcelain(obj.stdout or "")
enrich_with_log(worktree, branch, groups, callback) enrich_with_log(worktree, branch, groups)
callback(branch, groups)
end) end)
) )
end end