急に難しくなる複数台運用
ごくふつうの人にも便利なGitを使おう レベル1〜の続編である。
そして、話は急激に難しくなる。
まぁ、人生そういうものだろう。だいたいレベル15くらいまでは慣れも相まってゴリ押しで攻略できるが、レベル15くらいから頭を使わないと攻略できなくなってくる。
それはさておき、複数台運用をしようとすると、単にGitの知識だけでなく
- SSH
- リモートログインと認証
- diffとpatch
- ファイルシステムと権限管理
- ホスティングサービスソフトウェア(e.g. GitHub, GitLab, Gogs)
という5系統の知識が必要になる。
だからここまでと違い、じっくり取り組む必要がある。
座学の時間
SSH
役割
SSHは安全なシェルである。
だが、その定義に反して代表的実装であるOpenSSHは極めて多彩な機能を持つ。
そして、それらの機能の基盤になっているのは2つの基本的な機能
- 暗号化された安全な通信経路を確立する
- 認証を行う
である。
GitにおけるSSHはリモートホストとの間での通信という意味もあるが、それ以上に認証が重要である。
認証と鍵コマンド
SSHは認証方法をいくつも持っているが、最も代表的かつ強力なのが公開鍵認証である。
これは、非対称暗号鍵を用いた署名によって検証する。
仕組みを簡単に説明すると
- クライアントは秘密鍵によって共通の秘密に署名を行い、対応する署名と公開鍵を送信する
- サーバーは送られた公開鍵を用いて署名を検証し、その公開鍵に対応する秘密鍵を用いていることを確認する
- 有効性が確認できた場合、サーバーは登録されている公開鍵と送られてきた公開鍵を照合する
という手順で認証を行う。
このため、事前に公開鍵はサーバー側に登録されている必要がある。
SSHでのログインはログインしようとするユーザーを明らかにした上で行われ、鍵の照合は当該ユーザーのものとして登録されているものに対して行われる(通常~/.ssh/authorized_keysである)。
有効な鍵として登録されているのであればログイン自体は可能だが、鍵に対して個別に制約をかけたりすることもできるため、「どの鍵を使って」ログインしたかも重要になる。
このため、自身が所有するホスト間であれば
- 使用する鍵ペアを生成する
- リモートホストに公開鍵を登録する
- リモートホストへのアクセスに使用する鍵を指定した設定をする
という手順が事前に必要である。
ホスティングサービスの場合
ホスティングサービスだとアドレスにgit@を使うように書かれているのを見る機会があったりするけれど、つまりこれは「ウェブアプリ上に登録されている全ユーザーの鍵を1システムユーザーの鍵として書いている」ということ。
通常はgitユーザー。
サービス側でのユーザーとの照合は、鍵に対して結びつけているコマンドに含めている場合もあれば、サービスのコマンドが鍵コマンドとして呼び出された後で行っている場合もある。
つまり、SSHは使用した公開鍵はユーザーに登録されている鍵のどれと一致するかという照合だけれど、ホスティングサービスの場合は鍵がどのユーザーであるかの認証にも使われている。
手順と方法
鍵の生成
ssh-keygen -t ecdsa -f ~/.ssh/nameofkey_ecdsa
- このコマンドでは
~/.ssh/nameofkey_ecdsaという秘密鍵と~/.ssh/nameofkey_ecdsa.pubという公開鍵が生成される - アップロードするのは
.pubのついているほう。基本的に内容そのままコピペすればいい -
-t ecdsaでECDSA鍵を指定している- 実際のところ現在最も推奨されるのは
-t ed25519で生成できるEd25519鍵 - ただ、ごく稀にEd25519鍵に対応していない古い環境が存在するので、互換性も踏まえるとECDSA鍵が無難
- デフォルトであるRSA鍵はもはや非推奨のもの。サービスによっては使えない
- 実際のところ現在最も推奨されるのは
- ファイル名が
_ecdsaで終わっているのは制約ではなく慣例- 省略時の名前が
id_${type}になるようになっている
- 省略時の名前が
Windowsの場合は~/が使えないので、Powershellならこんな感じ
ssh-keygen -t ecdsa -f "$env:USERPROIFLE\.ssh\nameofkey_ecdsa"
cmdだとこう
ssh-keygen -t ecdsa -f "%USERPROFILE%\.ssh\nameofkey_ecdsa"
authorized_keysへの登録
今回の話題で自力でやる人は少ないと思うけれど、authorized_keysは1行1エントリなので、.pubの内容を行として追加するだけでいい。
リモートホスト側に書くこと、ログインしようとしているユーザーのものに書くことに注意。
SSHクライアントの設定
LinuxやMacの場合は~/.ssh/configが設定ファイルなので、コマンドラインから任意のエディタで開くようにすれば良い。
Windowsのほうはちょっとややこしい。
ssh-keygenした時点で~/.ssh自体はあると思うので、エクスプローラでパスとして
%USERPROFILE%\.ssh
を開き、そこのconfigファイルに書く。なければ作る。
テキストだけど、拡張子はないので間違って拡張子をつけないように。
内容的にはこんな感じ
Host code
HostName 192.168.2.10
User git
Port 222
IdentityFile ~/.ssh/nameofkey_ecdsa
IdentitiesOnly yes
説明
-
Host-sshコマンドでホスト名として識別される名前。好きな名前でいいけど、ホスト名として使える範囲 -
HostName- 通常SSHで打つアドレス。本当のアドレス -
User- ログインしようとするユーザー。ホスティングサービスだとfoo@みたいにアドレスに指定されている (大抵はgit) -
Port- 指定があれば書く(アドレスでは:222みたいな感じ)。なければ書かない -
IdentityFile- 生成した使用する鍵ファイル(秘密鍵)のパス。Windowsでも~/が使える -
IdentitiesOnly- たくさん鍵がある場合は書いておいたほうが良い
もしくはHostにホスト名を書けばHostとHostNameが同一という前提で扱われ、含めたいオプションだけ書くこともできる。
ホスティングサービスを使う場合は「sshの場合はこのコマンドをコピペしろ」みたいなのが出るので、そっちのほうが楽かもしれない。
Host github.com
IdentityFile ~/.ssh/github_ed25519
IdentitiesOnly yes
権限
リポジトリには「ファイルとしてのread/writeの権限」という概念がある。
- read -
clone,pull,log,diff - write -
push,commit,merge……
つまり、取得するだけならば読み出し権限があれば良い。
GitHubなら、アカウントすら持っていない状態でもgit cloneで手元に持ってきたり、git pullで差分を取得したりすることはできる。
それ以外のほとんどの操作はwrite権限がいる。
リポジトリに変更を加える操作だからだ。
SSHでログインしていればそのユーザーとして操作できるので、そのユーザーが(ファイル権限的に)所有しているリポジトリであれば当然操作が可能。
NASだとSFTPアクセスを可能にすることでついでにSSHでもアクセスできるようになったりするので、この方法でやるのが簡単。
また、ローカルに所有しているファイルであれば問題ないので、Dropboxのようなクラウド同期されるフォルダにリポジトリを作って、それぞれのホストは自分のホストにあるリポジトリからワーキングツリーを取る、というmirrored originな方式も可能。
共有環境を構築
クラウド同期経由
まずは一番簡単なクラウド同期が使えると仮定しよう。
まずは中央的に使うリポジトリを作る。
ここでは~/syncが同期されているフォルダだと仮定する。
とりあえず~/syncリポジトリ置き場を作って移動して
mkdir ~/sync/repos
cd ~/sync/repos
ベアリポジトリを作る。
ベアリポジトリは慣例上、.gitをつける。
.gitをつけておくと、Gitからは.gitをつけてもつけなくてもアクセスできるようになる。
git init --bare foo.git
これでリポジトリはクラウド同期によって各ホストからアクセスできるはずだ。
今度は同期されない場所にワーキングリポジトリとしてクローンする。
cd ~/Documents
git clone ~/sync/repos/foo.git
SSHホスト経由
sshd_config
設定ファイルは/etc/ssh/sshd_configにある。
簡単にセキュアで安心できる環境を作るなら、この設定ファイルで次のようにする。
DenyUsers root
AuthenticationMethods publickey
これで公開鍵のみでログインできるようになる。
(rootユーザーでのログインは無条件に禁止。)
sshdの起動と構成
Archlinux / Manjaro Linux
対象パッケージはopenssh。
sudo systemctl enable --now sshd # 起動時に自動起動 & 即時起動
Mac OS X
共有→リモートログイン
から設定する、らしい。
(私はMacを持っていないから詳しくは知らない。)
Windows
Windowsの場合はちょっと話が難しい。
OpenSSHサーバーがデフォルトで動いていないのもそうだが、それ以上にGitがLinux的システムを期待した動作をするのに対し、Windowsがunixとの互換性に乏しいことから問題が起きやすい。
結局、その問題からGitはMSYS2を用いて動作するようになっているのだが、そうするとGitのサーバーとして使う場合もMSYS経由でGitを使うように設定する必要が出る。
なので、WSL2を使ってLinuxから環境を作ったほうが楽だと言う結論になった。
ここではWSL2を使ったことがない人に向けて手順を説明する。
まず、「Windowsの機能の有効化または無効化」を開き、
- Linux 用 Windows サブシステム
- 仮想マシンプラットフォーム
の2つにチェックを入れてOKする。ここで再起動が必要。
次にPowerShell1を開き、次のように入力する。
wsl --update
続いてArchlinuxをインストールする。
wsl --install archlinux
一旦Windows Terminalを閉じて開くと、タブオプションにArchlinuxが追加される。
最小の環境としては次のようにするといい。
pacman -Syu
pacman -S zsh grml-zsh-config openssh nano
chsh # /bin/zshに変更する
nano /etc/ssh/sshd_config # 先に示したsshd_configの設定。Ctrl+Oで保存、Ctrl+Xで終了
useradd -m -s /bin/bash -U gitter
systemctl enable --now sshd # WSLのArchlinux起動中にsshdが起動するように
そしてファイアウォールを通過させ、WSLへポートを転送するためWindows側でPowerShellを管理者権限で開いて次のようにする。
この設定も永続なので1回でいい。
New-NetFirewallRule -DisplayName "WSL SSH" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 2222
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=22 connectaddress=127.0.0.1
これで外部ホストからWindowsの2222番ポートへのアクセスを仮想インターフェイスごしにlistenしているWSLの22番ポートに転送する。
WSL内へ接続させるためにはWSLのArchlinuxが起動している必要があることに注意すること。
クライアント側SSH鍵の生成と登録
SSH鍵の生成はssh-keygenで行う。
ssh-keygen -t ed25519 -f ~/.ssh/repokey_ed25519
Windowsでは~/が解釈されないのでこうなる。
ssh-keygen -t ed25519 -f %USERPROFILE%\.ssh\repokey_ed25519
これで秘密鍵~/.ssh/repokey_ed25519と公開鍵~/.ssh/repokey_ed25519.pubができる。
公開鍵は
ssh-ed25519 AAAAxxxxxxxxxxxxxxxxAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx haruka@qiita
みたいな形式になっていて1行で完結する。
公開鍵の登録はログインされる側のホストのログインされるユーザーの~/.ssh/authorized_keysに対して行うが、1行1鍵になるので、このままコピペしてもって行けばいい。
# ログインされるホストのログインされるユーザー環境
mkdir ~/.ssh # なければ
nano ~/.ssh/authorized_keys # nanoを使う場合
# nanoはCtrl+Oで保存、Ctrl+Xで終了
鍵を登録したら~/.ssh/configとして次のように書いておく。
Host gitrepository # sshコマンドで使う識別名。なんでもいい
HostName foohost.local # 実際のホストの名前/アドレス。LAN内のDHCP配布されているホストならmDNSを使うのが簡単
User gitter # リモート側のユーザー名
Port 2222 # リモート側のsshdのポート。デフォルト(22)のままなら省略
IdentityFile ~/.ssh/repokey_ed25519 # 秘密鍵
IdentitiesOnly yes # つけておくと後でいいことがあるかも
これで、
ssh gitrepository
でログインできるようになった。
ベアリポジトリの準備
ssh gitrepository
でログインした上で、リポジトリを準備する。
例えばこんな感じ。
mkdir -v repos
cd repos
git init --bare foorepos.git
ワーキングリポジトリにクローン
それぞれのクライアント環境から次のようにする。
git clone gitrepository:repos/foorepos.git
Gitホスティングを使う
下準備
GitHubのようなGitホスティングはサービスによってそれぞれ違うので一概には言えないがだいたい次のような手順になる。
アカウントの作成と準備
- アカウントを作る
- SSH鍵を作る
- SSH鍵をアカウントに登録する
- SSH鍵を検証する
SSH鍵の検証はサービスによってあったりなかったり。
ForgejoやGiteaの鍵検証のコマンドはうまく動かないので、
echo -n "$TOKEN" | ssh-keygen -Y sign -n gitea -f ~/.ssh/repokey_ed25519
という感じ。
ちなみに、ForgejoやGiteaは異様にトークンの有効期限が短いので、めっちゃ急いでやる必要がある。
リポジトリ
ホスティングサービス側のメニューからリポジトリを作る。
publicかprivateかという選択は間違えないように注意すること。
publicにした場合、全世界に公開される。
cloneするためのコマンドはホスティングサービス側で出ることが多いが、基本的にSSH鍵は自動認識されることを前提にしているから、~/.ssh/configで
Host github
HostName github.com
User git
IdentityFile ~/.ssh/repokey_ed25519
IdentitiesOnly yes
とかしておいて、
git clone github:exampleuser/examplerepo.git
のようにするほうが確実。
共同作業と同期
設定
ユーザー名とemailの設定が必要。
これらはコミットで検証される。
emailの有効性はGit自体では関係ないけれど、Gitホスティングサービスや、リポジトリの公開には影響がある場合がある。
git config user.name "John Doe"
git config user.email "john@example.com"
これはリポジトリ上でリポジトリに対して行う操作。--globalをつけるとシステム全体のデフォルトの設定になる。
上流の変更の取り込み
git pull
現在のブランチに対して取り込む。
もしdevelopブランチに上流のdevelopブランチの内容を取り込みたい場合は
git checkout develop
git pull origin develop
のようになる。
コミット
これは前回の記事の通りなのだけど、事前に必ずpullしておかないと事故のもと。
git pull
git status # 確認を入れること
git diff # 確認その2
git add -A
git commit -m "コメント"
上流へ反映
デフォルトのブランチ(masterもしくはmain)からデフォルトのブランチへの反映ならpushするだけ。
git push
今developブランチにいて、上流にdevelopブランチ(存在しなくても良い)に反映したい場合はこう。
git push origin develop
マージ
最後にpullしてからcommitする前に歴史が作られてしまうと、歴史が混在した状態になるので、pullしたときにマージが必要になる。
git merge
競合がなければこれで済む。
場合によっては
git config pull.rebase false
みたいに事前にやっておく必要がある。
競合
Gitの辛い状況が競合。
これは、同じファイルを取り込まれた変更と手元の変更の両方で変更していて、なおかつそれがGitが自動解消できない場合に発生する。
こうなると、ファイルに
<<<<<<<
手元側の内容
=======
取り込まれた内容
>>>>>>>
という表示がつく。
これをもとにファイルの整合性をとってからcommitする。
これはVSCodeだとめちゃくちゃわかりやすくなっているので、競合の解消はVSCodeをおすすめする。
個人利用・マルチホストのフロー
- 作業するホストのリポジトリで
git pullする - 作業する
- 作業が終わったら
git add -A && git commit -m "コメント" && git pushする
基本的にはこれだけ。
ちゃんと最初にpullすること、終わったらコミットしてpushすることを忘れなければ、本当にこれだけで複数ホストで切り替えながらの作業が可能。
続き
続編はチーム開発向けなので、一般向けの内容は本記事で終了。
解説記事
Qiitaに掲載したGit記事の裏話と、ちゃんとした説明 @Chienomi
-
PowerShellを使うにしてもWSLを使うにしても、Windows Terminalを導入して使うのがおすすめ。Windows 11ならデフォルトインストールされている。 ↩
