世界を動かす技術を、日本語で。

あなたはすでにGitサーバーを持っています

概要

  • Gitリポジトリ をサーバーで管理し、SSH経由で クローン・同期 する方法の解説
  • receive.denyCurrentBranch 設定による 複数端末での作業最適化
  • git-over-http 公開のための update-server-info 運用手順
  • Gitフック を活用した自動処理や静的サイト生成の実践例
  • バックアップバージョン管理 による安全性の確保

サーバー上のGitリポジトリをSSH経由でクローン・同期する方法

  • サーバー上に Gitリポジトリ を設置し、 SSHアクセス を利用した運用
  • ローカル端末で以下のコマンドを実行し、リポジトリをクローン git clone ssh://username@hostname/path/to/repo
  • ローカルで作業後、 originサーバーへプッシュ 可能
  • デフォルトでは、 現在チェックアウト中のブランチへのプッシュは禁止
  • サーバー側で以下コマンドを実行し、制限を緩和 git config receive.denyCurrentBranch updateInstead
  • 複数PC間での コード同期 や、 サーバー側ファイル編集 の効率化

GitリポジトリをHTTP経由で公開する手順

  • Webサーバーに Gitリポジトリ を公開し、HTTPクローンを有効化 git clone https://hostname/path/to/repo/.git
  • .gitディレクトリ名をURLから省略するにはサーバー側設定やリネームが必要 (ただしリネームはサーバー側Git操作が制限されるため注意)
  • HTTP経由でのクローンを有効にするため、サーバーで以下コマンドを実行 git update-server-info
  • リポジトリ更新のたびに再実行 が必要

git update-server-infoの自動化とGitフック活用

  • post-updateフック を利用し、git update-server-infoの自動実行を設定
    • サンプルフックをコピー cp .git/hooks/post-update.sample .git/hooks/post-update
    • 実行権限を付与 chmod a+x .git/hooks/post-update
  • post-updateフック はシェルスクリプトとしてカスタマイズ可能
  • 例えば、 静的サイトジェネレータ の自動実行も可能
    cat > .git/hooks/post-update <<EOF
    #!/bin/sh
    set -euo pipefail
    cd /path/to/site
    /path/to/generator
    EOF
    chmod a+x .git/hooks/post-update
    
  • ネットワーク遅延なくローカルで執筆 し、サーバーへプッシュ後に自動処理

Git運用のメリットと安全性

  • サーバー障害時も ローカルコピー がバックアップとして機能
  • 端末故障時も サーバーからの再取得 が可能
  • バージョン管理機能 により、誤削除やトラブル時の原因特定が容易
  • 信頼性と効率性 を両立した運用手法

Hackerたちの意見

もしかしたら年を取りすぎたのかもしれないけど、gitを使うのにsshアクセスが必要だって知らない人って本当にいるの?

ほとんどの人はgit=githubだと思ってるよね。

あまり知られていないことだけど、ディレクトリからクローンできるよ。バックアップ機能は実現できないけど、別のオプション/機能だね。

私も知らなかった - というか、サーバーがgitリポジトリを公開するために何をする必要があるのか考えたことがなかった。でももっと重要なのは、サーバーに変更をプッシュしてデプロイする理由がよくわからないってこと。私の頭の中のモデルでは、リポジトリにはSOTが含まれていて、サーバー上で動いているものは一時的なものだから、その二つを混ぜたくないんだ。ホットフィックスのために個別のファイルをscpするよりは楽かもしれないけど、SOTにプッシュして、サーバーにsshで入ってそこから変更をプルするのと何が違うの?

昔はベアリポジトリをDropboxに置いて、そこから実際のパスにクローンしてた。それで、1. 同期の競合を心配しなくてよかった。完了したら、ただoriginにプッシュするだけ。2. コードが自分のコンピュータの外にバックアップされてた。正確には覚えてないけど、スペースを節約できてたかも。そう思ってたけど、今はよくわからない。でも、かなり信頼性があったと思う。GitHubに移行してからはその方法をやめたけど、Codebergtailscaleを使って移行を考えてる。今は、自分のパソコンをインターネットに出さずにたくさんの選択肢がある気がする。

この方法でGitHubやGitLabスタイルのCI/CDをやる面白い/クリエイティブな方法って何かあるかな?プッシュ時に次に何をするか決めるエントリーポイントスクリプトみたいなのが必要?プッシュの目的に応じて変数をどうやって決めるんだろう?

gitはほとんどの人が実際に使い方を知らないものの一つだから、そうだね。

