refactor(git): URI scheme is now a git revspec, loaded via cat-file -p
This commit is contained in:
+2
-6
@@ -74,7 +74,7 @@ end
|
|||||||
---@param path string
|
---@param path string
|
||||||
local function show_file_in_split(worktree, user_ref, path)
|
local function show_file_in_split(worktree, user_ref, path)
|
||||||
local label = repo.rev_parse(worktree, user_ref, true) or user_ref
|
local label = repo.rev_parse(worktree, user_ref, true) or user_ref
|
||||||
local uri = "git://" .. label .. "//" .. path
|
local uri = "git://" .. label .. ":" .. path
|
||||||
local buf = vim.fn.bufadd(uri)
|
local buf = vim.fn.bufadd(uri)
|
||||||
vim.b[buf].git_worktree = worktree
|
vim.b[buf].git_worktree = worktree
|
||||||
vim.cmd("split " .. vim.fn.fnameescape(uri))
|
vim.cmd("split " .. vim.fn.fnameescape(uri))
|
||||||
@@ -119,11 +119,7 @@ local function run_in_split(worktree, args, conf)
|
|||||||
vim.b[buf].git_parent_ref =
|
vim.b[buf].git_parent_ref =
|
||||||
repo.rev_parse(worktree, user_ref .. "^", true)
|
repo.rev_parse(worktree, user_ref .. "^", true)
|
||||||
end
|
end
|
||||||
pcall(
|
pcall(vim.api.nvim_buf_set_name, buf, "git://" .. (sha or user_ref))
|
||||||
vim.api.nvim_buf_set_name,
|
|
||||||
buf,
|
|
||||||
"git://" .. (sha or user_ref) .. "//"
|
|
||||||
)
|
|
||||||
vim.bo[buf].filetype = conf.ft
|
vim.bo[buf].filetype = conf.ft
|
||||||
else
|
else
|
||||||
vim.bo[buf].filetype = conf.ft
|
vim.bo[buf].filetype = conf.ft
|
||||||
|
|||||||
+26
-27
@@ -57,32 +57,31 @@ local function attach_index_writer(buf, worktree, path)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Return a buffer holding the content at `<ref>:<path>`. `ref` is
|
---Return a buffer holding the content addressed by a git revspec. The
|
||||||
---"index", "HEAD", or a commit revspec.
|
---URI is `git://<revspec>` and BufReadCmd loads via `git cat-file -p`.
|
||||||
---@param worktree string
|
---@param worktree string
|
||||||
---@param ref string
|
---@param revspec string any revspec git understands (e.g. `HEAD:foo`, `:foo`, `:1:foo`, `<sha>`, `<sha>:foo`)
|
||||||
---@param path string
|
|
||||||
---@return integer
|
---@return integer
|
||||||
function M.git_show_buf(worktree, ref, path)
|
function M.git_show_buf(worktree, revspec)
|
||||||
local name = "git://" .. ref .. "//" .. path
|
local name = "git://" .. revspec
|
||||||
local buf = vim.fn.bufadd(name)
|
local buf = vim.fn.bufadd(name)
|
||||||
vim.b[buf].git_worktree = worktree
|
vim.b[buf].git_worktree = worktree
|
||||||
vim.fn.bufload(buf)
|
vim.fn.bufload(buf)
|
||||||
return buf
|
return buf
|
||||||
end
|
end
|
||||||
|
|
||||||
---BufReadCmd handler for `git://<ref>//<path>` URIs. Worktree comes from
|
---BufReadCmd handler for `git://<revspec>` URIs. Loads content via
|
||||||
---`vim.b[buf].git_worktree` if set, else from cwd. Ref of "index" maps
|
---`git cat-file -p <revspec>`. Worktree comes from `vim.b[buf]
|
||||||
---to `git show :<path>`; "worktree" leaves the buffer empty (placeholder
|
---.git_worktree` if set, else from cwd. Index entries (revspec form
|
||||||
---for missing files); anything else is a revspec.
|
---`:<path>` for stage 0) are made writable via `attach_index_writer`,
|
||||||
|
---so `:w` updates the index. Other revspecs are read-only.
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
function M.read_uri(buf)
|
function M.read_uri(buf)
|
||||||
local name = vim.api.nvim_buf_get_name(buf)
|
local name = vim.api.nvim_buf_get_name(buf)
|
||||||
local ref, path = name:match("^git://(.-)//(.*)$")
|
local revspec = name:match("^git://(.+)$")
|
||||||
if not ref or path == "" then
|
if not revspec then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast path -nil
|
|
||||||
|
|
||||||
local worktree = vim.b[buf].git_worktree or select(2, repo.resolve_cwd())
|
local worktree = vim.b[buf].git_worktree or select(2, repo.resolve_cwd())
|
||||||
if not worktree then
|
if not worktree then
|
||||||
@@ -94,27 +93,26 @@ function M.read_uri(buf)
|
|||||||
vim.bo[buf].swapfile = false
|
vim.bo[buf].swapfile = false
|
||||||
vim.bo[buf].bufhidden = "wipe"
|
vim.bo[buf].bufhidden = "wipe"
|
||||||
|
|
||||||
if ref == "worktree" then
|
local stdout = util.exec(
|
||||||
vim.bo[buf].buftype = "nofile"
|
{ "git", "cat-file", "-p", revspec },
|
||||||
vim.bo[buf].modifiable = false
|
{ cwd = worktree }
|
||||||
vim.bo[buf].modified = false
|
)
|
||||||
vim.api.nvim_exec_autocmds("BufReadPost", { buffer = buf })
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local revspec = ref == "index" and (":" .. path) or (ref .. ":" .. path)
|
|
||||||
local stdout = util.exec({ "git", "show", revspec }, { cwd = worktree })
|
|
||||||
if stdout then
|
if stdout then
|
||||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
|
||||||
end
|
end
|
||||||
|
|
||||||
if ref == "index" then
|
-- Stage-0 index entries (`:<path>` with no further `:`) are
|
||||||
|
-- editable; `:w` rewrites the index entry via `attach_index_writer`.
|
||||||
|
-- Anything else (HEAD:, <sha>:, :1:, :2:, :3:, bare object refs)
|
||||||
|
-- is read-only.
|
||||||
|
local index_path = revspec:match("^:([^:]+)$")
|
||||||
|
if index_path then
|
||||||
vim.bo[buf].buftype = "acwrite"
|
vim.bo[buf].buftype = "acwrite"
|
||||||
-- Re-running BufReadCmd (e.g. on `:edit`) would otherwise stack
|
-- Re-running BufReadCmd (e.g. on `:edit`) would otherwise stack
|
||||||
-- another BufWriteCmd on the same buffer, so each `:w` runs
|
-- another BufWriteCmd on the same buffer, so each `:w` runs
|
||||||
-- hash-object + update-index N times.
|
-- hash-object + update-index N times.
|
||||||
if not vim.b[buf].git_index_writer then
|
if not vim.b[buf].git_index_writer then
|
||||||
attach_index_writer(buf, worktree, path)
|
attach_index_writer(buf, worktree, index_path)
|
||||||
vim.b[buf].git_index_writer = true
|
vim.b[buf].git_index_writer = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -221,8 +219,9 @@ function M.split(opts)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local label = opts.ref == "" and "index" or opts.ref
|
-- Stage 0 (index) is `:<path>`; named refs are `<ref>:<path>`.
|
||||||
local uri = "git://" .. label .. "//" .. rel
|
local revspec = opts.ref == "" and (":" .. rel) or (opts.ref .. ":" .. rel)
|
||||||
|
local uri = "git://" .. revspec
|
||||||
-- Stash the worktree on the buffer so the BufReadCmd handler doesn't
|
-- Stash the worktree on the buffer so the BufReadCmd handler doesn't
|
||||||
-- fall back to cwd resolution (wrong when cwd != worktree).
|
-- fall back to cwd resolution (wrong when cwd != worktree).
|
||||||
local buf = vim.fn.bufadd(uri)
|
local buf = vim.fn.bufadd(uri)
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ function M.setup()
|
|||||||
end
|
end
|
||||||
vim.filetype.add({
|
vim.filetype.add({
|
||||||
pattern = {
|
pattern = {
|
||||||
["git://.-//(.+)"] = function(_, bufnr, inner)
|
["git://.*:([^:]+)$"] = function(_, bufnr, inner)
|
||||||
return vim.filetype.match({ filename = inner, buf = bufnr })
|
return vim.filetype.match({ filename = inner, buf = bufnr })
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|||||||
+10
-6
@@ -87,11 +87,14 @@ end
|
|||||||
---@param ref string the commit ref the blob represents (e.g. `<sha>` or `<sha>^`)
|
---@param ref string the commit ref the blob represents (e.g. `<sha>` or `<sha>^`)
|
||||||
---@return integer
|
---@return integer
|
||||||
local function blob_buf(worktree, blob, path, ref)
|
local function blob_buf(worktree, blob, path, ref)
|
||||||
local name = "git://" .. ref .. "//" .. path
|
local revspec = ref .. ":" .. path
|
||||||
if is_zero(blob) then
|
if is_zero(blob) then
|
||||||
return diff.empty_buf({ name = name, bufhidden = "hide" })
|
return diff.empty_buf({
|
||||||
|
name = "git://" .. revspec,
|
||||||
|
bufhidden = "hide",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
return diff.git_show_buf(worktree, ref, path)
|
return diff.git_show_buf(worktree, revspec)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param worktree string
|
---@param worktree string
|
||||||
@@ -142,7 +145,7 @@ end
|
|||||||
function M.open_commit(worktree, ref, opts)
|
function M.open_commit(worktree, ref, opts)
|
||||||
local split = opts and opts.split
|
local split = opts and opts.split
|
||||||
local sha = repo.rev_parse(worktree, ref, true) or ref
|
local sha = repo.rev_parse(worktree, ref, true) or ref
|
||||||
local name = "git://" .. sha .. "//"
|
local name = "git://" .. sha
|
||||||
local existing = vim.fn.bufnr(name)
|
local existing = vim.fn.bufnr(name)
|
||||||
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
|
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
|
||||||
if split == false then
|
if split == false then
|
||||||
@@ -218,7 +221,7 @@ function M.open_object(worktree, ref, opts)
|
|||||||
|
|
||||||
local split = opts and opts.split
|
local split = opts and opts.split
|
||||||
local sha = repo.rev_parse(worktree, ref, true) or ref
|
local sha = repo.rev_parse(worktree, ref, true) or ref
|
||||||
local name = "git://" .. sha .. "//"
|
local name = "git://" .. sha
|
||||||
local existing = vim.fn.bufnr(name)
|
local existing = vim.fn.bufnr(name)
|
||||||
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
|
if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then
|
||||||
if split == false then
|
if split == false then
|
||||||
@@ -279,7 +282,8 @@ 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
|
||||||
local buf = diff.git_show_buf(ctx.worktree, ctx.ref, entry_name)
|
local buf =
|
||||||
|
diff.git_show_buf(ctx.worktree, ctx.ref .. ":" .. entry_name)
|
||||||
vim.cmd.normal({ "m'", bang = true })
|
vim.cmd.normal({ "m'", bang = true })
|
||||||
vim.api.nvim_set_current_buf(buf)
|
vim.api.nvim_set_current_buf(buf)
|
||||||
else
|
else
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ local M = {}
|
|||||||
function M.show(worktree, ref, opts)
|
function M.show(worktree, ref, opts)
|
||||||
local split = opts and opts.split
|
local split = opts and opts.split
|
||||||
local sha = repo.rev_parse(worktree, ref, true) or ref
|
local sha = repo.rev_parse(worktree, ref, true) or ref
|
||||||
local name = "git://" .. sha .. "//"
|
local name = "git://" .. sha
|
||||||
-- Commit SHAs are immutable so a previously-opened buffer is still
|
-- Commit SHAs are immutable so a previously-opened buffer is still
|
||||||
-- valid. Reuse it instead of refetching.
|
-- valid. Reuse it instead of refetching.
|
||||||
local existing = vim.fn.bufnr(name)
|
local existing = vim.fn.bufnr(name)
|
||||||
|
|||||||
+15
-15
@@ -484,15 +484,19 @@ end
|
|||||||
---@return ow.Git.DiffSide
|
---@return ow.Git.DiffSide
|
||||||
local function head_pane(worktree, path)
|
local function head_pane(worktree, path)
|
||||||
return {
|
return {
|
||||||
buf = diff.git_show_buf(worktree, "HEAD", path),
|
buf = diff.git_show_buf(worktree, "HEAD:" .. path),
|
||||||
name = "git://HEAD//" .. path,
|
name = "git://HEAD:" .. path,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param worktree string
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return ow.Git.DiffSide
|
---@return ow.Git.DiffSide
|
||||||
local function head_empty_pane(path)
|
local function absent_pane(worktree, path)
|
||||||
return { buf = diff.empty_buf(), name = "git://HEAD//" .. path }
|
return {
|
||||||
|
buf = diff.empty_buf(),
|
||||||
|
name = "[absent] " .. vim.fs.joinpath(worktree, path),
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param worktree string
|
---@param worktree string
|
||||||
@@ -505,12 +509,6 @@ local function worktree_pane(worktree, path)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
|
||||||
---@return ow.Git.DiffSide
|
|
||||||
local function worktree_empty_pane(path)
|
|
||||||
return { buf = diff.empty_buf(), name = "git://worktree//" .. path }
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param s ow.Git.SidebarState
|
---@param s ow.Git.SidebarState
|
||||||
---@param entry ow.Git.FileEntry
|
---@param entry ow.Git.FileEntry
|
||||||
---@return ow.Git.DiffSide
|
---@return ow.Git.DiffSide
|
||||||
@@ -519,10 +517,12 @@ local function index_pane(s, entry)
|
|||||||
entry.section == "Untracked"
|
entry.section == "Untracked"
|
||||||
or (entry.section == "Staged" and entry.x == "D")
|
or (entry.section == "Staged" and entry.x == "D")
|
||||||
)
|
)
|
||||||
|
if not in_index then
|
||||||
|
return absent_pane(s.worktree, entry.path)
|
||||||
|
end
|
||||||
return {
|
return {
|
||||||
buf = in_index and diff.git_show_buf(s.worktree, "index", entry.path)
|
buf = diff.git_show_buf(s.worktree, ":" .. entry.path),
|
||||||
or diff.empty_buf(),
|
name = "git://:" .. entry.path,
|
||||||
name = "git://index//" .. entry.path,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -534,7 +534,7 @@ local function other_pane(s, entry)
|
|||||||
local worktree = s.worktree
|
local worktree = s.worktree
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
if entry.x == "A" then
|
if entry.x == "A" then
|
||||||
return head_empty_pane(p)
|
return absent_pane(worktree, p)
|
||||||
end
|
end
|
||||||
if entry.x == "D" then
|
if entry.x == "D" then
|
||||||
return head_pane(worktree, p)
|
return head_pane(worktree, p)
|
||||||
@@ -544,7 +544,7 @@ local function other_pane(s, entry)
|
|||||||
end
|
end
|
||||||
if entry.section == "Unstaged" then
|
if entry.section == "Unstaged" then
|
||||||
if entry.y == "D" then
|
if entry.y == "D" then
|
||||||
return worktree_empty_pane(p)
|
return absent_pane(worktree, p)
|
||||||
end
|
end
|
||||||
return worktree_pane(worktree, p)
|
return worktree_pane(worktree, p)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user