Gitでサイトを更新したい！

git で手元でバージョン管理してるのにいちいち `git-archive` で差分を出して
展開して、FTPで持って行って上書きなんてめんどくさいなぁ・・・

コマンドだけででうまくできないものかと思った時に使える技。
SSHが使える場合とFTPのみ使える場合とで２パターン用意しました。

WindowsがメインPCなのでWindowsでも使える手順となります。
使うgitはオーソドックスにmsysgitです。

----

SSHが使える場合
--------------------
昨今のレンタルサーバーではSSHを利用できるオプションがあります。

- [ロリポップ！](http://lolipop.jp/) (スタンダードプラン、エンタープライズプラン)
- [heteml](http://heteml.jp/)
- [さくらのレンタルサーバー](http://www.sakura.ne.jp/) (スタンダードプラン以上)
- [XServer](http://www.xserver.ne.jp/)

git は `ssh://` をリモートリポジトリに設定してやるとSSH経由で操作を行えるのでこいつを使います。
今回は[ロリポップ！](http://lolipop.jp/)を使った場合を想定して進めます。


### リモートリポジトリの準備をする
まずpushするリモートリポジトリを用意します。
SSHでログインし、適当な場所でベアリポジトリを作成してください。
公開する必要はないので公開領域とは別の場所にしておくのがいいでしょう。

ロリポップ！ではルートディレクトリの `web` 配下が公開領域になるので
そのとなりに `git` を作成しました。この配下にサイト毎にリポジトリをポコポコ置いていきます。
とりあえず下記を想定しました。

```
/ (ルートディレクトリ)
┣ web (公開領域)
┃ ┗ example.xyz
┃ 　 ┗ www (www.example.xyz … 本番環境)
┃ 　 ┗ dev (dev.example.xyz … ステージング環境)
┃
┗ git (リポジトリを置く場所)
　 ┗ example.xyz
```

というわけで作りましょう。


```sh
# ホームディレクトリに移動
cd ~/

# リポジトリ置き場のディレクトリを作成
mkdir git
cd git

# example.xyz 用のディレクトリを作成
mkdir example.xyz
cd example.xyz

# example.xyz をベアリポジトリとして初期化
git init --bare
```

これで push 先の準備が出来ました。


### post-receive を設定する
ベアリポジトリにはイベントフックの機能があり、具体的には今回
リモートにpushした後に実行して欲しいコマンドを設定しようと思います。
この設定は作成したベアリポジトリの `hooks` ディレクトリに `post-receive` を作成することで可能です。

このファイルはサーバーのシステムで実行できればなんでも構いませんが
今回はオーソドックスにシェルスクリプトで書きました。
作成するファイルは下記のように配置します。

```
/
┗ git
　 ┗ example.xyz
　 　 ┗ hooks
　 　 　 ┗ post-receive (作成するファイル)
```

hooksの内部には他にもフック処理のサンプルファイルが含まれているので
覗いてみると勉強になります。多分。

post-receive の中身は下記のようにしました

```sh
#!/bin/bash

# ステージング環境
cd ~/web/example.xyz/dev
git --git-dir=.git pull origin dev:dev

# 本番環境
cd ~/web/example.xyz/www
git --git-dir=.git pull origin master:master
```

ステージング環境と本番環境を分けて設定しています。
それぞれのディレクトリに移動して、originからそれぞれのブランチを指定してpullしています。
`--git-dir=.git` はこれがないとうまく動いてくれないので指定しました。

あとは忘れずに `post-receive` に実行権限を付与します。

```sh
# ファイルと権限を確認
ls -la ~/git/example.xyz/hooks/post-receive

# 実行権限を付ける
chmod -x ~/git/example.xyz/hooks/post-receive

# 実行権限が付いているはず
ls -la ~/git/example.xyz/hooks/post-receive
```


### 公開領域の準備をする
`post-receive` に指定したように公開領域でpullする必要があるので
公開領域のディレクトリもgitリポジトリにする必要があります。
こちらはワーキングディレクトリを公開するファイルとするので
ベアリポジトリにする必要はありません。

サクッと `git init` で初期化し、リモートを先ほど作成した
ベアリポジトリに設定していきます。

```sh
# ステージング環境の公開領域の設定
cd ~/web/example.xyz/dev
git init
git remote add origin ~/git/example.xyz

# 本番環境の公開領域の設定
cd ~/web/example.xyz/www
git init
git remote add origin ~/git/example.xyz
```

これでpushすると中身を更新するように設定出来ました。


### 実際にpushしてみる
ローカル側からpushすればサーバーにあるファイルが更新されるはずです。
ローカルのリポジトリのリモート設定にレンタルサーバーの設定を追加します。

ロリポップの場合はユーザー管理画面からSSH接続設定を確認して下さい。

```sh
# ロリポップの管理画面から確認できるSSH設定が下記の場合
# アカウント: main.jp-hoge
# サーバー　: ssh001.lolipop.jp
# ポート　　: 2222

ssh://main.jp-hoge@ssh001.lolipop.jp:2222/~/git/example.xyz
```

あとは実際に `git push` してみてください。


自分は`post-recieve`で設定したとおり、
dev と master の２つのブランチを用意し git-flow よろしく下記の手順をとっています。

1. ローカルの dev ブランチであれこれ更新
2. 適当なタイミングで `git push origin dev:dev` しステージング環境を更新
3. ステージング環境で動作に問題がないことを確認
4. dev ブランチを master にマージ
5. `git push origin master:master` して本番環境の更新

### 備考
SourceTreeではSSH経由でログインする時にちゃんとパスワード認証ダイアログが出たのですが
GitExtentionsを使うときはPuTTY経由だとうまく認証できませんでした。
そういう場合にはOpenSSHを利用してやるとうまくいきました。

----

FTPは使える場合
--------------------
SSHが使えるレンタルサーバーはいいもののFTPしか無いとか、
SSHを使えるようにするだけで上位プランにするのはちょっと...という場合もあると思います。
そういった場合には `git-ftp` が使えます。

### git-ftp とは
http://git-ftp.github.io/git-ftp/

gitで管理した内容をftpでアップロードするための拡張コマンド。
これを追加すると `git ftp push` でサーバーに差分をアップロードできるようになります。

### git-ftp の導入
導入時の設定は Git Bash から行います。一度済ませれば大丈夫。
任意のディレクトリでgit-ftpを clone します。

```sh
git clone git@github.com:git-ftp/git-ftp.git
```

そのあとは clone した場所にPATHを通すか、
clone したファイルのうち git-ftp を `/bin` にシンボリックリンクを設定します。

自分は後者を選択しました。
git-ftpに実行権限があるかも念のため確認しておいてください。

```sh
ln -s path/to/git-ftp /bin

ls -la /bin/git-ftp
```

導入はこれで終わりです。プロンプトで `git ftp` と叩いて下記のような出力が得られればOKです。

```sh
$ git ftp
git-ftp <action> [<options>] <url>
```

### git-ftp の設定
git-ftpの設定はSSHを使う場合と比較してシンプルです。
サーバー側でフックしたり、pullしたりできないのですべてローカルでまかないます。

設定はお馴染み `git config` から行います。
`git ftp` を叩きたいリポジトリで下記をそれぞれ設定します。

```sh
git config git-ftp.url ftp://<ホスト名>/<公開ディレクトリまでのパス>
git config git-ftp.user <FTPユーザー名>
git config git-ftp.password <FTPパスワード>
```

`git-ftp.url` の設定ではセキュリティを考えて、FTPSが利用できる場合には
スキーマを `ftp://` でなく `ftpes://` を指定して接続するのが望ましいでしょう。

また公開ディレクトリまでのパスはFTPクライアントで接続した時のパスを指定します。
ココで指定したディレクトリにファイルが展開されるようになります。

### git-ftp でのリモートの初期化と更新
git-ftp で更新をかける際には初回のみ `git ftp init` をして
FTPサーバー側の初期化をしてやる必要があります。

実際は今いるブランチの最新のコミットのファイルをアップロードして
`.git-ftp.log`というファイルをリモートに作成してコミットIDを記録している様子。

```sh
# 初期化コマンド
git ftp init

# 進捗が気になる人はこっちで初期化
git ftp init -vv
```

これでOK。あとはおもむろに `git ftp push` するとその時点の最新のコミットまでの差分が
FTP経由でサーバーに反映されるようになります。

```sh
git ftp push
```

これでFTPしかなくても安心してgitで更新管理ができるようになりました。
なお注意する点はgit-ftpでアップロードした時には
リポジトリの情報そのものはアップロードされないという点です。

「FTPでリモートリポジトリアップしてるからだぁいじょうぶだぁっはっはーん」などといって
ローカルのリポジトリを安易に消したりしないよう注意しましょう。

[Github](https://github.com/) や [Bitbucket](https://bitbucket.org/) にpushしておくのも手かと思います。


### SSHの時みたいにステージングと本番を分けたい
git-ftpには service といって環境ごとに設定を分けることができる仕組みがあります。
使い方は簡単で git-ftp の設定の時に git config を下記のように設定してやります。

```sh
# ステージング環境
git config git-ftp.dev.url ftp://<ホスト名>/<公開ディレクトリまでのパス>
git config git-ftp.dev.user <FTPユーザー名>
git config git-ftp.dev.password <FTPパスワード>

# 本番環境
git config git-ftp.www.url ftp://<ホスト名>/<公開ディレクトリまでのパス>
git config git-ftp.www.user <FTPユーザー名>
git config git-ftp.www.password <FTPパスワード>
```

`git config git-ftp.<任意の文字列>.url` とかで指定するといいみたいです。

またこの設定を利用して`git ftp init` や `git ftp push`するときは下記のようにします。

```sh
git ftp init -s dev # ステージング環境の初期化
git ftp init -s www # 本番環境の初期化

git ftp push -s dev # ステージング環境の更新
git ftp push -s www # 本番環境の更新
```