package main /* #cgo CFLAGS: -I./libgit2/include #cgo LDFLAGS: -L./libgit2/build -lgit2 -lssl -lcrypto -lz #include #include extern void list_cb(); */ import "C" import ( "fmt" "path" "runtime" "strings" "unsafe" ) func libgit_init() { C.git_libgit2_init() } type Repo *C.git_repository func repository_open(path string) (Repo, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) // not sure if this resource is used after repo shit. maybe I should make a datatype which stores all the resources? var repo *C.git_repository error := C.git_repository_open(&repo, cpath) if error < 0 { return nil, to_error(error) } return repo, nil } /// type Entry struct { Name string Filetype FileType gitEntry *C.git_tree_entry } func (entry *Entry) GetExtension() string { return strings.ToLower(path.Ext(entry.Name)) } type FileType C.int const ( Invalid FileType = -1 Submodule = 1 Directory = 2 File = 3 ) func fromPath(repo Repo, at string) ([]Entry, *Entry, error) { // dir_entry := C.git_tree_entry_byname(tree, "") var head *C.git_reference C.git_repository_head(&head, repo) var commit *C.git_commit C.git_reference_peel((**C.git_object)(unsafe.Pointer(&commit)), head, Submodule) var base_tree *C.git_tree C.git_commit_tree(&base_tree, commit) var tree *C.git_tree if at == "" { tree = base_tree } else { var dir_entry *C.git_tree_entry ret := C.git_tree_entry_bypath(&dir_entry, base_tree, C.CString(at)) if ret != 0 { panic("could not get dir at thing") } ty := (FileType)(C.git_tree_entry_type(dir_entry)) if ty == Directory { var dir_tree *C.git_tree C.git_tree_entry_to_object((**C.git_object)(unsafe.Pointer(&dir_tree)), repo, dir_entry) tree = dir_tree } else { if ty == File || ty == Submodule { entry := Entry{ Name: at, Filetype: ty, gitEntry: dir_entry, } return nil, &entry, nil } else { return nil, nil, fmt.Errorf("incorrect file? (at %s)", at) } } } num_entries := C.git_tree_entrycount(tree) var entries []Entry for ei := range num_entries { e := C.git_tree_entry_byindex(tree, ei) name := C.GoString(C.git_tree_entry_name(e)) filetype := (FileType)(C.git_tree_entry_type(e)) entries = append(entries, Entry{ Name: name, Filetype: filetype, gitEntry: e, }) } return entries, nil, nil } type Blob struct { Bytes []byte } func get_blob(repo Repo, entry *Entry) *Blob { var git_blob *C.git_blob C.git_blob_lookup(&git_blob, repo, C.git_tree_entry_id(entry.gitEntry)) data := C.git_blob_rawcontent(git_blob) size := C.git_blob_rawsize(git_blob) byteSlice := unsafe.Slice((*byte)(data), size) blob := &Blob{Bytes: byteSlice} runtime.AddCleanup(blob, func(ptr *C.git_blob) { C.git_blob_free(ptr) }, git_blob) return blob } // quick function to list all shit in a repository func list_all(repo Repo) ([]string, error) { var obj *C.git_object error := C.git_revparse_single(&obj, repo, C.CString("HEAD^{tree}")) if error < 0 { return nil, to_error(error) } tree := (*C.git_tree)(obj) var names []string error = C.git_tree_walk(tree, C.GIT_TREEWALK_PRE, (*[0]byte)(C.list_cb), unsafe.Pointer(&names)) // note, that we're getting our Go function *through* C if error < 0 { return nil, to_error(error) } return names, nil } //export list_cb func list_cb(root *C.char, entry *C.git_tree_entry, payload *C.void) { name := C.GoString(C.git_tree_entry_name(entry)) names := (*[]string)(unsafe.Pointer(payload)) *names = append(*names, name) } func to_error(error C.int) error { e := C.git_error_last() return fmt.Errorf("Error %d/%d: %s\n", error, e.klass, C.GoString(e.message)) }