Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions go/store/blobstore/git_blobstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type GitBlobstore struct {
gitDir string
ref string
runner *git.Runner
api git.GitAPI
}

var _ Blobstore = (*GitBlobstore)(nil)
Expand All @@ -45,7 +46,7 @@ func NewGitBlobstore(gitDir, ref string) (*GitBlobstore, error) {
if err != nil {
return nil, err
}
return &GitBlobstore{gitDir: gitDir, ref: ref, runner: r}, nil
return &GitBlobstore{gitDir: gitDir, ref: ref, runner: r, api: git.NewGitAPIImpl(r)}, nil
}

func (gbs *GitBlobstore) Path() string {
Expand All @@ -57,14 +58,14 @@ func (gbs *GitBlobstore) Exists(ctx context.Context, key string) (bool, error) {
if err != nil {
return false, err
}
commit, ok, err := git.TryResolveRefCommit(ctx, gbs.runner, gbs.ref)
commit, ok, err := gbs.api.TryResolveRefCommit(ctx, gbs.ref)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
_, err = git.ResolvePathBlob(ctx, gbs.runner, commit, key)
_, err = gbs.api.ResolvePathBlob(ctx, commit, key)
if err != nil {
if git.IsPathNotFound(err) {
return false, nil
Expand All @@ -79,7 +80,7 @@ func (gbs *GitBlobstore) Get(ctx context.Context, key string, br BlobRange) (io.
if err != nil {
return nil, 0, "", err
}
commit, ok, err := git.TryResolveRefCommit(ctx, gbs.runner, gbs.ref)
commit, ok, err := gbs.api.TryResolveRefCommit(ctx, gbs.ref)
if err != nil {
return nil, 0, "", err
}
Expand All @@ -92,23 +93,23 @@ func (gbs *GitBlobstore) Get(ctx context.Context, key string, br BlobRange) (io.
return nil, 0, "", &git.RefNotFoundError{Ref: gbs.ref}
}

blobOID, err := git.ResolvePathBlob(ctx, gbs.runner, commit, key)
blobOID, err := gbs.api.ResolvePathBlob(ctx, commit, key)
if err != nil {
if git.IsPathNotFound(err) {
return nil, 0, commit.String(), NotFound{Key: key}
}
return nil, 0, commit.String(), err
}

sz, err := git.BlobSize(ctx, gbs.runner, blobOID)
sz, err := gbs.api.BlobSize(ctx, blobOID)
if err != nil {
return nil, 0, commit.String(), err
}

// TODO(gitblobstore): This streaming implementation is correct but may be slow for workloads
// that do many small ranged reads (e.g. table index/footer reads). Consider caching/materializing
// blobs to a local file (or using a batched git cat-file mode) to serve ranges efficiently.
rc, err := git.BlobReader(ctx, gbs.runner, blobOID)
rc, err := gbs.api.BlobReader(ctx, blobOID)
if err != nil {
return nil, 0, commit.String(), err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,33 @@ package git

import (
"context"
"fmt"
"io"
)

// WriteAPI defines the git plumbing operations needed for Approach A (temporary index
// via GIT_INDEX_FILE) to perform updates without a working tree checkout.
//
// This file intentionally does not implement these operations yet; the current
// GitBlobstore milestone is read-only. All methods on the default implementation
// return ErrUnimplemented.
type WriteAPI interface {
// GitAPI defines the git plumbing operations needed by GitBlobstore. It includes both
// read and write operations to allow swapping implementations (e.g. git CLI vs a Go git
// library) while keeping callers stable.
type GitAPI interface {
// TryResolveRefCommit resolves |ref| to a commit OID. Returns ok=false if the ref does not exist.
TryResolveRefCommit(ctx context.Context, ref string) (oid OID, ok bool, err error)

// ResolveRefCommit resolves |ref| to a commit OID, returning RefNotFoundError if missing.
ResolveRefCommit(ctx context.Context, ref string) (OID, error)

// ResolvePathBlob resolves |path| within |commit| to a blob OID.
// It returns PathNotFoundError if the path does not exist, and NotBlobError if it
// resolves to a non-blob object.
ResolvePathBlob(ctx context.Context, commit OID, path string) (OID, error)

// CatFileType returns the git object type for |oid| (e.g. "blob", "tree", "commit").
CatFileType(ctx context.Context, oid OID) (string, error)

// BlobSize returns the size in bytes of the blob object |oid|.
BlobSize(ctx context.Context, oid OID) (int64, error)

// BlobReader returns a reader for blob contents.
BlobReader(ctx context.Context, oid OID) (io.ReadCloser, error)

// ReadTree populates |indexFile| with the entries from |commit|'s root tree.
// Equivalent plumbing:
// GIT_DIR=... GIT_INDEX_FILE=<indexFile> git read-tree <commit>^{tree}
Expand Down Expand Up @@ -62,43 +79,9 @@ type WriteAPI interface {
UpdateRef(ctx context.Context, ref string, newOID OID, msg string) error
}

// Identity represents git author/committer metadata. A future implementation
// may set this via environment variables (GIT_AUTHOR_NAME, etc.).
// Identity represents git author/committer metadata. A future implementation may set
// this via environment variables (GIT_AUTHOR_NAME, etc.).
type Identity struct {
Name string
Email string
}

// UnimplementedWriteAPI is the default write API for the read-only milestone.
// It can be embedded or returned by constructors to make write paths fail fast.
type UnimplementedWriteAPI struct{}

var _ WriteAPI = UnimplementedWriteAPI{}

func (UnimplementedWriteAPI) ReadTree(ctx context.Context, commit OID, indexFile string) error {
return fmt.Errorf("%w: ReadTree", ErrUnimplemented)
}

func (UnimplementedWriteAPI) ReadTreeEmpty(ctx context.Context, indexFile string) error {
return fmt.Errorf("%w: ReadTreeEmpty", ErrUnimplemented)
}

func (UnimplementedWriteAPI) UpdateIndexCacheInfo(ctx context.Context, indexFile string, mode string, oid OID, path string) error {
return fmt.Errorf("%w: UpdateIndexCacheInfo", ErrUnimplemented)
}

func (UnimplementedWriteAPI) WriteTree(ctx context.Context, indexFile string) (OID, error) {
return "", fmt.Errorf("%w: WriteTree", ErrUnimplemented)
}

func (UnimplementedWriteAPI) CommitTree(ctx context.Context, tree OID, parent *OID, message string, author *Identity) (OID, error) {
return "", fmt.Errorf("%w: CommitTree", ErrUnimplemented)
}

func (UnimplementedWriteAPI) UpdateRefCAS(ctx context.Context, ref string, newOID OID, oldOID OID, msg string) error {
return fmt.Errorf("%w: UpdateRefCAS", ErrUnimplemented)
}

func (UnimplementedWriteAPI) UpdateRef(ctx context.Context, ref string, newOID OID, msg string) error {
return fmt.Errorf("%w: UpdateRef", ErrUnimplemented)
}
5 changes: 0 additions & 5 deletions go/store/blobstore/internal/git/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ func (e *NotBlobError) Error() string {
return fmt.Sprintf("git path is not a blob (%s): %s:%s", e.Type, e.Commit, e.Path)
}

func IsRefNotFound(err error) bool {
var e *RefNotFoundError
return errors.As(err, &e)
}

func IsPathNotFound(err error) bool {
var e *PathNotFoundError
return errors.As(err, &e)
Expand Down
Loading
Loading