Re: sshfs + magit + gopls

Michael Albinus <[email protected]> Thu, 09 Apr 2026 13:22:33 +0200
Newsgroups gmane.emacs.tramp
Message-ID <[email protected]>
"Patrick Ohly" <[email protected]> writes:

Hi Patrick,

> That may very well be the root problem. However, in my case "remote"
> typically isn't that remote: it's a local desktop connected to the same
> 2.5Gbit Ethernet switch as my laptop. A proper network filesystem should
> be pretty fast, but then I face the challenge of teaching Emacs where to
> run commands. TRAMP makes that transparent.

That's only half of the story. Many git calls require a new ssh
connection every call, with all the overhead. And due to the sshfs
specifics, ControlMaster optimization cannot be used.

> But it's not just magit. Just opening a file can take half a minute.

It's also magit. As reproducer, I've opened
"/sshfs:freebsd14:/home/albinus/src/emacs", calling 'M-x magit-status'
afterwards.

According to the traces, there are 14 'ssh -q -e none -t -t ...' calls
due to this.

>>> I am using Emacs 30.1 with Tramp 2.8.1.3, go-mode 20250311.156,=20
>>> magit 20260401.2251 (all packages updated via elpa=20
>>> yesterday). gopls is installed in /usr/local/bin on the remote=20
>>> host.
>>>
>>> When I open a Go file, *Warnings* opens and I get:
>>>    =E2=9B=94 Warning (tramp): STDERR not supported
>>>
>>> I don't have a workaround. Because this takes up screen space=20
>>> it's pretty annoying.

That's true. Tramp's sshfs make-process implementation doesn't support
stderr. You could suppress this warning by by eval'ing

