175 lines
5.3 KiB
Lua
175 lines
5.3 KiB
Lua
local log = require("log")
|
|
local repo = require("git.repo")
|
|
|
|
local M = {}
|
|
|
|
---@param buf integer
|
|
---@param worktree string
|
|
---@param path string
|
|
local function attach_index_writer(buf, worktree, path)
|
|
vim.api.nvim_create_autocmd("BufWriteCmd", {
|
|
buffer = buf,
|
|
callback = function()
|
|
local body = table.concat(
|
|
vim.api.nvim_buf_get_lines(buf, 0, -1, false),
|
|
"\n"
|
|
) .. "\n"
|
|
local hash = vim.system(
|
|
{ "git", "hash-object", "-w", "--stdin" },
|
|
{ cwd = worktree, stdin = body, text = true }
|
|
):wait()
|
|
if hash.code ~= 0 then
|
|
log.error("git hash-object failed: %s", hash.stderr or "")
|
|
return
|
|
end
|
|
local sha = vim.trim(hash.stdout or "")
|
|
local mode = vim.b[buf].git_index_mode
|
|
if not mode then
|
|
mode = "100644"
|
|
local ls = vim.system(
|
|
{ "git", "ls-files", "-s", "--", path },
|
|
{ cwd = worktree, text = true }
|
|
):wait()
|
|
if ls.code == 0 and ls.stdout then
|
|
local m = ls.stdout:match("^(%d+)")
|
|
if m then
|
|
mode = m
|
|
end
|
|
end
|
|
vim.b[buf].git_index_mode = mode
|
|
end
|
|
local upd = vim.system({
|
|
"git",
|
|
"update-index",
|
|
"--cacheinfo",
|
|
mode .. "," .. sha .. "," .. path,
|
|
}, { cwd = worktree, text = true }):wait()
|
|
if upd.code ~= 0 then
|
|
log.error("git update-index failed: %s", upd.stderr or "")
|
|
return
|
|
end
|
|
vim.bo[buf].modified = false
|
|
end,
|
|
})
|
|
end
|
|
|
|
---@param worktree string
|
|
---@param revspec string anything `git show` accepts (e.g. `HEAD:foo`, `:foo`, blob SHA)
|
|
---@return string[]
|
|
local function read_show(worktree, revspec)
|
|
local result = vim.system(
|
|
{ "git", "show", revspec },
|
|
{ cwd = worktree, text = true }
|
|
)
|
|
:wait()
|
|
local content = result.code == 0 and (result.stdout or "") or ""
|
|
local lines = vim.split(content, "\n", { plain = true, trimempty = false })
|
|
if #lines > 0 and lines[#lines] == "" then
|
|
table.remove(lines)
|
|
end
|
|
return lines
|
|
end
|
|
|
|
---@param worktree string
|
|
---@param ref string '' for index, 'HEAD' or a sha for committed refs
|
|
---@param path string
|
|
---@param is_index boolean? true to hook :w to update the git index
|
|
---@return integer
|
|
function M.git_show_buf(worktree, ref, path, is_index)
|
|
local lines = read_show(worktree, ref .. ":" .. path)
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
|
vim.bo[buf].buftype = is_index and "acwrite" or "nofile"
|
|
vim.bo[buf].bufhidden = "wipe"
|
|
vim.bo[buf].swapfile = false
|
|
if is_index then
|
|
attach_index_writer(buf, worktree, path)
|
|
else
|
|
vim.bo[buf].modifiable = false
|
|
end
|
|
vim.bo[buf].modified = false
|
|
return buf
|
|
end
|
|
|
|
---@param worktree string
|
|
---@param blob string the blob SHA (full or abbreviated)
|
|
---@return integer
|
|
function M.git_show_blob(worktree, blob)
|
|
local lines = read_show(worktree, blob)
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
|
vim.bo[buf].buftype = "nofile"
|
|
vim.bo[buf].bufhidden = "wipe"
|
|
vim.bo[buf].swapfile = false
|
|
vim.bo[buf].modifiable = false
|
|
vim.bo[buf].modified = false
|
|
return buf
|
|
end
|
|
|
|
---@return integer
|
|
function M.empty_buf()
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.bo[buf].buftype = "nofile"
|
|
vim.bo[buf].bufhidden = "wipe"
|
|
vim.bo[buf].swapfile = false
|
|
vim.bo[buf].modifiable = false
|
|
vim.bo[buf].modified = false
|
|
return buf
|
|
end
|
|
|
|
---@param abs_path string
|
|
---@return integer
|
|
function M.load_file_buf(abs_path)
|
|
local buf = vim.fn.bufadd(abs_path)
|
|
vim.fn.bufload(buf)
|
|
return buf
|
|
end
|
|
|
|
---@param buf integer
|
|
---@param name string
|
|
local function set_buf_name_and_filetype(buf, name)
|
|
pcall(vim.api.nvim_buf_set_name, buf, name)
|
|
local ft = vim.filetype.match({ buf = buf })
|
|
if ft then
|
|
vim.bo[buf].filetype = ft
|
|
end
|
|
end
|
|
|
|
---@class ow.Git.SplitOpts
|
|
---@field ref string '' for index, 'HEAD' for HEAD
|
|
---@field vertical boolean
|
|
|
|
---@param opts ow.Git.SplitOpts
|
|
function M.split(opts)
|
|
local cur_buf = vim.api.nvim_get_current_buf()
|
|
local cur_path = vim.api.nvim_buf_get_name(cur_buf)
|
|
if cur_path == "" then
|
|
log.warning("no file in current buffer")
|
|
return
|
|
end
|
|
local _, worktree = repo.resolve(cur_path)
|
|
if not worktree then
|
|
log.warning("not in a git repository")
|
|
return
|
|
end
|
|
local rel = vim.fs.relpath(worktree, cur_path)
|
|
if not rel then
|
|
log.warning("file is outside the worktree")
|
|
return
|
|
end
|
|
|
|
local is_index = opts.ref == ""
|
|
local other = M.git_show_buf(worktree, opts.ref, rel, is_index)
|
|
local label = is_index and "index" or opts.ref
|
|
set_buf_name_and_filetype(other, "git://" .. label .. "/" .. rel)
|
|
|
|
local split_cmd = opts.vertical and "leftabove vertical sbuffer "
|
|
or "leftabove sbuffer "
|
|
vim.cmd(split_cmd .. other)
|
|
vim.cmd("diffthis")
|
|
vim.cmd("wincmd p")
|
|
vim.cmd("diffthis")
|
|
end
|
|
|
|
return M
|