package git import ( "context" "fmt" "path" "strings" ) type TaggedSource struct { Head string Status string TagObject string TagCommit string TagObjectType string } // InspectTaggedSource reads the local HEAD, worktree state, and selected tag. // Both lightweight historical tags and annotated tags are supported. func InspectTaggedSource(ctx context.Context, repositoryDir, version string) (TaggedSource, error) { if !validVersionRef(version) { return TaggedSource{}, fmt.Errorf("invalid tag version %q", version) } var source TaggedSource head, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", "HEAD^{commit}") if err != nil { return TaggedSource{}, fmt.Errorf("read checkout HEAD: %w", err) } source.Head = strings.TrimSpace(head) if !validObjectID(source.Head) { return TaggedSource{}, fmt.Errorf("Git returned an invalid checkout commit %q", source.Head) } status, err := run(ctx, repositoryDir, nil, "status", "--porcelain", "--untracked-files=all") if err != nil { return TaggedSource{}, fmt.Errorf("inspect tagged checkout state: %w", err) } source.Status = strings.TrimSpace(status) ref := "refs/tags/" + version object, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", ref) if err != nil { return TaggedSource{}, fmt.Errorf("read local tag %s: %w", version, err) } source.TagObject = strings.TrimSpace(object) if !validObjectID(source.TagObject) { return TaggedSource{}, fmt.Errorf("Git returned an invalid tag object %q", source.TagObject) } peeled, err := run(ctx, repositoryDir, nil, "rev-parse", "--verify", ref+"^{commit}") if err != nil { return TaggedSource{}, fmt.Errorf("resolve local tag %s to a commit: %w", version, err) } source.TagCommit = strings.TrimSpace(peeled) if !validObjectID(source.TagCommit) { return TaggedSource{}, fmt.Errorf("Git returned an invalid tagged commit %q", source.TagCommit) } tagType, err := run(ctx, repositoryDir, nil, "cat-file", "-t", ref) if err != nil { return TaggedSource{}, fmt.Errorf("inspect local tag %s: %w", version, err) } source.TagObjectType = strings.TrimSpace(tagType) if source.TagObjectType != "tag" && source.TagObjectType != "commit" { return TaggedSource{}, fmt.Errorf("local tag %s has unsupported object type %q", version, source.TagObjectType) } return source, nil } // ReadCommitFile reads a repository-relative file from a specific commit. func ReadCommitFile(ctx context.Context, repositoryDir, commit, name string) ([]byte, error) { if !validObjectID(commit) || name == "" || path.IsAbs(name) || path.Clean(name) != name || strings.HasPrefix(name, "../") || name == ".." || strings.Contains(name, "\\") { return nil, fmt.Errorf("invalid committed file identity %q at %q", name, commit) } content, err := run(ctx, repositoryDir, nil, "show", commit+":"+name) if err != nil { return nil, fmt.Errorf("read committed file %q: %w", name, err) } return []byte(content), nil }