Pure SSHなGit LFSを使う

以前のGit LFSは常にHTTPでサーバに接続していました。 しかし、git-lfsの最近のバージョンはSSH接続もサポートしています。
また、リモートのGitレポジトリとは違う場所にSSHで接続するGit LFSのサーバを作ることもできます。

やりたいこと

  • リモートのGitレポジトリとは別のSSHでアクセスできるマシンをGit LFSのサーバにしてデータを置きたい。
    • 例えば、100MB超のファイルを含むGitレポジトリをGitHubで管理したいが、GitHubのGit LFSが使えない場合。
      • 注: GitHubへは100MB超のファイルがあるとpushできません。またGit LFSの無料枠は1GBです。

条件等

  • LinuxなSSHサーバが既にある。とにかくSSH接続だけで済ませたい。
    • 逆に言うと、SSH接続が前提なので、パブリックなレポジトリではセキュリティ的に大変かもしれません。
  • Git LFSのロック機能には興味が無い
    • 今回、Git LFSのロック機能は全く検証していません。

Git LFSサーバ側の設定

既にSSHのサーバは設定されているものとします。

Git LFSをSSH経由で使う場合、サーバ側にgit-lfs-transferという実行ファイルが必要です。 今回は、Git LFSクライアント側の開発時にリファレンス実装として用いられたscutiger-lfsを使います。 Rustで実装されています。

ちなみに、2023年12月現在、Git LFSの実装リストによると、git-lfs-transferの実装は他に2つあります。

git-lfs-transfer のビルドとインストール

以下はDebian 12 (bookworm)を想定した手順となります。 もし、将来git-lfs-transfer実装のどれかがDebianパッケージ化されれば、それをインストールするだけで良いはずです。

まず、ScutigerのREADMEによると、ビルドにはRust 1.63以降、Cargo、Gnu Make、gitが必要です。 加えて、zlib、libpcre2、libgit2のダイナミックリンクが推奨されているので、それらの開発用パッケージもインストールします。

sudo apt update
sudo apt install rustc cargo make git zlib1g-dev libpcre2-dev libgit2-dev

次に、Scutigerのソースをダウンロードしてビルドします。 そしてビルドされたgit-lfs-transferを/usr/local/binにインストールします。
(ここでは/usr/local/binにパスが通っているものとします。)

git clone https://github.com/bk2204/scutiger
cd scutiger
make
sudo cp target/release/git-lfs-transfer /usr/local/bin/

Git LFS用のディレクトリを作成

Git LFSのデータ格納用のディレクトリとして、空のGitレポジトリを作成してください。 ここではそのパスを/home/gituser/git/testrepo.gitとします。

git init --bare --shared /home/gituser/git/testrepo.git

このようにbareレポジトリを作成した場合、実際のGit LFSのデータは/home/gituser/git/testrepo.git/lfs/に格納されます。
(注: SSH越しにGitを用いる場合はGitレポジトリをgroup writableにすることが多いため、例でも--sharedを指定しています。)

あるいは、bareレポジトリにしなくてもかまいません。 Git LFSのデータを格納するbareではないGitレポジトリが/home/gituser/git/testrepoの時、Git LFSのデータは /home/gituser/git/testrepo/.git/lfs/に格納されます。

scutiger-lfsのgit-lfs-transferはGitレポジトリの中にしかGit LFSのディレクトリを配置できないようなので今回は空のGitレポジトリを作っていますが、この辺はgit-lfs-transferの実装によっても異なると思います。

サーバ側の設定はこれで終わりです。

クライアント側の設定

ローカルのGitレポジトリの設定 (Gitレポジトリとは別の場所にGit LFSサーバを置きたい場合)

ここでは、Git LFSサーバのホスト名がssh.example.comであるとします。

Git LFSのエンドポイントをssh.example.comに向けるために、ローカルのGitレポジトリのトップディレクトリで

git config -f .lfsconfig lfs.url ssh://ssh.example.com/home/gituser/git/testrepo.git

を実行します。また、もしポート番号が(22ではなく)12345であれば

git config -f .lfsconfig lfs.url ssh://ssh.example.com:12345/home/gituser/git/testrepo.git

となります。

その他は通常のGit LFSと同じ使い方になります。まずは

git lfs install

を実行してください。 ただし、以下の補足にて述べるように、追加で

git config lfs.ssh.automultiplex false

を実行する必要があるかもしれません。

補足

  • ローカル側が正しく設定されていれば、git lfs envの実行結果に

    Endpoint=https://ssh.example.com/home/gituser/git/testrepo.git (auth=none)
      [email protected]:/home/gituser/git/testrepo.git
    

    という行があるはずです。 なお、SSHのポート番号を指定した場合、その値がここでは表示されないものの、内部で呼ばれる実際のsshコマンドでは-pオプションでポート番号が指定されます。

  • 繰り返しになりますが、今回用いたgit-lfs-transfer実装の都合により/home/gituser/git/testrepo.gitという空のGitレポジトリを作成しているだけで、それをGitレポジトリとしては使わない想定です。
    とはいえ、もちろんGitレポジトリとして普通に使うこともできます。 さらに、.lfsconfig がなければ、Git LFSのエンドポイントはリモートのGitレポジトリと同じ場所になります。 つまり、リモートのGitレポジトリにSSHでアクセスさせる構成なら、そのリモートのマシンにgit-lfs-transferを追加でインストールするだけでGit LFSが使えます。
    (というか、今回のようにリモートのGitレポジトリとGit LFSサーバを別のホストにする方が特殊だと思います。)

  • OpenSSHを使っている場合、複数のSSH通信を1つのコネクションにまとめることができます(マルチプレキシング)。 デフォルトではその機能が有効になっていますが、クライアントとサーバが同じホストにある場合にgit pushが途中で止まってしまったりと、環境によっては正しく動作しないことがあります。 その場合は

    git config lfs.ssh.automultiplex false
    

    を実行して、マルチプレキシングを無効にしてください。

  • (SSH接続に限った話ではありませんが) .gitattributesのコミットは忘れやすいので気をつけましょう。