diff --git a/lua/git/blame.lua b/lua/git/blame.lua index f2ef5d7..4d16f0e 100644 --- a/lua/git/blame.lua +++ b/lua/git/blame.lua @@ -192,14 +192,26 @@ local function resolve_source(buf) local name = vim.api.nvim_buf_get_name(buf) if util.is_uri(name) then local rev = object.parse_uri(name) - if not rev or not rev.base or not rev.path then + if not rev then return nil end local r = repo.find(buf) if not r then return nil end - return { repo = r, rel = rev.path, revision = rev.base } + local rel = rev.path + if not rel then + local state = r:state(buf) + rel = state and state.blob_path or nil + if not rel then + return nil + end + end + local revision = nil + if rev.path and rev.base and rev.stage == nil then + revision = rev.base + end + return { repo = r, rel = rel, revision = revision } end if not repo.is_worktree_buf(buf) then return nil diff --git a/lua/git/core/repo.lua b/lua/git/core/repo.lua index 48e4d44..17f02d7 100644 --- a/lua/git/core/repo.lua +++ b/lua/git/core/repo.lua @@ -19,6 +19,7 @@ end ---@field immutable boolean? ---@field index_writer boolean? ---@field index_mode string? +---@field blob_path string? ---@alias ow.Git.Repo.Event ---| "change" diff --git a/lua/git/object.lua b/lua/git/object.lua index d0619ce..eaa502c 100644 --- a/lua/git/object.lua +++ b/lua/git/object.lua @@ -321,6 +321,10 @@ local function side_buf(r, blob, path) if full then local buf = M.buf_for(r, Revision.new({ base = full })) set_ft_from_path(buf, path) + local state = r:state(buf) + if state then + state.blob_path = path + end return buf end local p = vim.fs.joinpath(r.worktree, path) diff --git a/test/git/blame_test.lua b/test/git/blame_test.lua index ce8f404..28a4510 100644 --- a/test/git/blame_test.lua +++ b/test/git/blame_test.lua @@ -467,6 +467,89 @@ t.test("line popup works in a git:// object buffer", function() ) end) +t.test( + "line popup works in a git:// buffer with a recovered path", + function() + local object = require("git.object") + local dir = h.make_repo({ ["a.txt"] = "alpha\nbeta\ngamma\n" }) + local sha = h.git(dir, "rev-parse", "HEAD").stdout + local blob = h.git(dir, "rev-parse", "HEAD:a.txt").stdout + local r = assert(require("git.core.repo").resolve(dir)) + + object.open(r, sha, { split = false }) + local commit_buf = vim.api.nvim_get_current_buf() + local found ---@type integer? + for i, l in ipairs(vim.api.nvim_buf_get_lines(commit_buf, 0, -1, false)) do + if l == "+++ b/a.txt" then + found = i + break + end + end + local lnum = assert(found, "expected a +++ b/a.txt line") + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + t.truthy(object.open_under_cursor()) + local gbuf = vim.api.nvim_get_current_buf() + t.eq(vim.api.nvim_buf_get_name(gbuf), "git://" .. blob) + + vim.api.nvim_win_set_cursor(0, { 1, 0 }) + blame.line_popup(gbuf) + local float = wait_float() + t.defer(function() + pcall(vim.api.nvim_win_close, float, true) + end) + local lines = vim.api.nvim_buf_get_lines( + vim.api.nvim_win_get_buf(float), + 0, + -1, + false + ) + t.truthy( + vim.startswith(lines[1] or "", sha:sub(1, 8)), + "the popup blames the commit even in a bare-blob git:// buffer" + ) + end +) + +t.test("line popup works in a git://:0: index buffer", function() + local object = require("git.object") + local Revision = require("git.core.revision") + local dir = h.make_repo({ ["a.txt"] = "alpha\nbeta\ngamma\n" }) + local sha = h.git(dir, "rev-parse", "HEAD").stdout + local r = assert(require("git.core.repo").resolve(dir)) + + local ibuf = object.buf_for(r, Revision.new({ stage = 0, path = "a.txt" })) + vim.api.nvim_set_current_buf(ibuf) + vim.api.nvim_win_set_cursor(0, { 1, 0 }) + blame.line_popup(ibuf) + local float = wait_float() + t.defer(function() + pcall(vim.api.nvim_win_close, float, true) + end) + local lines = vim.api.nvim_buf_get_lines( + vim.api.nvim_win_get_buf(float), + 0, + -1, + false + ) + t.truthy( + vim.startswith(lines[1] or "", sha:sub(1, 8)), + "the popup blames the commit for an index entry" + ) +end) + +t.test("line popup skips a bare git:// object dump", function() + local object = require("git.object") + local dir = h.make_repo({ ["a.txt"] = "alpha\n" }) + local r = assert(require("git.core.repo").resolve(dir)) + + object.open(r, "HEAD", { split = false }) + local cbuf = vim.api.nvim_get_current_buf() + t.quietly(function() + blame.line_popup(cbuf) + end) + t.eq(blame.state(cbuf), nil, "no blame state on a commit dump") +end) + t.test("open_commit opens the commit that last touched the line", function() local _, buf = setup("alpha\nbeta\ngamma\n") vim.api.nvim_set_current_buf(buf)