Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gitsigns gives up on a buffer #738

Closed
j-xella opened this issue Feb 9, 2023 · 13 comments
Closed

Gitsigns gives up on a buffer #738

j-xella opened this issue Feb 9, 2023 · 13 comments
Labels
bug Something isn't working

Comments

@j-xella
Copy link

j-xella commented Feb 9, 2023

Description

Something happens to a buffer, and gitsigns is unable to work with it any more. Other buffers are ok, but not this particular one. There could be several such buffers at a time.

Closing the window or even deleting the buffer with :bdel doesn't help - after the buffer is reopened, the problems continue.

If the fix is not awailable, I would like to be able to resolve this issue without restarting neovim at least - I have so many buffers and terminal windows open. Is it possible to restart/reset gitsigns without reopening neovim?

Neovim version

NVIM v0.9.0-dev-650+g870ca1de5 Build type: Release LuaJIT 2.1.0-beta3

Operating system and version

Red Hat Enterprise Linux Server 7.9 (Maipo)

Expected behavior

Gitsigns works as usual with a buffer. Displays hints in a gutter, jumps between hunks, etc...

Actual behavior

Nothing is displayed in a gutter.

When I try jumping between diffs, I get this:

Error executing vim.schedule lua callback: ...e/pack/packer/start/gitsigns.nvim/lua/gitsigns/async.lua:64: The coroutine failed with this message: vim/shared.lua:0: Expected table, got nil
stack traceback:
        [C]: in function 'assert'
        vim/shared.lua: in function 'tbl_isempty'
        ...ack/packer/start/gitsigns.nvim/lua/gitsigns/diff_int.lua:20: in function 'run_diff0'
        ...ack/packer/start/gitsigns.nvim/lua/gitsigns/diff_int.lua:73: in function 'run_diff'
        ...pack/packer/start/gitsigns.nvim/lua/gitsigns/actions.lua:242: in function 'get_hunks'
        ...pack/packer/start/gitsigns.nvim/lua/gitsigns/actions.lua:514: in function <...pack/packer/start/gitsigns.nvim/lua/gitsigns/actions.lua:506>
stack traceback:
        [C]: in function 'error'
        ...e/pack/packer/start/gitsigns.nvim/lua/gitsigns/async.lua:64: in function 'step'
        ...e/pack/packer/start/gitsigns.nvim/lua/gitsigns/async.lua:79: in function 'nav_hunk'
        ...pack/packer/start/gitsigns.nvim/lua/gitsigns/actions.lua:584: in function 'next_hunk'
        /users/xxxxxxx/.config/nvim/lua/aj_personal/gitsigns.lua:25: in function </users/xxxxxxx/.config/nvim/lua/aj_personal/gitsigns.lua:25>

Minimal config

This is my config:


