feat(git): navigate any git object from cat-file headers
This commit is contained in:
+67
-6
@@ -193,6 +193,63 @@ function M.open_commit(worktree, ref, opts)
|
|||||||
vim.bo[buf].filetype = "git"
|
vim.bo[buf].filetype = "git"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Open any git object in a buffer. Commits get the full
|
||||||
|
---`M.open_commit` view (cat-file + diff-tree). Trees, blobs, and tags
|
||||||
|
---dump `git cat-file -p` output as-is. The object type is detected via
|
||||||
|
---`git cat-file -t`.
|
||||||
|
---@param worktree string
|
||||||
|
---@param ref string
|
||||||
|
---@param opts ow.Git.OpenCommitOpts?
|
||||||
|
function M.open_object(worktree, ref, opts)
|
||||||
|
local type_out = util.exec(
|
||||||
|
{ "git", "cat-file", "-t", ref },
|
||||||
|
{ cwd = worktree, silent = true }
|
||||||
|
)
|
||||||
|
local obj_type = type_out and vim.trim(type_out) or ""
|
||||||
|
if obj_type == "" then
|
||||||
|
util.warning("not a git object: %s", ref)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj_type == "commit" then
|
||||||
|
M.open_commit(worktree, ref, opts)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local split = opts and opts.split
|
||||||
|
local sha = repo.rev_parse(worktree, ref, true) or ref
|
||||||
|
local name = "git://" .. sha .. "//"
|
||||||
|
local existing = vim.fn.bufnr(name)
|
||||||
|
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
|
||||||
|
if split == false then
|
||||||
|
vim.cmd.normal({ "m'", bang = true })
|
||||||
|
vim.api.nvim_set_current_buf(existing)
|
||||||
|
else
|
||||||
|
vim.api.nvim_open_win(existing, true, {
|
||||||
|
split = split or (vim.o.splitbelow and "below" or "above"),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local stdout = util.exec(
|
||||||
|
{ "git", "cat-file", "-p", ref },
|
||||||
|
{ cwd = worktree }
|
||||||
|
)
|
||||||
|
if not stdout then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local buf, _ = git.new_scratch({ name = name, split = split })
|
||||||
|
vim.b[buf].git_worktree = worktree
|
||||||
|
vim.b[buf].git_ref = sha
|
||||||
|
vim.bo[buf].modifiable = true
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
|
||||||
|
vim.bo[buf].modifiable = false
|
||||||
|
vim.bo[buf].modified = false
|
||||||
|
vim.bo[buf].filetype = "git"
|
||||||
|
end
|
||||||
|
|
||||||
---@return boolean dispatched true if the cursor was on an actionable line
|
---@return boolean dispatched true if the cursor was on an actionable line
|
||||||
function M.open_under_cursor()
|
function M.open_under_cursor()
|
||||||
local ctx = context()
|
local ctx = context()
|
||||||
@@ -202,12 +259,16 @@ function M.open_under_cursor()
|
|||||||
|
|
||||||
local line = vim.api.nvim_get_current_line()
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
|
||||||
-- Cat-file header navigation. `parent <sha>` opens the referenced
|
-- Cat-file header navigation: `parent <sha>` (commit), `tree <sha>`
|
||||||
-- commit. (`git show` doesn't emit a `parent` line, so this only
|
-- (commit / tag), `object <sha>` (tag's referent) all reference
|
||||||
-- fires inside `M.open_commit` buffers.)
|
-- another git object. Tree-entry lines `<mode> <type> <sha>\t<name>`
|
||||||
local parent_sha = line:match("^parent (%x+)$")
|
-- navigate to a child blob or subtree.
|
||||||
if parent_sha then
|
local sha = line:match("^parent (%x+)$")
|
||||||
M.open_commit(ctx.worktree, parent_sha, { split = false })
|
or line:match("^tree (%x+)$")
|
||||||
|
or line:match("^object (%x+)$")
|
||||||
|
or line:match("^%d+ %w+ (%x+)\t.+$")
|
||||||
|
if sha then
|
||||||
|
M.open_object(ctx.worktree, sha, { split = false })
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user