diff --git a/odb.go b/odb.go index 303065b..1f215ec 100644 --- a/odb.go +++ b/odb.go @@ -4,6 +4,7 @@ package git #include 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); 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); @@ -14,6 +15,7 @@ extern void _go_git_odb_writepack_free(git_odb_writepack *writepack); import "C" import ( "io" + "os" "reflect" "runtime" "unsafe" @@ -88,6 +90,27 @@ func NewOdbBackendOnePack(packfileIndexPath string) (backend *OdbBackend, err er 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() diff --git a/odb_test.go b/odb_test.go index e44c927..d79afa1 100644 --- a/odb_test.go +++ b/odb_test.go @@ -3,8 +3,11 @@ package git import ( "bytes" "errors" + "fmt" "io" "io/ioutil" + "os" + "path" "testing" ) @@ -186,3 +189,47 @@ func TestOdbWritepack(t *testing.T) { t.Errorf("mismatched indexed objects, expected 3, got %v", finalStats.IndexedObjects) } } + +func TestOdbBackendLoose(t *testing.T) { + t.Parallel() + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + _, _ = seedTestRepo(t, repo) + + odb, err := repo.Odb() + checkFatal(t, err) + + looseObjectsDir, err := ioutil.TempDir("", fmt.Sprintf("loose_objects_%s", path.Base(repo.Path()))) + checkFatal(t, err) + defer os.RemoveAll(looseObjectsDir) + + looseObjectsBackend, err := NewOdbBackendLoose(looseObjectsDir, -1, false, 0, 0) + checkFatal(t, err) + if err := odb.AddBackend(looseObjectsBackend, 999); err != nil { + looseObjectsBackend.Free() + checkFatal(t, err) + } + + str := "hello, world!" + + writeStream, err := odb.NewWriteStream(int64(len(str)), ObjectBlob) + checkFatal(t, err) + n, err := io.WriteString(writeStream, str) + checkFatal(t, err) + if n != len(str) { + t.Fatalf("Bad write length %v != %v", n, len(str)) + } + + err = writeStream.Close() + checkFatal(t, err) + + expectedId, err := NewOid("30f51a3fba5274d53522d0f19748456974647b4f") + checkFatal(t, err) + if !writeStream.Id.Equal(expectedId) { + t.Fatalf("writeStream.id = %v; want %v", writeStream.Id, expectedId) + } + + _, err = os.Stat(path.Join(looseObjectsDir, expectedId.String()[:2], expectedId.String()[2:])) + checkFatal(t, err) +}