Emacs: Update git-gutter annotations when staging or unstaging changes in magit-status buffer
Asked Answered
V

3

9

I use git-gutter for visualizing changes I make to version-controlled files, and magit for staging/committing/diffing etc.

When working on a project I usually keep a magit-status window open at all times. The problem I have is that when I stage or unstage changes in the magit-status buffer and then switch back to the window showing the file whose status I just updated, the fringe annotations produced by git-gutter are not adjusted automatically. (My current workaround to trigger an update is to hit SPC Backspace followed by C-x C-s to save the file, but that's not very efficient.)

I looked at git-gutter.el, and sure enough it provides a customizable variable called git-gutter:update-hooks which is set to

(after-save-hook after-revert-hook window-configuration-change-hook)

by default. So all I really need to do is add the correct hook to this list and I should be good to go. What's the name of the hook that is run when switching windows? I've looked at various sections of the Elisp manual and haven't been able to find what I am looking for. Alternatively, does magit provide a hook that is run when staging or unstaging changes?


EDIT:

If you are reading this because you're facing a similar problem: Both of the answers I got below are working solutions! For newer versions of magit, @lunaryorn's solution is short and sweet. @Jordon Biondo's solution requires adding a bit more custom code, but comes with generalizable (!) advice for creating custom hooks and injecting them into existing functionality. So, since I can only accept one answer: Boost your SO karma by rewarding both posters with an upvote :)

Viva answered 28/4, 2014 at 15:3 Comment(3)
I briefly looked at the Magit source code and didn't see any hooks at the ends of the staging functions, or at the end of the call process functions or on the refresh buffer functions. So, one possibility would be to make your own function for magit-stage-item and place your git-gutter update function at the tail end of it. Although it might be overkill, the post-command-hook runs every time you blink or sneeze (which I guess includes blinking).Breastbeating
@Breastbeating Magit runs magit-revert-buffer-hook in any buffer of a repo after every operation.Bordure
@lunaryorn -- thank you -- my installation of Magit doesn't have that hook, so I'll take a look at the most recent version and see what it has to offer.Breastbeating
A
8

Edit: with the latest versions of magit and git-gutter, this no longer requires so much configuration, see lunaryorns answer for a more up to date and simple solution.


Original Answer:

The switching window method might be a bit overkill since you'll be refreshing more than you need to be.

Magit does not offer before/after stage/unstage hooks, however we can make our own hooks using advice!

You can define two variables for your stage and unstage hooks.

(defvar my-magit-after-stage-hooks nil
  "Hooks to be run after staging one item in magit.")

(defvar my-magit-after-unstage-hooks nil
  "Hooks to be run after unstaging one item in magit.")

There is a nice wrapper function for running hooks: run-hooks we will use function advice to run our custom hooks after magit-stage-item and magit-unstage-item

(defadvice magit-stage-item (after run-my-after-stage-hooks activate)
  "Run `my-magit-after-stage-hooks` after staging an item in magit."
  (when (called-interactively-p 'interactive)
    (run-hooks 'my-magit-after-stage-hooks)))

(defadvice magit-unstage-item (after run-my-after-unstage-hooks activate)
  "Run `my-magit-after-unstage-hooks` after unstaging an item in magit."
  (when (called-interactively-p 'interactive)
    (run-hooks 'my-magit-after-unstage-hooks)))

For our hook we can just iterate over all the buffers, and refresh git-gutter when applicable because we don't know what is staged or unstaged. So We will just refresh the git-gutter display on all visible buffers that are running git-gutter-mode. (If you'd like to do all git-gutter buffers, just remove the get-buffer-window call.)

(defun my-refresh-visible-git-gutter-buffers ()
  "Refresh git-gutter-mode on all visible git-gutter-mode buffers."
  (dolist (buff (buffer-list))
    (with-current-buffer buff
      (when (and git-gutter-mode (get-buffer-window buff))
        (git-gutter-mode t)))))

Finally, just add your hook function to your custom hook!

(add-hook 'my-magit-after-unstage-hooks
          'my-refresh-visible-git-gutter-buffers)
(add-hook 'my-magit-after-stage-hooks
          'my-refresh-visible-git-gutter-buffers)

Ideally we would know what file(s) got staged/unstaged and only refresh those buffers, if you could use around advice on a deeper magit function and get the name of the magit status buffer item you are acting on and refresh only that. But this is a good start!

auto git gitter refreshing

Accustom answered 28/4, 2014 at 19:41 Comment(1)
This is great! Thanks a lot for taking the time to answer my question so thoroughly. I evaluated the code you posted in my *scratch* buffer and it seems to be doing exactly what I want. Wish I could upvote more than once ;) Seriously though, I learned a lot from this, so thanks again!Viva
S
6

This is what I needed to do with current magit:

(add-hook 'magit-post-refresh-hook
          #'git-gutter:update-all-windows)
Strobotron answered 1/7, 2016 at 20:35 Comment(4)
The git-gutter:update-all-windows command doesn't have any effect for me unless I run it directly one of the buffers with the Git gutter. This might be due to a recent change in git-gutter, since I thought your fix was working for me in September?!Deranged
This is still working fine for me with all packages updated just a few minutes ago. I'm running with global-git-gutter-mode FWIW.Strobotron
And I also have (add-hook 'magit-status-mode-hook #'magit-filenotify-mode), not sure if that matters.Strobotron
I haven't tried adding magit-filenotify-mode to the magit-status-mode-hook, but it depends on another package: github.com/magit/magit-filenotify.Deranged
K
2

Please note that current version of magit does not use `magit-revert-buffer-hook'. So, lunaryorn's solution may not work.

You can add git-gutter update hooks function of magit-after-revert-hook and magit-not-reverted-hook that are called when magit refreshes all visited buffers from current repository (e.g after commit or "g" command):

(add-hook 'git-gutter:update-hooks 'magit-after-revert-hook)
(add-hook 'git-gutter:update-hooks 'magit-not-reverted-hook)
Keilakeily answered 30/8, 2015 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.