2019年8月13日火曜日

git for windowsに対してWindows版OpenSSHを使ってリモートからリポジトリのclone/pushする方法

 長いタイトルだが、一日を費やして、問題を解決した。

あまりの感動

であり、これをみんなと

共有したい

と勝手に考えている。

どうせWindowsの話

だし、誰もこんなことやらないだろうが、もしかしたら一人ぐらいは困っているかもしれないのでブログにしておく。もっとも、1日悩むぐらいなら

最初からLinux

でやればなんの問題もない。

 gitのリポジトリに対して、適切に設定を行えば、sshを介してリモートからcloneなどのコマンドを利用できる。このとき、Windows10付属のOpenSSHと組みあわせてgit for Windowsに対してsshでのcloneがエラーになる。

C:\Temp\gtest> git clone -v user@host:D:\git\test.git test 
warning: agent returned different signature type ssh-rsa (expected rsa-sha2-512) '
'git-upload-pack' �́A����R�}���h�܂��͊O���R�}���h�A
����”\�ȃv���O�����܂��̓o�b�`�t�@�C���Ƃ��ĔF������Ă��܂���B
C:\Temp\gtest>

 文字化けしているが、どうもgit-upload-packがエラーになっているようである。最初はパスの記述がダメなのかといろいろと調べた。そもそも、gitでのURL記述にはいくつかの方法があるようだ。このうち、scpの記述に似たsshの記法では、パスに”:”が使えるらしい。このあたりは、git cloneのリファレンスにあるGIT URLsを見て欲しい。

 gitをご存じの方は、1行目のコマンドラインを見ただけで何をしたいのかがわかるとおもう。これは、他のマシン(これがWindows10のPC)にあるリポジトリ(D:\git\test.git)に対してcloneコマンドを実行しているもの。

鋭い方

はわかるように、これもWindowsマシンである。

この件に関してググってみたが、そのものズバリという答えは

なかった。

ただ、いくつかのやり方は見付けた。

その1 OpenSSHのデフォルトシェルをgit付属のgit bashに切り替える。

 さすがにgit以外を使う場合には、

cmd.exeを使いたい

ので却下。

その2 git for windows付属のsshd.exeを使う

 こちらも設定などが違って来るので受け入れがたい。また、git付属のsshd.exeはssh-agentサービスに対応していないよう。OpenSSHと並行して動作できるように

ポート番号を変える

という方法も思いついたが、ややこしいのでやめることにした。おそらく、この方法が成功するのは、「その1」と同じくデフォルトシェルがgit付属のbashになっているからだと思われる。

その3 Linuxマシン側にリモートリポジトリを置く

 素直な方法だが、普段はLinuxを使わないのと、Windowsの開発はVisual StudioなのでWindowsでやりたい。それになんか

負けた感じ

がする。

 だいたい、Windowsで全部済ませようとというのが

ダメ

なのかもしれないが……。

原因を調べてみる

 それで、いったい何が原因なのかを調べて見た。まずは、traceである。最初は、ソース見なきゃダメかも、と思っていたが、trace見たら意外に簡単に原因がわかった。

 環境変数GIT_TRACEにパスを指定すると、コマンドの挙動を指定したファイルに書き込んでくれる。

GIT_TRACE=C:\Temp\gitlog.txt

 また、Windows付属のOpenSSHを使わせたければ、環境変数GIT_SSHにパスを指定しておく(なくてもいいみたい)。

GIT_SSH=C:\Windows\System32\OpenSSH\ssh.exe

 それでtraceをみてみると、

09:36:54.713203 git.c:439               trace: built-in: git clone -v 'user@host:D:\git\test.git' test 
09:36:54.728856 run-command.c:663 trace: run_command: unset GIT_DIR;
'C:\Windows\System32\OpenSSH\ssh.exe' user@host 'git-upload-pack '\''D:\git\test.git'\'''

 これを見ると、ssh.exeコマンドに対してgit-upload-packコマンドを実行させていることがわかる。具体的には、

'C:\Windows\System32\OpenSSH\ssh.exe' user@host 'git-upload-pack '\''D:\git\test.git'\'''

を実行している。つまり、リモート側のWindowsでは、ssh経由でgit-upload-packというコマンドが起動される。このあたりに関しては、Gitサーバー-プロトコルに記述がある。

 余談ですが、Pro Git読んでおくべきです。gitについて、

何も知らなかった

ことが判明しただけでも読んだ価値があった。

 さて、話を戻して、ところが、git-upload-packは、

c:\Program Files\git\mingw64\bin\git-upload-pack.exe

と、PATHに設定されていないところにあるため、エラーが出ていたわけだ。おそらく化けていたのは、

'git-upload-pack' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

というcmd.exeのメッセージなのだと思われる(先頭部分が一致)。

じゃPATHを設定してみれば?

 では、PATHを設定すればいいのか? と思ったが、

これでもダメ

だった。というのは、実行されるのは、

 git-upload-pack 'D:\git\test.git'

というコマンドラインなのである。bashなどでは、引数をシングルクオートでくくっても、ダブルクオートで括ってもどちらも解釈され、そのあとで外されるのだが、Windows10標準のOpenSSHが起動するのはcmd.exeで、これは、ダブルクオートは解釈して外してくれるのだが、シングルクオートに対しては

何もしない

のである。このため、前記のコマンドラインは、エラーになってしまう。

C:\Temp\GIT>"c:\Program Files\Git\mingw64\bin\git-upload-pack.exe" 'd:\git\test.git'
fatal: ''d:\git\test.git'' does not appear to be a git repository

というわけで、なんとかしてgit-upload-pack.exeを実行させて、シングルクオートを外したパラメーターを渡さなければならない。

代わりのgit-upload-packコマンドを作る

 そこで、mingw64側にPATHが設定されていないので、これを利用して、PATHが通っているc:\Program Files\Git\cmdにgit-upload-pack.batを作る。コマンドラインでgit-upload-packが起動されると、このバッチファイルが起動されることになる。とりあえず、

なんでもバッチ

というのがWindowsである。bashに比べると

4096倍(当社比)不便

だが、Windowsではいつでも使える確実な手段である。MS-DOS以来、もう30年以上使ってるのでさすがにやめられない。

 このバッチファイルでシングルクオートを除去すればよい。具体的には、以下のようなバッチファイルgit-upload-pack.batを作る。

1@echo off
2set Y=%1%
3set X=%Y:'=%
4"C:\Program Files\Git\mingw64\bin\git-upload-pack.exe" %X%

 cmd.exeに詳しくない人に説明すると、

 1行目の”@echo off”は実行時のコマンドラインの表示をオフにするもの。

 2行目の”set Y=%1%”は、コマンドラインの引数(%1%)を環境変数Yに代入するもの。%1%は、コマンドライン最初の引数。これは、次の行で行う処理のためにわざわざ実行中に代入を行わせている。cmd.exeは、内容の確定している環境変数はバッチファイルを実行する前に置き換えてしまう。このため、内容の確定している環境変数を後から触るのは結構面倒なのである。 しかし、このように実行中に代入が行われる変数(ここではY)は、実行前に置き換えるわけにはいかないので、コマンドラインを実行するときに中身を参照する。つまりここで環境変数Yに代入を行うのは、次の行で置換させるための準備なのである。

 3行目はsetコマンドの拡張機能で変数の文字列置換を行っている。ここでset VAR2=%VAR1:X=Y%は、環境変数VAR1の中にある文字XをYに置換するsetコマンドの拡張機能である。Yを省略することでXを削除することができる。 前の行で代入した変数Yを使うことで実行前に置き換えられなくなっている。これが%1%のままだと、実行前に置き換えが行われてしまう。Yへの代入なしに「set X=%1:’=%」とすると、実行前に%1%が置き換わって実行されるのは「 set X=’test’:’=%」になってしまう。まさに、

わからなさの極致

といえるコマンドだ。

 4行目は、gitのgit-upload-pack.exeの起動である。途中にスペースを含むフォルダー名なので、ダブルクオートで括らないとダメである。

git pushも対応させる

 同様にpushのときには、traceを見ると”git-receive-pack.exe”を起動しようとしてエラーになっていることがわかった。解決方法は同じである。以下のgit-receive-pack.batを同じくcmdフォルダーに置く。

1@echo off
2set Y=%1%
3set X=%Y:'=%
4"C:\Program Files\Git\mingw64\bin\git-receive-pack.exe" %X%

 なお、これを行ったあとは、sshd(OpenSSH SSHサーバーサービス)を再起動しておく。言い忘れたが、環境変数などを変更したときもsshdを再起動しないと環境変数を見てくれない。

0 件のコメント:

コメントを投稿