require('gitsigns').setup {$                                                                                             
  numhl                        = true,  -- Toggle with `:Gitsigns toggle_numhl`$                                         
  signcolumn                   = false, -- Toggle with `:Gitsigns toggle_signs`$                                         
  attach_to_untracked          = false,$                                                                                 
  current_line_blame           = true,  -- Toggle with `:Gitsigns toggle_current_line_blame`$                            
  current_line_blame_formatter = '<author>, <abbrev_sha> <author_time:%y-%m-%d> - <summary>',$                           
$                                                                                                                        
  on_attach = function(bufnr)$                                                                                           
    local gs = package.loaded.gitsigns$                                                                                  
$                                                                                                                        
    local function map(mode, l, r, opts)$                                                                                
      opts = opts or {}$                                                                                                 
      opts.buffer = bufnr$                                                                                               
      vim.keymap.set(mode, l, r, opts)$                                                                                  
    end$                                                                                                                 
$                                                                                                                        
    -- Navigation$                                                                                                       
    map('n', ']c', function()$                                                                                           
      if vim.wo.diff then return ']c' end$                                                                               
      vim.schedule(function() gs.next_hunk() end)$                                                                       
      return '<Ignore>'$                                                                                                 
    end, {expr=true, desc="gitsigns: jump to the next diff or hunk"})$                                                   
$                                                                                                                        
    map('n', '[c', function()$                                                                                           
      if vim.wo.diff then return '[c' end$                                                                               
      vim.schedule(function() gs.prev_hunk() end)$                                                                       
      return '<Ignore>'$                                                                                                 
    end, {expr=true, desc="gitsigns: jump to the previous diff or hunk"})$                                               
$                                                                                                                        
    -- Actions$                                                                                                          
    map({'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>') -- it gets line info that way$                             
    map({'n', 'v'}, '<leader>hr', ':Gitsigns reset_hunk<CR>') -- it gets line info that way$                             
    map('n', '<leader>hS', gs.stage_buffer,    { desc = "gitsigns: stage the whole buffer" } )$                          
    map('n', '<leader>hu', gs.undo_stage_hunk, { desc = "gitsigns: undo the hunk staging (if possible)" })$              
    map('n', '<leader>hR', gs.reset_buffer,    { desc = "gitsigns: reset the whole buffer" } )$                          
    map('n', '<leader>hp', gs.preview_hunk,    { desc = "gitsigns: preview hunk (read-only)" } )$                        
    map('n', '<leader>td', gs.toggle_deleted,  { desc = "gitsigns: toggle inline display of old hunk versions" } )$      
$                                                                                                                        
    -- Text object$                                                                                                      
    map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')$                                                             
  end$                                                                                                                   require('gitsigns').setup {$                                                                                             
  numhl                        = true,  -- Toggle with `:Gitsigns toggle_numhl`$                                         
  signcolumn                   = false, -- Toggle with `:Gitsigns toggle_signs`$                                         
  attach_to_untracked          = false,$                                                                                 
  current_line_blame           = true,  -- Toggle with `:Gitsigns toggle_current_line_blame`$                            
  current_line_blame_formatter = '<author>, <abbrev_sha> <author_time:%y-%m-%d> - <summary>',$                           
$                                                                                                                        
  on_attach = function(bufnr)$                                                                                           
    local gs = package.loaded.gitsigns$                                                                                  
$                                                                                                                        
    local function map(mode, l, r, opts)$                                                                                
      opts = opts or {}$                                                                                                 
      opts.buffer = bufnr$                                                                                               
      vim.keymap.set(mode, l, r, opts)$                                                                                  
    end$                                                                                                                 
$                                                                                                                        
    -- Navigation$                                                                                                       
    map('n', ']c', function()$                                                                                           
      if vim.wo.diff then return ']c' end$                                                                               
      vim.schedule(function() gs.next_hunk() end)$                                                                       
      return '<Ignore>'$                                                                                                 
    end, {expr=true, desc="gitsigns: jump to the next diff or hunk"})$                                                   
$                                                                                                                        
    map('n', '[c', function()$                                                                                           
      if vim.wo.diff then return '[c' end$                                                                               
      vim.schedule(function() gs.prev_hunk() end)$                                                                       
      return '<Ignore>'$                                                                                                 
    end, {expr=true, desc="gitsigns: jump to the previous diff or hunk"})$                                               
$                                                                                                                        
    -- Actions$                                                                                                          
    map({'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>') -- it gets line info that way$                             
    map({'n', 'v'}, '<leader>hr', ':Gitsigns reset_hunk<CR>') -- it gets line info that way$                             
    map('n', '<leader>hS', gs.stage_buffer,    { desc = "gitsigns: stage the whole buffer" } )$                          
    map('n', '<leader>hu', gs.undo_stage_hunk, { desc = "gitsigns: undo the hunk staging (if possible)" })$              
    map('n', '<leader>hR', gs.reset_buffer,    { desc = "gitsigns: reset the whole buffer" } )$                          
    map('n', '<leader>hp', gs.preview_hunk,    { desc = "gitsigns: preview hunk (read-only)" } )$                        
    map('n', '<leader>td', gs.toggle_deleted,  { desc = "gitsigns: toggle inline display of old hunk versions" } )$      
$                                                                                                                        
    -- Text object$                                                                                                      
    map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')$                                                             
  end$

Steps to reproduce

Not sure. This happens rarely and inconsistently.

My guess would be that background git operations could be responsible somehow. I think the problems started when I did some command-line rebasing of the code opened in neovim buffers.

Gitsigns debug messages

No response

@j-xella j-xella added the bug Something isn't working label Feb 9, 2023
@lewis6991
Copy link
Owner

Not enough information. You haven't even provided the debug messages.

@lewis6991 lewis6991 closed this as not planned Won't fix, can't repro, duplicate, stale Feb 9, 2023
@j-xella
Copy link
Author

j-xella commented Feb 9, 2023

Not enough information. You haven't even provided the debug messages.

Yes, I did not provide debug messages. This issue does not happen consistently, and I normally do not run it with debug enabled...

I understand that this issue is very difficult to reproduce. But my quesion also was if there is a workaround possible to restore the gitsigns behaviour without restarting neovim? Clearly some internal gitsigns data becomes wrong. I wonder if providing the error message should at least give a clue where it happens, and maybe something can be done to fix inside neovim session it so that the work could continue?

@lewis6991
Copy link
Owner

I would expect closing and opening the buffer again to fix the issue. Perhaps Gitsigns isn't cleaning up the state properly.

@lewis6991 lewis6991 reopened this Feb 9, 2023
@fcopa

This comment was marked as spam.

@mystilleef
Copy link

To recreate this bug detach on InsertEnter and attach on InsertLeave over the course of an editing session.

This often happens because I dynamically attach and detach Gitsigns when entering and leaving Insert mode.

In Insert mode I like to turn off colors, highlights, and other visual distractions. I enable them back on in Normal mode.

Unfortunately, restarting Nvim is the only way I've found to resolve this issue.

@j-xella
Copy link
Author

j-xella commented Oct 18, 2023

I think the problem could be in throttle_by_id function. It creates a function wrapper that ensures that the function does not get invoked multiple times in parallel for any particular buffer. One of the ways to do it is via local running[id] map, where id is the buffer number. While the function is being executed, running[id] is true, and that ensures that the function will not be called again for that buffer. Once the function is finished, running[id] is set to nil/false, and the function can be called again.

The problem is that if an exception is raised while the function was running, running[id] is never reset, and the function for that buffer can't be called any more. I believe the wrapper should use pcall or xpcall to call the function, and ensure that running[id] is reset no matter what.

A workaround is to use bwipeout on the buffer. bdelete does not delete the buffer completely. When the buffer is loaded again, it retains the buffer id and hence gitsigns still doesn't work on it. bwipeout, on the other hand, does much better purge, and when the buffer is loaded again, it gets new buffer id assigned. Hence it is possible to make gitsigns work on it without restarting nvim. The drawback is that bwipeout deletes all buffer bookmarks and other buffer-specific settings.

@lewis6991
Copy link
Owner

Good find.

The problem is that the function being wrapped is async so pcall won't necessarily work.

@j-xella
Copy link
Author

j-xella commented Oct 18, 2023

I hope it is still possible to ensure that running[id] is reset at the end...

But if not, maybe it is possible to move the running map to the unit level and expose another function that could reset running[id] - to use in cases of emergency? Still better than bwipeout.

Another solution could be to use timeouts - store the timestamp of the last invocation, and, if the function is "running" for longer than certain amount of time, assume that it is not anymore and proceed.

@lewis6991
Copy link
Owner

Knowing the issue is coming from running[id] is valuable so I'm sure something can be figured out.

It may just be the case to make sure the async framework can properly handle pcall which is on my to-do list anyway.

@j-xella
Copy link
Author

j-xella commented Oct 20, 2023

I am not sure if this is the same issue or a slightly different one, but I am noticing much more cases where gitsigns stops working on a buffer. Like before, it does not happen every time, but multiple times a day. bwipeout helps, so it could be this issue. However, there are no visible error messages, only at some point I notice that gitsigns just does not work on a buffer any more.

  • If I perform "git rebase" while files from the repository are open in nvim, there is a high chance that gitsigns will break on one of those files.
  • I use neorg for note taking, and version neorg notes in git. Neorg is similar to gitsigns in a way that it does a lot of initialization (key maps, autoindent, conceal, etc...) on BufEnter, not just on nvim start-up. gitsigns fails often on neorg buffers.

@GiorgosXou

This comment was marked as outdated.

@lewis6991
Copy link
Owner

That stacktrace is from a version of gitsigns from at least 5 months a go. Please re-run on the latest.

@lewis6991
Copy link
Owner

This should be fixed by #925

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants