Skip to content

Commit ecd602f

Browse files
committed
fix(git): make show_diff_source() work with deleted files
Related to #2069
1 parent ec39c21 commit ecd602f

File tree

2 files changed

+80
-21
lines changed

2 files changed

+80
-21
lines changed

lua/mini/git.lua

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -386,16 +386,15 @@ MiniGit.show_diff_source = function(opts)
386386
lines = H.git_cli_output(args, cwd)
387387
end
388388
if #lines == 0 and not is_worktree then
389-
return H.notify('Can not show ' .. path .. 'at commit ' .. commit, 'WARN')
389+
return H.notify('Can not show ' .. path .. ' at commit ' .. commit, 'WARN')
390390
end
391391
H.show_in_split(mods, lines, 'show', table.concat(args, ' '))
392392
end
393393

394394
local has_before_shown = false
395395
if target ~= 'after' then
396-
-- "Before" file can be absend if hunk is from newly added file
397396
if src.path_before == nil then
398-
H.notify('Could not find "before" file', 'WARN')
397+
H.notify('No "before" as file was created', 'WARN')
399398
else
400399
show(src.commit_before, src.path_before, split)
401400
vim.api.nvim_win_set_cursor(0, { src.lnum_before, 0 })
@@ -404,9 +403,13 @@ MiniGit.show_diff_source = function(opts)
404403
end
405404

406405
if target ~= 'before' then
407-
local mods_after = has_before_shown and 'belowright vertical' or split
408-
show(src.commit_after, src.path_after, mods_after)
409-
vim.api.nvim_win_set_cursor(0, { src.lnum_after, 0 })
406+
if src.path_after == nil then
407+
H.notify('No "after" as file was deleted', 'WARN')
408+
else
409+
local mods_after = has_before_shown and 'belowright vertical' or split
410+
show(src.commit_after, src.path_after, mods_after)
411+
vim.api.nvim_win_set_cursor(0, { src.lnum_after, 0 })
412+
end
410413
end
411414
end
412415

@@ -1491,7 +1494,8 @@ H.diff_pos_to_source = function()
14911494
-- Try fall back to inferring target commits from 'mini.git' buffer name
14921495
if res.commit_before == nil or res.commit_after == nil then H.diff_parse_bufname(res) end
14931496

1494-
local all_present = res.lnum_after and res.path_after and res.commit_after
1497+
local all_present = (res.lnum_before and res.path_before and res.commit_before)
1498+
or (res.lnum_after and res.path_after and res.commit_after)
14951499
local is_in_order = commit_lnum <= paths_lnum and paths_lnum <= hunk_lnum
14961500
if not (all_present and is_in_order) then return nil end
14971501

@@ -1500,25 +1504,35 @@ end
15001504

15011505
H.diff_parse_paths = function(out, lines, lnum)
15021506
-- NOTE: with `diff.mnemonicPrefix=true` source and destination prefixes can
1503-
-- be not only `a` and `b`, but other characters
1504-
local pattern_before, pattern_after = '^%-%-%- [acio]/(.*)$', '^%+%+%+ [biw]/(.*)$'
1507+
-- be not only `a`/`b`, but other characters or none (if added/deleted file)
1508+
local pattern_before, pattern_after = '^%-%-%- ([acio]?)/(.*)$', '^%+%+%+ ([biw]?)/(.*)$'
15051509

15061510
-- Allow placing cursor directly on path defining lines
15071511
local cur_line = lines[lnum]
1508-
local path_before, path_after = string.match(cur_line, pattern_before), string.match(cur_line, pattern_after)
1509-
if path_before ~= nil or path_after ~= nil then
1510-
out.path_before = path_before or string.match(lines[lnum - 1] or '', pattern_before)
1511-
out.path_after = path_after or string.match(lines[lnum + 1] or '', pattern_after)
1512+
local prefix_before, path_before = string.match(cur_line, pattern_before)
1513+
local prefix_after, path_after = string.match(cur_line, pattern_after)
1514+
if path_before ~= nil then
1515+
out.path_before = path_before
1516+
prefix_after, out.path_after = string.match(lines[lnum + 1] or '', pattern_after)
1517+
out.lnum_before, out.lnum_after = 1, 1
1518+
elseif path_after ~= nil then
1519+
prefix_before, out.path_before = string.match(lines[lnum - 1] or '', pattern_before)
1520+
out.path_after = path_after
15121521
out.lnum_before, out.lnum_after = 1, 1
15131522
else
15141523
-- Iterate lines upward to find path patterns
15151524
while out.path_after == nil and lnum > 0 do
1516-
out.path_after = string.match(lines[lnum] or '', pattern_after)
1525+
prefix_after, out.path_after = string.match(lines[lnum] or '', pattern_after)
15171526
lnum = lnum - 1
15181527
end
1519-
out.path_before = string.match(lines[lnum] or '', pattern_before)
1528+
prefix_before, out.path_before = string.match(lines[lnum] or '', pattern_before)
15201529
end
15211530

