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.

falsum
  • 145
  • 5
  • 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 08:14

2 Answers2

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).

shackra
  • 2,702
  • 18
  • 47
  • 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 04:43
  • @Falsum can you post this script so I can wrap it in a function to call when I refresh ibuffer. – J Spen Nov 03 '19 at 01: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 03 '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 03 '19 at 18:06
  • @falsum - if it doesn't work then let me know but the function should just call vc-refresh-state on the buffers with a vc-backend. – J Spen Nov 03 '19 at 18:08
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)
  )
J Spen
  • 171
  • 5