git2go/odb.go

487 lines
12 KiB
Go
Raw Normal View History

2013-03-05 13:53:04 -06:00
package git
/*
#include <git2.h>
2013-05-21 14:37:08 -05:00
extern int git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
extern int git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync, unsigned int dir_mode, unsigned int file_mode);
2013-05-21 14:37:08 -05:00
extern int _go_git_odb_foreach(git_odb *db, void *payload);
extern void _go_git_odb_backend_free(git_odb_backend *backend);
extern int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload);
extern int _go_git_odb_writepack_append(git_odb_writepack *writepack, const void *, size_t, git_transfer_progress *);
extern int _go_git_odb_writepack_commit(git_odb_writepack *writepack, git_transfer_progress *);
extern void _go_git_odb_writepack_free(git_odb_writepack *writepack);
2013-03-05 13:53:04 -06:00
*/
import "C"
import (
"io"
"os"
2013-03-05 13:53:04 -06:00
"reflect"
"runtime"
"unsafe"
2013-03-05 13:53:04 -06:00
)
type Odb struct {
ptr *C.git_odb
}
type OdbBackend struct {
ptr *C.git_odb_backend
}
2013-12-19 16:24:44 -06:00
func NewOdb() (odb *Odb, err error) {
odb = new(Odb)
2014-12-05 19:44:57 -06:00
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_new(&odb.ptr)
if ret < 0 {
2014-04-03 15:49:22 -05:00
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(odb, (*Odb).Free)
2014-04-03 15:39:21 -05:00
return odb, nil
}
func NewOdbBackendFromC(ptr unsafe.Pointer) (backend *OdbBackend) {
backend = &OdbBackend{(*C.git_odb_backend)(ptr)}
2014-04-03 15:39:21 -05:00
return backend
2013-12-20 13:11:24 -06:00
}
func (v *Odb) AddAlternate(backend *OdbBackend, priority int) (err error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
2014-12-05 19:44:57 -06:00
ret := C.git_odb_add_alternate(v.ptr, backend.ptr, C.int(priority))
runtime.KeepAlive(v)
if ret < 0 {
backend.Free()
return MakeGitError(ret)
}
return nil
}
func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) {
2014-12-05 19:44:57 -06:00
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_add_backend(v.ptr, backend.ptr, C.int(priority))
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
if ret < 0 {
backend.Free()
2014-04-03 15:49:22 -05:00
return MakeGitError(ret)
}
return nil
}
func NewOdbBackendOnePack(packfileIndexPath string) (backend *OdbBackend, err error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
cstr := C.CString(packfileIndexPath)
defer C.free(unsafe.Pointer(cstr))
var odbOnePack *C.git_odb_backend = nil
ret := C.git_odb_backend_one_pack(&odbOnePack, cstr)
if ret < 0 {
return nil, MakeGitError(ret)
}
return NewOdbBackendFromC(unsafe.Pointer(odbOnePack)), nil
}
// NewOdbBackendLoose creates a backend for loose objects.
func NewOdbBackendLoose(objectsDir string, compressionLevel int, doFsync bool, dirMode os.FileMode, fileMode os.FileMode) (backend *OdbBackend, err error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var odbLoose *C.git_odb_backend = nil
var doFsyncInt C.int
if doFsync {
doFsyncInt = C.int(1)
}
cstr := C.CString(objectsDir)
defer C.free(unsafe.Pointer(cstr))
ret := C.git_odb_backend_loose(&odbLoose, cstr, C.int(compressionLevel), doFsyncInt, C.uint(dirMode), C.uint(fileMode))
if ret < 0 {
return nil, MakeGitError(ret)
}
return NewOdbBackendFromC(unsafe.Pointer(odbLoose)), nil
}
func (v *Odb) ReadHeader(oid *Oid) (uint64, ObjectType, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
2017-07-08 09:07:51 -05:00
var sz C.size_t
var cotype C.git_object_t
ret := C.git_odb_read_header(&sz, &cotype, v.ptr, oid.toC())
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
if ret < 0 {
return 0, ObjectInvalid, MakeGitError(ret)
}
return uint64(sz), ObjectType(cotype), nil
}
2017-07-08 09:07:51 -05:00
2013-03-05 13:53:04 -06:00
func (v *Odb) Exists(oid *Oid) bool {
ret := C.git_odb_exists(v.ptr, oid.toC())
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
runtime.KeepAlive(oid)
2013-03-05 13:53:04 -06:00
return ret != 0
}
func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
2013-03-05 13:53:04 -06:00
oid = new(Oid)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var size C.size_t
if len(data) > 0 {
size = C.size_t(len(data))
} else {
data = []byte{0}
size = C.size_t(0)
}
ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(&data[0]), size, C.git_object_t(otype))
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
2013-03-05 13:53:04 -06:00
if ret < 0 {
return nil, MakeGitError(ret)
2013-03-05 13:53:04 -06:00
}
2014-04-03 15:39:21 -05:00
return oid, nil
2013-03-05 13:53:04 -06:00
}
func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) {
obj = new(OdbObject)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
2013-03-05 13:53:04 -06:00
ret := C.git_odb_read(&obj.ptr, v.ptr, oid.toC())
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
runtime.KeepAlive(oid)
2013-03-05 13:53:04 -06:00
if ret < 0 {
return nil, MakeGitError(ret)
2013-03-05 13:53:04 -06:00
}
runtime.SetFinalizer(obj, (*OdbObject).Free)
2014-04-03 15:39:21 -05:00
return obj, nil
2013-03-05 13:53:04 -06:00
}
type OdbForEachCallback func(id *Oid) error
type foreachData struct {
callback OdbForEachCallback
2015-03-04 13:40:26 -06:00
err error
}
2013-05-21 14:37:08 -05:00
//export odbForEachCb
func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int {
data, ok := pointerHandles.Get(handle).(*foreachData)
if !ok {
panic("could not retrieve handle")
}
err := data.callback(newOidFromC(id))
if err != nil {
data.err = err
return C.GIT_EUSER
2013-05-21 14:37:08 -05:00
}
return 0
2013-05-21 14:37:08 -05:00
}
func (v *Odb) ForEach(callback OdbForEachCallback) error {
2015-03-04 13:40:26 -06:00
data := foreachData{
callback: callback,
2015-03-04 13:40:26 -06:00
err: nil,
}
2013-05-21 14:37:08 -05:00
2014-12-05 19:44:57 -06:00
runtime.LockOSThread()
defer runtime.UnlockOSThread()
handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
ret := C._go_git_odb_foreach(v.ptr, handle)
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
if ret == C.GIT_EUSER {
return data.err
} else if ret < 0 {
return MakeGitError(ret)
}
return nil
2013-05-21 14:37:08 -05:00
}
2014-01-25 15:18:32 -06:00
// Hash determines the object-ID (sha1) of a data buffer.
func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) {
oid = new(Oid)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var size C.size_t
if len(data) > 0 {
size = C.size_t(len(data))
} else {
data = []byte{0}
size = C.size_t(0)
}
ret := C.git_odb_hash(oid.toC(), unsafe.Pointer(&data[0]), size, C.git_object_t(otype))
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(data)
2014-01-25 15:18:32 -06:00
if ret < 0 {
return nil, MakeGitError(ret)
2014-01-25 15:18:32 -06:00
}
2014-04-03 15:39:21 -05:00
return oid, nil
2014-01-25 15:18:32 -06:00
}
// NewReadStream opens a read stream from the ODB. Reading from it will give you the
// contents of the object.
func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) {
stream := new(OdbReadStream)
var ctype C.git_object_t
var csize C.size_t
2014-12-05 19:44:57 -06:00
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_open_rstream(&stream.ptr, &csize, &ctype, v.ptr, id.toC())
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
runtime.KeepAlive(id)
if ret < 0 {
2014-02-26 09:14:31 -06:00
return nil, MakeGitError(ret)
}
stream.Size = uint64(csize)
stream.Type = ObjectType(ctype)
runtime.SetFinalizer(stream, (*OdbReadStream).Free)
return stream, nil
}
// NewWriteStream opens a write stream to the ODB, which allows you to
// create a new object in the database. The size and type must be
// known in advance
2015-06-07 21:11:21 -05:00
func (v *Odb) NewWriteStream(size int64, otype ObjectType) (*OdbWriteStream, error) {
stream := new(OdbWriteStream)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
2019-12-10 16:15:32 -06:00
ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.git_object_size_t(size), C.git_object_t(otype))
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(v)
if ret < 0 {
2014-02-26 09:14:31 -06:00
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(stream, (*OdbWriteStream).Free)
return stream, nil
}
// NewWritePack opens a stream for writing a pack file to the ODB. If the ODB
// layer understands pack files, then the given packfile will likely be
// streamed directly to disk (and a corresponding index created). If the ODB
// layer does not understand pack files, the objects will be stored in whatever
// format the ODB layer uses.
func (v *Odb) NewWritePack(callback TransferProgressCallback) (*OdbWritepack, error) {
writepack := new(OdbWritepack)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
writepack.callbacks.TransferProgressCallback = callback
writepack.callbacksHandle = pointerHandles.Track(&writepack.callbacks)
ret := C._go_git_odb_write_pack(&writepack.ptr, v.ptr, writepack.callbacksHandle)
runtime.KeepAlive(v)
if ret < 0 {
pointerHandles.Untrack(writepack.callbacksHandle)
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(writepack, (*OdbWritepack).Free)
return writepack, nil
}
func (v *OdbBackend) Free() {
C._go_git_odb_backend_free(v.ptr)
}
2013-03-05 13:53:04 -06:00
type OdbObject struct {
ptr *C.git_odb_object
}
func (v *OdbObject) Free() {
runtime.SetFinalizer(v, nil)
C.git_odb_object_free(v.ptr)
2013-03-05 13:53:04 -06:00
}
2013-03-06 10:15:28 -06:00
func (object *OdbObject) Id() (oid *Oid) {
2017-07-08 09:07:51 -05:00
ret := newOidFromC(C.git_odb_object_id(object.ptr))
runtime.KeepAlive(object)
return ret
2013-03-05 13:53:04 -06:00
}
2013-03-06 10:15:28 -06:00
func (object *OdbObject) Len() (len uint64) {
2017-07-08 09:07:51 -05:00
ret := uint64(C.git_odb_object_size(object.ptr))
runtime.KeepAlive(object)
return ret
2013-03-06 10:15:28 -06:00
}
func (object *OdbObject) Type() ObjectType {
2017-07-08 09:07:51 -05:00
ret := ObjectType(C.git_odb_object_type(object.ptr))
runtime.KeepAlive(object)
return ret
}
2017-07-08 09:07:51 -05:00
// Data returns a slice pointing to the unmanaged object memory. You must make
// sure the object is referenced for at least as long as the slice is used.
2013-03-06 10:15:28 -06:00
func (object *OdbObject) Data() (data []byte) {
var c_blob unsafe.Pointer = C.git_odb_object_data(object.ptr)
var blob []byte
len := int(C.git_odb_object_size(object.ptr))
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&blob)))
sliceHeader.Cap = len
sliceHeader.Len = len
sliceHeader.Data = uintptr(c_blob)
return blob
2013-03-05 13:53:04 -06:00
}
type OdbReadStream struct {
ptr *C.git_odb_stream
Size uint64
Type ObjectType
}
// Read reads from the stream
func (stream *OdbReadStream) Read(data []byte) (int, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
ptr := (*C.char)(unsafe.Pointer(header.Data))
size := C.size_t(header.Cap)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_stream_read(stream.ptr, ptr, size)
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(stream)
if ret < 0 {
2014-02-26 09:14:31 -06:00
return 0, MakeGitError(ret)
}
if ret == 0 {
return 0, io.EOF
}
header.Len = int(ret)
return len(data), nil
}
// Close is a dummy function in order to implement the Closer and
// ReadCloser interfaces
func (stream *OdbReadStream) Close() error {
return nil
}
func (stream *OdbReadStream) Free() {
runtime.SetFinalizer(stream, nil)
C.git_odb_stream_free(stream.ptr)
}
type OdbWriteStream struct {
ptr *C.git_odb_stream
Id Oid
}
// Write writes to the stream
func (stream *OdbWriteStream) Write(data []byte) (int, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
ptr := (*C.char)(unsafe.Pointer(header.Data))
size := C.size_t(header.Len)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_stream_write(stream.ptr, ptr, size)
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(stream)
if ret < 0 {
2014-02-26 09:14:31 -06:00
return 0, MakeGitError(ret)
}
return len(data), nil
}
// Close signals that all the data has been written and stores the
// resulting object id in the stream's Id field.
func (stream *OdbWriteStream) Close() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr)
2017-07-08 09:07:51 -05:00
runtime.KeepAlive(stream)
if ret < 0 {
2014-02-26 09:14:31 -06:00
return MakeGitError(ret)
}
return nil
}
func (stream *OdbWriteStream) Free() {
runtime.SetFinalizer(stream, nil)
C.git_odb_stream_free(stream.ptr)
}
// OdbWritepack is a stream to write a packfile to the ODB.
type OdbWritepack struct {
ptr *C.git_odb_writepack
stats C.git_transfer_progress
callbacks RemoteCallbacks
callbacksHandle unsafe.Pointer
}
func (writepack *OdbWritepack) Write(data []byte) (int, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
ptr := unsafe.Pointer(header.Data)
size := C.size_t(header.Len)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C._go_git_odb_writepack_append(writepack.ptr, ptr, size, &writepack.stats)
runtime.KeepAlive(writepack)
if ret < 0 {
return 0, MakeGitError(ret)
}
return len(data), nil
}
func (writepack *OdbWritepack) Commit() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C._go_git_odb_writepack_commit(writepack.ptr, &writepack.stats)
runtime.KeepAlive(writepack)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
func (writepack *OdbWritepack) Free() {
pointerHandles.Untrack(writepack.callbacksHandle)
runtime.SetFinalizer(writepack, nil)
C._go_git_odb_writepack_free(writepack.ptr)
}