おひとり Github ライフを快適に過ごす gh コマンドを作った - 2017冬

※ これは、[Windows で Git - 2017冬](https://qiita.com/kikuchi_kentaro/private/8734cedbab9a0f1ada9f) からの続きです

![create.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/e4be0ec0-0b87-3db0-220e-02deae4c743b.gif)

# tl;dr

- **[gh](https://gist.github.com/kentork/3d4be89cdd501d815e8ab03268bbb41c)** というコマンドを作った
    - Github から興味あるプロジェクト落としてくる & あとで探すのサクッとしたい
        - まぁまぁ楽になった
    - Github にプロジェクト作るのをサクッとしたい
        - 同様に **gh** を使った
        - まぁまぁ楽になった
    - チームで Github 使うの楽にしたい
        - 未熟者ゆえ見えない
        - これはいつかまた

# Github

言わずと知れた、Git リポジトリホスティングサービス。

## Github との付き合い方

自分の中には、Github と付き合って行く上でのいくつかの異なる姿勢がある。

① 他者プロジェクトの成果の利用
② 個人的プロジェクトの開発
③ 他者プロジェクトへの貢献
③ チームとしての開発

④ は経験不足ゆえに自分の中で未だ固まりきれてはおらず、今回は ①, ②, ③ をメインにまとめる。

ひとりぼっちの Github が今始まる

# ① 他者プロジェクトの成果の利用

現在、OSS の多くが Github 上にホスティングされ、且つプロジェクトのマネジメントツールとして利用されている。Github を開かない日が無いくらいである。

### Github の Web サイトで完結できること

Github の Web サイト自体はそれなりに使いやすく、以下のケースでは Web サイト上でほぼ完結できる。

- README や Wiki で使い方や情報を得る
- 発生したエラーについて、Issues や Pull Request に言及が無いか検索
- 数枚のソースファイルの中身をチェック
- Release にあるバイナリをダウンロード

しかし、それでは足りないケースもある。

### Github からソース持ってきたい

- ソースコードをガッツリ見たい
- 動かさなきゃ分からない
- テンプレートとして利用したい
- 独自にビルドして使いたい

てなことを考えると、途端に **手元 ( ローカル ) に欲しくなる**。

### でも、結構面倒なんです

#### ● 取得する

1. Github 見ていて、『あ、これ手元にほしい』って思う
1. **どこに clone するか考える**
    - あとで **探しやすい** 場所を考える
        - どうせ分からなくなるんだけど…
1. clone したい先のフォルダが無い場合、フォルダを作成
1. Terminal でさっきのフォルダに移動
1. ようやく `git clone << URL >>`

#### ● 探す

1. 落としたはずの場所にない
1. 別の場所に2つある
1. 変に階層化されてる
    1. Fork 元と Fork 先があって名前かぶったり

これをもっと楽にしたい。

## 『いきなり Clone』

とにかく、欲しいと思ったら、いきなり取得したい。
あとで探すとか考える暇もなく。

### 為の前準備

突然ですが、前準備します。
[Scoop](https://qiita.com/kikuchi_kentaro/private/77793e4a21db6ffdb7cd) 使います。`setenv, setpath 関数` [[ここ参照]](https://gist.github.com/kentork/bf783d2a5378f32dbacb40d8897e7942) も使ってます。

特にこだわりが無ければ、以下を実行してさっさと次に進んでください。

```ps1
PS> scoop install go fzf hub gibo micro ripgrep file grep
PS> mkdir ~/.go/bin
PS> setenv GOPATH $(Convert-Path ~/.go)
PS> setpath ~/.go/bin
PS> go get -u github.com/motemen/ghq github.com/sonatard/ghs github.com/nishanths/license
PS> mkdir ~/.ghq
PS> setenv GHQ_ROOT $(Convert-Path ~/.ghq)
```

以下、個別に解説。

| 名前 | 用途 | Scoop | Go Get | 備考 |
|---:|:---|:---|:---|:---|
| **[Go](https://golang.org/)** | Go ツールのビルド | scoop install go | |
| **[ghq](https://github.com/motemen/ghq)** | Github から Clone を簡単に | | go get -u github.com/motemen/ghq| [作者解説記事](http://motemen.hatenablog.com/entry/2014/06/01/introducing-ghq) |
| **[ghs](https://github.com/sonatard/ghs)** | Github のプロジェクト検索を手軽に | | go get -u github.com/sonatard/ghs | [作者の解説記事](https://qiita.com/sonatard/items/c11063cd3671c07b6e0a) |
| **[hub](https://github.com/github/hub)** | Github 操作用 CLI クライアント | scoop install hub | go get -u github.com/github/hub | |
| **[fzf](https://github.com/junegunn/fzf)** | インクリメンタルにファジーに検索 | scoop install fzf | | |
| **[ripgrep](https://github.com/BurntSushi/ripgrep)** | Grep 検索 | scoop install ripgrep | | ag が好きならそっちでも (scoop install ag) |
| **[chroma](https://sourceforge.net/projects/mingw)** | Syntax Higlight | | go get -u github.com/alecthomas/chroma/cmd/chroma | |
| **[gibo](https://github.com/simonwhitaker/gibo)** | `.gitignore` 生成 | scoop install gibo | | |
| **[license](https://github.com/nishanths/license)** | Github 対応ライセンス生成 | | go get -u github.com/nishanths/license | |
| **[micro](https://github.com/zyedidia/micro)** | CLI テキストエディタ | scoop install micro | | |
| file, grep | お馴染みコマンド | scoop install file grep | | |

## 『続・いきなり Clone』

**理想**は、

- 『あ、これ手元にほしい』と思ったら即 clone できる
    - ある程度曖昧な記憶でも、探してきたい
- 取ってきたものをすぐ探せる
    - ある程度曖昧な記憶でも、探してきたい
- Github の Git 管理外の情報 ( Issues, Pull Request ) は、Github のページで見たい
- 直接エディタも開きたいし、エクスプローラ開きたい
    - GUI との連携は必須、Windowser なので

これを、先程インストールしたツールを組み合わせて実現する。

### gh

上記を実現するため、`gh` というコマンドとしてまとめた。
この [Gist](https://gist.github.com/kentork/3d4be89cdd501d815e8ab03268bbb41c) にある関数群で構成される。Powershell 製である。

```sh
usage: gh <subcommand> [option]
  register [<account>]     - register account to gh

  create                   - create new Github repo

  clone(get) [<repo>]      - search a repo incrementally (or specified <repo>) and clone it

  show .                   - show the Github page of the current directory repo
  show [<repo>]            - select from local repos incrementally (or specified <repo>) and show its Github page
  show -g                  - search in global Github and select incrementally, and show its Github page
  open                     - alias to 'show -g'

  rm [-r] .                - remove the current directory repo
                           '-r' can make deleting with remote repo (require the token has 'delete repo' scope)
  rm [-r] [<repo>]         - select from local repos incrementally (or specified <repo>) and remove it

  list(ls)                 - list local repos

  cd    [<repo>]           - select from local repos incrementally (or specified <repo>) and move to there
  pushd [<repo>]           - select from local repos incrementally (or specified <repo>) and move to there with saving previous path to stack
                             if you will undo moving, enter 'popd'
  pwd   [<repo>]           - select from local repos incrementally (or specified <repo>) and output its path
  dir   [<repo>]           - select from local repos incrementally (or specified <repo>) and launch explorer
  edit  [<repo>]           - select from local repos incrementally (or specified <repo>) and launch editor

  find . [-e][-d]          - find a file incrementally in the current directory repo
                             '-e' you can open it with an editor
                             '-d' you can open it with an explorer
  find   [-e][-d] [<repo>] - select from local repos incrementally (or specified <repo>) and find a file incrementally in it

  grep . [-e][-d]          - grep contents incrementally in the current directory repo
                             '-e' you can open it with an editor
                             '-d' you can open it with an explorer
  grep   [-e][-d] [<repo>] - select from local repos incrementally (or specified <repo>) and grep contents incrementally in it
```

以下、こんな感じに使ってます、というのをユースケース毎に見ていく。

### ◆『あ、これ手元にほしい』と思ったら即 Clone

**Q:** Github のページを見ていて、『へぇ、これ面白そう』と思ったら
**A:** プロジェクト名をコピーして、こんな感じ
![get.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/a30f74ba-b629-2e50-669d-bc7876eb7ec9.gif)

```ps1
PS> gh get jaredpalmer\react-fns
PS> gh ls
PS> gh cd  # インクリメンタルサーチ
```
※ Github のベース URL ( `https://github.com/<account>/<repo>` ) を取得するため、[拙作の拡張](https://chrome.google.com/webstore/detail/copy-github-url/gfpgjjgfendkmmeackcancgddjkanmgc)を使ってます。

**Q:** 前に見た、gizmo という NYTimes 製の Go フレームワーク落としたいな
**A:** こんな感じ
![get002.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/a2407c14-a397-e64d-5996-4eae35820c1c.gif)

```ps1
PS> gh get
Searching - enter the query string: gizmo
# 検索結果からインクリメンタルサーチ
# 'facebook/' のように、最後に '/' 入れると、そのアカウントのリポジトリがリストされる

PS> gh ls
PS> gh cd  # インクリメンタルサーチ
```

**Q:** 削除したい
**A:** 以下コマンドで、インクリメンタルサーチしての削除も可能

```ps1
gh rm
```

### ◆取ってきたものをすぐ探せる

**Q:** 『あのリポジトリ取ったっけ』『mobx のリポジトリどこだっけ』『mobx のリポジトリに移動したい ( 終わったら戻りたい )』
**A:** こんな感じ
![ls.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/1d6a049c-6560-00ba-9c48-7744b44c19cc.gif)

```ps1
PS> gh ls     # ローカルリポジトリ
PS> gh pwd    # インクリメンタルサーチして、パス表示
PS> gh pushd  # インクリメンタルサーチして、現在パスをディレクトリスタックに残したまま移動

移動先で色々作業

PS> popd      # ディレクトリスタックにある元のパスに戻る
```

### ◆ Githubのページが直ぐ見られる

**Q:** あのリポジトリの Issues 見たい
**A:** こんな感じ
![show001.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/74f5242b-c3d7-5f49-ceab-7d66c85179f5.gif)

```ps1
PS> gh show    # インクリメンタルサーチして、Github ページ表示

PS> gh cd
PS> gh show .  # 現在位置の Git の origin の Github ページ表示
```

### ◆ 直接エディタも開きたい

**Q:** gizmo プロジェクトをエディタで開きたい・エクスプローラで開きたい
**A:** こんな感じ
![edit.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/6cb48f37-10e0-b406-2f90-8d5f741f6ce1.gif)

```ps1
PS> gh edit    # インクリメンタルサーチして、その Repository を VS Code で開く
PS> gh dir     # インクリメンタルサーチして、その Repository をエクスプローラで開く
```

現在は VS Code が起動するが、書き換えれば変更は可能。

```ps1:profile.ps1
...
$IDE    = "code {0}"         # `gh edit` で起動されるエディタ
$EDITOR = "subl -n -w {0}"   # スクリプトのフローに組み込まれたエディタ (Wait 必須)
# $EDITOR = "micro {1} {0}"
...
```

#### ★ VS Code でもっと探しやすく

VS Code には、**[Git Project Manager](https://marketplace.visualstudio.com/items?itemName=felipecaputo.git-project-manager)** という拡張がある。

![gpm.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/4205fa4d-2df2-f0a2-3e5e-4ee97e863fe0.gif)

これは、指定のフォルダ以下にある Git リポジトリを一覧してくれて、そこから選択して開けるようにしてくれる優れもの。
ここに、ghq で落としてくるフォルダを指定しておけば、`Crlt + Alt + P` か `Command Pallet → GPM: Open Git Project` から gh 管理プロジェクトが開けるようになる。

```json:VSCode_UserSetting
  "gitProjectManager.baseProjectsFolders": [
    "C:\\Users\\<< user name >>\\.ghq"
  ],
  "gitProjectManager.checkRemoteOrigin": false,
  "gitProjectManager.maxDepthRecursion": 1,
```

### ◆ おまけ : ファイル探したい
![find.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/f6e7ff95-2118-18bf-4779-1135a9750cce.gif)

```ps1
PS> gh find     # まずは検索対象を選択し、その中のファイルをインクリメンタルサーチ
PS> gh find -e  # 探したファイルをエディタで開く
PS> gh find -d  # 探したファイルのあるフォルダをエクスプローラで開く

PS> gh find .      # カレントディレクトリのリポジトリ内検索もできる
```

grep もある ( けど、`fzf` プレビューが上手くいかない…

```ps1
PS> gh grep 
PS> gh grep -e
PS> gh grep -d
```

ただ、検索系は option 命な所あるので、自分なりなカスタマイズは必要そう。

## 所感

それなりに満足している。
リポジトリを取ってくるハードルはだいぶ下がったように思う。

# ② 個人プロジェクトの開発

昔は、『こういうもの作りたいな』と思ったら適当にフォルダ作ってゴリゴリ書いて、数日後には忘れてしまっていた。

Git で管理するようになってから、少なくとも作業履歴は残る様になったが、自分がこれから何をしようとしていて、今何が問題なのか、統一した管理ができていないので有耶無耶になる。

結局、ひとりで開発しているプロジェクトであっても、最初から Github を使うのが良いんじゃないのかと思っている。
少し前から Personal ユーザでも Private Repository は無制限にもなった訳だし。

## 『いきなり Create New Repository』 への壁

そこでよく面倒と感じる事が、

- そもそも、Github 上に Repository 作るのが結構手間
    - Web ページ開いて作成 ~ ローカルへの Clone
- 複数アカウントの管理
    - 鍵管理
        - ホスト名が同じだと、`~/.ssh/config` では切り替えられない
            - 別名 ( `Host github-private` とか ) で頑張ったりもするけど…
    - `git config user.name` や `git config user.email` の切り替え
        - 気付かずに Commit → Push して、後から気がつくとか
- git プロトコルに対する壁
    - 社内ネットワークから外に出られない

など。

**理想**としては、

- CLI からサクッと Github 上に作って欲しい
    - `npm init` みたいに、Enter 連打で作りたい
- 複数アカウントでも、間違いなく操作できる
    - 認証
    - git config
- git ( ssh ) の制限に引っかかりたくない

## 『いきなり Create New Repository』

とにかく、プロジェクト作ろうと思ったら即作りたい。
リモートにもローカルにも。

### 為の前準備

#### Github Token の取得

まずは、Github の Token を取得する必要がある。
以下の記事は非常に簡潔で参考になる。

[GitHub「Personal access tokens」の設定方法](https://qiita.com/kz800/items/497ec70bff3e555dacd0)

一つ注意が必要なのが、権限として **repo:*** と **user:email** を付与して欲しい事。
通常、repo だけで生成するケースが多いと思うが、メールアドレスも取りたいので付ける必要がある。
( 削除機能を利用するためには、**delete_repo** も必要。後述 )

## 『続・いきなり Create New Repository』

それでは、いきなり化していく。
① と同様、**`gh`** コマンドを使っていく。

### ◆ トークンの登録

![register.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/9990a45d-c49e-d153-fe28-c80fb048de26.gif)

```ps1
PS> gh register
```

ここでアカウント名とトークン登録しておくと、後々便利になる。
登録は複数可能。組織アカウントも可能。キーはアカウント名 or 組織名なので、同じトークンで複数アカウント登録もできる。

### ◆ リポジトリ作成
![create.gif](https://qiita-image-store.s3.amazonaws.com/0/20198/e4be0ec0-0b87-3db0-220e-02deae4c743b.gif)

```ps1
PS> gh create
```

ここで重要なのは、作ったと同時にローカルにもリポジトリができるのだが、そのリポジトリの git config, git remote を以下のように書き換えていること。

```ps1
PS> git config --local user.name
kentork

PS> git config --local user.email
4198504+kentork@users.noreply.github.com

PS> git remote -v
origin  https://kentork:<<先程登録したトークン>>@github.com/kentork/sample-mobx (fetch)
origin  https://kentork:<<先程登録したトークン>>@github.com/kentork/sample-mobx (push)
```

アカウントに紐付いたトークン・ユーザ名・メールアドレスを設定している為、複数ユーザ利用時の git config の切り替えも不要、鍵の切り替えも不要。

https なので git の壁も越えられる。

[参考記事: https+アクセストークンを使ってGitHubのアカウントを使い分ける](https://qiita.com/tq_jappy/items/6e2f81f372e4abaa5139)

###  ◆ 実は、`gh get` でも

実はこの書き換え、`gh get` している時にも同様に git config, git remote に設定している。
なので、既存のリポジトリでも同様の運用ができる。

書き換え自体は `gh` で行っているが、書き換えている設定は git 標準の機能なので、以降は普通に git コマンドで操作可能。

###  ◆ 不要になったら、削除したい

気軽に作れる為には、気軽に削除できないと駄目。
Token 作成時に、 `:delete_repo` sucope を付与していれば、以下コマンドで Github リポジトリも一緒に削除してくれる

```ps1
gh rm -r
```

## 所感

これもそれなりに満足している。
何か新しいことを始めようとした時、『まず Github に作るか』からスタートする様になった。

# ③ 他者プロジェクトへの貢献

ここで想定しているのは、『このライブラリ便利だけど、バグあるな』という時に、それを解消しよう、という姿勢 ( ひとりで Github とは言え、ちょっとした部分は協力して直していきたい )。

## Pull Request

Github 上で開発する場合、基本的には **Pull Request** で機能を改善してく。
やり方として、いくつかのバリエーションが存在する

### a. **Shared Repository Model** - 共有リポジトリ上でブランチーベースの Pull Request

あなたと私の共有リポジトリ上でブランチを切ってそのブランチ単位で Pull Request を出す。
組織に所属して Write 権限与えられてたり、Collaborators として招待されてないとできないので、知らない人といきなりは難しいかも。

### b. **Fork and Pull Model** - フォークベースの Pull Request

自アカウントにフォークして、そっちに修正を上げて本体に Pull Request を出す。
赤の他人であっても迷惑かけずにできるので、サクッと始めやすいのはこっち。

## 『いきなり Fork』 

ひとりで Github なので、ここはやはり Fork ベース。
Fork ベースと言っても、微妙に違うやり方が 2 通りある。

b-1. Fork したリポジトリを Clone して開発を始める場合
b-2. オリジナルリポジトリの Remote に Fork したリポジトリを追加する場合

`gh` コマンドでは、このどちらにも対応していく

WIP...

# ④ チームとしての開発

開発フロー、ブランチ戦略、コードレビュー。
これについて語れるだけの経験がないし、正解も見えない。

沈黙。

# まとめ

ひとりでの Github を最大限活かす為の活動はまぁまぁ満足した。
次は、ちーむで Github を活かしたい。

# 補足

ここにまとめた事は Mac ユーザや Linux ユーザであれば既に知られているノウハウばかりであるが、Windows オンリーユーザには余り馴染みがないかもしれない。

そういう意味で、強く意識したのは、
CLI の良い所を取り入れつつも、しかしあくまで GUI 文化圏である Windows にフィットする形にする事。 

**Simple よりも Easy** 

だからこそ、もし `gh` を使ってくれる人がいるなら、自分に合わせてカスタマイズして欲しい。

# 参考

他プラットフォームでの利用も参考に。

- [ghq, peco, hubで快適Gitライフを手に入れよう！](https://qiita.com/itkrt2y/items/0671d1f48e66f21241e2)
- [ghqでリポジトリ管理とpeco連携で快適git生活](https://qiita.com/strsk/items/9151cef7e68f0746820d)
- [コマンドラインからgithubを検索してgit cloneをする with ghs, peco and ghq](https://qiita.com/sonatard/items/c11063cd3671c07b6e0a)

# おまけ

## `gh` コマンドの課題

1. git submodule 対応
    - git config, git remote  の書き換えが submodule まで反映できてない
1. fzf や micro 終了時に、コンソールが勝手に clearされる
    - 未だ原因不明
1. Powershell このまま使い続ける？
    - Powershell 書くのはまぁまぁ辛い
    - Go とか Python にしといたほうが良かったのかなぁ
1. テンプレート作成ツールの対応
    - `stack new` や `peract create` などのコマンドは、自分でフォルダごとプロジェクトを作ったり `README.md`, `.gitignore` を勝手に上書きしたりするので、`gh create` と共存できない
    - 一時フォルダのような場所を作り、_テンプレート作成 → 移動 → Github リポ作成_ みたいなフローが必要