1531+
-- - Don't treat '--- /dev/null' and '+++ /dev/null' matches as paths
1532+
-- Need to check prefix to work in cases like '--- a/dev/null'
1533+
if prefix_before == '' then out.path_before = nil end
1534+
if prefix_after == '' then out.path_after = nil end
1535+
15221536
return lnum
15231537
end
15241538

tests/test_git.lua

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -781,13 +781,13 @@ T['show_diff_source()']['works when there is no "before" file'] = function()
781781
'Author: Neo McVim <[email protected]>',
782782
'Date: Sat May 4 16:24:15 2024 +0300',
783783
'',
784-
'Add file.',
784+
'Add file with relative path "dev/null".',
785785
'',
786-
'diff --git a/file b/file',
786+
'diff --git a/dev/null b/dev/null',
787787
'new file mode 100644',
788788
'index 0000000..f9264f7',
789789
'--- /dev/null',
790-
'+++ b/file',
790+
'+++ b/dev/null',
791791
'@@ -0,0 +1,2 @@',
792792
'+Hello',
793793
'+World',
@@ -803,7 +803,7 @@ T['show_diff_source()']['works when there is no "before" file'] = function()
803803

804804
validate_git_spawn_log({})
805805
clear_spawn_log()
806-
validate_notifications({ { '(mini.git) Could not find "before" file', 'WARN' } })
806+
validate_notifications({ { '(mini.git) No "before" as file was created', 'WARN' } })
807807
clear_notify_log()
808808

809809
-- Target "both" should show only "after" in a specified split
@@ -812,9 +812,54 @@ T['show_diff_source()']['works when there is no "before" file'] = function()
812812

813813
eq(child.api.nvim_tabpage_get_number(0), 2)
814814
eq(#child.api.nvim_tabpage_list_wins(0), 1)
815-
validate_minigit_name(0, 'show 5ed8432441b495fa9bd4ad2e4f635bae64e95cc2:file')
815+
validate_minigit_name(0, 'show 5ed8432441b495fa9bd4ad2e4f635bae64e95cc2:dev/null')
816+
817+
validate_notifications({ { '(mini.git) No "before" as file was created', 'WARN' } })
818+
end
819+
820+
T['show_diff_source()']['works when there is no "after" file'] = function()
821+
child.lua([[_G.stdio_queue = {
822+
{ { 'out', 'Line 1\nCurrent line 2\nLine 3' } }, -- Diff source
823+
}]])
824+
set_lines({
825+
'commit 5ed8432441b495fa9bd4ad2e4f635bae64e95cc2',
826+
'Author: Neo McVim <[email protected]>',
827+
'Date: Sat May 4 16:24:15 2024 +0300',
828+
'',
829+
'Remove file with relative path "dev/null".',
830+
'',
831+
'diff --git a/dev/null b/dev/null',
832+
'new file mode 100644',
833+
'index 0000000..f9264f7',
834+
'--- a/dev/null',
835+
'+++ /dev/null',
836+
'@@ -2 +0,0 @@',
837+
'-Hello',
838+
'-World',
839+
})
840+
841+
-- Target "after" should do nothing while showing notification
842+
local init_buf_id = get_buf()
843+
set_cursor(13, 0)
844+
845+
show_diff_source({ target = 'after' })
846+
eq(get_buf(), init_buf_id)
847+
eq(child.api.nvim_buf_get_name(0), '')
848+
849+
validate_git_spawn_log({})
850+
clear_spawn_log()
851+
validate_notifications({ { '(mini.git) No "after" as file was deleted', 'WARN' } })
852+
clear_notify_log()
853+
854+
-- Target "both" should show only "before" in a specified split
855+
set_cursor(13, 0)
856+
show_diff_source({ target = 'both' })
857+
858+
eq(child.api.nvim_tabpage_get_number(0), 2)
859+
eq(#child.api.nvim_tabpage_list_wins(0), 1)
860+
validate_minigit_name(0, 'show 5ed8432441b495fa9bd4ad2e4f635bae64e95cc2~:dev/null')
816861

817-
validate_notifications({ { '(mini.git) Could not find "before" file', 'WARN' } })
862+
validate_notifications({ { '(mini.git) No "after" as file was deleted', 'WARN' } })
818863
end
819864

820865
T['show_diff_source()']['does not depend on cursor column'] = function()

0 commit comments

Comments
 (0)