From c097cb255096f333e14d341082a84f572b394fa2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 10 Apr 2024 15:03:53 +0100 Subject: [PATCH] fix(stage): staging of files with no nl at eof Previously when diffing two files where one did not have a newline at the end of the file, gitsigns was unable to stage such differences since this information was not captured during the diff stage. If you run `vim.diff('a', 'a\n')` you get the result: @@ -1 +1 @@ -a \ No newline at end of file +a However if you run `vim.diff('a', 'a\n', {result_type='indices'})` you get: { {1, 1, 1, 1} } And since Gitsigns tracks changes as a list of text lines, the information about a missing newline at the end of the file is not correctly tracked. The main consequence of this is that staging hunks which contain these lines would result in an error as the generated patch would not apply since it was missing "\ No newline at end of file". To fix this, the internal hunk object now tracks this end of file information and patches are now generated correctly. --- lua/gitsigns/diff_int.lua | 6 ++++++ lua/gitsigns/hunks.lua | 10 ++++++++++ test/actions_spec.lua | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/lua/gitsigns/diff_int.lua b/lua/gitsigns/diff_int.lua index f9445273c..658cc0b84 100644 --- a/lua/gitsigns/diff_int.lua +++ b/lua/gitsigns/diff_int.lua @@ -102,11 +102,17 @@ function M.run_diff(fa, fb, linematch) for i = rs, rs + rc - 1 do hunk.removed.lines[#hunk.removed.lines + 1] = fa[i] or '' end + if rs + rc >= #fa and fa[#fa] ~= '' then + hunk.removed.no_nl_at_eof = true + end end if ac > 0 then for i = as, as + ac - 1 do hunk.added.lines[#hunk.added.lines + 1] = fb[i] or '' end + if as + ac >= #fb and fb[#fb] ~= '' then + hunk.added.no_nl_at_eof = true + end end hunks[#hunks + 1] = hunk end diff --git a/lua/gitsigns/hunks.lua b/lua/gitsigns/hunks.lua index 3b79dccaa..32938730e 100644 --- a/lua/gitsigns/hunks.lua +++ b/lua/gitsigns/hunks.lua @@ -11,6 +11,7 @@ local min, max = math.min, math.max --- @field start integer --- @field count integer --- @field lines string[] +--- @field no_nl_at_eof? true --- @class (exact) Gitsigns.Hunk.Hunk --- @field type Gitsigns.Hunk.Type @@ -265,10 +266,19 @@ function M.create_patch(relpath, hunks, mode_bits, invert) for _, l in ipairs(pre_lines) do results[#results + 1] = '-' .. l end + + if process_hunk.removed.no_nl_at_eof then + results[#results + 1] = '\\ No newline at end of file' + end + for _, l in ipairs(now_lines) do results[#results + 1] = '+' .. l end + if process_hunk.added.no_nl_at_eof then + results[#results + 1] = '\\ No newline at end of file' + end + process_hunk.removed.start = start + offset offset = offset + (now_count - pre_count) end diff --git a/test/actions_spec.lua b/test/actions_spec.lua index 24295a018..8eab693f1 100644 --- a/test/actions_spec.lua +++ b/test/actions_spec.lua @@ -302,4 +302,23 @@ describe('actions', function() command('Gitsigns prev_hunk') check_cursor({ 4, 0 }) end) + + it('can stage hunks with no NL at EOF', function() + setup_test_repo() + local newfile = helpers.newfile + exec_lua([[vim.g.editorconfig = false]]) + system("printf 'This is a file with no nl at eof' > "..newfile) + helpers.gitm({ + { 'add', newfile }, + { 'commit', '-m', 'commit on main' }, + }) + + edit(newfile) + check({ status = { head = 'master', added = 0, changed = 0, removed = 0 } }) + feed('x') + check({ status = { head = 'master', added = 0, changed = 1, removed = 0 } }) + command('Gitsigns stage_hunk') + check({ status = { head = 'master', added = 0, changed = 0, removed = 0 } }) + end) + end)