18
Help us understand the problem. What are the problem?

posted at

updated at

Organization

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 に設定を書き込むのですが、今回は割愛させてください。

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

command/init.go
package command

import "github.com/codegangsta/cli"

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

}


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

command/init.go
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 checkout -b developを動作させるためにはこのように書きます。

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

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

command/init.go
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"を追加している点です。

command/feature.go
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]...)

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

command/feature.go
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 install

サブタイトルの通り、コマンドを実行するだけで、
$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度やってみてはいかがでしょうか??

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
18
Help us understand the problem. What are the problem?