126 lines
2.9 KiB
Lua
126 lines
2.9 KiB
Lua
local util = require("git.util")
|
|
|
|
local M = {}
|
|
|
|
---@param path string
|
|
---@return string? gitdir
|
|
---@return string? worktree
|
|
function M.resolve(path)
|
|
local found = vim.fs.find(".git", { upward = true, path = path })[1]
|
|
if not found then
|
|
return nil
|
|
end
|
|
local worktree = vim.fs.dirname(found)
|
|
local stat = vim.uv.fs_stat(found)
|
|
if not stat then
|
|
return nil
|
|
end
|
|
if stat.type == "directory" then
|
|
return found, worktree
|
|
end
|
|
local f = io.open(found, "r")
|
|
if not f then
|
|
return nil
|
|
end
|
|
local content = f:read("*a")
|
|
f:close()
|
|
local gitdir = content:match("gitdir:%s*(%S+)")
|
|
if not gitdir then
|
|
util.warning(".git file at %s has no `gitdir:` line", found)
|
|
return nil
|
|
end
|
|
if not gitdir:match("^/") then
|
|
gitdir = vim.fs.joinpath(worktree, gitdir)
|
|
end
|
|
return vim.fs.normalize(gitdir), worktree
|
|
end
|
|
|
|
---@return string? gitdir
|
|
---@return string? worktree
|
|
function M.resolve_cwd()
|
|
local path = vim.api.nvim_buf_get_name(0)
|
|
if path == "" or path:match("^%a+://") then
|
|
path = vim.fn.getcwd()
|
|
end
|
|
return M.resolve(path)
|
|
end
|
|
|
|
---@param path string
|
|
---@return string?
|
|
function M.head(path)
|
|
local gitdir = M.resolve(path)
|
|
if not gitdir then
|
|
return nil
|
|
end
|
|
local f = io.open(vim.fs.joinpath(gitdir, "HEAD"), "r")
|
|
if not f then
|
|
return nil
|
|
end
|
|
local first = f:read("*l")
|
|
f:close()
|
|
if not first then
|
|
return nil
|
|
end
|
|
local branch = first:match("^ref:%s*refs/heads/(%S+)")
|
|
if branch then
|
|
return branch
|
|
end
|
|
local sha = first:match("^(%x+)")
|
|
if sha then
|
|
return sha:sub(1, 7)
|
|
end
|
|
return nil
|
|
end
|
|
|
|
---@param worktree string
|
|
---@return string[]
|
|
function M.list_refs(worktree)
|
|
local out = util.exec({
|
|
"git",
|
|
"for-each-ref",
|
|
"--format=%(refname:short)",
|
|
"refs/heads",
|
|
"refs/tags",
|
|
"refs/remotes",
|
|
}, { cwd = worktree, silent = true })
|
|
if not out then
|
|
return {}
|
|
end
|
|
local refs = util.split_lines(out)
|
|
table.insert(refs, 1, "HEAD")
|
|
return refs
|
|
end
|
|
|
|
---@param arg_lead string
|
|
---@return string[]
|
|
function M.complete_rev(arg_lead)
|
|
local _, worktree = M.resolve_cwd()
|
|
if not worktree then
|
|
return {}
|
|
end
|
|
local matches = {}
|
|
for _, ref in ipairs(M.list_refs(worktree)) do
|
|
if ref:sub(1, #arg_lead) == arg_lead then
|
|
table.insert(matches, ref)
|
|
end
|
|
end
|
|
return matches
|
|
end
|
|
|
|
---@param worktree string
|
|
---@param rev string
|
|
---@param short boolean
|
|
---@return string?
|
|
function M.rev_parse(worktree, rev, short)
|
|
local cmd = { "git", "rev-parse", "--verify", "--quiet" }
|
|
if short then
|
|
table.insert(cmd, "--short")
|
|
end
|
|
table.insert(cmd, rev)
|
|
local stdout = util.exec(cmd, { cwd = worktree, silent = true })
|
|
local trimmed = stdout and vim.trim(stdout) or ""
|
|
return trimmed ~= "" and trimmed or nil
|
|
end
|
|
|
|
return M
|