--8<---------------cut here---------------start------------->8---
(setq warning-suppress-types '((tramp)))
--8<---------------cut here---------------end--------------->8---

But this will suppress also other Tramp warnings you need to see.

>>> That's because it doesn't find the gopls command (visible in=20
>>> *lsp-log*). I found that I can work around that by creating a=20
>>> symlink to it in /bin. The reasons seems to be
>>> https://emacs.stackexchange.com/questions/81829/tramp-sshfs-remote-comm=
and-execution-doesnt-respect-tramp-remote-path-or-tra
>>> I think it should work the same as ssh and use the remote path=20
>>> settings.

tramp-remote-path is intended for methods of the tramp-sh backend like
"ssh", not for the "sshfs" method.

> Both the ssh and sshfs TRAMP methods support remote command execution
> through ssh. I found some prior discussion about whether the sshfs
> method should execute commands locally and the conclusion was that this
> would go against the goals of TRAMP. I concur, I am using TRAMP because
> all my tooling is installed on the remote host. In the case of sshfs,
> the mounted sshfs filesystem is only used for file transfers.
>
> My expectation was that both methods do remote command execution exactly
> the same way, with feature parity and perhaps even using the same
> code. But apparently that is not the case.

There is no feature parity. sshfs support for remote processes is
limited. There a no plans to improve it; at least no plans by me.

>>> magit gathers some information, but then fails with:
>>>
>>>    magit-rev-diff-count: Wrong type argument: stringp, nil
>>>
>>> This is after some more warnings about stderr, so maybe related?
>>
>> You've lost me here, sorry. Can you give an MRE? I think there=20
>> might be too much going on.
>
> MRE =3D https://en.wikipedia.org/wiki/Meal,_Ready-to-Eat, i.e. a simple
> reproducer?

"Minimal reproducible example"

> A simple test would be to open a file in a remote git working tree with
> /sshfs:<host>/... instead of /ssh:<host>/... and then bringing up the
> magit status page.

That reproduces the error for me, my scenario is mentioned above. The
backtrace is

--8<---------------cut here---------------start------------->8---
Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  split-string(nil "\11")
  magit-rev-diff-count("fatal: cannot describe '923aac2e4ec76b51891ba711c44=
ee08f7211ea2a'" nil)
  magit-get-next-tag(nil t)
  magit-insert-tags-header()
  magit-run-section-hook(magit-status-headers-hook)
  magit-insert-headers(magit-status-headers-hook)
  magit-insert-status-headers()
  magit-run-section-hook(magit-status-sections-hook)
  magit-status-refresh-buffer()
  #f(compiled-function () #<bytecode -0x1864b8c86a00967>)()
  magit-refresh-buffer(nil :initial-section magit-status-goto-initial-secti=
on :select-section nil)
  magit-setup-buffer-internal(magit-status-mode nil ((magit-buffer-diff-arg=
s ("--no-ext-diff")) (magit-buffer-diff-files nil) (magit-buffer-log-args (=
"-n256" "--decorate")) (magit-buffer-log-files nil)) :initial-section magit=
-status-goto-initial-section :select-section nil)
  magit-status-setup-buffer("/sshfs:freebsd14:/home/albinus/src/emacs/")
  magit-status("/sshfs:freebsd14:/home/albinus/src/emacs/" ((25 . 20) (("/s=
shfs:freebsd14:/home/albinus/src/emacs/" "rev-list" "--count" "--left-right=
" "fatal: cannot describe '923aac2e4ec76b51891ba711c44ee08f7211ea2a'...")) =
(("/sshfs:freebsd14:/home/albinus/src/emacs/" "describe" "--contains" "HEAD=
") . "fatal: cannot describe '923aac2e4ec76b51891ba711c44ee08f7211ea2a'") (=
("/sshfs:freebsd14:/home/albinus/src/emacs/" "describe" "--long" "--tags") =
. "emacs-30.1-180773-g923aac2e4ec") (("/sshfs:freebsd14:/home/albinus/src/e=
macs/" magit-get-push-branch "master" nil)) (("/sshfs:freebsd14:/home/albin=
us/src/emacs/" "rev-parse" "--verify" "--abbrev-ref" "master") . "master") =
(("/sshfs:freebsd14:/home/albinus/src/emacs/" "log" "--no-walk" "--format=
=3D%s" #("origin/master^{commit}" 0 13 (font-lock-face magit-branch-remote =
face magit-branch-remote)) "--") . "Merge branch 'master' of git.sv.gnu.org=
:/srv/git/emacs") (("/sshfs:freebsd14:/home/albinus/src/emacs/" magit-get-u=
pstream-branch "master") . #("origin/master" 0 13 (face magit-branch-remote=
 font-lock-face magit-branch-remote))) (("/sshfs:freebsd14:/home/albinus/sr=
c/emacs/" "rev-parse" "--verify" "--abbrev-ref" "master@{upstream}") . "ori=
gin/master") (("/sshfs:freebsd14:/home/albinus/src/emacs/" "symbolic-ref" "=
--short" "HEAD") . "master") (("/sshfs:freebsd14:/home/albinus/src/emacs/" =
"log" "--no-walk" "--format=3D%h %s" "HEAD^{commit}" "--") . "923aac2e4ec M=
erge branch 'master' of git.sv.gnu.org:/srv/git/emacs") (("/sshfs:freebsd14=
:/home/albinus/src/emacs/" . config) . #<hash-table equal 20/24 0xc3b3488 .=
..>) (("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev-parse" "--verify" "=
HEAD") . "923aac2e4ec76b51891ba711c44ee08f7211ea2a") ("/sshfs:freebsd14:/ho=
me/albinus/src/emacs/" . "2.46.2") (("/sshfs:freebsd14:/home/albinus/src/em=
acs/" . magit-toplevel) . "/sshfs:freebsd14:/home/albinus/src/emacs/") (("/=
sshfs:freebsd14:/home/albinus/src/emacs/" "rev-parse" "--show-cdup") . "") =
(("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev-parse" "--show-toplevel"=
) . "/home/albinus/src/emacs") (("/home/albinus/" "config" "--default=3D_" =
"core.bare") . "_") (("/home/albinus/" . magit-toplevel)) (("/home/albinus/=
" "rev-parse" "--git-dir")) (("/home/albinus/" "rev-parse" "--show-toplevel=
"))))
  funcall-interactively(magit-status "/sshfs:freebsd14:/home/albinus/src/em=
acs/" ((25 . 20) (("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev-list" "=
--count" "--left-right" "fatal: cannot describe '923aac2e4ec76b51891ba711c4=
4ee08f7211ea2a'...")) (("/sshfs:freebsd14:/home/albinus/src/emacs/" "descri=
be" "--contains" "HEAD") . "fatal: cannot describe '923aac2e4ec76b51891ba71=
1c44ee08f7211ea2a'") (("/sshfs:freebsd14:/home/albinus/src/emacs/" "describ=
e" "--long" "--tags") . "emacs-30.1-180773-g923aac2e4ec") (("/sshfs:freebsd=
14:/home/albinus/src/emacs/" magit-get-push-branch "master" nil)) (("/sshfs=
:freebsd14:/home/albinus/src/emacs/" "rev-parse" "--verify" "--abbrev-ref" =
"master") . "master") (("/sshfs:freebsd14:/home/albinus/src/emacs/" "log" "=
--no-walk" "--format=3D%s" #("origin/master^{commit}" 0 13 (font-lock-face =
magit-branch-remote face magit-branch-remote)) "--") . "Merge branch 'maste=
r' of git.sv.gnu.org:/srv/git/emacs") (("/sshfs:freebsd14:/home/albinus/src=
/emacs/" magit-get-upstream-branch "master") . #("origin/master" 0 13 (face=
 magit-branch-remote font-lock-face magit-branch-remote))) (("/sshfs:freebs=
d14:/home/albinus/src/emacs/" "rev-parse" "--verify" "--abbrev-ref" "master=
@{upstream}") . "origin/master") (("/sshfs:freebsd14:/home/albinus/src/emac=
s/" "symbolic-ref" "--short" "HEAD") . "master") (("/sshfs:freebsd14:/home/=
albinus/src/emacs/" "log" "--no-walk" "--format=3D%h %s" "HEAD^{commit}" "-=
-") . "923aac2e4ec Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs")=
 (("/sshfs:freebsd14:/home/albinus/src/emacs/" . config) . #<hash-table equ=
al 20/24 0xc3b3488 ...>) (("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev=
-parse" "--verify" "HEAD") . "923aac2e4ec76b51891ba711c44ee08f7211ea2a") ("=
/sshfs:freebsd14:/home/albinus/src/emacs/" . "2.46.2") (("/sshfs:freebsd14:=
/home/albinus/src/emacs/" . magit-toplevel) . "/sshfs:freebsd14:/home/albin=
us/src/emacs/") (("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev-parse" "=
--show-cdup") . "") (("/sshfs:freebsd14:/home/albinus/src/emacs/" "rev-pars=
e" "--show-toplevel") . "/home/albinus/src/emacs") (("/home/albinus/" "conf=
ig" "--default=3D_" "core.bare") . "_") (("/home/albinus/" . magit-toplevel=
)) (("/home/albinus/" "rev-parse" "--git-dir")) (("/home/albinus/" "rev-par=
se" "--show-toplevel"))))
  command-execute(magit-status record)
  execute-extended-command(nil "magit-status" nil)
  funcall-interactively(execute-extended-command nil "magit-status" nil)
  command-execute(execute-extended-command)
--8<---------------cut here---------------end--------------->8---

Sorry, but I cannot say anything about. I don't use magit myself.

>>> All of this works with tramp over ssh.=20
>>
>> This might come down to what I said earlier about sshfs not being=20
>> an environment.
>
> What does "environment" mean in this case? Does that refer to how both
> methods are implemented?

Yes.

Perhaps you give the tramp-rpc backend a try. It is brand new, still in
alpha version, but acc. to my experience stable. And it has even some
magit-specific optimizations on board. See <https://github.com/ArthurHeyman=
s/emacs-tramp-rpc>.

> Thanks, Patrick

Best regards, Michael.