feat: add :Gdiff command for unified diff against git revision

Compares current buffer against any git revision (default HEAD), opens result
in vsplit with full diffs.nvim syntax highlighting.
This commit is contained in:
Barrett Ruth 2026-02-04 18:14:18 -05:00
parent 2ce76e7683
commit bf2c91f79f
5 changed files with 271 additions and 0 deletions

66
lua/diffs/git.lua Normal file
View file

@ -0,0 +1,66 @@
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