local M = {} ---@param filepath string ---@return string? function M.get_git_dir(filepath) local dir = vim.fn.fnamemodify(filepath, ':h') local result = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--git-dir' }) if vim.v.shell_error ~= 0 then return nil end local git_dir = result[1] if not git_dir then return nil end if vim.startswith(git_dir, '/') then return git_dir end return vim.fn.fnamemodify(dir .. '/' .. git_dir, ':p'):gsub('/+$', '') end ---@param filepath string ---@return string? function M.get_repo_root(filepath) local dir = vim.fn.fnamemodify(filepath, ':h') local result = vim.fn.systemlist({ 'git', '-C', dir, 'rev-parse', '--show-toplevel' }) if vim.v.shell_error ~= 0 then return nil end return result[1] end ---@param revision string ---@param filepath string ---@return string[]?, string? function M.get_file_content(revision, filepath) local repo_root = M.get_repo_root(filepath) if not repo_root then return nil, 'not in a git repository' end local rel_path = vim.fn.fnamemodify(filepath, ':.') if vim.startswith(filepath, repo_root) then rel_path = filepath:sub(#repo_root + 2) end local result = vim.fn.systemlist({ 'git', '-C', repo_root, 'show', revision .. ':' .. rel_path }) if vim.v.shell_error ~= 0 then return nil, 'failed to get file at revision: ' .. revision end return result, nil end ---@param filepath string ---@return string? function M.get_relative_path(filepath) local repo_root = M.get_repo_root(filepath) if not repo_root then return nil end if vim.startswith(filepath, repo_root) then return filepath:sub(#repo_root + 2) end return vim.fn.fnamemodify(filepath, ':.') end return M