windowsでsymlinkを含んだリポジトリを扱う方法のまとめ

まず、windows環境でsymlinkが使われているようなリポジトリを、何も設定せずにcloneしてくると、symlinkが死ぬ。これをどうにかする必要があった。いろいろ試行錯誤した結果のまとめのメモ。

要約

端的にまとめるなら以下ができれば良い。

  • .gitconfigで core.symlinks = true を設定
  • windowsを開発者モードに変更する

はい。

windowsでsymlinkを壊さずcloneする方法

そのまま使うと壊れるので壊さない方法を。

core.symlinks = true

自分の環境のところで有効にするには以下を実行すれば良い。

$ git config --global core.symlinks true

developer mode

あとは、権限が不足しているようなので追加して上げる必要がある。ところで管理者権限が必要と言われるので、このままだと、管理者モードで開いたものでしかsymlinkが扱えない。

どうやら管理者モードを有効にしないといけないらしい。

Do the following to enable Developer Mode on the Windows 10 machine:

  1. Use the shortcut Windows-I to open the Settings application.
  2. Navigate to Update & Security > For Developers.
  3. Switch from "Windows Store apps" to "Developer Mode" on the screen.

はい。

gpedit?

ちなみに、以下の記事などでは、「ローカルセキュリティーポリシー」というものや「gpedit」というものを使って頑張って権限を設定しているがその理由がよくわからなかった。

windowsでsymlinkを作る方法

次にwindowsでsymlinkを作る方法。Powerhellでは以下のような形でsymlinkが作れるらしい。

New-Item -Type SymbolicLink <link name> -Value <target file>

ところでこの方法では問題があり、絶対パスとしてのsymlinkしか作れないようだった。 work-aroundとしてコマンドプロンプトの時代に使われていたmklinkを使う方法が紹介されていた。

このような感じで使うらしい。

cmd /C mklink /D <link name> .\mytarget

はい。

まとめ

core.symlinks = true と 開発者モードが必要。この辺りはwindowsを常用している人にとっては常識なのかもしれない。

eshellを使うようにしたらwindows環境でのEmacsもマシにはなった

諸事情によりwindowsの環境で作業しなければいけないことになった。windowsでの開発自体はWSLなどで便利になったものの直接windows環境で作業したい事がある。困っていたのは特にシェルの環境だった。

困りどころ

M-x shellで動くシェルはコマンドプロンプトのように見えた。おそらく何らかの設定をすればPowerShellが使えるようになるのかもしれないが、PowerShellも使いにくい。最もunix系のシェルと異なる解釈をされて困ったのは"*"の扱い。これがCLIのアプリにそのまま渡されてくるので、grepなどが死ぬ。

ソフトウェアのインストール自体はscoopを使っていた1

scoop.sh

加えて、シェルとエディタ(Emacs)との行ったり来たりがやりづらかった。

eshellの利用

元々Macの環境やLinuxの環境でで作業するときには、M-x shellで立ち上がるシェル(bash)をそのまま使っていた。ちょっとした仕掛けとしてC-j Sで現在開いているバッファのディレクトリ上にcdした状態で開く関数を作っていた。

具体的に言うと以下ができて欲しい。

  • 開いたバッファからeshellを起動
  • eshellからバッファを開く

前者はC-j Sにバインドしていた関数で環境がwindowsならeshellを使うようにした。後者はeshellのページを見てaliasの設定を追加した。

後者は、emacswikiの情報を見て設定を追加した。

以下のような設定。

(defun current-directory ()
  (if load-file-name
      (file-name-directory load-file-name)
    default-directory))

(defun my:shell-on-current-dir (&optional arg)
  (interactive "P")
  (let ((dir (current-directory)))
    (cond ((equal system-type 'windows-nt)
           (my:eshell-on-dir dir arg))
          (t
           (my:shell-on-dir dir arg)))))

(defun my:eshell-on-dir (dir &optional arg)
  (cl-letf (((symbol-function 'pop-to-buffer-same-window)
             (lambda (buf)
               (pop-to-buffer buf))))
    (eshell arg)
    (cd dir)
    (eshell-emit-prompt)
    (goto-char (point-max))))

(defun my:shell-on-dir (dir &optional arg)
  (comint-simple-send (get-buffer-process dir)
                      (concat "cd " dir))
  (goto-char (point-max)))

aliasは以下のような感じ。

alias emacs 'for i in ${eshell-flatten-list $*} {find-file $i}'
alias e 'for i in ${eshell-flatten-list $*} {find-file $i}'

細かい話としては、eshellではelispがそのまま呼べてしまうので、grepが死ぬ。このへんはripgrep (rg)を使うようにすればどうにかなった。


  1. 後で考えてみるとchocolateyでも良かったかもしれない。https://chocolatey.org/