Function bodies 172 total
cmd.renderFileList function · go · L540-L569 (30 LOC)cmd/pr_preview_render.go
func renderFileList(files []github.PullRequestFile, fileComments map[string]int, totalChanged int, haPaths map[string]bool, prURL string, contentWidth int) string {
var displayFiles []github.PullRequestFile
for _, f := range files {
if haPaths[f.Path] {
continue
}
if len(displayFiles) >= maxPreviewFiles {
break
}
displayFiles = append(displayFiles, f)
}
cw := computeFileColumnWidths(displayFiles, fileComments)
header := sectionHeader(fmt.Sprintf("Files (%d)", totalChanged))
var sb strings.Builder
sb.WriteString(header)
sb.WriteString("\n")
for _, f := range displayFiles {
sb.WriteString(formatFileEntry(f, fileComments[f.Path], prURL, contentWidth, cw))
sb.WriteString("\n")
}
remaining := totalChanged - len(displayFiles) - len(haPaths)
if remaining > 0 {
fmt.Fprintf(&sb, " … and %d more\n", remaining)
}
return strings.TrimRight(sb.String(), "\n")
}cmd.renderBody function · go · L571-L597 (27 LOC)cmd/pr_preview_render.go
func renderBody(body string, width int, colorMode string) string {
if body == "" {
return ""
}
opts := []glamour.TermRendererOption{
glamour.WithAutoStyle(),
glamour.WithWordWrap(max(width-4, 20)),
}
switch colorMode {
case "always":
opts = append(opts, glamour.WithColorProfile(termenv.ANSI256))
case "never":
opts = append(opts, glamour.WithColorProfile(termenv.Ascii))
}
// TODO: replace with structured debug logging when a logging framework is added
renderer, err := glamour.NewTermRenderer(opts...)
if err != nil {
fmt.Fprintf(os.Stderr, "warning: markdown renderer init failed, using plain text: %v\n", err)
return wrapBody(body, width)
}
rendered, err := renderer.Render(body)
if err != nil {
fmt.Fprintf(os.Stderr, "warning: markdown rendering failed, using plain text: %v\n", err)
return wrapBody(body, width)
}
return strings.TrimSpace(rendered)
}cmd.collapseTimeline function · go · L610-L623 (14 LOC)cmd/pr_preview_render.go
func collapseTimeline(events []github.TimelineEvent) []collapsedEvent {
var collapsed []collapsedEvent
for _, e := range events {
n := len(collapsed)
if n > 0 && isConsecutiveCommit(collapsed[n-1], e) {
collapsed[n-1].count++
collapsed[n-1].event.CreatedAt = e.CreatedAt
collapsed[n-1].event.Details = e.Details
} else {
collapsed = append(collapsed, collapsedEvent{count: 1, event: e})
}
}
return collapsed
}cmd.renderTimeline function · go · L625-L673 (49 LOC)cmd/pr_preview_render.go
func renderTimeline(pr github.PullRequest, timeline []github.TimelineEvent) string {
var events []github.TimelineEvent
if !pr.CreatedAt.IsZero() {
events = append(events, github.TimelineEvent{
Actor: pr.AuthorLogin,
CreatedAt: pr.CreatedAt,
Type: github.TimelineEventOpened,
})
}
events = append(events, timeline...)
slices.SortFunc(events, func(a, b github.TimelineEvent) int {
return a.CreatedAt.Compare(b.CreatedAt)
})
collapsed := collapseTimeline(events)
if len(collapsed) == 0 {
return ""
}
header := sectionHeader("Activity")
timeStyle := lipgloss.NewStyle().Foreground(colorGray).Width(16).Align(lipgloss.Right)
lineStyle := lipgloss.NewStyle().Foreground(colorPurple)
var sb strings.Builder
sb.WriteString(header)
sb.WriteString("\n")
for i, ce := range collapsed {
e := ce.event
var icon, msg string
switch {
case e.Type == github.TimelineEventOpened:
icon = colorIcon(iconOpened, colorGreen)
msg = fmt.Sprintf("@%s opened this Pcmd.renderPreview function · go · L675-L716 (42 LOC)cmd/pr_preview_render.go
func renderPreview(w io.Writer, pr github.PullRequest, fileComments map[string]int, timeline []github.TimelineEvent, width int, colorMode string) error {
border := lipgloss.RoundedBorder()
boxStyle := lipgloss.NewStyle().
Border(border).
BorderForeground(colorPurple).
Width(width-2).
Padding(0, 1)
var sections []string
sections = append(sections, boxStyle.Render(renderHeader(pr)))
if checksContent := renderChecks(pr); checksContent != "" {
sections = append(sections, boxStyle.Render(checksContent))
}
scored := scoreFiles(pr.Files, fileComments)
contentWidth := width - 4
highActivity, shownFiles := renderHighActivity(scored, pr.URL, contentWidth)
if highActivity != "" {
sections = append(sections, boxStyle.Render(highActivity))
}
haPaths := make(map[string]bool, len(shownFiles))
for _, sf := range shownFiles {
haPaths[sf.file.Path] = true
}
sections = append(sections, boxStyle.Render(renderFileList(pr.Files, fileComments, pr.FilesChanged, haPaths, pr.cmd.runPrune function · go · L49-L67 (19 LOC)cmd/prune.go
func runPrune(cmd *cobra.Command, _ []string) error {
rt, err := loadCommandRuntime()
if err != nil {
return err
}
ghClient, err := rt.newCachedGitHubClient()
if err != nil {
return fmt.Errorf("failed to create GitHub client: %w", err)
}
ctx := &statusContext{
ghClient: ghClient,
gitClient: git.New(false, rt.mainWorktreePath, rt.cfg.Git.Timeout),
mainWorktreePath: rt.mainWorktreePath,
}
return executePrune(cmd.OutOrStdout(), ctx)
}cmd.executePrune function · go · L69-L111 (43 LOC)cmd/prune.go
func executePrune(w io.Writer, ctx *statusContext) error {
statuses, err := gatherStatuses(ctx)
if err != nil {
return err
}
remoteBranches, err := buildRemoteBranchSet(ctx.gitClient)
if err != nil {
return err
}
prunables := findPrunable(statuses, remoteBranches)
if len(prunables) == 0 {
_, err := fmt.Fprintln(w, "Nothing to prune.")
return err
}
selected, err := promptPruneSelection(prunables)
if err != nil {
if errors.Is(err, huh.ErrUserAborted) {
return nil
}
return err
}
if len(selected) == 0 {
_, err := fmt.Fprintln(w, "No worktrees selected.")
return err
}
confirmed, err := promptPruneConfirm(len(selected))
if err != nil {
if errors.Is(err, huh.ErrUserAborted) {
return nil
}
return err
}
if !confirmed {
_, err := fmt.Fprintln(w, "Aborted.")
return err
}
return executeRemovals(w, ctx.gitClient, selected)
}Open data scored by Repobility · https://repobility.com
cmd.buildRemoteBranchSet function · go · L113-L130 (18 LOC)cmd/prune.go
func buildRemoteBranchSet(gitClient git.Git) (map[string]bool, error) {
remotes, err := gitClient.ListRemotes()
if err != nil {
return nil, fmt.Errorf("failed to list remotes: %w", err)
}
remoteBranches := make(map[string]bool)
for _, remote := range remotes {
branches, err := gitClient.ListRemoteBranches(remote)
if err != nil {
return nil, fmt.Errorf("failed to list branches for remote %q: %w", remote, err)
}
for _, b := range branches {
remoteBranches[remote+"/"+b.Name] = true
}
}
return remoteBranches, nil
}cmd.findPrunable function · go · L132-L149 (18 LOC)cmd/prune.go
func findPrunable(statuses []worktreeStatus, remoteBranches map[string]bool) []prunable {
var result []prunable
for _, ws := range statuses {
if ws.isMain {
continue
}
if reason := pruneReason(ws, remoteBranches); reason != "" {
result = append(result, prunable{
branchName: ws.branchName,
name: filepath.Base(ws.absPath),
path: ws.absPath,
reason: reason,
})
}
}
return result
}cmd.pruneReason function · go · L151-L170 (20 LOC)cmd/prune.go
func pruneReason(ws worktreeStatus, remoteBranches map[string]bool) string {
if _, err := os.Stat(ws.absPath); os.IsNotExist(err) {
return "orphaned"
}
if ws.pr != nil {
switch ws.pr.State {
case github.PRStateMerged:
return fmt.Sprintf("PR #%d merged", ws.pr.Number)
case github.PRStateClosed:
return fmt.Sprintf("PR #%d closed", ws.pr.Number)
}
}
if ws.tracking.upstream != "" && !remoteBranches[ws.tracking.upstream] {
return "upstream gone"
}
return ""
}cmd.promptPruneSelection function · go · L178-L206 (29 LOC)cmd/prune.go
func promptPruneSelection(prunables []prunable) ([]prunable, error) {
options := make([]huh.Option[int], len(prunables))
for i, p := range prunables {
label := fmt.Sprintf("%s %s",
p.name,
lipgloss.NewStyle().Foreground(colorGray).Render("("+p.reason+")"),
)
options[i] = huh.NewOption(label, i).Selected(true)
}
var selected []int
err := huh.NewForm(
huh.NewGroup(
huh.NewMultiSelect[int]().
Title("Select worktrees to remove").
Options(options...).
Value(&selected),
),
).WithKeyMap(pruneKeyMap()).Run()
if err != nil {
return nil, err
}
var result []prunable
for _, idx := range selected {
result = append(result, prunables[idx])
}
return result, nil
}cmd.promptPruneConfirm function · go · L208-L228 (21 LOC)cmd/prune.go
func promptPruneConfirm(count int) (bool, error) {
noun := "worktree"
if count > 1 {
noun = "worktrees"
}
var confirmed bool
err := huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title(fmt.Sprintf("Remove %d %s?", count, noun)).
Affirmative("Yes").
Negative("No").
Value(&confirmed),
),
).WithKeyMap(pruneKeyMap()).Run()
if err != nil {
return false, err
}
return confirmed, nil
}cmd.executeRemovals function · go · L235-L248 (14 LOC)cmd/prune.go
func executeRemovals(w io.Writer, gitClient git.Git, selected []prunable) error {
results := make([]pruneResult, len(selected))
for i, p := range selected {
var err error
if p.reason == "orphaned" {
err = removeOrphanedWorktree(gitClient, p.branchName)
} else {
err = removeWorktreeAndBranch(gitClient, p.path, p.branchName)
}
results[i] = pruneResult{prunable: p, err: err}
}
return renderPruneResults(w, results)
}cmd.renderPruneResults function · go · L250-L293 (44 LOC)cmd/prune.go
func renderPruneResults(w io.Writer, results []pruneResult) error {
successStyle := lipgloss.NewStyle().Foreground(colorGreen)
failStyle := lipgloss.NewStyle().Foreground(colorRed)
reasonStyle := lipgloss.NewStyle().Foreground(colorGray)
var rows [][]string
var removed, failed int
for _, r := range results {
icon := successStyle.Render(iconCheck)
status := ""
if r.err != nil {
icon = failStyle.Render(iconCross)
status = failStyle.Render(r.err.Error())
failed++
} else {
removed++
}
rows = append(rows, []string{icon, r.prunable.name, r.prunable.branchName, reasonStyle.Render(r.prunable.reason), status})
}
t := table.New().
Border(lipgloss.HiddenBorder()).
StyleFunc(func(_, _ int) lipgloss.Style {
return lipgloss.NewStyle().Padding(0, 1)
}).
Rows(rows...)
if _, err := fmt.Fprintln(w, t); err != nil {
return err
}
summary := successStyle.Render(fmt.Sprintf("%d removed", removed))
if failed > 0 {
summary += ", " + failStyle.Render(fmt.Scmd.removeOrphanedWorktree function · go · L295-L307 (13 LOC)cmd/prune.go
func removeOrphanedWorktree(gitClient git.Git, branchName string) error {
if err := gitClient.PruneWorktrees(); err != nil {
return fmt.Errorf("failed to prune worktrees: %w", err)
}
if branchName != "" {
if err := gitClient.DeleteBranch(branchName, true); err != nil {
if !strings.Contains(err.Error(), "not found") {
return fmt.Errorf("failed to delete branch %q: %w", branchName, err)
}
}
}
return nil
}Repobility analyzer · published findings · https://repobility.com
cmd.init function · go · L37-L42 (6 LOC)cmd/remove.go
func init() {
removeCmd.GroupID = "worktree"
removeCmd.Flags().BoolVar(&removeForceFlag, "force", false, "Force removal even with uncommitted changes")
removeCmd.Flags().BoolVar(&removeKeepBranchFlag, "keep-branch", false, "Keep the local branch after removing the worktree")
rootCmd.AddCommand(removeCmd)
}cmd.runRemove function · go · L49-L63 (15 LOC)cmd/remove.go
func runRemove(cmd *cobra.Command, args []string) error {
rt, err := loadCommandRuntime()
if err != nil {
return err
}
// Root the git client at mainWorktreePath so that post-removal commands
// (DeleteBranch, PruneWorktrees) still work even when cwd is the removed worktree.
ctx := &removeContext{
gitClient: git.New(false, rt.mainWorktreePath, rt.cfg.Git.Timeout),
mainWorktreePath: rt.mainWorktreePath,
}
return executeRemove(cmd.OutOrStdout(), ctx, args[0], removeForceFlag, removeKeepBranchFlag)
}cmd.executeRemove function · go · L65-L117 (53 LOC)cmd/remove.go
func executeRemove(w io.Writer, ctx *removeContext, target string, force, keepBranch bool) error {
worktrees, err := ctx.gitClient.ListWorktrees()
if err != nil {
return fmt.Errorf("failed to list worktrees: %w", err)
}
workspacePath, err := ctx.gitClient.GetWorkspacePath()
if err != nil {
return fmt.Errorf("failed to get workspace path: %w", err)
}
wt, err := resolveTarget(target, worktrees, workspacePath)
if err != nil {
return err
}
if wt.AbsolutePath == ctx.mainWorktreePath {
return errors.New("cannot remove the main worktree")
}
if !force {
dirty, err := ctx.gitClient.IsWorktreeDirty(wt.AbsolutePath)
if err != nil {
return fmt.Errorf("failed to check worktree state: %w", err)
}
if dirty {
return fmt.Errorf("worktree %q has uncommitted changes; use --force to remove anyway", filepath.Base(wt.AbsolutePath))
}
}
branchName := extractBranchName(wt)
if err := ctx.gitClient.RemoveWorktree(wt.AbsolutePath, force); err != nil {
return fmt.Errcmd.resolveTarget function · go · L119-L140 (22 LOC)cmd/remove.go
func resolveTarget(target string, worktrees []git.Worktree, workspacePath string) (*git.Worktree, error) {
for i := range worktrees {
if worktrees[i].AbsolutePath == target {
return &worktrees[i], nil
}
}
absTarget := filepath.Join(workspacePath, target)
for i := range worktrees {
if worktrees[i].AbsolutePath == absTarget {
return &worktrees[i], nil
}
}
for i := range worktrees {
if name := extractBranchName(&worktrees[i]); name == target {
return &worktrees[i], nil
}
}
return nil, fmt.Errorf("no worktree found matching %q", target)
}cmd.extractBranchName function · go · L142-L150 (9 LOC)cmd/remove.go
func extractBranchName(wt *git.Worktree) string {
if wt.Ref == nil {
return ""
}
if branch, ok := wt.Ref.FullBranch(); ok {
return branch.Name
}
return ""
}cmd.removeWorktreeAndBranch function · go · L152-L167 (16 LOC)cmd/remove.go
func removeWorktreeAndBranch(gitClient git.Git, absPath, branchName string) error {
if err := gitClient.RemoveWorktree(absPath, true); err != nil {
return fmt.Errorf("failed to remove worktree %q: %w", filepath.Base(absPath), err)
}
if branchName != "" {
if err := gitClient.DeleteBranch(branchName, true); err != nil {
if !strings.Contains(err.Error(), "not found") {
return fmt.Errorf("failed to delete branch %q: %w", branchName, err)
}
}
}
if err := gitClient.PruneWorktrees(); err != nil {
return fmt.Errorf("failed to prune worktrees: %w", err)
}
return nil
}cmd.init function · go · L23-L41 (19 LOC)cmd/root.go
func init() {
rootCmd.Version = Version
rootCmd.PersistentFlags().Bool("debug", false, "Enable debug logging")
rootCmd.AddGroup(
&cobra.Group{ID: "worktree", Title: "Worktree Commands:"},
&cobra.Group{ID: "pr", Title: "Pull Request Commands:"},
&cobra.Group{ID: "config", Title: "Configuration Commands:"},
)
rootCmd.SetHelpCommandGroupID("config")
rootCmd.SetCompletionCommandGroupID("config")
configureLogStyles()
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
if debug, _ := cmd.Flags().GetBool("debug"); debug {
log.SetLevel(log.DebugLevel)
}
}
}cmd.configureLogStyles function · go · L43-L65 (23 LOC)cmd/root.go
func configureLogStyles() {
styles := log.DefaultStyles()
// Catppuccin Latte (light) / Mocha (dark) palette.
muted := lipgloss.AdaptiveColor{Light: "#6c6f85", Dark: "#7f849c"}
styles.Caller = lipgloss.NewStyle().Foreground(muted)
styles.Key = lipgloss.NewStyle().Foreground(muted)
styles.Prefix = lipgloss.NewStyle().Foreground(muted).Bold(true)
styles.Separator = lipgloss.NewStyle().Foreground(muted)
styles.Levels[log.DebugLevel] = styles.Levels[log.DebugLevel].
Foreground(lipgloss.AdaptiveColor{Light: "#8c8fa1", Dark: "#7f849c"})
styles.Levels[log.InfoLevel] = styles.Levels[log.InfoLevel].
Foreground(lipgloss.AdaptiveColor{Light: "#179299", Dark: "#94e2d5"})
styles.Levels[log.WarnLevel] = styles.Levels[log.WarnLevel].
Foreground(lipgloss.AdaptiveColor{Light: "#df8e1d", Dark: "#f9e2af"})
styles.Levels[log.ErrorLevel] = styles.Levels[log.ErrorLevel].
Foreground(lipgloss.AdaptiveColor{Light: "#d20f39", Dark: "#f38ba8"})
styles.Levels[log.FatalLevel] = styles.Levels[Same scanner, your repo: https://repobility.com — Repobility
cmd.commandRuntime.newCachedGitHubClient method · go · L31-L38 (8 LOC)cmd/runtime_context.go
func (rt *commandRuntime) newCachedGitHubClient() (github.GitHub, error) {
dir, err := cache.DefaultDir()
if err != nil {
return nil, err
}
c := cache.New(dir, rt.cfg.GitHub.PreviewCacheTTL)
return github.New(rt.cwd, rt.cfg.Git.Timeout, c), nil
}cmd.loadCommandRuntime function · go · L40-L98 (59 LOC)cmd/runtime_context.go
func loadCommandRuntime() (*commandRuntime, error) {
originalCwd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("failed to get current directory: %w", err)
}
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get user home directory: %w", err)
}
defaultTimeout := config.DefaultConfig().Git.Timeout
gitDir := originalCwd
initGit := git.New(false, gitDir, defaultTimeout)
worktreeRoot, err := initGit.GetWorktreeRoot()
if err != nil {
return nil, fmt.Errorf("git error: %w", err)
}
if worktreeRoot == "" {
log.Debug("not in a git repository, attempting workspace root detection", "cwd", originalCwd)
bootstrapPaths := config.BootstrapConfigPaths(originalCwd, homeDir)
bootstrapResult, err := config.NewDefaultLoader().Load(bootstrapPaths)
if err != nil {
return nil, fmt.Errorf("failed to load bootstrap config: %w", err)
}
log.Debug("bootstrap config loaded", "paths", bootstrapPaths, "sources", bootstrapResult.Sourcecmd.resolveWorkspaceRoot function · go · L102-L131 (30 LOC)cmd/runtime_context.go
func resolveWorkspaceRoot(cwd string, primaryBranches []string, timeout time.Duration) (string, error) {
if len(primaryBranches) == 0 {
return "", errors.New("no primary branches configured")
}
log.Debug("probing for primary worktree", "candidates", primaryBranches)
for _, name := range primaryBranches {
candidate := filepath.Join(cwd, name)
if !hasGitMarker(candidate) {
continue
}
testGit := git.New(false, candidate, timeout)
testRoot, err := testGit.GetWorktreeRoot()
if err != nil {
log.Debug("candidate git error", "dir", candidate, "err", err)
continue
}
if testRoot == "" {
log.Debug("candidate has .git marker but is not a valid repo", "dir", candidate)
continue
}
log.Debug("found valid worktree", "dir", candidate)
return testRoot, nil
}
return "", errors.New("no valid worktree found in workspace root")
}cmd.runStatus function · go · L52-L75 (24 LOC)cmd/status.go
func runStatus(cmd *cobra.Command, _ []string) error {
rt, err := loadCommandRuntime()
if err != nil {
return err
}
ghClient, err := rt.newCachedGitHubClient()
if err != nil {
return fmt.Errorf("failed to create GitHub client: %w", err)
}
ctx := &statusContext{
ghClient: ghClient,
gitClient: rt.gitClient,
mainWorktreePath: rt.mainWorktreePath,
}
statuses, err := gatherStatuses(ctx)
if err != nil {
return err
}
return renderStatusTable(cmd.OutOrStdout(), statuses)
}cmd.gatherStatuses function · go · L77-L100 (24 LOC)cmd/status.go
func gatherStatuses(ctx *statusContext) ([]worktreeStatus, error) {
worktrees, err := ctx.gitClient.ListWorktrees()
if err != nil {
return nil, fmt.Errorf("failed to list worktrees: %w", err)
}
branches, err := ctx.gitClient.ListLocalBranches()
if err != nil {
return nil, fmt.Errorf("failed to list branches: %w", err)
}
branchMap := make(map[string]git.LocalBranch, len(branches))
for _, b := range branches {
branchMap[b.Name] = b
}
var statuses []worktreeStatus
for _, wt := range worktrees {
ws := buildWorktreeStatus(ctx, wt, branchMap)
statuses = append(statuses, ws)
}
return statuses, nil
}cmd.buildWorktreeStatus function · go · L102-L136 (35 LOC)cmd/status.go
func buildWorktreeStatus(ctx *statusContext, wt git.Worktree, branchMap map[string]git.LocalBranch) worktreeStatus {
ws := worktreeStatus{
absPath: wt.AbsolutePath,
isMain: wt.AbsolutePath == ctx.mainWorktreePath,
}
ws.branchName = extractBranchName(&wt)
if ws.branchName == "" {
return ws
}
if b, ok := branchMap[ws.branchName]; ok {
ws.tracking = trackingInfo{
ahead: b.Ahead,
behind: b.Behind,
upstream: b.UpstreamName,
}
}
dirty, err := ctx.gitClient.IsWorktreeDirty(wt.AbsolutePath)
if err == nil {
ws.dirty = dirty
}
pr, err := ctx.ghClient.GetPullRequestByBranch(ws.branchName)
if err == nil && pr != nil {
ws.kind = "PR"
ws.pr = pr
} else if !ws.isMain {
ws.kind = "local"
}
return ws
}cmd.renderStatusTable function · go · L138-L183 (46 LOC)cmd/status.go
func renderStatusTable(w io.Writer, statuses []worktreeStatus) error {
if len(statuses) == 0 {
_, err := fmt.Fprintln(w, "No worktrees found.")
return err
}
purple := lipgloss.Color("99")
gray := lipgloss.Color("245")
lightGray := lipgloss.Color("241")
headerStyle := lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center)
cellStyle := lipgloss.NewStyle().Padding(0, 1)
oddRowStyle := cellStyle.Foreground(gray)
evenRowStyle := cellStyle.Foreground(lightGray)
rows := make([][]string, len(statuses))
for i, ws := range statuses {
rows[i] = []string{
filepath.Base(ws.absPath),
ws.branchName,
formatDirtyStatus(ws),
formatTracking(ws.tracking),
formatKind(ws),
formatPRInfo(ws),
}
}
t := table.New().
Border(lipgloss.NormalBorder()).
BorderStyle(lipgloss.NewStyle().Foreground(purple)).
StyleFunc(func(row, col int) lipgloss.Style {
switch {
case row == table.HeaderRow:
return headerStyle
case row%2 == 0:
return evecmd.formatDirtyStatus function · go · L185-L193 (9 LOC)cmd/status.go
func formatDirtyStatus(ws worktreeStatus) string {
if ws.branchName == "" {
return ""
}
if ws.dirty {
return colorIcon(iconCross, colorRed) + " dirty"
}
return colorIcon(iconCheck, colorGreen) + " clean"
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
cmd.formatTracking function · go · L195-L210 (16 LOC)cmd/status.go
func formatTracking(ti trackingInfo) string {
if ti.upstream == "" {
return ""
}
if ti.ahead == 0 && ti.behind == 0 {
return "\u2261" // ≡
}
var parts []string
if ti.ahead > 0 {
parts = append(parts, fmt.Sprintf("\u2191%d", ti.ahead))
}
if ti.behind > 0 {
parts = append(parts, fmt.Sprintf("\u2193%d", ti.behind))
}
return strings.Join(parts, " ")
}cmd.formatKind function · go · L212-L221 (10 LOC)cmd/status.go
func formatKind(ws worktreeStatus) string {
switch ws.kind {
case "PR":
return lipgloss.NewStyle().Foreground(colorPurple).Render("PR")
case "local":
return lipgloss.NewStyle().Foreground(colorGray).Render("local")
default:
return ""
}
}cmd.formatPRInfo function · go · L223-L246 (24 LOC)cmd/status.go
func formatPRInfo(ws worktreeStatus) string {
if ws.pr == nil {
return ""
}
pr := ws.pr
stateStr := lipgloss.NewStyle().Foreground(stateColor(pr.State)).Render(strings.ToLower(string(pr.State)))
info := fmt.Sprintf("#%d %s", pr.Number, stateStr)
if checksStr := formatChecksSummary(pr.StatusChecks); checksStr != "" {
info += " " + checksStr
}
if reviewStr := formatReviewSummary(pr.Reviews); reviewStr != "" {
info += " " + reviewStr
}
info += fmt.Sprintf(" %s %s",
lipgloss.NewStyle().Foreground(colorGreen).Render(fmt.Sprintf("+%d", pr.LinesAdded)),
lipgloss.NewStyle().Foreground(colorRed).Render(fmt.Sprintf("-%d", pr.LinesDeleted)),
)
return info
}cmd.formatChecksSummary function · go · L248-L264 (17 LOC)cmd/status.go
func formatChecksSummary(checks []github.StatusCheck) string {
if len(checks) == 0 {
return ""
}
passed := 0
for _, c := range checks {
if c.Conclusion == github.CheckConclusionSuccess {
passed++
}
}
total := len(checks)
icon := colorIcon(iconCross, colorRed)
if passed == total {
icon = colorIcon(iconCheck, colorGreen)
}
return icon + fmt.Sprintf("%d/%d checks", passed, total)
}cmd.formatReviewSummary function · go · L266-L292 (27 LOC)cmd/status.go
func formatReviewSummary(reviews []github.Review) string {
if len(reviews) == 0 {
return ""
}
deduped := deduplicateReviews(reviews)
hasApproval := false
hasChangesRequested := false
for _, r := range deduped {
switch r.State {
case github.ReviewStateApproved:
hasApproval = true
case github.ReviewStateChangesRequested:
hasChangesRequested = true
}
}
if hasChangesRequested {
return lipgloss.NewStyle().Foreground(colorRed).Render("changes requested")
}
if hasApproval {
return lipgloss.NewStyle().Foreground(colorGreen).Render("approved")
}
noun := "reviews"
if len(deduped) == 1 {
noun = "review"
}
return lipgloss.NewStyle().Foreground(colorGray).Render(fmt.Sprintf("%d %s", len(deduped), noun))
}cache.New function · go · L32-L38 (7 LOC)internal/cache/cache.go
func New(dir string, ttl time.Duration) *Cache {
return &Cache{
dir: dir,
log: clog.Default().WithPrefix("cache"),
ttl: ttl,
}
}cache.DefaultDir function · go · L41-L47 (7 LOC)internal/cache/cache.go
func DefaultDir() (string, error) {
base, err := os.UserCacheDir()
if err != nil {
return "", fmt.Errorf("failed to get user cache dir: %w", err)
}
return filepath.Join(base, "grove", "v1"), nil
}cache.Cache.Get method · go · L57-L86 (30 LOC)internal/cache/cache.go
func (c *Cache) Get(key string) (string, bool) {
if c == nil || c.ttl == 0 {
return "", false
}
path := c.path(key)
data, err := os.ReadFile(path)
if err != nil {
return "", false
}
var env envelope
if err := json.Unmarshal(data, &env); err != nil {
c.log.Debug("cache: corrupt entry", "key", key, "error", err)
return "", false
}
if env.Key != key {
c.log.Debug("cache: key mismatch", "want", key, "got", env.Key)
return "", false
}
if time.Since(env.CreatedAt) > c.ttl {
c.log.Debug("cache: expired", "key", key, "age", time.Since(env.CreatedAt))
return "", false
}
c.log.Debug("cache: hit", "key", key)
return env.Payload, true
}Open data scored by Repobility · https://repobility.com
cache.Cache.Set method · go · L89-L133 (45 LOC)internal/cache/cache.go
func (c *Cache) Set(key string, payload string) {
if c == nil || c.ttl == 0 {
return
}
if err := os.MkdirAll(c.dir, 0o755); err != nil {
c.log.Debug("cache: mkdir failed", "error", err)
return
}
env := envelope{
CreatedAt: time.Now(),
Key: key,
Payload: payload,
}
data, err := json.Marshal(env)
if err != nil {
c.log.Debug("cache: marshal failed", "error", err)
return
}
tmp, err := os.CreateTemp(c.dir, ".tmp-*")
if err != nil {
c.log.Debug("cache: temp file failed", "error", err)
return
}
tmpName := tmp.Name()
if _, err := tmp.Write(data); err != nil {
_ = tmp.Close()
_ = os.Remove(tmpName)
c.log.Debug("cache: write failed", "error", err)
return
}
if err := tmp.Close(); err != nil {
_ = os.Remove(tmpName)
c.log.Debug("cache: close failed", "error", err)
return
}
if err := os.Rename(tmpName, c.path(key)); err != nil {
_ = os.Remove(tmpName)
c.log.Debug("cache: rename failed", "error", err)
}
}cache.Cache.Clear method · go · L136-L154 (19 LOC)internal/cache/cache.go
func (c *Cache) Clear() error {
if c == nil {
return nil
}
entries, err := os.ReadDir(c.dir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("failed to read cache dir: %w", err)
}
var errs []error
for _, e := range entries {
if err := os.Remove(filepath.Join(c.dir, e.Name())); err != nil && !os.IsNotExist(err) {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}config.Config.Validate method · go · L20-L43 (24 LOC)internal/config/config.go
func (c Config) Validate() error {
if c.Git.Timeout < 0 {
return errors.New("git.timeout cannot be negative")
}
if c.GitHub.PreviewCacheTTL < 0 {
return errors.New("github.preview_cache_ttl cannot be negative")
}
if c.PullRequest.WorktreePrefix == "" {
return errors.New("pull_request.worktree_prefix cannot be empty")
}
if c.Slugify.HashLength < 0 {
return errors.New("slugify.hash_length cannot be negative")
}
if c.Slugify.MaxLength < 0 {
return errors.New("slugify.max_length cannot be negative")
}
if c.Slugify.MaxLength > 0 && c.Slugify.HashLength > c.Slugify.MaxLength-2 {
return errors.New("slugify.hash_length must be at least 2 less than slugify.max_length")
}
if len(c.Workspace.PrimaryBranches) == 0 {
return errors.New("workspace.primary_branches cannot be empty")
}
return nil
}config.DefaultConfig function · go · L6-L35 (30 LOC)internal/config/defaults.go
func DefaultConfig() Config {
return Config{
Git: GitConfig{
Timeout: 5 * time.Second,
},
GitHub: GitHubConfig{
PreviewCacheTTL: 5 * time.Minute,
},
LocalBranch: LocalBranchConfig{
BranchPrefix: "feature/",
StripBranchPrefix: []string{"feature/"},
WorktreePrefix: "wt-",
},
PullRequest: PullRequestConfig{
BranchTemplate: "{{.BranchName}}",
WorktreePrefix: "pr-",
},
Slugify: SlugifyConfig{
CollapseDashes: true,
HashLength: 4,
Lowercase: true,
MaxLength: 50,
ReplaceNonAlphanum: true,
TrimDashes: true,
},
Workspace: WorkspaceConfig{
PrimaryBranches: []string{"main", "develop", "master"},
},
}
}config.ConfigPaths function · go · L23-L75 (53 LOC)internal/config/discovery.go
func ConfigPaths(cwd, worktreeRoot, gitRoot, homeDir string) []string {
var paths []string
seen := make(map[string]bool)
addPath := func(dir string) {
if dir == "" {
return
}
path := filepath.Join(dir, configFileName)
if !seen[path] {
seen[path] = true
paths = append(paths, path)
}
}
if xdgConfigDir := os.Getenv("XDG_CONFIG_HOME"); xdgConfigDir != "" {
addPath(filepath.Join(xdgConfigDir, "grove"))
} else if homeDir != "" {
addPath(filepath.Join(homeDir, ".config", "grove"))
}
if gitRoot != "" && homeDir != "" {
// Collect ancestors from gitRoot's parent up to home.
// Skip cwd so it appears only via addPath(cwd) below (highest priority),
// but always include homeDir since it's the walk boundary.
var ancestors []string
current := filepath.Dir(gitRoot)
for current != "" && len(current) >= len(homeDir) {
if current != cwd || current == homeDir {
ancestors = append(ancestors, current)
}
if current == homeDir {
break
}
paconfig.OSFileSystem.Exists method · go · L27-L33 (7 LOC)internal/config/loader.go
func (OSFileSystem) Exists(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return !info.IsDir()
}config.NewLoader function · go · L42-L47 (6 LOC)internal/config/loader.go
func NewLoader(fs FileSystem) *Loader {
return &Loader{
fs: fs,
log: log.Default().WithPrefix("config"),
}
}config.Loader.Load method · go · L57-L86 (30 LOC)internal/config/loader.go
func (l *Loader) Load(paths []string) (LoadResult, error) {
cfg := DefaultConfig()
var sourcePaths []string
for _, path := range paths {
if !l.fs.Exists(path) {
continue // Skip missing files
}
metadata, err := toml.DecodeFile(path, &cfg)
if err != nil {
return LoadResult{}, fmt.Errorf("failed to parse %s: %w", path, err)
}
if undecoded := metadata.Undecoded(); len(undecoded) > 0 {
l.log.Warn("unknown config keys", "path", path, "keys", undecoded)
}
sourcePaths = append(sourcePaths, path)
}
if err := cfg.Validate(); err != nil {
return LoadResult{}, fmt.Errorf("invalid config: %w", err)
}
return LoadResult{
Config: cfg,
SourcePaths: sourcePaths,
}, nil
}Repobility analyzer · published findings · https://repobility.com
git.New function · go · L27-L34 (8 LOC)internal/git/git_cli.go
func New(dryRun bool, workingDir string, timeout time.Duration) Git {
return &GitCli{
dryRun: dryRun,
log: clog.Default().WithPrefix("git"),
timeout: timeout,
workingDir: workingDir,
}
}git.GitCli.executeGitCommand method · go · L36-L62 (27 LOC)internal/git/git_cli.go
func (g *GitCli) executeGitCommand(args ...string) (string, error) {
g.log.Debug("Executing git command", "cmd", "git", "args", args, "workingDir", g.workingDir)
ctx, cancel := context.WithTimeout(context.Background(), g.timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "git", args...)
cmd.Dir = g.workingDir
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
g.log.Warn("git command timed out", "args", args, "timeout", g.timeout, "error", err)
return "", fmt.Errorf("git %s timed out after %s", strings.Join(args, " "), g.timeout)
}
g.log.Debug("Git command failed", "args", args, "stderr", stderr.String(), "error", err)
return "", fmt.Errorf("git %s failed: %w: %s", strings.Join(args, " "), err, stderr.String())
}
output := strings.TrimSpace(stdout.String())
g.log.Debug("Git command succeeded", "argit.GitCli.executeMutatingGitCommand method · go · L65-L74 (10 LOC)internal/git/git_cli.go
func (g *GitCli) executeMutatingGitCommand(errContext string, args ...string) error {
if g.dryRun {
g.log.Info("Would execute git command", "cmd", "git", "args", args)
return nil
}
if _, err := g.executeGitCommand(args...); err != nil {
return fmt.Errorf("%s: %w", errContext, err)
}
return nil
}