commit.go

  1package cmd
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"time"
  7
  8	gansi "github.com/charmbracelet/glamour/v2/ansi"
  9	"github.com/charmbracelet/soft-serve/git"
 10	"github.com/charmbracelet/soft-serve/pkg/backend"
 11	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 12	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
 13	"github.com/spf13/cobra"
 14)
 15
 16// commitCommand returns a command that prints the contents of a commit.
 17func commitCommand() *cobra.Command {
 18	var color bool
 19	var patchOnly bool
 20
 21	cmd := &cobra.Command{
 22		Use:               "commit repo SHA",
 23		Short:             "Print out the contents of a diff",
 24		Args:              cobra.ExactArgs(2),
 25		PersistentPreRunE: checkIfReadable,
 26		RunE: func(cmd *cobra.Command, args []string) error {
 27			ctx := cmd.Context()
 28			be := backend.FromContext(ctx)
 29			repoName := args[0]
 30			commitSHA := args[1]
 31
 32			rr, err := be.Repository(ctx, repoName)
 33			if err != nil {
 34				return err //nolint:wrapcheck
 35			}
 36
 37			r, err := rr.Open()
 38			if err != nil {
 39				return err //nolint:wrapcheck
 40			}
 41
 42			commit, err := r.CommitByRevision(commitSHA)
 43			if err != nil {
 44				return err //nolint:wrapcheck
 45			}
 46
 47			patch, err := r.Patch(commit)
 48			if err != nil {
 49				return err //nolint:wrapcheck
 50			}
 51
 52			diff, err := r.Diff(commit)
 53			if err != nil {
 54				return err //nolint:wrapcheck
 55			}
 56
 57			commonStyle := styles.DefaultStyles()
 58			style := commonStyle.Log
 59
 60			s := strings.Builder{}
 61			commitLine := "commit " + commitSHA
 62			authorLine := "Author: " + commit.Author.Name
 63			dateLine := "Date:   " + commit.Committer.When.UTC().Format(time.UnixDate)
 64			msgLine := strings.ReplaceAll(commit.Message, "\r\n", "\n")
 65			statsLine := renderStats(diff, commonStyle, color)
 66			diffLine := renderDiff(patch, color)
 67
 68			if patchOnly {
 69				cmd.Println(
 70					diffLine,
 71				)
 72				return nil
 73			}
 74
 75			if color {
 76				s.WriteString(fmt.Sprintf("%s\n%s\n%s\n%s\n",
 77					style.CommitHash.Render(commitLine),
 78					style.CommitAuthor.Render(authorLine),
 79					style.CommitDate.Render(dateLine),
 80					style.CommitBody.Render(msgLine),
 81				))
 82			} else {
 83				s.WriteString(fmt.Sprintf("%s\n%s\n%s\n%s\n",
 84					commitLine,
 85					authorLine,
 86					dateLine,
 87					msgLine,
 88				))
 89			}
 90
 91			s.WriteString(fmt.Sprintf("\n%s\n%s",
 92				statsLine,
 93				diffLine,
 94			))
 95
 96			cmd.Println(
 97				s.String(),
 98			)
 99
100			return nil
101		},
102	}
103
104	cmd.Flags().BoolVarP(&color, "color", "c", false, "Colorize output")
105	cmd.Flags().BoolVarP(&patchOnly, "patch", "p", false, "Output patch only")
106
107	return cmd
108}
109
110func renderDiff(patch string, color bool) string {
111	c := patch
112
113	if color {
114		var s strings.Builder
115		var pr strings.Builder
116
117		diffChroma := &gansi.CodeBlockElement{
118			Code:     patch,
119			Language: "diff",
120		}
121
122		err := diffChroma.Render(&pr, common.StyleRenderer())
123
124		if err != nil {
125			s.WriteString(fmt.Sprintf("\n%s", err.Error()))
126		} else {
127			s.WriteString(fmt.Sprintf("\n%s", pr.String()))
128		}
129
130		c = s.String()
131	}
132
133	return c
134}
135
136func renderStats(diff *git.Diff, commonStyle *styles.Styles, color bool) string {
137	style := commonStyle.Log
138	c := diff.Stats().String()
139
140	if color {
141		s := strings.Split(c, "\n")
142
143		for i, line := range s {
144			ch := strings.Split(line, "|")
145			if len(ch) > 1 {
146				adddel := ch[len(ch)-1]
147				adddel = strings.ReplaceAll(adddel, "+", style.CommitStatsAdd.Render("+"))
148				adddel = strings.ReplaceAll(adddel, "-", style.CommitStatsDel.Render("-"))
149				s[i] = strings.Join(ch[:len(ch)-1], "|") + "|" + adddel
150			}
151		}
152
153		return strings.Join(s, "\n")
154	}
155
156	return c
157}