package git /* #cgo pkg-config: libgit2 #include #include */ import "C" import ( "bytes" "encoding/hex" "errors" "runtime" "strings" "unsafe" ) type ErrorClass int const ( ErrClassNone ErrorClass = C.GITERR_NONE ErrClassNoMemory = C.GITERR_NOMEMORY ErrClassOs = C.GITERR_OS ErrClassInvalid = C.GITERR_INVALID ErrClassReference = C.GITERR_REFERENCE ErrClassZlib = C.GITERR_ZLIB ErrClassRepository = C.GITERR_REPOSITORY ErrClassConfig = C.GITERR_CONFIG ErrClassRegex = C.GITERR_REGEX ErrClassOdb = C.GITERR_ODB ErrClassIndex = C.GITERR_INDEX ErrClassObject = C.GITERR_OBJECT ErrClassNet = C.GITERR_NET ErrClassTag = C.GITERR_TAG ErrClassTree = C.GITERR_TREE ErrClassIndexer = C.GITERR_INDEXER ErrClassSSL = C.GITERR_SSL ErrClassSubmodule = C.GITERR_SUBMODULE ErrClassThread = C.GITERR_THREAD ErrClassStash = C.GITERR_STASH ErrClassCheckout = C.GITERR_CHECKOUT ErrClassFetchHead = C.GITERR_FETCHHEAD ErrClassMerge = C.GITERR_MERGE ErrClassSsh = C.GITERR_SSH ErrClassFilter = C.GITERR_FILTER ErrClassRevert = C.GITERR_REVERT ErrClassCallback = C.GITERR_CALLBACK ) type ErrorCode int const ( ErrOk ErrorCode = C.GIT_OK /*< No error */ ErrGeneric = C.GIT_ERROR /*< Generic error */ ErrNotFound = C.GIT_ENOTFOUND /*< Requested object could not be found */ ErrExists = C.GIT_EEXISTS /*< Object exists preventing operation */ ErrAmbigious = C.GIT_EAMBIGUOUS /*< More than one object matches */ ErrBuffs = C.GIT_EBUFS /*< Output buffer too short to hold data */ /* 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 = C.GIT_EUSER ErrBareRepo = C.GIT_EBAREREPO /*< Operation not allowed on bare repository */ ErrUnbornBranch = C.GIT_EUNBORNBRANCH /*< HEAD refers to branch with no commits */ ErrUnmerged = C.GIT_EUNMERGED /*< Merge in progress prevented operation */ ErrNonFastForward = C.GIT_ENONFASTFORWARD /*< Reference was not fast-forwardable */ ErrInvalidSpec = C.GIT_EINVALIDSPEC /*< Name/ref spec was not in a valid format */ ErrMergeConflict = C.GIT_EMERGECONFLICT /*< Merge conflicts prevented operation */ ErrLocked = C.GIT_ELOCKED /*< Lock file prevented operation */ ErrModified = C.GIT_EMODIFIED /*< Reference value does not match expected */ ErrPassthrough = C.GIT_PASSTHROUGH /*< Internal only */ ErrIterOver = C.GIT_ITEROVER /*< Signals end of iteration with iterator */ ) func init() { C.git_threads_init() } // 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 != ErrIterOver { 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 }