package git import ( "fmt" "time" gogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/object" ) // GitBackend implements low-level Git object operations type GitBackend struct { repo *gogit.Repository } // NewGitBackend creates a new GitBackend instance func NewGitBackend(repo *gogit.Repository) *GitBackend { return &GitBackend{repo: repo} } // CreateBlob creates a new blob object from the given content func (gb *GitBackend) CreateBlob(content []byte) (string, error) { store := gb.repo.Storer // Create a blob object blob := store.NewEncodedObject() blob.SetType(plumbing.BlobObject) blob.SetSize(int64(len(content))) writer, err := blob.Writer() if err != nil { return "", fmt.Errorf("failed to get blob writer: %w", err) } _, err = writer.Write(content) if err != nil { writer.Close() return "", fmt.Errorf("failed to write blob content: %w", err) } if err := writer.Close(); err != nil { return "", fmt.Errorf("failed to close blob writer: %w", err) } // Store the blob hash, err := store.SetEncodedObject(blob) if err != nil { return "", fmt.Errorf("failed to store blob: %w", err) } return hash.String(), nil } // TreeEntry represents an entry in a Git tree type TreeEntry struct { Mode filemode.FileMode Name string Hash plumbing.Hash } // CreateTree creates a new tree object from the given entries func (gb *GitBackend) CreateTree(entries []TreeEntry) (string, error) { store := gb.repo.Storer // Create a new tree object tree := &object.Tree{} treeEntries := make([]object.TreeEntry, len(entries)) for i, entry := range entries { treeEntries[i] = object.TreeEntry{ Name: entry.Name, Mode: entry.Mode, Hash: entry.Hash, } } tree.Entries = treeEntries // Encode and store the tree obj := store.NewEncodedObject() if err := tree.Encode(obj); err != nil { return "", fmt.Errorf("failed to encode tree: %w", err) } hash, err := store.SetEncodedObject(obj) if err != nil { return "", fmt.Errorf("failed to store tree: %w", err) } return hash.String(), nil } // CreateCommit creates a new commit object func (gb *GitBackend) CreateCommit(treeHash, parentHash, message, author string) (string, error) { store := gb.repo.Storer // Parse hashes tree := plumbing.NewHash(treeHash) var parents []plumbing.Hash if parentHash != "" { parents = []plumbing.Hash{plumbing.NewHash(parentHash)} } // Create commit object commit := &object.Commit{ Author: object.Signature{ Name: author, Email: "onyx@local", When: time.Now(), }, Committer: object.Signature{ Name: author, Email: "onyx@local", When: time.Now(), }, Message: message, TreeHash: tree, } if len(parents) > 0 { commit.ParentHashes = parents } // Encode and store the commit obj := store.NewEncodedObject() if err := commit.Encode(obj); err != nil { return "", fmt.Errorf("failed to encode commit: %w", err) } hash, err := store.SetEncodedObject(obj) if err != nil { return "", fmt.Errorf("failed to store commit: %w", err) } return hash.String(), nil } // UpdateRef updates a Git reference to point to a new SHA func (gb *GitBackend) UpdateRef(refName, sha string) error { hash := plumbing.NewHash(sha) ref := plumbing.NewHashReference(plumbing.ReferenceName(refName), hash) if err := gb.repo.Storer.SetReference(ref); err != nil { return fmt.Errorf("failed to update reference %s: %w", refName, err) } return nil } // GetRef retrieves the SHA that a reference points to func (gb *GitBackend) GetRef(refName string) (string, error) { ref, err := gb.repo.Reference(plumbing.ReferenceName(refName), true) if err != nil { return "", fmt.Errorf("failed to get reference %s: %w", refName, err) } return ref.Hash().String(), nil } // GetObject retrieves a Git object by its SHA func (gb *GitBackend) GetObject(sha string) (object.Object, error) { hash := plumbing.NewHash(sha) obj, err := gb.repo.Object(plumbing.AnyObject, hash) if err != nil { return nil, fmt.Errorf("failed to get object %s: %w", sha, err) } return obj, nil } // GetBlob retrieves a blob object by its SHA func (gb *GitBackend) GetBlob(sha string) (*object.Blob, error) { hash := plumbing.NewHash(sha) blob, err := gb.repo.BlobObject(hash) if err != nil { return nil, fmt.Errorf("failed to get blob %s: %w", sha, err) } return blob, nil } // GetTree retrieves a tree object by its SHA func (gb *GitBackend) GetTree(sha string) (*object.Tree, error) { hash := plumbing.NewHash(sha) tree, err := gb.repo.TreeObject(hash) if err != nil { return nil, fmt.Errorf("failed to get tree %s: %w", sha, err) } return tree, nil } // GetCommit retrieves a commit object by its SHA func (gb *GitBackend) GetCommit(sha string) (*object.Commit, error) { hash := plumbing.NewHash(sha) commit, err := gb.repo.CommitObject(hash) if err != nil { return nil, fmt.Errorf("failed to get commit %s: %w", sha, err) } return commit, nil } // HashFromString converts a string SHA to a plumbing.Hash func HashFromString(sha string) plumbing.Hash { return plumbing.NewHash(sha) }