Emacs で勝手に vc-follow-link が実行されて辛い

vc-follow-link が勝手に実行されるため、enhanced ruby mode で erm が死んで、毎回 erm-reset を実行することを強いられて辛い思いをしていました。
以下、再現手順、原因、対応方法です。

再現手順

vc-follow-symlinks はデフォルトの ‘ask であることが前提です。Emacs 24.3.1 です。

mkdir -p /private/tmp/vc
cd /private/tmp/vc/
mkdir ./truedir
touch ./truedir/file
git init
git add ./truedir
git commit -m 'Initial commit'
ln -s ./truedir ./symdir
emacs -q /private/tmp/vc/symdir/file

“Followed link to /private/tmp/vc/truedir/file” というメッセージが表示されて、/private/tmp/vc/truedir/file を開いている状態になったと思います。

原因

vc-find-file-hook では Git などでバージョン管理されていないファイルを開こうとした時に、そのファイル自体や親ディレクトリがシンボリックリンクだった場合にリンクを辿ろうとします。
っで、既にリンク先のファイルを開いていたら無条件でリンクを辿る仕様になっていて、「既にリンク先のファイルを開いているかどうか」の判定がバグっています。

(get-file-buffer
  (abbreviate-file-name
    (file-chase-links buffer-file-name)))

file-chase-links は

Chase links in FILENAME until a name that is not a link.
Unlike `file-truename’, this does not check whether a parent
directory name is a symbolic link.

とあるように、親ディレクトリがシンボリックリンクの場合はリンクを辿らず指定した名前をそのまま返します。

なので、親ディレクトリがシンボリックリンクの場合は必ず「既にリンク先のファイルを開いている」という判定結果になり、無条件でリンクを辿ることになります。

対応

次のいずれかで解決できます。

  • vc-follow-symlinks を nil にしてリンクを辿らないようにする
  • vc-handled-backends を nil にして vc を実質無効にする
  • vc-find-file-hook の around advice を作成するなり、同名の関数を定義するなりして (file-chase-links buffer-file-name) を buffer-file-truename に変更する

/usr/local/Cellar/emacs/24.3/share/emacs/24.3/lisp/vc/vc-hooks.el を編集して /usr/local/Cellar/emacs/24.3/share/emacs/24.3/lisp/vc/vc-hooks.elc を作成するのが良いと思ったんですが、変更しても反映されず、/usr/local/Cellar/emacs/24.3/share/emacs/24.3/lisp/vc/vc-hooks.elc を削除してもどういうわけか load-history 的には /usr/local/Cellar/emacs/24.3/share/emacs/24.3/lisp/vc/vc-hooks.elc が読み込まれ続け、パッと見原因を追求するのが辛そうだったのでやめました。