commits

package
v0.15.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 10 Imported by: 0

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:

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

View Source
const (
	// MaxCommitsToRetrieve limits the number of commits to retrieve from history.
	MaxCommitsToRetrieve = 1000
	// DefaultShortHashLength is the default length for abbreviated commit hashes.
	DefaultShortHashLength = 7
)
View Source
const (
	// DefaultDisplayTitleLength is the default max length for commit titles in display.
	DefaultDisplayTitleLength = 80
)
View Source
const (
	// SelectionPageSize is the number of commits to show at once in the selection UI.
	SelectionPageSize = 15
)

Variables

View Source
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

func ParseCommitMessage(fullMessage string) (string, string)

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

func FilterValidCommits(commits []Commit) []Commit

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

func ParseCommit(gitCommit *object.Commit) Commit

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

func (c *Commit) FormattedForDisplay() string

FormattedForDisplay returns "[ShortHash] TitleTruncated(DefaultDisplayTitleLength)" for UI display.

func (*Commit) IsMergeCommit

func (c *Commit) IsMergeCommit() bool

IsMergeCommit returns true if commit has 2+ parent commits.

func (*Commit) IsValid

func (c *Commit) IsValid() bool

IsValid returns true if message is non-empty after trimming whitespace.

func (*Commit) TitleTruncated

func (c *Commit) TitleTruncated(maxLen int) string

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 NewRenderer

func NewRenderer() *Renderer

NewRenderer creates a new selection renderer.

func (*Renderer) DisplaySelectionPrompt

func (r *Renderer) DisplaySelectionPrompt(commits []Commit) (int, error)

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

func (r *Retriever) GetCommits(branch string) ([]Commit, error)

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

func (r *Retriever) GetCommitsSinceBranch(currentBranch, baseBranch string) ([]Commit, error)

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:

  1. Manual override: if msgFlagValue is non-empty, it is parsed and returned directly.
  2. Auto-select: if exactly one valid commit exists, it is selected automatically.
  3. 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.

func (*Retriever) SetLogger

func (r *Retriever) SetLogger(logger *slog.Logger)

SetLogger sets the logger for the retriever.

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:

  1. Manual override: if msgFlagValue is non-empty, it is parsed and returned.
  2. Auto-select: if exactly one valid commit exists, it is selected automatically.
  3. 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.

func (*Selector) SetLogger

func (s *Selector) SetLogger(logger *slog.Logger)

SetLogger sets the logger for the selector.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL