gitsigns.nvim icon indicating copy to clipboard operation
gitsigns.nvim copied to clipboard

Allow blame to work in revision buffers

Open emmanueltouzery opened this issue 2 years ago • 14 comments

Is your feature request related to a problem? Please describe. I'm developing a neovim git-related plugin: https://github.com/emmanueltouzery/agitator.nvim/ In this plugin, I offer among others the possibility to view (read-only) the contents of a file as they are in another branch, without changing branch (this file may or may not exist in the branch the user is currently working in). So for instance you're working on the develop branch, and you'd like to see the contents of that (or any other) file as it is in the master branch, without changing branches. To do that, the plugin gets the contents of the file from another branch using git commands, then dumps these contents in a new buffer, then makes that buffer read-only.

I was interested in using gitsigns read-only features on that buffer. So, viewing the line blame info on the other branch. Although this is not the checked-out branch right now, and although this file is not saved to disk.

Describe the solution you'd like I'd like a mechanism to report to gitsigns that this buffer represents file X from project P as of commit C. Then gitsigns would trust that information and support gitsigns features (for instance per-line blame and so on). The mechanism could be the buffer name, a gitsigns function to call or any other mechanism.

Describe alternatives you've considered I noticed that gitsigns supports fugitive integration. So I've changed my plugin to name buffers with the fugitive:// pattern, and then called gitsigns.attach(vim.fn.bufnr('%')). This worked, gitsigns successfully parsed project, file, and commit id from the fugitive meta-data, and took them into account. However, gitsigns didn't use that information in the way I expected: gitsigns shows not the blame as of after that commit, but as of before that commit, as if the changes for the commit were being typed right now. So the changes introduced with that commit are marked as "Not committed yet".

I think I can understand this by looking at the gitsigns source, function CacheEntry.get_compare_rev:

   if self.commit then
      return string.format('%s^', self.commit)
   end

so it seems to take the parent commit of the one I specified as a base for the comparison, while I'd like to have that exact commit as a base (so just show git blame info as of that commit).

I also briefly looked at gitsigns:// URLs, but they didn't work immediately for me (unlike fugitive urls), they seemed too internal to be relied upon, and after discussion on the gitter, it was thought they'd probably not work for that intent without changes in gitsigns.

Additional context My library is not exactly super popular, so I'm not sure a new complex integration is worth it. But if it could be done in a "cheap" way that allows other future integrations, it may be worth it.

emmanueltouzery avatar Jul 17 '22 18:07 emmanueltouzery

A possible mechanism could also be a buffer-scoped variable, possibly guarded by a function so that the mechanism could be changed in the future.

emmanueltouzery avatar Jul 17 '22 20:07 emmanueltouzery

Have you tried running Gitsigns change_base <rev> after opening the buffer? It looks like the main issue here is the default base.

lewis6991 avatar Jul 17 '22 20:07 lewis6991

hmm I'm not sure what the whole procedure would be then.

  1. create a blank buffer
  2. fill it in with the contents of the file on that branch
  3. make it read-only
  4. run Gitsigns attach
  5. run Gitsigns change_base <rev>

that's presumably not enough, because I would still need to tell gitsigns about the filename+project on disk? Possibly the project on disk could be found through the working directory or the filename or the combination of both. But the problem is that typically the user would have the plain file opened in nvim on the branch (s)he's working on (typically you'd use that feature to have the same file from two branches side-by-side). So I can't give the real filename, because vim complains with E95: Buffer with this name already exists.

I've tried the change_base idea on top of the fugitive uri hack, but that didn't seem to help either (but I don't think you meant that anyway).

emmanueltouzery avatar Jul 17 '22 20:07 emmanueltouzery

because I would still need to tell gitsigns about the filename+project on disk?

Isn't that what the URI is for?

I've tried the change_base idea on top of the fugitive uri hack, but that didn't seem to help either (but I don't think you meant that anyway).

That is what I meant.

lewis6991 avatar Jul 17 '22 20:07 lewis6991

Right so that doesn't work. It does update the marks on the left of the buffer: the changes from that commit are not anymore shown as "updated lines". However if I ask for a blame for that lines I still see "Not Committed Yet". Maybe there's a cache for the per-line blame that's not cleared by the change_base?

emmanueltouzery avatar Jul 17 '22 20:07 emmanueltouzery

Maybe there's a cache for the per-line blame that's not cleared by the change_base?

There is a cache! And I'm not sure if it is invalidted on change_base(). My guess is that it isn't, since most of the time the blames wouldn't change.

lewis6991 avatar Jul 17 '22 20:07 lewis6991

Btw, Gitisigns currently has a show action which is intended to be able to show any file for any revision without running a diff. Currently it can only show any revision of the current file.

Maybe you can help improve that to work for any file?

lewis6991 avatar Jul 17 '22 21:07 lewis6991

Maybe I don't understand you correctly regarding the show action. But my main idea would be to have the normal gitsigns actions work without the user needing to call them any differently. It seems you're suggesting to have the user use different actions in that case?

emmanueltouzery avatar Jul 17 '22 21:07 emmanueltouzery

my bad, I understood you now. It would be a one-time call by my library to tell you which file and revision :+1:

emmanueltouzery avatar Jul 17 '22 21:07 emmanueltouzery

The show action just opens a read-only buffer of a file at a specific revision. All other actions should work with that buffer.

lewis6991 avatar Jul 17 '22 21:07 lewis6991

Right so I just tried the show feature and... it works exactly as the fugitive uri workaround that I have now! Meaning when I ask to view commit X, it shows me X^. The lines of code which were added by commit X are marked as "Not Committed Yet" in the git blame. And same behavior afterwards if I run change_base, only the sidebar is affected (btw, I don't think that's the blame cache at play there, I commented it to no effect).

emmanueltouzery avatar Jul 17 '22 21:07 emmanueltouzery

interestingly, i was using a slightly older version of gitsigns, 27aeb2e715c32cbb99aa0b326b31739464b61644. With that version I get the X^ behavior. I now tried the latest and with that version I don't get anything on the blame line when using show, while I have the same X^ behavior using the fugitive trick :(

emmanueltouzery avatar Jul 17 '22 21:07 emmanueltouzery

I just tried show, and it turns out that it doesn't run gitsigns attach (I thought it did) as buftype is set, so I don't know how you are seeing anything in blame.

EDIT: You commented you are using the newest version now, so makes sense. This must be a regression.

lewis6991 avatar Jul 17 '22 21:07 lewis6991

I think the issue is that blame doesn't properly work on revisions of files. The run_blame function in git.tl doesn't take in the revision to blame against.

lewis6991 avatar Jul 17 '22 21:07 lewis6991