Skip to content

Commit

Permalink
fix(stage): staging of files with no nl at eof
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
lewis6991 committed Apr 10, 2024
1 parent 1a50b94 commit c097cb2
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 0 deletions.
6 changes: 6 additions & 0 deletions lua/gitsigns/diff_int.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions lua/gitsigns/hunks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions test/actions_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit c097cb2

Please sign in to comment.