Documentation
¶
Overview ¶
Package commits provides commit history retrieval and message selection for merge/pull requests.
The package handles the complete commit message workflow:
- Retrieving commits from git branches via go-git
- Filtering out merge commits and empty messages
- Auto-selecting single commit messages
- Interactive selection when multiple commits exist
- Manual message override via CLI flag
The architecture uses three interfaces for testability:
- CommitRetriever for git operations
- MessageSelector for selection logic
- SelectionRenderer for terminal UI
Usage:
retriever := commits.NewRetriever(repo)
selection, err := retriever.GetMessageForMR("feature", "main", "")
fmt.Println(selection.Title) // First line of selected commit message
Thread Safety: Retriever and Selector are not safe for concurrent use. Each goroutine should create its own instance.
Index ¶
- Constants
- Variables
- func ParseCommitMessage(fullMessage string) (string, string)
- type Commit
- type CommitList
- type CommitRetriever
- type MessageSelection
- type MessageSelector
- type Renderer
- type Retriever
- func (r *Retriever) GetCommits(branch string) ([]Commit, error)
- func (r *Retriever) GetCommitsSinceBranch(currentBranch, baseBranch string) ([]Commit, error)
- func (r *Retriever) GetMessageForMR(branch, mainBranch, msgFlagValue string) (MessageSelection, error)
- func (r *Retriever) SetLogger(logger *slog.Logger)
- type SelectionMethod
- type SelectionRenderer
- type Selector
Constants ¶
const ( // MaxCommitsToRetrieve limits the number of commits to retrieve from history. MaxCommitsToRetrieve = 1000 // DefaultShortHashLength is the default length for abbreviated commit hashes. DefaultShortHashLength = 7 )
const (
// DefaultDisplayTitleLength is the default max length for commit titles in display.
DefaultDisplayTitleLength = 80
)
const (
// SelectionPageSize is the number of commits to show at once in the selection UI.
SelectionPageSize = 15
)
Variables ¶
var ( // ErrNoCommits is returned when no commits are found on the branch. ErrNoCommits = errors.New("no commits found on branch") // ErrAllCommitsInvalid is returned when all commits have empty messages or are merge commits. ErrAllCommitsInvalid = errors.New("all commits have empty messages") // ErrSelectionCancelled is returned when user cancels the interactive commit selection. ErrSelectionCancelled = errors.New("commit selection cancelled by user") // ErrMultipleCommitsFound is returned when multiple commits exist and interactive selection is needed. ErrMultipleCommitsFound = errors.New("multiple commits found") )
Functions ¶
func ParseCommitMessage ¶
ParseCommitMessage splits commit message into title (first line) and body (remaining lines). Title and body are trimmed of whitespace. Returns empty body if commit message is single-line.
Types ¶
type Commit ¶
type Commit struct {
// Hash is the full SHA-1 hash of the commit (40 characters).
Hash string
// ShortHash is the abbreviated hash for display (first 7 characters).
ShortHash string
// Message is the full commit message (title + body, preserving formatting).
Message string
// Title is the first line of commit message (used for MR/PR title).
Title string
// Body is the remaining lines after first line (used for MR/PR description).
Body string
// Author is the commit author name and email.
Author string
// Timestamp is when the commit was created.
Timestamp time.Time
// ParentHashes contains SHA hashes of parent commits (empty for initial commit, 2+ for merge commits).
ParentHashes []string
}
Commit represents a single git commit with its metadata and message content.
func FilterValidCommits ¶
FilterValidCommits returns commits that are not merge commits and have non-empty messages. A commit is excluded if Commit.IsMergeCommit returns true or Commit.IsValid returns false. Returns an empty slice if all commits are filtered out.
func ParseCommit ¶
ParseCommit converts a go-git object.Commit to the domain Commit type. The commit message is split into title and body using ParseCommitMessage. The short hash is the first DefaultShortHashLength characters of the full SHA-1 hash.
Parameters:
- gitCommit: a go-git commit object (must not be nil)
func (*Commit) FormattedForDisplay ¶
FormattedForDisplay returns "[ShortHash] TitleTruncated(DefaultDisplayTitleLength)" for UI display.
func (*Commit) IsMergeCommit ¶
IsMergeCommit returns true if commit has 2+ parent commits.
func (*Commit) TitleTruncated ¶
TitleTruncated returns the title truncated to maxLen characters with "..." suffix if longer. If maxLen is less than 3, the result may be shorter than expected.
type CommitList ¶
type CommitList struct {
// All contains all commits retrieved from git history (including merge commits).
All []Commit
// Valid contains filtered list excluding merge commits and empty messages.
Valid []Commit
// Branch is the name of the branch these commits belong to.
Branch string
// RetrievalTimestamp is when the commits were retrieved.
RetrievalTimestamp time.Time
}
CommitList represents a collection of commits from a branch, with filtering and selection capabilities.
func BuildCommitList ¶
func BuildCommitList(all []Commit, branch string) CommitList
BuildCommitList constructs a CommitList with FilterValidCommits applied automatically. The RetrievalTimestamp is set to the current time.
func (*CommitList) Count ¶
func (cl *CommitList) Count() int
Count returns the number of valid commits.
func (*CommitList) HasMultipleCommits ¶
func (cl *CommitList) HasMultipleCommits() bool
HasMultipleCommits returns true if 2+ valid commits exist.
func (*CommitList) HasSingleCommit ¶
func (cl *CommitList) HasSingleCommit() bool
HasSingleCommit returns true if exactly 1 valid commit exists.
func (*CommitList) IsEmpty ¶
func (cl *CommitList) IsEmpty() bool
IsEmpty returns true if zero valid commits exist.
type CommitRetriever ¶
type CommitRetriever interface {
// GetCommits retrieves all commits from the specified branch.
// Returns empty slice if branch has no commits.
// Returns error if branch doesn't exist or git operation fails.
GetCommits(branch string) ([]Commit, error)
}
CommitRetriever defines the interface for external git operations (retrieve commits, parse history).
type MessageSelection ¶
type MessageSelection struct {
// Title is the MR/PR title (first line of selected message).
Title string
// Body is the MR/PR description (remaining lines of selected message).
Body string
// SourceCommitHash is the hash of the commit the message came from (empty if manual override).
SourceCommitHash string
// SelectionMethod indicates how the message was selected (AUTO, INTERACTIVE, MANUAL).
SelectionMethod SelectionMethod
// ManualOverride is true if -msg flag was used.
ManualOverride bool
}
MessageSelection represents the result of the commit message selection process.
func (*MessageSelection) FullMessage ¶
func (ms *MessageSelection) FullMessage() string
FullMessage returns title + "\n\n" + body (reconstructed full message).
func (*MessageSelection) IsFromCommit ¶
func (ms *MessageSelection) IsFromCommit() bool
IsFromCommit returns true if SourceCommitHash is non-empty.
func (*MessageSelection) IsManualOverride ¶
func (ms *MessageSelection) IsManualOverride() bool
IsManualOverride returns true if message was provided via -msg flag.
type MessageSelector ¶
type MessageSelector interface {
// GetMessageForMR determines which commit message to use for MR/PR.
// Handles auto-selection, interactive selection, and manual override.
// Returns ErrNoCommits if no valid commits exist.
// Returns ErrSelectionCancelled if user cancels interactive selection.
GetMessageForMR(commits []Commit, msgFlagValue string) (MessageSelection, error)
}
MessageSelector defines the interface for internal selection logic (auto-select, filter, validate).
type Renderer ¶
type Renderer struct{}
Renderer implements the SelectionRenderer interface using the survey library for interactive terminal prompts.
func (*Renderer) DisplaySelectionPrompt ¶
DisplaySelectionPrompt shows an interactive commit selection UI using survey.Select. Each commit is displayed as "[ShortHash] Title" with at most SelectionPageSize items visible.
Parameters:
- commits: the list of commits to present (must not be empty)
Returns the zero-based index of the selected commit. Returns ErrAllCommitsInvalid if commits is empty. Returns ErrSelectionCancelled if the user cancels with Ctrl+C.
type Retriever ¶
type Retriever struct {
// contains filtered or unexported fields
}
Retriever handles commit history retrieval and message selection. It wraps a go-git repository and provides methods to retrieve commits, filter them, and select appropriate messages for merge/pull requests.
Not safe for concurrent use.
func NewRetriever ¶
func NewRetriever(repo *git.Repository) *Retriever
NewRetriever creates a new commit retriever for the given repository.
Parameters:
- repo: an opened go-git repository (must not be nil)
The retriever uses slog.Default() for logging. Call Retriever.SetLogger to override.
func (*Retriever) GetCommits ¶
GetCommits retrieves all commits from the specified branch, up to MaxCommitsToRetrieve.
Parameters:
- branch: local branch name (e.g., "feature-x", not "refs/heads/feature-x")
Returns ErrNoCommits if the branch has no commits. Returns a wrapped error if the branch doesn't exist or git operations fail.
Memory: allocates up to MaxCommitsToRetrieve (1000) Commit structs.
func (*Retriever) GetCommitsSinceBranch ¶
GetCommitsSinceBranch retrieves commits from currentBranch since it diverged from baseBranch. Only returns commits unique to currentBranch (not present in baseBranch).
Parameters:
- currentBranch: the feature branch to read commits from
- baseBranch: the branch to compare against (e.g., "main")
Returns ErrNoCommits if no commits exist since divergence. Returns a wrapped error if either branch doesn't exist or git operations fail. At most MaxCommitsToRetrieve commits are returned.
func (*Retriever) GetMessageForMR ¶
func (r *Retriever) GetMessageForMR(branch, mainBranch, msgFlagValue string) (MessageSelection, error)
GetMessageForMR determines which commit message to use for MR/PR. It applies the following priority:
- Manual override: if msgFlagValue is non-empty, it is parsed and returned directly.
- Auto-select: if exactly one valid commit exists, it is selected automatically.
- Multiple commits: returns ErrMultipleCommitsFound (interactive selection not yet implemented).
Parameters:
- branch: the feature branch name
- mainBranch: the base branch to compare against (e.g., "main")
- msgFlagValue: manual message from --msg flag (empty string to skip)
Returns ErrNoCommits if no commits exist since divergence. Returns ErrAllCommitsInvalid if all commits are merge commits or have empty messages.
type SelectionMethod ¶
type SelectionMethod int
SelectionMethod represents how the commit message was selected for the MR/PR.
const ( // SelectionAuto indicates single commit auto-selected (no user prompt). SelectionAuto SelectionMethod = iota // SelectionInteractive indicates user selected from multiple commits via UI. SelectionInteractive // SelectionManual indicates user provided custom message via -msg flag. SelectionManual )
type SelectionRenderer ¶
type SelectionRenderer interface {
// DisplaySelectionPrompt shows interactive commit selection UI.
// Returns selected commit index.
// Returns error if user cancels (Ctrl+C).
DisplaySelectionPrompt(commits []Commit) (int, error)
}
SelectionRenderer defines the interface for UI rendering (display list, handle input).
type Selector ¶
type Selector struct {
// contains filtered or unexported fields
}
Selector handles commit message selection logic with support for auto-selection, interactive prompts, and manual override. It delegates UI rendering to a SelectionRenderer implementation.
Not safe for concurrent use.
func NewSelector ¶
func NewSelector(renderer SelectionRenderer) *Selector
NewSelector creates a new message selector with the given renderer.
Parameters:
- renderer: the UI renderer for interactive selection (must not be nil)
func (*Selector) GetMessageForMR ¶
func (s *Selector) GetMessageForMR(commits []Commit, msgFlagValue string) (MessageSelection, error)
GetMessageForMR determines which commit message to use for MR/PR. It applies the following priority:
- Manual override: if msgFlagValue is non-empty, it is parsed and returned.
- Auto-select: if exactly one valid commit exists, it is selected automatically.
- Interactive: if multiple valid commits exist, the renderer prompts the user.
Parameters:
- commits: all commits from the branch (will be filtered internally)
- msgFlagValue: manual message from --msg flag (empty string to skip)
Returns ErrAllCommitsInvalid if no valid commits exist after filtering. Returns ErrSelectionCancelled if the user cancels interactive selection.