AllIsHackedOff

Just a memo, just a progress

Goでgitリポジトリを操作しようと軽くトライした話

はじめに

PerlRubyで色々ツールを作っていた頃
Git::Repository - search.cpan.org
GitHub - schacon/ruby-git: Ruby/Git is a Ruby library that can be used to create, read and manipulate Git repositories by wrapping system calls to the git binary.

この辺りを使って、差分を見てよしなに本番作業の手順書を自動生成したりしたのを思い出して、今でも個人プロジェクトではRubyとかPerlで描いてしまうことが多いのですが、Goでそのあたりを書くためにはどうすればいいか試行錯誤した話を書きます。

使用できるライブラリ

go git などでぐぐると下記ライブラリがヒットします。サンプルコードを動かすなどしてとりあえず触ってみることに (私のローカルの環境はgo1.6.3かgo1.7を使っています)

src-d/go-gitを試す

READMEには

go get -u gopkg.in/src-d/go-git.v4/...

と v4を go get して利用するように記載されているのですが、READMEに書かれたコードが動かず、出鼻をくじかれました。

% go run main.go                                                                                                                                 (git)-[master]
# gopkg.in/src-d/go-git.v4/plumbing/format/packfile
../../../../../../gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go:158: undefined: io.SeekCurrent
../../../../../../gopkg.in/src-d/go-git.v4/plumbing/format/packfile/scanner.go:314: undefined: io.SeekCurrent

problems install v4 · Issue #107 · src-d/go-git · GitHub こちらに書かれている通り

Hahaha, yes, we only support 1.7, とのことです。goのversionを変えたら正常に動作しました。

go1.6でも、

go get -u gopkg.in/src-d/go-git.v3/...

v3を go get してくれば無事に動く模様です。 (しかしインターフェースが若干異なるようです)

用例

用例をつらつら書こうと思ったのですが、下記公式の example がよく整理されています。 go-git/examples at 78516127590ef4a8157f8850be007966b142c1ca · src-d/go-git · GitHub

git pull でデプロイしている個人プロジェクトがあるので、それらのdeploy用の簡単なツールなど書いてみました。 (shellとかansibleで事足りるんじゃないかという話はあります)

複数の開発用サーバ(ホストに番号が振られている)などに一斉に変更を反映したい場合のツール作りなどに使えそうです (適当に全サーバにはいってgit pullなど)

libgit2/git2goを触る

lib2git自体が必要なので、Macで利用する場合はにhomebrewでdownloadします。

brew install lib2git

Github側にはドキュメントが少ないので godoc をせっせと読みながら使う感じになります。 git - GoDoc

使い方としてはgit側の使いたい機能を godoclib2git のドキュメとから引いて対応させていく感じです。 st

package main

import (
    "fmt"

    "github.com/libgit2/git2go"
)

func main() {
    repoPath := "path_to_git_repo"
    r, err := git.OpenRepository(repoPath)

    if err != nil {
        panic(r)
    }

    // conf := r.Config
    st, err := r.StatusFile(repoPath)
    fmt.Printf("%v", st)

    stlist, err := r.StatusList(&git.StatusOptions{
        Flags: git.StatusOptIncludeUntracked,
        Show:  git.StatusShowIndexAndWorkdir,
    })
    if err != nil {
        panic(err)
    }
    fmt.Println("%v", stlist)
}

まとめ

lib2gitに依存しないという意味では src-d/go-git はそれなりに使いやすそうだという感想です。 lib2gitを利用する方がAPIは色々提供されているし(各種バインディングがすでにあることで枯れている)ため、痒いところに手が届かないということはなさそうに見えました。