Subject: bug#79137: 31.0.50; (vc-buffer-sync-fileset) cannot complete if unrelated no-longer-reachable TRAMP buffers exist

From: Dima Kogan <[email-masked]>
Date: Thu, 31 Jul 2025 14:22:55 -0700

Hi. I'm using the bleeding-edge emacs from git: 99080d0c049. On Debian.
GTK.

I have an emacs session with lots of buffers. Some of these were remote
TRAMP /ssh: buffers, talking to a server that isn't accessible anymore.

I'm looking at a LOCAL file (not one of those unrelated TRAMP buffers).
I want a git-diff, so I hit C-x v D (vc-root-diff). Emacs then tries to
contact the no-longer-reachable server, which always fails, and I never
get my diff. The git repository in question is local. There's no reason
to even consider those other buffers. Probably what it should do is to
find the root of the repository, and reject all buffers that aren't
direct descendants of that path.

I tracked down the problem to this function:

  (defun vc-buffer-sync-fileset (fileset &optional not-essential missing-in-dirs)
    "Call `vc-buffer-sync' for most buffers visiting files in FILESET.
  NOT-ESSENTIAL means it is okay to continue if the user says not to save.

  For files named explicitly in FILESET, this function always syncs their
  buffers.  By contrast, for directories named in FILESET, its behavior
  depends on MISSING-IN-DIRS.  For each directory named in FILESET, it
  considers buffers visiting any file contained within that directory or
  its subdirectories.  If MISSING-IN-DIRS is nil, it syncs only those
  buffers whose files exist on disk.  Otherwise it syncs all of them."
    ;; This treatment of directories named in FILESET is wanted for, at
    ;; least, users with `vc-find-revision-no-save' set to non-nil: not
    ;; treating directories this way would imply calling `vc-buffer-sync'
    ;; on all buffers generated by \\`C-x v ~' during \\`C-x v D'.
    (let (dirs buffers)
      (dolist (name (cadr fileset))
        (if (file-directory-p name)
            (push name dirs)
          (when-let* ((buf (find-buffer-visiting name)))
            (push buf buffers))))
      (when dirs
        (setq buffers
              (cl-nunion buffers
                         (match-buffers
                          (lambda (buf)
                            (and-let*
                                ((file (buffer-local-value 'buffer-file-name buf))
                                 ((or missing-in-dirs (file-exists-p file)))
                                 ((cl-some (lambda (dir)
                                             (file-in-directory-p file dir))
                                           dirs)))))))))
      (dolist (buf buffers)
        (with-current-buffer buf
          (vc-buffer-sync not-essential)))))

The (file-exists-p file) call is the one that makes TRAMP look at ssh.
Probably a bit more logic is needed in that area. I can probably code
this up myself; let me know if yall want a patch.

Thanks.