| package git |
| |
| /* |
| #include <git2.h> |
| #include <git2/sys/openssl.h> |
| #cgo pkg-config: libgit2 |
| */ |
| 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 |
| // Authentication failed |
| ErrAuth ErrorCode = C.GIT_EAUTH |
| ) |
| |
| 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 |
| } |