2020-02-25 11:11:07 +02:00
|
|
|
package presentation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
2022-03-19 03:26:30 +02:00
|
|
|
"github.com/jesseduffield/generics/set"
|
2022-01-19 09:32:27 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
2020-09-29 12:28:39 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
2021-10-30 08:42:43 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/authors"
|
2021-11-02 07:39:15 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/graph"
|
2022-04-23 03:41:40 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
2021-07-27 15:00:37 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
2020-02-25 11:11:07 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
2021-07-16 14:06:01 +02:00
|
|
|
"github.com/kyokomi/emoji/v2"
|
2022-08-07 01:44:50 +02:00
|
|
|
"github.com/sasha-s/go-deadlock"
|
2020-02-25 11:11:07 +02:00
|
|
|
)
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
type pipeSetCacheKey struct {
|
|
|
|
commitSha string
|
|
|
|
commitCount int
|
|
|
|
}
|
2020-02-25 11:11:07 +02:00
|
|
|
|
2022-03-19 00:38:49 +02:00
|
|
|
var (
|
|
|
|
pipeSetCache = make(map[pipeSetCacheKey][][]*graph.Pipe)
|
2022-08-07 01:44:50 +02:00
|
|
|
mutex deadlock.Mutex
|
2022-03-19 00:38:49 +02:00
|
|
|
)
|
2021-11-02 07:39:15 +02:00
|
|
|
|
2022-01-26 06:42:06 +02:00
|
|
|
type bisectBounds struct {
|
|
|
|
newIndex int
|
|
|
|
oldIndex int
|
|
|
|
}
|
2022-01-19 09:32:27 +02:00
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
func GetCommitListDisplayStrings(
|
|
|
|
commits []*models.Commit,
|
|
|
|
fullDescription bool,
|
2022-03-19 03:26:30 +02:00
|
|
|
cherryPickedCommitShaSet *set.Set[string],
|
2021-11-02 07:39:15 +02:00
|
|
|
diffName string,
|
2022-05-13 14:56:07 +02:00
|
|
|
timeFormat string,
|
2021-11-02 07:39:15 +02:00
|
|
|
parseEmoji bool,
|
|
|
|
selectedCommitSha string,
|
|
|
|
startIdx int,
|
|
|
|
length int,
|
2021-11-02 11:05:23 +02:00
|
|
|
showGraph bool,
|
2022-01-19 09:32:27 +02:00
|
|
|
bisectInfo *git_commands.BisectInfo,
|
2021-11-02 07:39:15 +02:00
|
|
|
) [][]string {
|
|
|
|
mutex.Lock()
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
|
|
if len(commits) == 0 {
|
|
|
|
return nil
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 12:33:22 +02:00
|
|
|
if startIdx > len(commits) {
|
|
|
|
return nil
|
|
|
|
}
|
2021-11-02 07:39:15 +02:00
|
|
|
|
2022-01-22 03:56:57 +02:00
|
|
|
end := utils.Min(startIdx+length, len(commits))
|
2022-01-24 10:42:40 +02:00
|
|
|
// this is where my non-TODO commits begin
|
|
|
|
rebaseOffset := utils.Min(indexOfFirstNonTODOCommit(commits), end)
|
2022-01-22 03:56:57 +02:00
|
|
|
|
|
|
|
filteredCommits := commits[startIdx:end]
|
2021-11-02 07:39:15 +02:00
|
|
|
|
2022-01-26 06:42:06 +02:00
|
|
|
bisectBounds := getbisectBounds(commits, bisectInfo)
|
|
|
|
|
2022-01-22 03:56:57 +02:00
|
|
|
// function expects to be passed the index of the commit in terms of the `commits` slice
|
2021-11-02 11:05:23 +02:00
|
|
|
var getGraphLine func(int) string
|
|
|
|
if showGraph {
|
2022-01-22 03:56:57 +02:00
|
|
|
// this is where the graph begins (may be beyond the TODO commits depending on startIdx,
|
|
|
|
// but we'll never include TODO commits as part of the graph because it'll be messy)
|
|
|
|
graphOffset := utils.Max(startIdx, rebaseOffset)
|
|
|
|
|
|
|
|
pipeSets := loadPipesets(commits[rebaseOffset:])
|
|
|
|
pipeSetOffset := utils.Max(startIdx-rebaseOffset, 0)
|
|
|
|
graphPipeSets := pipeSets[pipeSetOffset:utils.Max(end-rebaseOffset, 0)]
|
|
|
|
graphCommits := commits[graphOffset:end]
|
|
|
|
graphLines := graph.RenderAux(
|
|
|
|
graphPipeSets,
|
|
|
|
graphCommits,
|
|
|
|
selectedCommitSha,
|
|
|
|
)
|
|
|
|
getGraphLine = func(idx int) string {
|
|
|
|
if idx >= graphOffset {
|
|
|
|
return graphLines[idx-graphOffset]
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
2021-11-02 11:05:23 +02:00
|
|
|
} else {
|
|
|
|
getGraphLine = func(idx int) string { return "" }
|
|
|
|
}
|
|
|
|
|
|
|
|
lines := make([][]string, 0, len(filteredCommits))
|
2022-01-19 09:32:27 +02:00
|
|
|
var bisectStatus BisectStatus
|
2021-11-02 07:39:15 +02:00
|
|
|
for i, commit := range filteredCommits {
|
2022-01-26 06:42:06 +02:00
|
|
|
unfilteredIdx := i + startIdx
|
|
|
|
bisectStatus = getBisectStatus(unfilteredIdx, commit.Sha, bisectInfo, bisectBounds)
|
2022-01-19 09:32:27 +02:00
|
|
|
lines = append(lines, displayCommit(
|
|
|
|
commit,
|
2022-03-19 03:26:30 +02:00
|
|
|
cherryPickedCommitShaSet,
|
2022-01-19 09:32:27 +02:00
|
|
|
diffName,
|
2022-05-13 14:56:07 +02:00
|
|
|
timeFormat,
|
2022-01-19 09:32:27 +02:00
|
|
|
parseEmoji,
|
2022-01-26 06:42:06 +02:00
|
|
|
getGraphLine(unfilteredIdx),
|
2022-01-19 09:32:27 +02:00
|
|
|
fullDescription,
|
|
|
|
bisectStatus,
|
|
|
|
bisectInfo,
|
|
|
|
))
|
2021-11-02 07:39:15 +02:00
|
|
|
}
|
2020-02-25 11:11:07 +02:00
|
|
|
return lines
|
|
|
|
}
|
|
|
|
|
2022-01-26 06:42:06 +02:00
|
|
|
func getbisectBounds(commits []*models.Commit, bisectInfo *git_commands.BisectInfo) *bisectBounds {
|
|
|
|
if !bisectInfo.Bisecting() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
bisectBounds := &bisectBounds{}
|
|
|
|
|
|
|
|
for i, commit := range commits {
|
|
|
|
if commit.Sha == bisectInfo.GetNewSha() {
|
|
|
|
bisectBounds.newIndex = i
|
|
|
|
}
|
|
|
|
|
|
|
|
status, ok := bisectInfo.Status(commit.Sha)
|
|
|
|
if ok && status == git_commands.BisectStatusOld {
|
|
|
|
bisectBounds.oldIndex = i
|
|
|
|
return bisectBounds
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shouldn't land here
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-22 03:56:57 +02:00
|
|
|
// precondition: slice is not empty
|
|
|
|
func indexOfFirstNonTODOCommit(commits []*models.Commit) int {
|
|
|
|
for i, commit := range commits {
|
|
|
|
if !commit.IsTODO() {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shouldn't land here
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadPipesets(commits []*models.Commit) [][]*graph.Pipe {
|
|
|
|
// given that our cache key is a commit sha and a commit count, it's very important that we don't actually try to render pipes
|
|
|
|
// when dealing with things like filtered commits.
|
|
|
|
cacheKey := pipeSetCacheKey{
|
|
|
|
commitSha: commits[0].Sha,
|
|
|
|
commitCount: len(commits),
|
|
|
|
}
|
|
|
|
|
|
|
|
pipeSets, ok := pipeSetCache[cacheKey]
|
|
|
|
if !ok {
|
|
|
|
// pipe sets are unique to a commit head. and a commit count. Sometimes we haven't loaded everything for that.
|
|
|
|
// so let's just cache it based on that.
|
|
|
|
getStyle := func(commit *models.Commit) style.TextStyle {
|
2022-05-08 06:23:29 +02:00
|
|
|
return authors.AuthorStyle(commit.AuthorName)
|
2022-01-22 03:56:57 +02:00
|
|
|
}
|
|
|
|
pipeSets = graph.GetPipeSets(commits, getStyle)
|
|
|
|
pipeSetCache[cacheKey] = pipeSets
|
|
|
|
}
|
|
|
|
|
|
|
|
return pipeSets
|
|
|
|
}
|
|
|
|
|
2022-01-19 09:32:27 +02:00
|
|
|
// similar to the git_commands.BisectStatus but more gui-focused
|
|
|
|
type BisectStatus int
|
|
|
|
|
|
|
|
const (
|
|
|
|
BisectStatusNone BisectStatus = iota
|
|
|
|
BisectStatusOld
|
|
|
|
BisectStatusNew
|
|
|
|
BisectStatusSkipped
|
|
|
|
// adding candidate here which isn't present in the commands package because
|
|
|
|
// we need to actually go through the commits to get this info
|
|
|
|
BisectStatusCandidate
|
|
|
|
// also adding this
|
|
|
|
BisectStatusCurrent
|
|
|
|
)
|
|
|
|
|
2022-01-26 06:42:06 +02:00
|
|
|
func getBisectStatus(index int, commitSha string, bisectInfo *git_commands.BisectInfo, bisectBounds *bisectBounds) BisectStatus {
|
2022-01-19 09:32:27 +02:00
|
|
|
if !bisectInfo.Started() {
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusNone
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if bisectInfo.GetCurrentSha() == commitSha {
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusCurrent
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
status, ok := bisectInfo.Status(commitSha)
|
|
|
|
if ok {
|
|
|
|
switch status {
|
|
|
|
case git_commands.BisectStatusNew:
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusNew
|
2022-01-19 09:32:27 +02:00
|
|
|
case git_commands.BisectStatusOld:
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusOld
|
2022-01-19 09:32:27 +02:00
|
|
|
case git_commands.BisectStatusSkipped:
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusSkipped
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
} else {
|
2022-01-26 06:42:06 +02:00
|
|
|
if bisectBounds != nil && index >= bisectBounds.newIndex && index <= bisectBounds.oldIndex {
|
|
|
|
return BisectStatusCandidate
|
2022-01-19 09:32:27 +02:00
|
|
|
} else {
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusNone
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// should never land here
|
2022-01-26 06:42:06 +02:00
|
|
|
return BisectStatusNone
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func getBisectStatusText(bisectStatus BisectStatus, bisectInfo *git_commands.BisectInfo) string {
|
|
|
|
if bisectStatus == BisectStatusNone {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
style := getBisectStatusColor(bisectStatus)
|
|
|
|
|
|
|
|
switch bisectStatus {
|
|
|
|
case BisectStatusNew:
|
|
|
|
return style.Sprintf("<-- " + bisectInfo.NewTerm())
|
|
|
|
case BisectStatusOld:
|
|
|
|
return style.Sprintf("<-- " + bisectInfo.OldTerm())
|
|
|
|
case BisectStatusCurrent:
|
|
|
|
// TODO: i18n
|
|
|
|
return style.Sprintf("<-- current")
|
|
|
|
case BisectStatusSkipped:
|
|
|
|
return style.Sprintf("<-- skipped")
|
|
|
|
case BisectStatusCandidate:
|
|
|
|
return style.Sprintf("?")
|
2022-03-19 00:38:49 +02:00
|
|
|
case BisectStatusNone:
|
|
|
|
return ""
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
func displayCommit(
|
|
|
|
commit *models.Commit,
|
2022-03-19 03:26:30 +02:00
|
|
|
cherryPickedCommitShaSet *set.Set[string],
|
2021-11-02 07:39:15 +02:00
|
|
|
diffName string,
|
2022-05-13 14:56:07 +02:00
|
|
|
timeFormat string,
|
2021-11-02 07:39:15 +02:00
|
|
|
parseEmoji bool,
|
|
|
|
graphLine string,
|
|
|
|
fullDescription bool,
|
2022-01-19 09:32:27 +02:00
|
|
|
bisectStatus BisectStatus,
|
|
|
|
bisectInfo *git_commands.BisectInfo,
|
2021-11-02 07:39:15 +02:00
|
|
|
) []string {
|
2022-03-19 03:26:30 +02:00
|
|
|
shaColor := getShaColor(commit, diffName, cherryPickedCommitShaSet, bisectStatus, bisectInfo)
|
2022-01-19 09:32:27 +02:00
|
|
|
bisectString := getBisectStatusText(bisectStatus, bisectInfo)
|
2021-11-02 07:39:15 +02:00
|
|
|
|
|
|
|
actionString := ""
|
|
|
|
if commit.Action != "" {
|
|
|
|
actionString = actionColorMap(commit.Action).Sprint(commit.Action) + " "
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
tagString := ""
|
2021-11-02 07:39:15 +02:00
|
|
|
if fullDescription {
|
|
|
|
if commit.ExtraInfo != "" {
|
|
|
|
tagString = style.FgMagenta.SetBold().Sprint(commit.ExtraInfo) + " "
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(commit.Tags) > 0 {
|
|
|
|
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(commit.Tags, " ")) + " "
|
|
|
|
}
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
name := commit.Name
|
2021-07-16 14:06:01 +02:00
|
|
|
if parseEmoji {
|
|
|
|
name = emoji.Sprint(name)
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
authorFunc := authors.ShortAuthor
|
|
|
|
if fullDescription {
|
|
|
|
authorFunc = authors.LongAuthor
|
2021-07-27 15:00:37 +02:00
|
|
|
}
|
2021-11-02 07:39:15 +02:00
|
|
|
|
2022-04-23 03:41:40 +02:00
|
|
|
cols := make([]string, 0, 7)
|
|
|
|
if icons.IsIconEnabled() {
|
|
|
|
cols = append(cols, shaColor.Sprint(icons.IconForCommit(commit)))
|
|
|
|
}
|
2021-11-02 07:39:15 +02:00
|
|
|
cols = append(cols, shaColor.Sprint(commit.ShortSha()))
|
2022-01-19 09:32:27 +02:00
|
|
|
cols = append(cols, bisectString)
|
2021-11-02 07:39:15 +02:00
|
|
|
if fullDescription {
|
2022-05-13 14:56:07 +02:00
|
|
|
cols = append(cols, style.FgBlue.Sprint(utils.UnixToDate(commit.UnixTimestamp, timeFormat)))
|
2021-11-02 07:39:15 +02:00
|
|
|
}
|
|
|
|
cols = append(
|
|
|
|
cols,
|
|
|
|
actionString,
|
2022-05-08 06:23:29 +02:00
|
|
|
authorFunc(commit.AuthorName),
|
2021-11-02 07:39:15 +02:00
|
|
|
graphLine+tagString+theme.DefaultTextColor.Sprint(name),
|
|
|
|
)
|
|
|
|
|
|
|
|
return cols
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func getBisectStatusColor(status BisectStatus) style.TextStyle {
|
|
|
|
switch status {
|
|
|
|
case BisectStatusNone:
|
|
|
|
return style.FgBlack
|
|
|
|
case BisectStatusNew:
|
|
|
|
return style.FgRed
|
|
|
|
case BisectStatusOld:
|
|
|
|
return style.FgGreen
|
|
|
|
case BisectStatusSkipped:
|
|
|
|
return style.FgYellow
|
|
|
|
case BisectStatusCurrent:
|
|
|
|
return style.FgMagenta
|
|
|
|
case BisectStatusCandidate:
|
|
|
|
return style.FgBlue
|
|
|
|
}
|
2021-11-02 07:39:15 +02:00
|
|
|
|
2022-01-19 09:32:27 +02:00
|
|
|
// shouldn't land here
|
|
|
|
return style.FgWhite
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
2022-01-19 09:32:27 +02:00
|
|
|
func getShaColor(
|
|
|
|
commit *models.Commit,
|
|
|
|
diffName string,
|
2022-03-19 03:26:30 +02:00
|
|
|
cherryPickedCommitShaSet *set.Set[string],
|
2022-01-19 09:32:27 +02:00
|
|
|
bisectStatus BisectStatus,
|
|
|
|
bisectInfo *git_commands.BisectInfo,
|
|
|
|
) style.TextStyle {
|
|
|
|
if bisectInfo.Started() {
|
|
|
|
return getBisectStatusColor(bisectStatus)
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
diffed := commit.Sha == diffName
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor := theme.DefaultTextColor
|
2021-11-02 07:39:15 +02:00
|
|
|
switch commit.Status {
|
2020-02-25 11:11:07 +02:00
|
|
|
case "unpushed":
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = style.FgRed
|
2020-02-25 11:11:07 +02:00
|
|
|
case "pushed":
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = style.FgYellow
|
2020-02-25 11:11:07 +02:00
|
|
|
case "merged":
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = style.FgGreen
|
2020-02-25 11:11:07 +02:00
|
|
|
case "rebasing":
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = style.FgBlue
|
2020-02-25 11:11:07 +02:00
|
|
|
case "reflog":
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = style.FgBlue
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
2020-03-29 05:34:17 +02:00
|
|
|
if diffed {
|
2021-07-27 15:00:37 +02:00
|
|
|
shaColor = theme.DiffTerminalColor
|
2022-03-19 03:26:30 +02:00
|
|
|
} else if cherryPickedCommitShaSet.Includes(commit.Sha) {
|
2021-09-29 13:53:31 +02:00
|
|
|
shaColor = theme.CherryPickedCommitTextStyle
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 07:39:15 +02:00
|
|
|
return shaColor
|
2020-02-25 11:11:07 +02:00
|
|
|
}
|
2020-08-27 09:00:43 +02:00
|
|
|
|
2021-07-27 15:00:37 +02:00
|
|
|
func actionColorMap(str string) style.TextStyle {
|
2020-08-27 09:00:43 +02:00
|
|
|
switch str {
|
|
|
|
case "pick":
|
2021-07-27 15:00:37 +02:00
|
|
|
return style.FgCyan
|
2020-08-27 09:00:43 +02:00
|
|
|
case "drop":
|
2021-07-27 15:00:37 +02:00
|
|
|
return style.FgRed
|
2020-08-27 09:00:43 +02:00
|
|
|
case "edit":
|
2021-07-27 15:00:37 +02:00
|
|
|
return style.FgGreen
|
2020-08-27 09:00:43 +02:00
|
|
|
case "fixup":
|
2021-07-27 15:00:37 +02:00
|
|
|
return style.FgMagenta
|
2020-08-27 09:00:43 +02:00
|
|
|
default:
|
2021-07-27 15:00:37 +02:00
|
|
|
return style.FgYellow
|
2020-08-27 09:00:43 +02:00
|
|
|
}
|
|
|
|
}
|