実は先週これに気づいたばかりで、まだ試してないんだ。プログラミングをほぼ50年やってて、Gitを13年使ってるけど、バカじゃないよ(ただ、時々これに異議を唱える人もいるけど、特に配偶者とかね)。

2007年からgitを使ってるけど、これに気づいたのは去年のこと。Gitは特に、知ってる専門家たちが微妙に違う使い方をするから混乱しやすいんだよね。だから、文化としては自分なりの独自のウィザードになるまで適当にやる感じ。靴ひもを結べないウィザードになっちゃうけど、サンダルが好きだからね。

そうなんだ。公式の中央リポジトリが設定されない限り、一人ではプロジェクトを始められないって言われたことがある。俺は「git initさえあれば始められるよ」って返したけど、聞いてもらえなかった。

それについては考えたことなかったな。誰かに聞かれたら、もちろんそうだよねって思うけど。単に考えたことがなかっただけなんだ。

サーバー側でリポジトリをベアにしておけば、チェックアウトしたブランチや「.git」ディレクトリの名前変更を心配しなくて済むよ。

これはサーバーサイドのファイルを遅延なく作業する素晴らしい方法だよね。記事で言及されているユースケースだけど、ベアリポジトリではうまくいかない。でも、SSHで接続するサーバーが機械間でコードを同期するための中心点だけなら、君の言う通りだよ:記事で言ってた複数の手間は、中央リポジトリをベアにすることで解決できる。

初めてgitを使ったのは2006年頃で、最初に試した3つのコマンドはこれだった:git init、git commit -am Initial\ commit、git clone . ssh://server/path/to/repo でも、うまくいかなかったんだ。リモートサーバーにsshで接続して、まずそのパスで「git init」しないといけない。なんて未開だろう。Bitkeeperや他のいくつかのツールは、まだ存在しないリモートパスにプッシュできて、それを作ってくれたんだ。もしかしたらgitもその後に追加したのかもしれないけど、その時は大きな欠落だと思ったよ。

ハハ、cloneの第二引数がURLになれるか確認させられたよ。でも、明示的に""だから、「git push --mirror」の代わりには使えないんだね :)

