feat(object): use <commit>:<path> URIs when entering a file from a commit dump
This commit is contained in:
+64
-11
@@ -337,10 +337,45 @@ local function side_buf(r, blob, path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@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 blob string?
|
||||||
---@param path string
|
---@param path string
|
||||||
local function load_side(r, blob, path)
|
---@return integer?
|
||||||
local buf = side_buf(r, blob, path)
|
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
|
if not buf then
|
||||||
util.error("no content for %s", path)
|
util.error("no content for %s", path)
|
||||||
return
|
return
|
||||||
@@ -350,14 +385,15 @@ local function load_side(r, blob, path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@param r ow.Git.Repo
|
||||||
|
---@param commit string?
|
||||||
---@param section ow.Git.DiffSection
|
---@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
|
if not section.blob_a or not section.blob_b then
|
||||||
util.error("no index line, cannot determine blob SHAs")
|
util.error("no index line, cannot determine blob SHAs")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local left = side_buf(r, section.blob_a, section.path_a)
|
local left = side(r, commit, true, section.blob_a, section.path_a)
|
||||||
local right = side_buf(r, section.blob_b, section.path_b)
|
local right = side(r, commit, false, section.blob_b, section.path_b)
|
||||||
if left and right then
|
if left and right then
|
||||||
vim.cmd.normal({ "m'", bang = true })
|
vim.cmd.normal({ "m'", bang = true })
|
||||||
vim.api.nvim_set_current_buf(right)
|
vim.api.nvim_set_current_buf(right)
|
||||||
@@ -377,6 +413,21 @@ local function open_section(r, section)
|
|||||||
vim.api.nvim_set_current_buf(buf)
|
vim.api.nvim_set_current_buf(buf)
|
||||||
end
|
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
|
---@class ow.Git.Object.OpenOpts
|
||||||
---@field split (false|"above"|"below"|"left"|"right")?
|
---@field split (false|"above"|"below"|"left"|"right")?
|
||||||
|
|
||||||
@@ -423,7 +474,7 @@ function M.open_under_cursor()
|
|||||||
line:match("^%d+ (%w+) (%x+)\t(.+)$")
|
line:match("^%d+ (%w+) (%x+)\t(.+)$")
|
||||||
if entry_sha then
|
if entry_sha then
|
||||||
if entry_type == "blob" 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
|
else
|
||||||
M.open(r, entry_sha, { split = false })
|
M.open(r, entry_sha, { split = false })
|
||||||
end
|
end
|
||||||
@@ -435,24 +486,26 @@ function M.open_under_cursor()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local commit = commit_in_buf(r, vim.api.nvim_get_current_buf())
|
||||||
|
|
||||||
if line:match("^diff %-%-git ") then
|
if line:match("^diff %-%-git ") then
|
||||||
open_section(r, section)
|
open_section(r, commit, section)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if line:match("^%-%-%- ") then
|
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
|
return true
|
||||||
end
|
end
|
||||||
if line:match("^%+%+%+ ") then
|
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
|
return true
|
||||||
end
|
end
|
||||||
local prefix = line:sub(1, 1)
|
local prefix = line:sub(1, 1)
|
||||||
if prefix == "+" then
|
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
|
return true
|
||||||
elseif prefix == "-" then
|
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
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -473,19 +473,20 @@ t.test(
|
|||||||
local object = require("git.object")
|
local object = require("git.object")
|
||||||
local dir = h.make_repo({ ["a.txt"] = "alpha\nbeta\ngamma\n" })
|
local dir = h.make_repo({ ["a.txt"] = "alpha\nbeta\ngamma\n" })
|
||||||
local sha = h.git(dir, "rev-parse", "HEAD").stdout
|
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 blob = h.git(dir, "rev-parse", "HEAD:a.txt").stdout
|
||||||
local r = assert(require("git.core.repo").resolve(dir))
|
local r = assert(require("git.core.repo").resolve(dir))
|
||||||
|
|
||||||
object.open(r, sha, { split = false })
|
object.open(r, tree, { split = false })
|
||||||
local commit_buf = vim.api.nvim_get_current_buf()
|
local tree_buf = vim.api.nvim_get_current_buf()
|
||||||
local found ---@type integer?
|
local found ---@type integer?
|
||||||
for i, l in ipairs(vim.api.nvim_buf_get_lines(commit_buf, 0, -1, false)) do
|
for i, l in ipairs(vim.api.nvim_buf_get_lines(tree_buf, 0, -1, false)) do
|
||||||
if l == "+++ b/a.txt" then
|
if l:match("^%d+ blob %x+\ta%.txt$") then
|
||||||
found = i
|
found = i
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local lnum = assert(found, "expected a +++ b/a.txt line")
|
local lnum = assert(found, "expected an a.txt tree entry")
|
||||||
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
|
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
|
||||||
t.truthy(object.open_under_cursor())
|
t.truthy(object.open_under_cursor())
|
||||||
local gbuf = vim.api.nvim_get_current_buf()
|
local gbuf = vim.api.nvim_get_current_buf()
|
||||||
|
|||||||
@@ -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)
|
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. parent_sha)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
t.test("open_under_cursor on a '+++ b/<path>' line loads the blob", function()
|
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 dir = h.make_repo({ ["a.txt"] = "first\n" })
|
||||||
local r = assert(require("git.core.repo").resolve(dir))
|
local r = assert(require("git.core.repo").resolve(dir))
|
||||||
local blob_sha = h.git(dir, "rev-parse", "HEAD:a.txt").stdout
|
local commit_sha = h.git(dir, "rev-parse", "HEAD").stdout
|
||||||
|
|
||||||
object.open(r, "HEAD", { split = false })
|
object.open(r, "HEAD", { split = false })
|
||||||
local lnum = assert(find_line(0, "+++ b/a.txt"), "expected a +++ line")
|
local lnum = assert(find_line(0, "+++ b/a.txt"), "expected a +++ line")
|
||||||
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
|
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://" .. 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.truthy(object.open_under_cursor())
|
||||||
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. blob_sha)
|
t.eq(vim.api.nvim_buf_get_name(0), "git://" .. blob_sha)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
|
|
||||||
t.test("open_under_cursor returns false on a non-dispatchable line", function()
|
t.test("open_under_cursor returns false on a non-dispatchable line", function()
|
||||||
local dir = h.make_repo({ a = "first\n" })
|
local dir = h.make_repo({ a = "first\n" })
|
||||||
|
|||||||
Reference in New Issue
Block a user