Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lua/diffview/scene/file_entry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,13 @@ function FileEntry:validate_stage_buffers(stat)
local is_modified = vim.bo[f.bufnr].modified

if f.blob_hash then
-- `new_hash` is nil when the path no longer has a stage-0 blob at
-- all (e.g. a newly added file that was unstaged, leaving it
-- untracked). That is a content change like any other: the buffer
-- still shows the old staged blob and must be invalidated.
local new_hash = self.adapter:file_blob_hash(f.path)

if new_hash and new_hash ~= f.blob_hash then
if new_hash ~= f.blob_hash then
if is_modified then
utils.warn(
(
Expand Down
19 changes: 19 additions & 0 deletions lua/diffview/scene/views/diff/diff_view.lua
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,25 @@ local update_files_impl = debounce.debounce_trailing(
)
end

-- A status change can flip a side's nulled state even when the
-- revs are identical (e.g. unstaging a newly added file: the
-- working entry goes "M" -> "?" and its a-side no longer has a
-- stage-0 blob). The NOOP path only copies `status` onto the old
-- entry; the Files' `nulled` flags are fixed at construction, so
-- the old a-side would keep showing — and try to reload — a blob
-- that no longer exists. Replace the entry instead.
if not replace_noop then
for _, sym in ipairs({ "a", "b", "c", "d" }) do
local of = old_file.layout:get_file_for(sym)
local nf = new_file.layout:get_file_for(sym)

if (of and of.nulled or false) ~= (nf and nf.nulled or false) then
replace_noop = true
break
end
end
end

if replace_noop then
if self.panel.cur_file == old_file then
self.panel:set_cur_file(new_file)
Expand Down
8 changes: 6 additions & 2 deletions lua/diffview/vcs/adapters/git/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1843,17 +1843,21 @@ end
---@param rev_arg string?
---@return string?
function GitAdapter:file_blob_hash(path, rev_arg)
-- Empty output with exit code 0 is a real outcome, not a failure: the path
-- has no blob at that rev (e.g. a newly added file that was unstaged no
-- longer exists in the index at all). `fail_on_empty` would burn retries
-- and spam the log for it; instead let the empty result through and map it
-- to nil so callers can distinguish "no blob" from a blob hash.
local out, code = self:exec_sync({
"rev-parse",
"--revs-only",
fmt("%s:%s", rev_arg or "", path),
}, {
cwd = self.ctx.toplevel,
retry = 2,
fail_on_empty = true,
})

if code ~= 0 then
if code ~= 0 or not out[1] or vim.trim(out[1]) == "" then
return
end

Expand Down
Loading