これを何年もやってきたよ。公開リポジトリに対して「読み取り専用」のUIが必要なら、cgit(https://git.zx2c4.com/cgit/about/)を使って公開できるよ。これで他の人がsshを使わずにgit cloneできるようになる。私はプライベートリポジトリはプライベートに保って、いくつかの公開リポジトリをcgitで公開してる。

git clone ssh://username@hostname/path/to/repo これは git clone username@hostname:path/to/repo と同じだし、ローカルとリモートのユーザー名が一致してれば、git clone hostname:path/to/repo でもいけるよ。(パスに先頭の / がなければ、リモートのホームディレクトリに対して相対的になる)

途中で何かを失った気がする。git init --bare で作ると、作業セットなしのgitリポジトリができる(通常は .git ディレクトリにある内容だけ)。これで foo.git のようなものを作れるんだよね、foo/.git の代わりに。「origin」もクローンしたリモートのデフォルト名に過ぎない。何でも呼べるし、好きなだけリモートを持てる。フェッチとプッシュのパスを変えることで、同じリモートにプッシュする際に名前空間を分けることもできる。ある会社では、個人のブランチでルート名前空間を汚さないために $user/$feature にプッシュするのが一般的だったし、ローカルリポジトリ全体のバックアップ用に backup/$user というのもよくあった。複数のホストから作業する時は、ホスト名の名前空間を追加して、中央サーバーに戻るのではなく、直接別のサーバーにプッシュすることが多い。ドキュメントやサーバー設定がある小さな静的サイトのリポジトリでは、リモートはこんな感じ:[remote “my-server”] url = ssh+git://…/deploy/path.git fetch = +refs/heads/:refs/remotes/my-server push = +refs/heads/:refs/remotes/my-laptop これで、自分のコンピュータからそのサーバーに直接プッシュできるけど、そのブランチはサーバーのブランチを上書きしない。逆の git pull のように動作するから、ファイアウォールや自分のノートパソコンがルーティングできない状況で便利なんだ。

Gitは常に明確に分散型の「ピアツーピア」バージョン管理システムで、SVNみたいな中央集権型とは違うんだ。プロトコル自体には「サーバー」と「クライアント」を区別するものがないから、中央集権的に使うのは単に自分が選んだワークフロー(実際には誰かが選んでくれたもの)に過ぎないよ。リポジトリのクローンはどれも他のクローンのリモートになれるし、ローカルファイルシステムに「gitサーバー」(つまり別のディレクトリ)を持つのも、場合によっては全然合理的なワークフローだよ。

git clone --mirrorも知っておくといいよ。これもリモートリポジトリの正確なクローン(すべてのブランチ、タグ、ノートなどを含む)を作るベアリポジトリを作るんだ。通常のクローンとは違って、リモートのローカルトラッキングブランチ用に設定されているわけじゃない。ただし、GitHubからクローンする場合はプルリクエストは含まれないけどね。

GitHubがデフォルトのリモートを「origin」じゃなくて「github」って名付けてたら、新人にとってもっと良かったし、混乱も少なかっただろうなってずっと思ってたよ。

静的サイトを公開するのにこれを使うのは注意が必要だよ。うっかり.gitディレクトリを公開しちゃうことがあるからね。俺も前にこれでやられたことがある(幸いペンテスターにね)。Apacheを設定して.gitディレクトリをブロックしなきゃいけなかったよ。

公開されていないディレクトリを除外する代わりに、明示的にpublicディレクトリ(またはdocdoc-root、好きな名前で)を作るのが好きなんだ。それからサーバーをそのサブディレクトリを指すように設定して、リポジトリのことは気にしない。俺は通常、etclogディレクトリもトップレベルに置いて、サーバーの設定をetcに入れて、ログ内のすべてを無視するgitignoreルールを作ってるけど、簡単にデプロイできるように準備はできてるよ。ウェブルートがすでにサブディレクトリだから、よりセンシティブなものも同じリポジトリに入れられるし、公開する心配もないんだ。

Gitを使えば使うほど、奥深さを発見するよ。たくさんの機能や概念があって、基本を理解していると思いがちだけど、その起源や理由を掘り下げないと、構築されている考え方を理解するのは難しい。APIの表面積も思っているよりずっと大きいし、氷山みたいだよ。だから、ある意味でかなり低レベルに感じるんだ。多分、最も理にかなったデフォルトの使い方をするための高レベルなCLIが必要だと思う。だって、ほとんどの人が使うメンタルモデルは不十分だしね。

この考え方は本当に大事だと思う。ざっくり言うと、GitHubはgitに対して、Gmailはメールに対してのものだよね。みんなが使いたいと思っていて、ちゃんと機能しているならそれでいいけど、中央集権的なものを持つことが目的じゃなかったってことを忘れちゃいけない。そういう考え方が、すべての構造に組み込まれているんだから。

gitが中央集権的に使わないように設計されたかは分からないな。一般的なCLIと同じように、単にPCやサーバーで動くように作られたもので、誰が使っているかなんて気にしてなかったし、企業がそれを製品にするのを止めるものはなかったよ。トーバルズがgitとLinuxを作って、LinuxをGitHubに載せたんだ。

これを見て、数ヶ月前のHNでの別の議論を思い出した。インターネットの標準文化がどう変わってきたかを考えてたんだ。「80年代と90年代(それ以前も)では、主に公共の利益のために働く学者や趣味のハッカーが中心だった。ティム・バーナーズ=リーやヴィント・サーフ、ウェブやインターネットの標準のためのIETF、RSSのデイブ・ワイナーを思い出してみて。00年代以降は、資金力のある企業とそのために働くエンジニアたちが主流になった。Googleを考えてみて。IETFからは、みんなが自分のサーバーを運営するという前提でメールプロトコルの標準が出てきた。でもGoogleからはGmailが出てきた。[ウェブ]は、完全にホスティングされたプロプライエタリなソフトウェアに対するユーザーの快適さのための新しいメカニズムを作り出した。例えばGoogle Docsみたいに。これによって、ユーザー向けのソフトウェアをオープンソースに保とうとする多くの努力が脇に追いやられた。オープンプロトコルやオープンソースソフトウェアを推進することに最も反応しやすいユーザーの中でも、GitHubのような奇妙な妥協が生まれた。オープンソースのデスクトップソフトウェア(git)の上に構築され、分散型のために設計されたオープンソースのストレージフォーマット(gitリポジトリ)を持ちながらも、100%プロプライエタリで中央集権的なプラットフォーム(例:GitHub.comのリポジトリホスティングやGitHub Issues)になってしまっているんだ。」

もし複数の人がgitリポジトリにssh-pushする場合、どうやってアクセスを同期させるの?同期が必要だと思うんだけど。