commit b1d50b70ea8c4be9c19fe91768d6653736660c4c Author: Vicent Marti Date: Tue Mar 5 20:53:04 2013 +0100 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..46a7407 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2013 The git2go contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..94aedea --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +git2go +====== + +Go bindings for [libgit2](http://libgit2.github.com/). Pre-release, things may or may not work. Operator get me Beijing-jing-jing-jing! + +License +------- + +M to the I to the T. + +Authors +------- + +- Carlos Martín (@cmn) +- Vicent Martí (@vmg) + diff --git a/blob.go b/blob.go new file mode 100644 index 0000000..91064b1 --- /dev/null +++ b/blob.go @@ -0,0 +1,26 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "unsafe" +) + +type Blob struct { + ptr *C.git_object +} + +func freeBlob(blob *Blob) { + C.git_object_free(blob.ptr) +} + +func (v *Blob) Contents() []byte { + size := C.int(C.git_blob_rawsize(v.ptr)) + buffer := unsafe.Pointer(C.git_blob_rawcontent(v.ptr)) + return C.GoBytes(buffer, size) +} + diff --git a/commit.go b/commit.go new file mode 100644 index 0000000..bba02e8 --- /dev/null +++ b/commit.go @@ -0,0 +1,52 @@ +package git + +/* +#include +#include + +extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); +*/ +import "C" + +import ( +) + +// Commit +type Commit struct { + ptr *C.git_commit +} + +func (c *Commit) Id() *Oid { + return newOidFromC(C.git_commit_id(c.ptr)) +} + +func (c *Commit) Message() string { + return C.GoString(C.git_commit_message(c.ptr)) +} + +func (c *Commit) Tree() (*Tree, error) { + tree := new(Tree) + + err := C.git_commit_tree(&tree.ptr, c.ptr) + if err < 0 { + return nil, LastError() + } + return tree, nil +} + +func (c *Commit) TreeId() *Oid { + return newOidFromC(C.git_commit_tree_id(c.ptr)) +} + +/* TODO */ +/* +func (c *Commit) Author() *Signature { + ptr := C.git_commit_author(c.ptr) + return &Signature{ptr} +} + +func (c *Commit) Committer() *Signature { + ptr := C.git_commit_committer(c.ptr) + return &Signature{ptr} +} +*/ diff --git a/config.go b/config.go new file mode 100644 index 0000000..ec8b8d0 --- /dev/null +++ b/config.go @@ -0,0 +1,69 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "unsafe" +) + +type Config struct { + ptr *C.git_config +} + +func (c *Config) LookupInt32(name string) (v int32, err error) { + var out C.int32_t + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ret := C.git_config_get_int32(&out, c.ptr, cname) + if ret < 0 { + return 0, LastError() + } + + return int32(out), nil +} + +func (c *Config) LookupInt64(name string) (v int64, err error) { + var out C.int64_t + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ret := C.git_config_get_int64(&out, c.ptr, cname) + if ret < 0 { + return 0, LastError() + } + + return int64(out), nil +} + +func (c *Config) LookupString(name string) (v string, err error) { + var ptr *C.char + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ret := C.git_config_get_string(&ptr, c.ptr, cname) + if ret < 0 { + return "", LastError() + } + + return C.GoString(ptr), nil +} + +func (c *Config) Set(name, value string) (err error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + cvalue := C.CString(value) + defer C.free(unsafe.Pointer(cvalue)) + + ret := C.git_config_set_string(c.ptr, cname, cvalue) + if ret < 0 { + return LastError() + } + + return nil +} diff --git a/git.go b/git.go new file mode 100644 index 0000000..84716e3 --- /dev/null +++ b/git.go @@ -0,0 +1,92 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "unsafe" +) + +const ( + ITEROVER = C.GIT_ITEROVER + EEXISTS = C.GIT_EEXISTS + ENOTFOUND = C.GIT_ENOTFOUND +) + +func init() { + C.git_threads_init() +} + +// Oid +type Oid struct { + bytes [20]byte +} + +func newOidFromC(coid *C.git_oid) *Oid { + oid := new(Oid) + copy(oid.bytes[0:20], C.GoBytes(unsafe.Pointer(coid), 20)) + return oid +} + +func NewOid(b []byte) *Oid { + oid := new(Oid) + copy(oid.bytes[0:20], b[0:20]) + return oid +} + +func (oid *Oid) toC() *C.git_oid { + return (*C.git_oid)(unsafe.Pointer(&oid.bytes)) +} + +func NewOidFromString(s string) (*Oid, error) { + o := new(Oid) + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + + if C.git_oid_fromstr(o.toC(), cs) < 0 { + return nil, LastError() + } + + return o, nil +} + +func (oid *Oid) String() string { + buf := make([]byte, 40) + C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), oid.toC()) + return string(buf) +} + +func (oid *Oid) Bytes() []byte { + return oid.bytes[0:] +} + +type GitError struct { + Message string + Code int +} + +func (e GitError) Error() string{ + return e.Message +} + +func LastError() error { + err := C.giterr_last() + return &GitError{C.GoString(err.message), int(err.klass)} +} + +func cbool(b bool) C.int { + if (b) { + return C.int(1) + } + return C.int(0) +} + +func ucbool(b bool) C.uint { + if (b) { + return C.uint(1) + } + return C.uint(0) +} diff --git a/odb.go b/odb.go new file mode 100644 index 0000000..5688080 --- /dev/null +++ b/odb.go @@ -0,0 +1,71 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "unsafe" + "reflect" + "runtime" +) + +var ( + OBJ_ANY = C.GIT_OBJ_ANY + OBJ_BAD = C.GIT_OBJ_BAD + OBJ_COMMIT = C.GIT_OBJ_COMMIT + OBJ_TREE = C.GIT_OBJ_TREE + OBJ_BLOB = C.GIT_OBJ_BLOB + OBJ_TAG = C.GIT_OBJ_TAG +) + +type Odb struct { + ptr *C.git_odb +} + +func (v *Odb) Exists(oid *Oid) bool { + ret := C.git_odb_exists(v.ptr, oid.toC()) + return ret != 0 +} + +func (v *Odb) Write(data []byte, otype int) (oid *Oid, err error) { + oid = new(Oid) + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(hdr.Data), C.size_t(hdr.Len), C.git_otype(otype)) + + if ret < 0 { + err = LastError() + } + + return +} + +func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) { + obj = new(OdbObject) + ret := C.git_odb_read(&obj.ptr, v.ptr, oid.toC()) + if ret < 0 { + return nil, LastError() + } + + runtime.SetFinalizer(obj, freeOdbObject) + return +} + +type OdbObject struct { + ptr *C.git_odb_object +} + +func freeOdbObject(obj *OdbObject) { + C.git_odb_object_free(obj.ptr) +} + +func (v *OdbObject) Type() int { + return int(C.git_odb_object_type(v.ptr)) +} + +func (v *OdbObject) Size() int64 { + return int64(C.git_odb_object_size(v.ptr)) +} + diff --git a/repository.go b/repository.go new file mode 100644 index 0000000..3b845e7 --- /dev/null +++ b/repository.go @@ -0,0 +1,141 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "unsafe" + "runtime" +) + +// Repository +type Repository struct { + ptr *C.git_repository +} + +func Open(path string) (*Repository, error) { + repo := new(Repository) + + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + + ret := C.git_repository_open(&repo.ptr, cpath) + if ret < 0 { + return nil, LastError() + } + + runtime.SetFinalizer(repo, freeRepository) + return repo, nil +} + +func Init(path string, isbare bool) (*Repository, error) { + repo := new(Repository) + + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + + ret := C.git_repository_init(&repo.ptr, cpath, ucbool(isbare)) + if ret < 0 { + return nil, LastError() + } + + runtime.SetFinalizer(repo, freeRepository) + return repo, nil +} + +func freeRepository(repo *Repository) { + C.git_repository_free(repo.ptr) +} + +func (v *Repository) Config() (*Config, error) { + config := new(Config) + + ret := C.git_repository_config(&config.ptr, v.ptr) + if ret < 0 { + return nil, LastError() + } + + return config, nil +} + +func (v *Repository) LookupTree(oid *Oid) (*Tree, error) { + tree := new(Tree) + ret := C.git_tree_lookup(&tree.ptr, v.ptr, oid.toC()) + if ret < 0 { + return nil, LastError() + } + + return tree, nil +} + +func (v *Repository) LookupCommit(o *Oid) (*Commit, error) { + commit := new(Commit) + ecode := C.git_commit_lookup(&commit.ptr, v.ptr, o.toC()) + if ecode < 0 { + return nil, LastError() + } + + return commit, nil +} + +/* TODO +func (v *Repository) Commit( + refname string, author, committer *Signature, + message string, tree *Tree, parents ...*Commit) (*Oid, error) { + + oid := new(Oid) + cref := C.CString(refname) + defer C.free(unsafe.Pointer(cref)) + cmsg := C.CString(message) + defer C.free(unsafe.Pointer(cmsg)) + nparents := len(parents) + var cparents []*C.git_commit = nil + var parentsarg **C.git_commit = nil + if nparents > 0 { + cparents = make([]*C.git_commit, nparents) + for i, v := range parents { + cparents[i] = v.ptr + } + parentsarg = &cparents[0] + } + + ret := C.git_commit_create( + oid.toC(), v.ptr, cref, + author.git_signature, committer.git_signature, + nil, cmsg, tree.ptr, C.int(nparents), parentsarg) + + if ret < GIT_SUCCESS { + return nil, LastError() + } + + return oid, nil +} +*/ + +func freeOdb(odb *Odb) { + C.git_odb_free(odb.ptr) +} + +func (v *Repository) Odb() (odb *Odb, err error) { + odb = new(Odb) + if ret := C.git_repository_odb(&odb.ptr, v.ptr); ret < 0 { + return nil, LastError() + } + + runtime.SetFinalizer(odb, freeOdb) + return +} + +func (v *Repository) TreeBuilder() (*TreeBuilder, error) { + bld := new(TreeBuilder) + if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 { + return nil, LastError() + } + + bld.repo = v + return bld, nil +} + diff --git a/tree.go b/tree.go new file mode 100644 index 0000000..324fc1f --- /dev/null +++ b/tree.go @@ -0,0 +1,127 @@ +package git + +/* +#include +#include + +extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); +*/ +import "C" + +import ( + "unsafe" +) + +type Tree struct { + ptr *C.git_tree +} + +type TreeEntry struct { + Name string + Oid *Oid + Type int +} + +func newTreeEntry(entry *C.git_tree_entry) *TreeEntry { + return &TreeEntry{ + C.GoString(C.git_tree_entry_name(entry)), + newOidFromC(C.git_tree_entry_id(entry)), + int(C.git_tree_entry_type(entry)), + } +} + +func (t *Tree) Free() { + C.git_tree_free(t.ptr) +} + +func TreeLookup(repo *Repository, oid *Oid) (*Tree, error) { + tree := new(Tree) + err := C.git_tree_lookup(&tree.ptr, repo.ptr, oid.toC()) + if err < 0 { + return nil, LastError() + } + return tree, nil +} + +func (t *Tree) EntryByName(filename string) *TreeEntry { + cname := C.CString(filename) + defer C.free(unsafe.Pointer(cname)) + + entry := C.git_tree_entry_byname(t.ptr, cname) + if entry == nil { + return nil + } + + return newTreeEntry(entry) +} + +func (t *Tree) EntryByIndex(index uint64) *TreeEntry { + entry := C.git_tree_entry_byindex(t.ptr, C.size_t(index)) + if entry == nil { + return nil + } + + return newTreeEntry(entry) +} + +func (t *Tree) EntryCount() uint64 { + num := C.git_tree_entrycount(t.ptr) + return uint64(num) +} + +type TreeWalkCallback func(string, *TreeEntry) int + +//export CallbackGitTreeWalk +func CallbackGitTreeWalk(_root unsafe.Pointer, _entry unsafe.Pointer, ptr unsafe.Pointer) C.int { + root := C.GoString((*C.char)(_root)) + entry := (*C.git_tree_entry)(_entry) + callback := *(*TreeWalkCallback)(ptr) + + return C.int(callback(root, newTreeEntry(entry))) +} + +func (t *Tree) Walk(callback TreeWalkCallback) error { + err := C._go_git_treewalk( + t.ptr, + C.GIT_TREEWALK_PRE, + unsafe.Pointer(&callback), + ) + + if err < 0 { + return LastError() + } + + return nil +} + +type TreeBuilder struct { + ptr *C.git_treebuilder + repo *Repository +} + +func freeTreeBuilder(v *TreeBuilder) { + C.git_treebuilder_free(v.ptr) +} + +func (v *TreeBuilder) Insert(filename string, id *Oid, filemode int) (error) { + cfilename := C.CString(filename) + defer C.free(unsafe.Pointer(cfilename)) + + err := C.git_treebuilder_insert(nil, v.ptr, cfilename, id.toC(), C.git_filemode_t(filemode)) + if err < 0 { + return LastError() + } + + return nil +} + +func (v *TreeBuilder) Write() (*Oid, error) { + oid := new(Oid) + err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr) + + if err < 0 { + return nil, LastError() + } + + return oid, nil +} diff --git a/wrapper.c b/wrapper.c new file mode 100644 index 0000000..4e1304e --- /dev/null +++ b/wrapper.c @@ -0,0 +1,9 @@ +#include "_cgo_export.h" +#include "git2.h" + +int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr) +{ + return git_tree_walk(tree, mode, (git_treewalk_cb)&CallbackGitTreeWalk, ptr); +} + +/* EOF */