package validation import ( "context" "fmt" "gitea.maximumdirect.net/eric/ReleaseKit/internal/git" ) // CandidateTree is an inventory of one captured commit with blob contents // loaded lazily by object ID. type CandidateTree struct { repositoryDir string entries []git.TreeEntry byPath map[string]int blobs map[string][]byte } // ReadCandidateTree enumerates the captured commit without reading from the // worktree. func ReadCandidateTree(ctx context.Context, repositoryDir, commit string) (*CandidateTree, error) { entries, err := git.ReadTree(ctx, repositoryDir, commit) if err != nil { return nil, err } tree := &CandidateTree{ repositoryDir: repositoryDir, entries: entries, byPath: make(map[string]int, len(entries)), blobs: make(map[string][]byte), } for index, entry := range entries { tree.byPath[entry.Path] = index } return tree, nil } // Entries returns the committed tree metadata in Git's tree order. func (tree *CandidateTree) Entries() []git.TreeEntry { return append([]git.TreeEntry(nil), tree.entries...) } // ReadFile returns the committed blob bytes for path. Contents are cached by // object ID for later reads from this tree. func (tree *CandidateTree) ReadFile(ctx context.Context, path string) ([]byte, error) { index, ok := tree.byPath[path] if !ok { return nil, fmt.Errorf("committed path %q does not exist", path) } entry := tree.entries[index] if entry.Type != "blob" { return nil, fmt.Errorf("committed path %q is a %s entry, not a blob", path, entry.Type) } if contents, ok := tree.blobs[entry.ObjectID]; ok { return append([]byte(nil), contents...), nil } contents, err := git.ReadBlob(ctx, tree.repositoryDir, entry.ObjectID) if err != nil { return nil, fmt.Errorf("read committed path %q: %w", path, err) } tree.blobs[entry.ObjectID] = append([]byte(nil), contents...) return contents, nil } // Entry returns the committed metadata for path when present. func (tree *CandidateTree) Entry(path string) (git.TreeEntry, bool) { index, ok := tree.byPath[path] if !ok { return git.TreeEntry{}, false } return tree.entries[index], true }