diff --git a/lua/git/diff.lua b/lua/git/diff.lua index b3caeab..0369907 100644 --- a/lua/git/diff.lua +++ b/lua/git/diff.lua @@ -81,17 +81,13 @@ local function uri_split(opts, buf, rev) local object = require("git.object") if opts.rev and opts.rev:find(":", 1, true) then - local content = util.exec( - { "git", "cat-file", "-p", opts.rev }, - { cwd = r.worktree, silent = true } - ) - if not content then + if not r:rev_parse(opts.rev, true) then util.error("invalid rev: %s", opts.rev) return end place_pair( buf, - object.buf_for(r, Revision.parse(opts.rev), content), + object.buf_for(r, Revision.parse(opts.rev)), false, opts.vertical ) @@ -123,15 +119,11 @@ local function uri_split(opts, buf, rev) local m = mapping[rev.stage] or { Revision.new({ stage = 0, path = rev.path }), true } local other_rev, left = m[1], m[2] - local content = util.exec( - { "git", "cat-file", "-p", other_rev:format() }, - { cwd = r.worktree, silent = true } - ) - if not content then + if not r:rev_parse(other_rev:format(), true) then util.error("invalid rev: %s", other_rev:format()) return 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 ---@class ow.Git.Diff.SplitOpts @@ -172,15 +164,11 @@ function M.split(opts) else rev = Revision.new({ base = opts.rev, path = rel }) end - local content = util.exec( - { "git", "cat-file", "-p", rev:format() }, - { cwd = r.worktree, silent = true } - ) - if not content then + if not r:rev_parse(rev:format(), true) then util.error("invalid rev: %s", rev:format()) return 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) end diff --git a/lua/git/init.lua b/lua/git/init.lua index d75c2e8..eed4b28 100644 --- a/lua/git/init.lua +++ b/lua/git/init.lua @@ -71,7 +71,7 @@ function M.init() pattern = "git://*", group = group, callback = function(args) - require("git.object").read_uri(args.buf, { force = true }) + require("git.object").read_uri(args.buf) end, }) diff --git a/lua/git/log_view.lua b/lua/git/log_view.lua index 803dfbe..f813e4f 100644 --- a/lua/git/log_view.lua +++ b/lua/git/log_view.lua @@ -49,10 +49,10 @@ local function fetch(worktree, max_count) end ---@param buf integer -local function populate(buf) - local r = repo.resolve(buf) - local state = r and r:state(buf) - if not r or not state then +---@param r ow.Git.Repo +local function populate(buf, r) + local state = r:state(buf) + if not state then return end local stdout = fetch(r.worktree, state.log_max_count) @@ -110,7 +110,7 @@ function M.read_uri(buf) end attach_dispatch(buf) - populate(buf) + populate(buf, r) end ---@class ow.Git.Log.OpenOpts @@ -144,7 +144,7 @@ function M.open(opts) end if was_loaded then - populate(buf) + populate(buf, r) end end @@ -183,6 +183,6 @@ function M.complete_glog(arg_lead) return matches end -repo.on_uri_refresh(M.URI_PREFIX, M.read_uri) +repo.on_uri_refresh(M.URI_PREFIX, populate) return M diff --git a/lua/git/object.lua b/lua/git/object.lua index 1f9bcf8..40bbf33 100644 --- a/lua/git/object.lua +++ b/lua/git/object.lua @@ -71,6 +71,20 @@ local function is_zero(sha) return sha == nil or sha:match("^0+$") ~= nil 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 r ow.Git.Repo ---@param path string @@ -141,23 +155,16 @@ end ---@param r ow.Git.Repo ---@param rev ow.Git.Revision ----@param content string? ---@return integer -function M.buf_for(r, rev, content) +function M.buf_for(r, rev) local buf = vim.fn.bufadd(M.format_uri(rev)) 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) return buf end ---@param buf integer ----@param opts { force?: boolean }? -function M.read_uri(buf, opts) - opts = opts or {} +function M.read_uri(buf) local name = vim.api.nvim_buf_get_name(buf) local rev = M.parse_uri(name) if not rev then @@ -176,27 +183,20 @@ function M.read_uri(buf, opts) vim.bo[buf].swapfile = false 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) - if not rev_sha then - return - end - if - not opts.force - and (rev_sha == state.sha or vim.bo[buf].modified) - then - return - end - stdout = util.exec( - { "git", "cat-file", "-p", rev_str }, - { cwd = r.worktree } - ) + local rev_sha = r:rev_parse(rev_str, true) + if not rev_sha then + return end - if stdout and rev.path == nil then + local stdout = util.exec( + { "git", "cat-file", "-p", rev_str }, + { cwd = r.worktree } + ) + if not stdout then + return + end + + if rev.path == nil then local commit_sha = r:rev_parse(rev_str .. "^{commit}", true) if commit_sha then local patch = util.exec({ @@ -216,12 +216,11 @@ function M.read_uri(buf, opts) end end - if stdout then - vim.bo[buf].modifiable = true - vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout)) - end + vim.bo[buf].modifiable = true + vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout)) - 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 vim.bo[buf].buftype = "acwrite" @@ -249,6 +248,24 @@ function M.read_uri(buf, opts) vim.api.nvim_exec_autocmds("BufReadPost", { buffer = buf }) 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 blob string? ---@param path string @@ -314,15 +331,11 @@ function M.open(r, rev, opts) parsed.base = sha end end - local content = util.exec( - { "git", "cat-file", "-p", parsed:format() }, - { cwd = r.worktree, silent = true } - ) - if not content then + if not r:rev_parse(parsed:format(), true) then util.error("not a git object: %s", rev) return end - local buf = M.buf_for(r, parsed, content) + local buf = M.buf_for(r, parsed) util.place_buf(buf, opts and opts.split) end @@ -384,6 +397,6 @@ function M.open_under_cursor() return false end -repo.on_uri_refresh(M.URI_PREFIX, M.read_uri) +repo.on_uri_refresh(M.URI_PREFIX, refresh) return M diff --git a/lua/git/repo.lua b/lua/git/repo.lua index 588142a..af3a396 100644 --- a/lua/git/repo.lua +++ b/lua/git/repo.lua @@ -16,10 +16,10 @@ end ---@field repo ow.Git.Repo ---@field sha string? ---@field parent_sha string? +---@field immutable boolean? ---@field index_writer boolean? ---@field index_mode string? ---@field log_max_count integer? ----@field pending_content string? ---@alias ow.Git.Repo.Event "refresh" @@ -208,7 +208,7 @@ function M.on(event, fn) end ---@param prefix string ----@param fn fun(buf: integer) +---@param fn fun(buf: integer, r: ow.Git.Repo) ---@return fun() unsubscribe function M.on_uri_refresh(prefix, fn) 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 local name = vim.api.nvim_buf_get_name(buf) if name:sub(1, #prefix) == prefix then - fn(buf) + fn(buf, r) end end end