fix: support blame for git < 2.41

Fixes #1093
This commit is contained in:
Lewis Russell 2025-01-20 14:24:01 +00:00 committed by Lewis Russell
parent f41b934e70
commit a5b801e7b1
6 changed files with 37 additions and 24 deletions

View File

@ -87,12 +87,23 @@ function CacheEntry:run_blame(lnum, opts)
local bufnr = self.bufnr
local blame --- @type table<integer,Gitsigns.BlameInfo?>?
local lnum0 --- @type integer?
-- Always send contents if buffer represents an editable file on disk.
-- Otherwise do not sent contents buffer revision is from tree and git version
-- is below 2.41.
--
-- This avoids the error:
-- "fatal: cannot use --contents with final commit object name"
local send_contents = vim.bo[bufnr].buftype == ''
or (not self.git_obj:from_tree() and not require('gitsigns.git.version').check(2, 41))
repeat
local buftext = util.buf_lines(bufnr, true)
local contents = send_contents and buftext or nil
local tick = vim.b[bufnr].changedtick
lnum0 = #buftext > BLAME_THRESHOLD_LEN and lnum or nil
-- TODO(lewis6991): Cancel blame on changedtick
blame = self.git_obj:run_blame(buftext, lnum0, self.git_obj.revision, opts)
blame = self.git_obj:run_blame(contents, lnum0, self.git_obj.revision, opts)
async.scheduler()
if not vim.api.nvim_buf_is_valid(bufnr) then
return {}

View File

@ -99,7 +99,7 @@ local function create_revision_buf(bufnr, base)
end
-- allow editing the index revision
if not bcache.git_obj.revision then
if not base then
vim.bo[dbuf].buftype = 'acwrite'
api.nvim_create_autocmd('BufReadCmd', {

View File

@ -125,7 +125,7 @@ end
--- @param silent? boolean
--- @return Gitsigns.FileInfo
function Obj:file_info_index(file, silent)
local has_eol = check_version({ 2, 9 })
local has_eol = check_version(2, 9)
-- --others + --exclude-standard means ignored files won't return info, but
-- untracked files will. Unlike file_info_tree which won't return untracked
@ -272,13 +272,13 @@ function Obj:unstage_file()
end
--- @async
--- @param lines string[]
--- @param contents? string[]
--- @param lnum? integer
--- @param revision? string
--- @param opts? Gitsigns.BlameOpts
--- @return table<integer,Gitsigns.BlameInfo?>
function Obj:run_blame(lines, lnum, revision, opts)
return require('gitsigns.git.blame').run_blame(self, lines, lnum, revision, opts)
function Obj:run_blame(contents, lnum, revision, opts)
return require('gitsigns.git.blame').run_blame(self, contents, lnum, revision, opts)
end
--- @async

View File

@ -195,20 +195,21 @@ local function bufferred_line_reader(f)
end
--- @param obj Gitsigns.GitObj
--- @param lines string[]
--- @param contents? string[]
--- @param lnum? integer
--- @param revision? string
--- @param opts? Gitsigns.BlameOpts
--- @return table<integer, Gitsigns.BlameInfo>
function M.run_blame(obj, lines, lnum, revision, opts)
function M.run_blame(obj, contents, lnum, revision, opts)
local ret = {} --- @type table<integer,Gitsigns.BlameInfo>
if not obj.object_name or obj.repo.abbrev_head == '' then
assert(contents, 'contents must be provided for untracked files')
-- As we support attaching to untracked files we need to return something if
-- the file isn't isn't tracked in git.
-- If abbrev_head is empty, then assume the repo has no commits
local commit = not_committed(obj.file)
for i in ipairs(lines) do
for i in ipairs(contents) do
ret[i] = {
orig_lnum = 0,
final_lnum = i,
@ -219,7 +220,10 @@ function M.run_blame(obj, lines, lnum, revision, opts)
return ret
end
local args = { 'blame', '--contents', '-', '--incremental' }
local args = { 'blame', '--incremental' }
if contents then
vim.list_extend(args, { '--contents', '-' })
end
opts = opts or {}
@ -255,8 +259,10 @@ function M.run_blame(obj, lines, lnum, revision, opts)
reader(data)
end
local contents_str = contents and table.concat(contents, '\n') or nil
local _, stderr =
obj.repo:command(args, { stdin = lines, stdout = on_stdout, ignore_error = true })
obj.repo:command(args, { stdin = contents_str, stdout = on_stdout, ignore_error = true })
if stderr then
error_once('Error running git-blame: ' .. stderr)

View File

@ -213,7 +213,7 @@ end
function M.get_info(cwd, gitdir, toplevel)
-- Does git rev-parse have --absolute-git-dir, added in 2.13:
-- https://public-inbox.org/git/20170203024829.8071-16-szeder.dev@gmail.com/
local has_abs_gd = check_version({ 2, 13 })
local has_abs_gd = check_version(2, 13)
-- Wait for internal scheduler to settle before running command (#215)
async.scheduler()

View File

@ -73,28 +73,24 @@ end
--- @async
--- Usage: check_version{2,3}
--- @param version {[1]: integer, [2]:integer, [3]:integer}?
--- @param major? integer
--- @param minor? integer
--- @param patch? integer
--- @return boolean
function M.check(version)
function M.check(major, minor, patch)
if not M.version then
set_version()
end
if not M.version then
return false
end
if not version then
elseif not major or not minor then
return false
end
if M.version.major < version[1] then
elseif M.version.major < major then
return false
end
if version[2] and M.version.minor < version[2] then
elseif minor and M.version.minor < minor then
return false
end
if version[3] and M.version.patch < version[3] then
elseif patch and M.version.patch < patch then
return false
end
return true