Compare commits

...

3 Commits

6 changed files with 218 additions and 24 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ The plugin auto-initializes from its `plugin/git.lua` and exposes `lua/git/`. No
With `vim.pack`:
```lua
vim.pack.add({ "https://git.owall.dev/warg/git.nvim" })
vim.pack.add({ "https://git.owall.se/warg/git.nvim" })
```
## Commands
+14 -2
View File
@@ -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
+1
View File
@@ -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"
+68 -11
View File
@@ -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)
@@ -333,10 +337,45 @@ local function side_buf(r, blob, path)
end
---@param r ow.Git.Repo
---@param commit string
---@param parent boolean
---@param path string
---@return integer?
local function commit_side_buf(r, commit, parent, path)
local rev = parent and (commit .. "^") or commit
local sha = r:rev_parse(rev, false)
if not sha then
return nil
end
if not r:rev_parse(sha .. ":" .. path, false) then
return nil
end
return M.buf_for(r, Revision.new({ base = sha, path = path }))
end
---@param r ow.Git.Repo
---@param commit string?
---@param parent boolean
---@param blob string?
---@param path string
local function load_side(r, blob, path)
local buf = side_buf(r, blob, path)
---@return integer?
local function side(r, commit, parent, blob, path)
if commit then
local buf = commit_side_buf(r, commit, parent, path)
if buf then
return buf
end
end
return side_buf(r, blob, path)
end
---@param r ow.Git.Repo
---@param commit string?
---@param parent boolean
---@param blob string?
---@param path string
local function load_side(r, commit, parent, blob, path)
local buf = side(r, commit, parent, blob, path)
if not buf then
util.error("no content for %s", path)
return
@@ -346,14 +385,15 @@ local function load_side(r, blob, path)
end
---@param r ow.Git.Repo
---@param commit string?
---@param section ow.Git.DiffSection
local function open_section(r, section)
local function open_section(r, commit, section)
if not section.blob_a or not section.blob_b then
util.error("no index line, cannot determine blob SHAs")
return
end
local left = side_buf(r, section.blob_a, section.path_a)
local right = side_buf(r, section.blob_b, section.path_b)
local left = side(r, commit, true, section.blob_a, section.path_a)
local right = side(r, commit, false, section.blob_b, section.path_b)
if left and right then
vim.cmd.normal({ "m'", bang = true })
vim.api.nvim_set_current_buf(right)
@@ -373,6 +413,21 @@ local function open_section(r, section)
vim.api.nvim_set_current_buf(buf)
end
---@param r ow.Git.Repo
---@param buf integer
---@return string?
local function commit_in_buf(r, buf)
local name = vim.api.nvim_buf_get_name(buf)
if not vim.startswith(name, M.URI_PREFIX) then
return nil
end
local rev = M.parse_uri(name)
if not rev or not rev.base or rev.path then
return nil
end
return r:rev_parse(rev.base .. "^{commit}", false)
end
---@class ow.Git.Object.OpenOpts
---@field split (false|"above"|"below"|"left"|"right")?
@@ -419,7 +474,7 @@ function M.open_under_cursor()
line:match("^%d+ (%w+) (%x+)\t(.+)$")
if entry_sha then
if entry_type == "blob" then
load_side(r, entry_sha, entry_name --[[@as string]])
load_side(r, nil, false, entry_sha, entry_name --[[@as string]])
else
M.open(r, entry_sha, { split = false })
end
@@ -431,24 +486,26 @@ function M.open_under_cursor()
return false
end
local commit = commit_in_buf(r, vim.api.nvim_get_current_buf())
if line:match("^diff %-%-git ") then
open_section(r, section)
open_section(r, commit, section)
return true
end
if line:match("^%-%-%- ") then
load_side(r, section.blob_a, section.path_a)
load_side(r, commit, true, section.blob_a, section.path_a)
return true
end
if line:match("^%+%+%+ ") then
load_side(r, section.blob_b, section.path_b)
load_side(r, commit, false, section.blob_b, section.path_b)
return true
end
local prefix = line:sub(1, 1)
if prefix == "+" then
load_side(r, section.blob_b, section.path_b)
load_side(r, commit, false, section.blob_b, section.path_b)
return true
elseif prefix == "-" then
load_side(r, section.blob_a, section.path_a)
load_side(r, commit, true, section.blob_a, section.path_a)
return true
end
return false
+84
View File
@@ -467,6 +467,90 @@ t.test("line popup works in a git:// object buffer", function()
)
end)
t.test(
"line popup works in a git://<blob-sha> 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 tree = h.git(dir, "rev-parse", "HEAD^{tree}").stdout
local blob = h.git(dir, "rev-parse", "HEAD:a.txt").stdout
local r = assert(require("git.core.repo").resolve(dir))
object.open(r, tree, { split = false })
local tree_buf = vim.api.nvim_get_current_buf()
local found ---@type integer?
for i, l in ipairs(vim.api.nvim_buf_get_lines(tree_buf, 0, -1, false)) do
if l:match("^%d+ blob %x+\ta%.txt$") then
found = i
break
end
end
local lnum = assert(found, "expected an a.txt tree entry")
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://<commit-sha> 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)
+50 -10
View File
@@ -202,18 +202,58 @@ t.test("open_under_cursor on a 'parent <sha>' line opens the parent", function()
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. parent_sha)
end)
t.test("open_under_cursor on a '+++ b/<path>' line loads the blob", function()
local dir = h.make_repo({ ["a.txt"] = "first\n" })
local r = assert(require("git.core.repo").resolve(dir))
local blob_sha = h.git(dir, "rev-parse", "HEAD:a.txt").stdout
t.test(
"open_under_cursor on a '+++ b/<path>' line opens the file at the commit",
function()
local dir = h.make_repo({ ["a.txt"] = "first\n" })
local r = assert(require("git.core.repo").resolve(dir))
local commit_sha = h.git(dir, "rev-parse", "HEAD").stdout
object.open(r, "HEAD", { split = false })
local lnum = assert(find_line(0, "+++ b/a.txt"), "expected a +++ line")
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
object.open(r, "HEAD", { split = false })
local lnum = assert(find_line(0, "+++ b/a.txt"), "expected a +++ line")
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
t.truthy(object.open_under_cursor())
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. blob_sha)
end)
t.truthy(object.open_under_cursor())
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. commit_sha .. ":a.txt")
end
)
t.test(
"open_under_cursor on a '--- a/<path>' line opens the parent at that path",
function()
local dir = h.make_repo({ ["a.txt"] = "first\n" })
t.write(dir, "a.txt", "second\n")
h.git(dir, "add", "a.txt")
h.git(dir, "commit", "-q", "-m", "second")
local r = assert(require("git.core.repo").resolve(dir))
local parent_sha = h.git(dir, "rev-parse", "HEAD~").stdout
object.open(r, "HEAD", { split = false })
local lnum = assert(find_line(0, "--- a/a.txt"), "expected a --- line")
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
t.truthy(object.open_under_cursor())
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. parent_sha .. ":a.txt")
end
)
t.test(
"open_under_cursor on a tree-entry blob still uses the bare blob URI",
function()
local dir = h.make_repo({ ["a.txt"] = "first\n" })
local r = assert(require("git.core.repo").resolve(dir))
local tree_sha = h.git(dir, "rev-parse", "HEAD^{tree}").stdout
local blob_sha = h.git(dir, "rev-parse", "HEAD:a.txt").stdout
object.open(r, tree_sha, { split = false })
local lnum =
assert(find_line(0, "100644 blob "), "expected a tree entry line")
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
t.truthy(object.open_under_cursor())
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. blob_sha)
end
)
t.test("open_under_cursor returns false on a non-dispatchable line", function()
local dir = h.make_repo({ a = "first\n" })