msysgit で guilt を利用する

Vimのリポジトリも code.google.com での mercurial ベースの運用から、github へリポジトリを移してきていて、そろそろ hg + mq によるパッチ管理から、git + guilt へ移行しなきゃな、という事で、msysgitでguiltを導入したメモです。

[Git for Windows 2.7.0](https://github.com/git-for-windows/git/releases/tag/v2.7.0.windows.1) で確認しました。
*2016.09.05追記* [Git for Windows 2.10.0](https://github.com/git-for-windows/git/releases/tag/v2.10.0.windows.1) で確認し直したところ、変更点があったので追記しておきます。

以下は Git Bash での作業を想定しています。

guilt を clone する
---------------------

http://repo.or.cz/w/guilt.git からリポジトリを clone します。

```
cd ~/Work
git clone http://repo.or.cz/guilt.git
```

ghq を使っている場合は、以下で clone できます。

```
ghq get -u --shallow http://repo.or.cz/guilt.git
```

インストールする
----------------

### ファイルのコピー

#### `make` と `install` がある場合 (≒ MSYS, MINGW, [Git for Windows SDK](https://github.com/git-for-windows/build-extra/releases/latest) インストール済)

Git Bash を管理者権限で起動して、リポジトリのディレクトリで `make install` します。

#### `make` と `install` が未インストールな場合

 Git Bash を管理者権限で起動して、以下のようにファイルをコピーします。
 
 (guilt のソースディレクトリに `cd` している体で)

```sh
mkdir -p /usr/local/bin
cp guilt /usr/local/bin/
chmod 755 /usr/local/bin/guilt
mkdir -p /usr/local/lib/guilt
cp guilt-* /usr/local/lib/guilt/
chmod 755 /usr/local/lib/guilt/guilt-*
cp os.* /usr/local/lib/guilt/
```

### 追加の処理

msysgit (MSYS) 環境は元々サポート外です。

サポートされたOS環境かどうかは、`/usr/local/lib/guit/os.(システム名)` というファイルが存在しているかで判断しています。

システム名は `uname -s` の結果で判別していますので

```
cp /usr/local/lib/guilt/os.{Linux, `uname -s`}
```

もしくは

```
ln -s /usr/local/lib/guilt/os.{Linux,`uname -s`}
```
(Windows の NTFS でシンボリックリンクを貼りたい場合は管理者権限が必要なので注意)

として、動作するよう変更します。 (`/usr/local/bin/guilt`の該当する箇所を`Linux`に書き換えても可)

### guilt サブコマンド打ち間違い時にエラーが出るのを修正する

このままで一応 guilt は利用可能なのですが、サブコマンドを指定せずに単体で `guilt` を実行した場合や、
サブコマンドが見つからなかった場合に、Git for Windows では、以下のような理由によりエラーが表示されてしまいます。

#### guilt で利用されている find の `-perm +111` とという指定の仕方が最近の find では未サポート

**2016.09.05 追記**
本現象については、[2bd00ac2](http://repo.or.cz/guilt.git/commit/2bd00ac2e816a341723fa9209389dd9a246f8858) にて対応がされたため、ほとんどの場合で下記修正は不要になりました。

~~http://stackoverflow.com/a/4458361 によると GNU find のバージョン 4.5.12 から `+NNN` という指定はサポートされなくなった模様です。かわりに、 `/NNN` もしくは `-executable` を使うようになりました。~~

~~以下のように変更してください。~~

```diff
@@ -73,8 +73,8 @@

 guilt_commands()
 {
-       find "$GUILT_PATH/../lib/guilt" -maxdepth 1 -name "guilt-*" -type f -perm +111 2> /dev/null | sed -e "s/.*\\/$GUILT-//"
-       find "$GUILT_PATH" -maxdepth 1 -name "guilt-*" -type f -perm +111 | sed -e "s/.*\\/$GUILT-//"
+       find "$GUILT_PATH/../lib/guilt" -maxdepth 1 -name "guilt-*" -type f -perm /111 2> /dev/null | sed -e "s/.*\\/$GUILT-//"
+       find "$GUILT_PATH" -maxdepth 1 -name "guilt-*" -type f -perm /111 | sed -e "s/.*\\/$GUILT-//"
 }
```

#### `column` コマンドが Git for Windows には用意されていない

guilt が内部で使っている `column` コマンドは、MSYS2 にはあるのですが、Git for Windows には用意されていないため、その場合は使用しないように変更する必要があります。

そこで、以下のように修正します。

```diff
 # by default, we shouldn't fail
@@ -122,7 +122,7 @@
        disp "Guilt v$GUILT_VERSION"
        disp ""
        disp "Pick a command:"
-       guilt_commands | sort | column | column -t | sed -e 's/^/       /'
+       guilt_commands | sort | sed -e 's/^/    /'

        disp ""
        disp "Example:"
```

`column` をかまさないように変更したため、サブコマンドなしで `guilt` と実行した時に候補が縦に並んで表示されてしまいますが、そこは目を瞑りましょう。

### **2016.09.05 追記** Git for Windows 2.10 以降で、 guilt コマンド実行時にエラーになるのを修正する

これは自分だけの環境かもしれません。

Git for Windows 2.10.0 上で `guilt` コマンドを実行すると、以下のエラーが出力されるようになりました。

```
C:\Git\mingw64/libexec/git-core/git-sh-setup: 6 行: .: git-sh-i18n: ファイルが見つかりません
```

辿っていくと, git-sh-setup からの git-sh-i18n の呼出が、以下のようなっており、この処理がエラーとなっているようです。

```
. git-sh-i18n
```

そこで、`/mingw64/libexec/git-core/git-sh-setup` を以下のように修正します。

```diff
@@ -3,7 +3,7 @@
 # a few helper shell functions.

 # Source git-sh-i18n for gettext support.
-. git-sh-i18n
+. "$(git --exec-path)/git-sh-i18n"

 # Having this variable in your environment would break scripts because
 # you would cause "cd" to be taken to unexpected places.  If you
```



guiltベースへ引越し
-------------------

### guilt init

guilt はgitのブランチ毎にパッチセットを管理できるようで、masterブランチで`guilt init`した場合は`.git/patches/master`にパッチ用のディレクトリが作らます。

ので、必要に応じてブランチを変更しておきます。

```
cd /path/to/git_repo
git checkout -b test
guilt init
```

これで `.git/patches/test` ができているはず。

### mqからの移行

mq で利用していたパッチセットをコピーしてきます

```
cp /path/to/hg_repo/.hg/patches/{*.diff,*.patch} /path/to/git_repo/.git/patches/test/
```

`series` ファイルも (`#-`/`#+` によるパッチ適用/非適用のスイッチまで)互換性があるのでそのまま使えます。

```
cp /path/to/hg_repo/.hg/patches/series /path/to/git_repo/.git/patches/test/
```

mq と guilt で `status` ファイルのフォーマットに互換性はないので、ファイルが空の時を除いてコピーしてきてはいけません。

mqのリポジトリをそのまま使う
---------------------

よく考えたら、`status` 以外は互換性があるので、 mq のリポジトリを clone/checkout すればguiltにも利用できました。

```
mkdir .git/patches
hg clone https://bitbucket.org/path/to/mq .git/patches/test
```

guilt は空の `status` ファイルがないとエラーになるので (mqのリポジトリでは`status`はignoreされている)、これだけは追加してやらないといけないようです。

```
touch .git/patches/test/status
```

CMD や PowerShell から使う
-----------------------

```bat:guilt.cmd
@For %%i IN ("git.exe") DO @SET GIT=%%~dp$PATH:i
@IF NOT EXIST "%GIT%" @GOTO :end
@For %%s IN ("sh.exe") DO @SET SH=%%~dp$GIT:s
@IF EXIST "%SH%" @GOTO :execute
@SET GIT=%GIT:~0,-4%bin
@For %%s IN ("sh.exe") DO @SET SH=%%~dp$GIT:s
@IF NOT EXIST "%SH%" @GOTO :end
:execute
@SETLOCAL
@SET PATH=%PATH%;%SH%..\usr\bin
@"%SH%sh.exe" /usr/local/bin/guilt %1 %2 %3 %4 %5 %6 %7 %8 %9
@ENDLOCAL
:end
```

のような内容を `guilt.cmd` というファイル名で PATH の通ったディレクトリに保存しておけば、PowerShell等から `guilt` コマンドが利用可能です。

git を使う上で、`C:\Program Files\Git\cmd` か `C:\Program Files\Git\bin` のどちらかにPATHを通してあると想定していますが、環境によってうまく動かないかもしれません。

不具合
------

手元では、差し当って下記のみです。

- guilt help が動かない
  (Documentationディレクトリでmake installすればよさげ / msysgit にはそもそも man がない)