refactor(git): split URI read from conditional refresh

This commit is contained in:
2026-05-06 04:46:01 +02:00
parent abb5434893
commit abc09d4ae3
5 changed files with 70 additions and 69 deletions
+6 -18
View File
@@ -81,17 +81,13 @@ local function uri_split(opts, buf, rev)
local object = require("git.object") local object = require("git.object")
if opts.rev and opts.rev:find(":", 1, true) then if opts.rev and opts.rev:find(":", 1, true) then
local content = util.exec( if not r:rev_parse(opts.rev, true) then
{ "git", "cat-file", "-p", opts.rev },
{ cwd = r.worktree, silent = true }
)
if not content then
util.error("invalid rev: %s", opts.rev) util.error("invalid rev: %s", opts.rev)
return return
end end
place_pair( place_pair(
buf, buf,
object.buf_for(r, Revision.parse(opts.rev), content), object.buf_for(r, Revision.parse(opts.rev)),
false, false,
opts.vertical opts.vertical
) )
@@ -123,15 +119,11 @@ local function uri_split(opts, buf, rev)
local m = mapping[rev.stage] local m = mapping[rev.stage]
or { Revision.new({ stage = 0, path = rev.path }), true } or { Revision.new({ stage = 0, path = rev.path }), true }
local other_rev, left = m[1], m[2] local other_rev, left = m[1], m[2]
local content = util.exec( if not r:rev_parse(other_rev:format(), true) then
{ "git", "cat-file", "-p", other_rev:format() },
{ cwd = r.worktree, silent = true }
)
if not content then
util.error("invalid rev: %s", other_rev:format()) util.error("invalid rev: %s", other_rev:format())
return return
end end
place_pair(buf, object.buf_for(r, other_rev, content), left, opts.vertical) place_pair(buf, object.buf_for(r, other_rev), left, opts.vertical)
end end
---@class ow.Git.Diff.SplitOpts ---@class ow.Git.Diff.SplitOpts
@@ -172,15 +164,11 @@ function M.split(opts)
else else
rev = Revision.new({ base = opts.rev, path = rel }) rev = Revision.new({ base = opts.rev, path = rel })
end end
local content = util.exec( if not r:rev_parse(rev:format(), true) then
{ "git", "cat-file", "-p", rev:format() },
{ cwd = r.worktree, silent = true }
)
if not content then
util.error("invalid rev: %s", rev:format()) util.error("invalid rev: %s", rev:format())
return return
end end
local buf = require("git.object").buf_for(r, rev, content) local buf = require("git.object").buf_for(r, rev)
place_pair(buf, cur_buf, true, opts.vertical) place_pair(buf, cur_buf, true, opts.vertical)
end end
+1 -1
View File
@@ -71,7 +71,7 @@ function M.init()
pattern = "git://*", pattern = "git://*",
group = group, group = group,
callback = function(args) callback = function(args)
require("git.object").read_uri(args.buf, { force = true }) require("git.object").read_uri(args.buf)
end, end,
}) })
+7 -7
View File
@@ -49,10 +49,10 @@ local function fetch(worktree, max_count)
end end
---@param buf integer ---@param buf integer
local function populate(buf) ---@param r ow.Git.Repo
local r = repo.resolve(buf) local function populate(buf, r)
local state = r and r:state(buf) local state = r:state(buf)
if not r or not state then if not state then
return return
end end
local stdout = fetch(r.worktree, state.log_max_count) local stdout = fetch(r.worktree, state.log_max_count)
@@ -110,7 +110,7 @@ function M.read_uri(buf)
end end
attach_dispatch(buf) attach_dispatch(buf)
populate(buf) populate(buf, r)
end end
---@class ow.Git.Log.OpenOpts ---@class ow.Git.Log.OpenOpts
@@ -144,7 +144,7 @@ function M.open(opts)
end end
if was_loaded then if was_loaded then
populate(buf) populate(buf, r)
end end
end end
@@ -183,6 +183,6 @@ function M.complete_glog(arg_lead)
return matches return matches
end end
repo.on_uri_refresh(M.URI_PREFIX, M.read_uri) repo.on_uri_refresh(M.URI_PREFIX, populate)
return M return M
+44 -31
View File
@@ -71,6 +71,20 @@ local function is_zero(sha)
return sha == nil or sha:match("^0+$") ~= nil return sha == nil or sha:match("^0+$") ~= nil
end end
---@param rev ow.Git.Revision
---@return boolean
local function is_immutable_rev(rev)
if rev.stage ~= nil then
return false
end
local base = rev.base
if not base then
return false
end
local stripped = base:gsub("%^%b{}", ""):gsub("[%^~]%d*", "")
return stripped:match("^%x+$") ~= nil and #stripped >= 7
end
---@param buf integer ---@param buf integer
---@param r ow.Git.Repo ---@param r ow.Git.Repo
---@param path string ---@param path string
@@ -141,23 +155,16 @@ end
---@param r ow.Git.Repo ---@param r ow.Git.Repo
---@param rev ow.Git.Revision ---@param rev ow.Git.Revision
---@param content string?
---@return integer ---@return integer
function M.buf_for(r, rev, content) function M.buf_for(r, rev)
local buf = vim.fn.bufadd(M.format_uri(rev)) local buf = vim.fn.bufadd(M.format_uri(rev))
repo.bind(buf, r) repo.bind(buf, r)
if content and not vim.api.nvim_buf_is_loaded(buf) then
local state = r:state(buf) --[[@as -nil]]
state.pending_content = content
end
vim.fn.bufload(buf) vim.fn.bufload(buf)
return buf return buf
end end
---@param buf integer ---@param buf integer
---@param opts { force?: boolean }? function M.read_uri(buf)
function M.read_uri(buf, opts)
opts = opts or {}
local name = vim.api.nvim_buf_get_name(buf) local name = vim.api.nvim_buf_get_name(buf)
local rev = M.parse_uri(name) local rev = M.parse_uri(name)
if not rev then if not rev then
@@ -176,27 +183,20 @@ function M.read_uri(buf, opts)
vim.bo[buf].swapfile = false vim.bo[buf].swapfile = false
vim.bo[buf].bufhidden = "hide" vim.bo[buf].bufhidden = "hide"
---@type string?
local stdout = state.pending_content
state.pending_content = nil
if stdout == nil then
local rev_sha = r:rev_parse(rev_str, true) local rev_sha = r:rev_parse(rev_str, true)
if not rev_sha then if not rev_sha then
return return
end end
if
not opts.force local stdout = util.exec(
and (rev_sha == state.sha or vim.bo[buf].modified)
then
return
end
stdout = util.exec(
{ "git", "cat-file", "-p", rev_str }, { "git", "cat-file", "-p", rev_str },
{ cwd = r.worktree } { cwd = r.worktree }
) )
if not stdout then
return
end end
if stdout and rev.path == nil then if rev.path == nil then
local commit_sha = r:rev_parse(rev_str .. "^{commit}", true) local commit_sha = r:rev_parse(rev_str .. "^{commit}", true)
if commit_sha then if commit_sha then
local patch = util.exec({ local patch = util.exec({
@@ -216,12 +216,11 @@ function M.read_uri(buf, opts)
end end
end end
if stdout then
vim.bo[buf].modifiable = true vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout)) vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
end
state.sha = r:rev_parse(rev_str, true) state.sha = rev_sha
state.immutable = is_immutable_rev(rev)
if rev.stage == 0 and rev.path then if rev.stage == 0 and rev.path then
vim.bo[buf].buftype = "acwrite" vim.bo[buf].buftype = "acwrite"
@@ -249,6 +248,24 @@ function M.read_uri(buf, opts)
vim.api.nvim_exec_autocmds("BufReadPost", { buffer = buf }) vim.api.nvim_exec_autocmds("BufReadPost", { buffer = buf })
end end
---@param buf integer
---@param r ow.Git.Repo
local function refresh(buf, r)
local state = r:state(buf)
if not state or state.immutable or vim.bo[buf].modified then
return
end
local rev = M.parse_uri(vim.api.nvim_buf_get_name(buf))
if not rev then
return
end
local rev_sha = r:rev_parse(rev:format(), true)
if not rev_sha or rev_sha == state.sha then
return
end
M.read_uri(buf)
end
---@param r ow.Git.Repo ---@param r ow.Git.Repo
---@param blob string? ---@param blob string?
---@param path string ---@param path string
@@ -314,15 +331,11 @@ function M.open(r, rev, opts)
parsed.base = sha parsed.base = sha
end end
end end
local content = util.exec( if not r:rev_parse(parsed:format(), true) then
{ "git", "cat-file", "-p", parsed:format() },
{ cwd = r.worktree, silent = true }
)
if not content then
util.error("not a git object: %s", rev) util.error("not a git object: %s", rev)
return return
end end
local buf = M.buf_for(r, parsed, content) local buf = M.buf_for(r, parsed)
util.place_buf(buf, opts and opts.split) util.place_buf(buf, opts and opts.split)
end end
@@ -384,6 +397,6 @@ function M.open_under_cursor()
return false return false
end end
repo.on_uri_refresh(M.URI_PREFIX, M.read_uri) repo.on_uri_refresh(M.URI_PREFIX, refresh)
return M return M
+3 -3
View File
@@ -16,10 +16,10 @@ end
---@field repo ow.Git.Repo ---@field repo ow.Git.Repo
---@field sha string? ---@field sha string?
---@field parent_sha string? ---@field parent_sha string?
---@field immutable boolean?
---@field index_writer boolean? ---@field index_writer boolean?
---@field index_mode string? ---@field index_mode string?
---@field log_max_count integer? ---@field log_max_count integer?
---@field pending_content string?
---@alias ow.Git.Repo.Event "refresh" ---@alias ow.Git.Repo.Event "refresh"
@@ -208,7 +208,7 @@ function M.on(event, fn)
end end
---@param prefix string ---@param prefix string
---@param fn fun(buf: integer) ---@param fn fun(buf: integer, r: ow.Git.Repo)
---@return fun() unsubscribe ---@return fun() unsubscribe
function M.on_uri_refresh(prefix, fn) function M.on_uri_refresh(prefix, fn)
return M.on("refresh", function(r) return M.on("refresh", function(r)
@@ -216,7 +216,7 @@ function M.on_uri_refresh(prefix, fn)
if vim.api.nvim_buf_is_valid(buf) then if vim.api.nvim_buf_is_valid(buf) then
local name = vim.api.nvim_buf_get_name(buf) local name = vim.api.nvim_buf_get_name(buf)
if name:sub(1, #prefix) == prefix then if name:sub(1, #prefix) == prefix then
fn(buf) fn(buf, r)
end end
end end
end end