3

I use ibuffer-vc, a replacement for list-buffer that displays the VC status of the listed files. I noted that the VC status stays as "edited" even after I push my changes. That is, the VC status does not differentiate between the following two situations: (1) I modified a file and I still have to push my changes; and (2) I pushed my changes and the local and remote versions are the same.

The VC status for both situations is "edited". However, I would like to differentiate between these two types of situations.

My question is: Assuming that this is the typical behavior of VC status, is there a way of changing my Emacs config so that the VC status could differentiate between the two situations cited above?

Note: I use git and magit for my version control. My understanding is that ibuffer-vc uses vc-state to get the status of a file.

1
  • 1
    I happen to stumble upon the same issue, I thought that using force-mode-line-update would have some effect but it didn't change anything.
    – shackra
    Apr 15 '18 at 8:14
2

Use vc-refresh-state like this to get rid of the edited vc-state:

(defun vc-state-refresh-post-command-hook ()
    "Check if command in `this-command' was executed, then run `vc-refresh-state'"
    (when (memq this-command '(other-window kill-buffer ido-kill-buffer ido-switch-buffer))
      (vc-refresh-state)))

(add-hook 'after-save-hook 'vc-refresh-state)
(add-hook 'after-revert-hook 'vc-refresh-state)
(add-hook 'post-command-hook #'vc-state-refresh-post-command-hook)

You can change the sentence '(other-window kill-buffer ido-kill-buffer ido-switch-buffer) to use a variable you can redefine in your configuration with the true commands you use (say, ace-window instead of other-window because you use it, etc).

6
  • This works like a charm! I added a function that goes through every buffer and run vc-refresh-state on them. I'm currently using buffer-list to give me the list of buffers. Does anyone happen to know the name of the variable that only lists the buffers that are in version control?
    – falsum
    Jun 23 '18 at 19:45
  • 1
    @falsum I think there is no such variable, which in such case maybe using a map function with a custom function that check if the file visited is part of a version control system.
    – shackra
    Jun 30 '18 at 4:43
  • @Falsum can you post this script so I can wrap it in a function to call when I refresh ibuffer.
    – J Spen
    Nov 3 '19 at 1:26
  • @JSpen This might not be the best way of doing this, but I simply run (dolist (b (buffer-list)) (with-current-buffer b (vc-refresh-state))) with the ibuffer-hook
    – falsum
    Nov 3 '19 at 13:32
  • @falsum I wrote a new answer below so let me know if it works for you. If it is a better answer to your question feel free to accept it :).
    – J Spen
    Nov 3 '19 at 18:06
0

I have a better way of doing this because I don't like putting unnecessary things in the post-command-hook. Then, they are run every time you run a command. I defined the following function that only takes buffers controlled by a vc backend and refreshes all of them. Then I just set this to a keybinding in ibuffer-mode-map. I use a prefix key for all my commands in ibuffer, which is set to h but just change the keybinding for jj/ibuffer-vc-refresh-state. Also, I set it to run in an ibuffer-mode-hook, but if you have a ton of buffers open this might cause a bit of a lag.

(eval-after-load "ibuffer-vc"
  '(progn
(defun jj/vc-refresh-state-all-buffers ()
    "Refresh all vc buffer statuses by calling `vc-refresh-state` on each one if it has an associated vc backend."
    (interactive)
    (dolist (buf (buffer-list))
      (let ((file-name (with-current-buffer buf
             (file-truename (or buffer-file-name
                        default-directory)))))
    (when (ibuffer-vc--include-file-p file-name)
      (let ((backend (ibuffer-vc--deduce-backend file-name)))
        (when backend
          (with-current-buffer buf (vc-refresh-state))
          ))))))

  (defun jj/ibuffer-vc-refresh-state ()
    (interactive)
    (jj/vc-refresh-state-all-buffers)
    (ibuffer-redisplay))
  ))
(add-hook 'ibuffer-mode-hook 'jj/ibuffer-vc-refresh-state)
(define-prefix-command 'ibuffer-h-prefix-map)
(define-key ibuffer-mode-map (kbd "h") 'ibuffer-h-prefix-map)
(define-key ibuffer-h-prefix-map (kbd "h") 'describe-mode)
(define-key ibuffer-h-prefix-map (kbd "r") 'jj/ibuffer-vc-refresh-state)     
    ))

Also, if you use use-package then the below is my setup:

(use-package ibuffer
  :bind (
     :map ibuffer-mode-map
     :prefix-map ibuffer-h-prefix-map
     :prefix "h"
     ("h" . describe-mode)
     ("g" . ibuffer-clear-filter-groups)
     ("r" . ibuffer-clear-filter-groups)))

(use-package ibuffer-vc
  :bind (:map ibuffer-h-prefix-map
          ("v" . ibuffer-vc-set-filter-groups-by-vc-root)
          ("V" . jj/ibuffer-vc-refresh-state))
  :config
  (defun jj/vc-refresh-state-all-buffers ()
    "Refresh all vc buffer statuses by calling `vc-refresh-state` on each one if it has an associated vc backend. Uses functions from `ibuffer-vc`, so decouple these functions if you need to use this without loading ibuffer-vc."
    (interactive)
    (dolist (buf (buffer-list))
      (let ((file-name (with-current-buffer buf
             (file-truename (or buffer-file-name
                        default-directory)))))
    (when (ibuffer-vc--include-file-p file-name)
      (let ((backend (ibuffer-vc--deduce-backend file-name)))
        (when backend
          (with-current-buffer buf (vc-refresh-state))
          ))))))

  (defun jj/ibuffer-vc-refresh-state ()
    "Refresh all vc buffer statuses and redisplay to update the current status in ibuffer."
    (interactive)
    (jj/vc-refresh-state-all-buffers)
    (ibuffer-redisplay))

  (add-hook 'ibuffer-mode-hook 'jj/ibuffer-vc-refresh-state)
  )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.