post Image
golangでgitのサブコマンドを作ろう

Golang とは

ってしっかりと説明する必要が無い気がするので簡単に、

  • コンパイル型のプログラミング言語
  • オブジェクト指向
  • 並列処理得意だよ

いろいろなところに採用されているので golang? しらん
って人も恩恵は受けているかも。

Gitサブコマンド とは

Shellのパスが通ったところに、git-hoge のようなファイルを置いていると
git hoge のように実行できるようになるものです。
多くの方が知っているかと思いますがそれを今回goで作ってしまおうというものです。

ちなみに、
ShellのものはGithub – tj/git-extras にまとめられています。

結局何するの??

Gitの開発フローは、GitFlowやGithubFlow, GitlabFlow などありますが、
会社やプロダクトによって異なりますよね。

GitFlowでは少しかゆいところに手が届かないので
ならば作ってしまおうと言う魂胆です

セットアップ

今回使用するものを自分のマシンに落としてきましょう
やり方は README しっかりに書かれてますね (当たり前

$ go get -d github.com/tcnksm/gcli
$ go get -d github.com/codegangsta/cli
$ go get -d github.com/tcnksm/gcli
$ cd $GOPATH/src/github.com/tcnksm/gcli
$ make install
====> Install depedencies...
go get -v github.com/jteeuwen/go-bindata/...
go get -v -d -t ./...
====> Install gcli in /Users/yuta/.go/bin ...

プロジェクト生成

セットアップが正常に完了したら、ベースとなるものを生成しましょう。
-cには、コマンドオプションを書いてください。

$ gcli new -c init,feature,hotfix,release,tag git-cirkit

====> WARNING: You are not in the directory gcli expects.
      The codes will be generated be in $GOPATH/src/github.com/Yuta Mizui.
      Not in the current directory. This is because the output
      codes use import path based on that path.

  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/README.md
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/CHANGELOG.md
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/feature.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/feature_test.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/main.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/.gitignore
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/release.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/release_test.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/hotfix.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/commands.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/init.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/version.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/init_test.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/tag.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/hotfix_test.go
  Created /Users/yuta/.go/src/github.com/Yuta Mizui/git-cirkit/command/tag_test.go
====> Successfully generated git-cirkit

Command owner (author) name. This value is also used for import path name. By default, owner name is extracted from ~/.gitconfig variable.

-oまたは-ownerを付け忘れてしまったので、上記のように生成されますね

気を取り直してもう一度

$ gcli new -c init,feature,hotfix,release,tag -o mziyut git-cirkit

====> WARNING: You are not in the directory gcli expects.
      The codes will be generated be in $GOPATH/src/github.com/mziyut.
      Not in the current directory. This is because the output
      codes use import path based on that path.

  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/README.md
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/CHANGELOG.md
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/feature.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/feature_test.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/main.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/.gitignore
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/release.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/release_test.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/hotfix.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/commands.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/init.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/version.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/init_test.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/tag.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/hotfix_test.go
  Created /Users/yuta/.go/src/github.com/mziyut/git-cirkit/command/tag_test.go
====> Successfully generated git-cirkit

生成されたようなので、とりあえずBuildしてみましょう。

$ go build github.com/mziyut/git-cirkit
$ ./git-cirkit                                                                                                                                                
NAME:
   git-cirkit -

USAGE:
   ./git-cirkit [global options] command [command options] [arguments...]

VERSION:
   0.1.0

AUTHOR(S):
   Yuta Mizui

COMMANDS:
   init
   feature
   hotfix
   release
   tag
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h       show help
   --version, -v    print the version

実際に書いてみよう

今回、開発フローが、git flowだったと仮定して作成しているので、
git initの動作を書いてみることにしましょう。

※ 本当は、.git/config に設定を書き込むのですが、今回は割愛させてください。

生成されたファイルはこのようになってます。

package command

import "github.com/codegangsta/cli"

func CmdInit(c *cli.Context) {
    // Write your code here

}


発行したいコマンドは、git chackout -b develop だとしましょう。
goからコマンドを実行するには、"os/exec"packageが必要なのでimportに書き足そうと思います。
ついでに、コマンドラインに出力出力するために、"fmt"も追加しましょう。

package command

import (
    "fmt"
    "github.com/codegangsta/cli"
    "os/exec"
)

func CmdInit(c *cli.Context) {
    // Write your code here

}

あ、ちなみに、import部分は、このようにも書けます。
書き方はお任せします。

import "fmt"
import "github.com/codegangsta/cli"
import "os/exec"

実際に、git chackout -b developを動作させるためにはこのように書きます。

exec.Command("git", "checkout", "-b", "develop").CombinedOutput()

最終的に以下のようになります。

package command

import (
    "fmt"
    "github.com/codegangsta/cli"
    "os/exec"
)

func CmdInit(c *cli.Context) {
    out, _ := exec.Command("git", "checkout", "-b", "develop").CombinedOutput()
    fmt.Println(string(out))
}

out, __ には errorが返ってきますが、今回はエラー処理しないので _にしています。

ではここまで書いたら、Buildしてみましょう。

 $ go build github.com/mziyut/git-cirkit
$ git branch
* master

$ ./git-cirkit init                                                                                                                               
Switched to a new branch 'develop'

予想していた通りの動作ができたと重います。
では、次に、featureを作ってみましよう。

inintを作成した時と同じように、importに
"fmt", "os/exec"を追加します。

initのときと異なるのは、コマンドライン引数を取得するために、
"os"を追加している点です。

package command

import (
    "fmt"
    "github.com/codegangsta/cli"
    "os"
    "os/exec"
)

func CmdFeature(c *cli.Context) {
    // Write your code here

}

featureではbranch番号(feature/◯◯◯)を取得し、branch名にするために、
文字の連結を行うのですが、以下のようにgoの文字連結は遅いので有名です。

文字列を構築する必要がある場合、[]byte型の値を作ってそれに文字列を追加していって、最後に値を文字列に変換するのがよい。

以上の投稿にある通り書かれているのでそのように実装していこうと思います。

command := os.Args[2]
branch := make([]byte, 0, 15)
branch = append(branch, "feature/"...)
branch = append(branch, os.Args[3]...)

最終的に以下のようになります。

package command

import (
    "fmt"
    "github.com/codegangsta/cli"
    "os"
    "os/exec"
)

func CmdFeature(c *cli.Context) {
    command := os.Args[2]
    branch := make([]byte, 0, 15)
    branch = append(branch, "feature/"...)
    branch = append(branch, os.Args[3]...)

    exec.Command("git", "checkout", "develop").CombinedOutput()

    switch command {
    case "start":
        out, _ := exec.Command("git", "checkout", "-b", string(branch)).CombinedOutput()
        fmt.Println(string(out))
    case "end":
        out, _ := exec.Command("git", "marge", string(branch)).CombinedOutput()
        fmt.Println(string(out))
    }
}

では(いろいろ不十分かと思いますが)、Buildしてみましょう。

 $ go build github.com/mziyut/git-cirkit
$ git branch
* master

$ ./git-cirkit feature start 1234                                                                                                                   
Switched to a new branch 'feature/1234'

とりあえずここまで書けたので、
実際に git cirkitで動作するようにしてみましょう

go insall

サブタイトルの通り、コマンドを実行するだけで、
$GOPATH/binに作成したものを配置してくれます。

それではやってみましょう。

$ git cirkit
git: 'cirkit' is not a git command. See 'git --help'.

実行前はありませんが・・・・

$ go install
$ git cirkit                                                                                                                                   
NAME:
   git-cirkit -

USAGE:
   git-cirkit [global options] command [command options] [arguments...]

VERSION:
   0.1.0

AUTHOR(S):
   Yuta Mizui

COMMANDS:
   init
   feature
   hotfix
   release
   tag
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h       show help
   --version, -v    print the version

さいごに

ここまで簡単に、Linux,Mac,Windows環境で動作するコマンドラインツールを作成できました。
簡単なツールであればすぐ作成できると思うので、
ぜひ1度やってみてはいかがでしょうか??

今回使用させて頂いたもの


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。