package git /* #include #include */ import "C" import ( "bytes" "encoding/hex" "errors" "runtime" "strings" "unsafe" ) type ErrorClass int const ( ErrClassNone ErrorClass = C.GITERR_NONE ErrClassNoMemory ErrorClass = C.GITERR_NOMEMORY ErrClassOs ErrorClass = C.GITERR_OS ErrClassInvalid ErrorClass = C.GITERR_INVALID ErrClassReference ErrorClass = C.GITERR_REFERENCE ErrClassZlib ErrorClass = C.GITERR_ZLIB ErrClassRepository ErrorClass = C.GITERR_REPOSITORY ErrClassConfig ErrorClass = C.GITERR_CONFIG ErrClassRegex ErrorClass = C.GITERR_REGEX ErrClassOdb ErrorClass = C.GITERR_ODB ErrClassIndex ErrorClass = C.GITERR_INDEX ErrClassObject ErrorClass = C.GITERR_OBJECT ErrClassNet ErrorClass = C.GITERR_NET ErrClassTag ErrorClass = C.GITERR_TAG ErrClassTree ErrorClass = C.GITERR_TREE ErrClassIndexer ErrorClass = C.GITERR_INDEXER ErrClassSSL ErrorClass = C.GITERR_SSL ErrClassSubmodule ErrorClass = C.GITERR_SUBMODULE ErrClassThread ErrorClass = C.GITERR_THREAD ErrClassStash ErrorClass = C.GITERR_STASH ErrClassCheckout ErrorClass = C.GITERR_CHECKOUT ErrClassFetchHead ErrorClass = C.GITERR_FETCHHEAD ErrClassMerge ErrorClass = C.GITERR_MERGE ErrClassSsh ErrorClass = C.GITERR_SSH ErrClassFilter ErrorClass = C.GITERR_FILTER ErrClassRevert ErrorClass = C.GITERR_REVERT ErrClassCallback ErrorClass = C.GITERR_CALLBACK ) type ErrorCode int const ( // No error ErrOk ErrorCode = C.GIT_OK // Generic error ErrGeneric ErrorCode = C.GIT_ERROR // Requested object could not be found ErrNotFound ErrorCode = C.GIT_ENOTFOUND // Object exists preventing operation ErrExists ErrorCode = C.GIT_EEXISTS // More than one object matches ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS // Output buffer too short to hold data ErrBuffs ErrorCode = C.GIT_EBUFS // GIT_EUSER is a special error that is never generated by libgit2 // code. You can return it from a callback (e.g to stop an iteration) // to know that it was generated by the callback and not by libgit2. ErrUser ErrorCode = C.GIT_EUSER // Operation not allowed on bare repository ErrBareRepo ErrorCode = C.GIT_EBAREREPO // HEAD refers to branch with no commits ErrUnbornBranch ErrorCode = C.GIT_EUNBORNBRANCH // Merge in progress prevented operation ErrUnmerged ErrorCode = C.GIT_EUNMERGED // Reference was not fast-forwardable ErrNonFastForward ErrorCode = C.GIT_ENONFASTFORWARD // Name/ref spec was not in a valid format ErrInvalidSpec ErrorCode = C.GIT_EINVALIDSPEC // Checkout conflicts prevented operation ErrConflict ErrorCode = C.GIT_ECONFLICT // Lock file prevented operation ErrLocked ErrorCode = C.GIT_ELOCKED // Reference value does not match expected ErrModified ErrorCode = C.GIT_EMODIFIED // Internal only ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH // Signals end of iteration with iterator ErrIterOver ErrorCode = C.GIT_ITEROVER ) var ( ErrInvalid = errors.New("Invalid state for operation") ) var pointerHandles *HandleList func init() { pointerHandles = NewHandleList() C.git_libgit2_init() // This is not something we should be doing, as we may be // stomping all over someone else's setup. The user should do // this themselves or use some binding/wrapper which does it // in such a way that they can be sure they're the only ones // setting it up. C.git_openssl_set_locking() } // Oid represents the id for a Git object. type Oid [20]byte func newOidFromC(coid *C.git_oid) *Oid { if coid == nil { return nil } oid := new(Oid) copy(oid[0:20], C.GoBytes(unsafe.Pointer(coid), 20)) return oid } func NewOidFromBytes(b []byte) *Oid { oid := new(Oid) copy(oid[0:20], b[0:20]) return oid } func (oid *Oid) toC() *C.git_oid { return (*C.git_oid)(unsafe.Pointer(oid)) } func NewOid(s string) (*Oid, error) { if len(s) > C.GIT_OID_HEXSZ { return nil, errors.New("string is too long for oid") } o := new(Oid) slice, error := hex.DecodeString(s) if error != nil { return nil, error } if len(slice) != 20 { return nil, &GitError{"Invalid Oid", ErrClassNone, ErrGeneric} } copy(o[:], slice[:20]) return o, nil } func (oid *Oid) String() string { return hex.EncodeToString(oid[:]) } func (oid *Oid) Cmp(oid2 *Oid) int { return bytes.Compare(oid[:], oid2[:]) } func (oid *Oid) Copy() *Oid { ret := new(Oid) copy(ret[:], oid[:]) return ret } func (oid *Oid) Equal(oid2 *Oid) bool { return bytes.Equal(oid[:], oid2[:]) } func (oid *Oid) IsZero() bool { for _, a := range oid { if a != 0 { return false } } return true } func (oid *Oid) NCmp(oid2 *Oid, n uint) int { return bytes.Compare(oid[:n], oid2[:n]) } func ShortenOids(ids []*Oid, minlen int) (int, error) { shorten := C.git_oid_shorten_new(C.size_t(minlen)) if shorten == nil { panic("Out of memory") } defer C.git_oid_shorten_free(shorten) var ret C.int runtime.LockOSThread() defer runtime.UnlockOSThread() for _, id := range ids { buf := make([]byte, 41) C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), id.toC()) buf[40] = 0 ret = C.git_oid_shorten_add(shorten, (*C.char)(unsafe.Pointer(&buf[0]))) if ret < 0 { return int(ret), MakeGitError(ret) } } return int(ret), nil } type GitError struct { Message string Class ErrorClass Code ErrorCode } func (e GitError) Error() string { return e.Message } func IsErrorClass(err error, c ErrorClass) bool { if err == nil { return false } if gitError, ok := err.(*GitError); ok { return gitError.Class == c } return false } func IsErrorCode(err error, c ErrorCode) bool { if err == nil { return false } if gitError, ok := err.(*GitError); ok { return gitError.Code == c } return false } func MakeGitError(errorCode C.int) error { var errMessage string var errClass ErrorClass if errorCode != C.GIT_ITEROVER { err := C.giterr_last() if err != nil { errMessage = C.GoString(err.message) errClass = ErrorClass(err.klass) } else { errClass = ErrClassInvalid } } return &GitError{errMessage, errClass, ErrorCode(errorCode)} } func MakeGitError2(err int) error { return MakeGitError(C.int(err)) } 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) } func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) { ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR))) defer C.free(unsafe.Pointer(ceildirs)) cstart := C.CString(start) defer C.free(unsafe.Pointer(cstart)) var buf C.git_buf defer C.git_buf_free(&buf) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_discover(&buf, cstart, cbool(across_fs), ceildirs) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(buf.ptr), nil }