Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@ type LogConfig struct {
ShowGraph string `yaml:"showGraph" jsonschema:"enum=always,enum=never,enum=when-maximised"`
// displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
ShowWholeGraph bool `yaml:"showWholeGraph"`
// Controls how much information is shown per commit in the commits panel.
// One of 'normal' | 'comfortable' | 'spacious'
//
// Can be changed from within lazygit with `Log menu -> Commit display format` (`<ctrl+l>` in the commits window by default).
CommitDisplayFormat string `yaml:"commitDisplayFormat" jsonschema:"enum=normal,enum=comfortable,enum=spacious"`
}

type CommitPrefixConfig struct {
Expand Down Expand Up @@ -942,9 +947,10 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig {
SquashMergeMessage: "Squash merge {{selectedRef}} into {{currentBranch}}",
},
Log: LogConfig{
Order: "topo-order",
ShowGraph: "always",
ShowWholeGraph: false,
Order: "topo-order",
ShowGraph: "always",
ShowWholeGraph: false,
CommitDisplayFormat: "normal",
},
LocalBranchSortOrder: "date",
RemoteBranchSortOrder: "date",
Expand Down
60 changes: 60 additions & 0 deletions pkg/gui/context/local_commits_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package context
import (
"fmt"
"log"
"sort"
"strings"
"sync/atomic"
"time"

"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)

Expand Down Expand Up @@ -63,6 +66,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
endIdx,
shouldShowGraph(c),
c.Model().BisectInfo,
c.UserConfig().Git.Log.CommitDisplayFormat,
)
}

Expand Down Expand Up @@ -108,6 +112,14 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
})
}

var selectedCommitHashPtr *string
if c.Context().Current().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
if selectedCommit := viewModel.GetSelected(); selectedCommit != nil {
selectedCommitHashPtr = selectedCommit.HashPtr()
}
}
result = appendCommitDetailItems(c, c.Model().Commits, selectedCommitHashPtr, result)

return result
}

Expand Down Expand Up @@ -247,6 +259,54 @@ func (self *LocalCommitsViewModel) GetCommits() []*models.Commit {
return self.getModel()
}

// In the 'comfortable' and 'spacious' display formats, insert an extra
// (unselectable) line below each commit showing author and date, similar to
// `git log`'s two-line layouts. The line starts with the graph's connector
// characters so the graph stays continuous, and is rendered from the
// graph/subject column so it aligns with the commit subject. 'spacious'
// additionally inserts an empty connector-only row between commits. The
// resulting list must stay sorted by Index, which is what the list renderer
// assumes, so we re-sort after appending.
func appendCommitDetailItems(c *ContextCommon, commits []*models.Commit, selectedCommitHashPtr *string, result []*NonModelItem) []*NonModelItem {
format := c.UserConfig().Git.Log.CommitDisplayFormat
if format != "comfortable" && format != "spacious" {
return result
}

var connectorLines []string
if shouldShowGraph(c) {
connectorLines = presentation.GetCommitGraphConnectorLines(commits, selectedCommitHashPtr)
}

now := time.Now()
timeFormat := c.UserConfig().Gui.TimeFormat
shortTimeFormat := c.UserConfig().Gui.ShortTimeFormat
for i, commit := range commits {
if commit.IsTODO() {
continue
}
connector := ""
if connectorLines != nil {
connector = connectorLines[i]
}
result = append(result, &NonModelItem{
Index: i + 1,
Column: 6, // the graph+subject column produced by displayCommit
Content: connector + style.FgCyan.Sprint(commit.AuthorName) + " " +
style.FgBlue.Sprint(utils.UnixToDateSmart(now, commit.UnixTimestamp, timeFormat, shortTimeFormat)),
})
if format == "spacious" && i < len(commits)-1 {
result = append(result, &NonModelItem{
Index: i + 1,
Column: 6,
Content: connector,
})
}
}
sort.SliceStable(result, func(a, b int) bool { return result[a].Index < result[b].Index })
return result
}

func shouldShowGraph(c *ContextCommon) bool {
if c.Modes().Filtering.Active() {
return false
Expand Down
9 changes: 9 additions & 0 deletions pkg/gui/context/sub_commits_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func NewSubCommitsContext(
endIdx,
shouldShowGraph(c),
git_commands.NewNullBisectInfo(),
c.UserConfig().Git.Log.CommitDisplayFormat,
)
}

Expand Down Expand Up @@ -104,6 +105,14 @@ func NewSubCommitsContext(
})
}

var selectedCommitHashPtr *string
if c.Context().Current().GetKey() == SUB_COMMITS_CONTEXT_KEY {
if selectedCommit := viewModel.GetSelected(); selectedCommit != nil {
selectedCommitHashPtr = selectedCommit.HashPtr()
}
}
result = appendCommitDetailItems(c, c.Model().SubCommits, selectedCommitHashPtr, result)

return result
}

Expand Down
36 changes: 36 additions & 0 deletions pkg/gui/controllers/local_commits_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,42 @@ func (self *LocalCommitsController) handleOpenLogMenu() error {
})
},
},
{
Label: self.c.Tr.CommitDisplayFormat,
Tooltip: self.c.Tr.CommitDisplayFormatTooltip,
OpensMenu: true,
OnPress: func() error {
currentValue := self.c.UserConfig().Git.Log.CommitDisplayFormat
onPress := func(value string) func() error {
return func() error {
self.c.UserConfig().Git.Log.CommitDisplayFormat = value
self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
return nil
}
}
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.CommitDisplayFormat,
Items: []*types.MenuItem{
{
Label: "normal",
OnPress: onPress("normal"),
Widget: types.MakeMenuRadioButton(currentValue == "normal"),
},
{
Label: "comfortable",
OnPress: onPress("comfortable"),
Widget: types.MakeMenuRadioButton(currentValue == "comfortable"),
},
{
Label: "spacious",
OnPress: onPress("spacious"),
Widget: types.MakeMenuRadioButton(currentValue == "spacious"),
},
},
})
},
},
{
Label: self.c.Tr.SortCommits,
Tooltip: self.c.Tr.SortCommitsTooltip,
Expand Down
29 changes: 29 additions & 0 deletions pkg/gui/presentation/commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func GetCommitListDisplayStrings(
endIdx int,
showGraph bool,
bisectInfo *git_commands.BisectInfo,
commitDisplayFormat string,
) [][]string {
mutex.Lock()
defer mutex.Unlock()
Expand Down Expand Up @@ -204,11 +205,34 @@ func GetCommitListDisplayStrings(
fullDescription,
bisectStatus,
bisectInfo,
commitDisplayFormat,
))
}
return lines
}

// GetCommitGraphConnectorLines returns, for each commit, the graph connector
// line to draw below it. Used by the two-line display formats ('comfortable'
// and 'spacious') for their inserted author/date rows, so that the graph stays
// continuous. Entries are empty for TODO commits and in divergence views
// (where no single graph spans the list).
func GetCommitGraphConnectorLines(commits []*models.Commit, selectedCommitHashPtr *string) []string {
mutex.Lock()
defer mutex.Unlock()

result := make([]string, len(commits))
if len(commits) == 0 || commits[0].Divergence != models.DivergenceNone {
return result
}

rebaseOffset := indexOfFirstNonTODOCommit(commits)
pipeSets := loadPipesets(commits[rebaseOffset:])
for i, pipeSet := range pipeSets {
result[rebaseOffset+i] = graph.RenderConnectorRow(pipeSet, selectedCommitHashPtr)
}
return result
}

func getbisectBounds(commits []*models.Commit, bisectInfo *git_commands.BisectInfo) *bisectBounds {
if !bisectInfo.Bisecting() {
return nil
Expand Down Expand Up @@ -355,6 +379,7 @@ func displayCommit(
fullDescription bool,
bisectStatus BisectStatus,
bisectInfo *git_commands.BisectInfo,
commitDisplayFormat string,
) []string {
bisectString := getBisectStatusText(bisectStatus, bisectInfo)

Expand Down Expand Up @@ -438,6 +463,10 @@ func displayCommit(
authorLength = common.UserConfig().Gui.CommitAuthorLongLength
}
author := authors.AuthorWithLength(commit.AuthorName, authorLength)
// The two-line formats show the author on the second line instead
if commitDisplayFormat == "comfortable" || commitDisplayFormat == "spacious" {
author = ""
}

cols := make([]string, 0, 7)
cols = append(
Expand Down
1 change: 1 addition & 0 deletions pkg/gui/presentation/commits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
s.endIdx,
s.showGraph,
s.bisectInfo,
"normal",
)

renderedLines, _ := utils.RenderDisplayStrings(result, nil)
Expand Down
38 changes: 38 additions & 0 deletions pkg/gui/presentation/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,44 @@ func RenderAux(pipeSets [][]Pipe, commits []*models.Commit, selectedCommitHashPt
return lo.Flatten(chunks)
}

// RenderConnectorRow renders the row of vertical connectors to draw below the
// commit row rendered from the same pipe set. The width matches the commit
// row's graph width, so text following the connectors aligns with the commit's
// subject. Pipes sourced from the selected commit get the same highlight style
// as in the commit rows, so the highlighted path stays unbroken.
func RenderConnectorRow(pipes []Pipe, selectedCommitHashPtr *string) string {
maxPos := int16(0)
for _, pipe := range pipes {
if pipe.right() > maxPos {
maxPos = pipe.right()
}
}

styles := make([]*style.TextStyle, maxPos+1)
for _, pipe := range pipes {
if pipe.kind != TERMINATES && styles[pipe.toPos] == nil {
styles[pipe.toPos] = pipe.style
}
}
for _, pipe := range pipes {
if pipe.kind != TERMINATES && equalHashes(pipe.fromHash, selectedCommitHashPtr) {
styles[pipe.toPos] = &highlightStyle
}
}

writer := &strings.Builder{}
writer.Grow(len(styles) * 2)
for _, s := range styles {
if s != nil {
writer.WriteString(s.Sprint("│"))
} else {
writer.WriteString(" ")
}
writer.WriteString(" ")
}
return writer.String()
}

func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *models.Commit) *style.TextStyle) []Pipe {
maxPos := int16(0)
for _, pipe := range prevPipes {
Expand Down
4 changes: 4 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,8 @@ type TranslationSet struct {
SortOrderPrompt string
SortCommits string
SortCommitsTooltip string
CommitDisplayFormat string
CommitDisplayFormatTooltip string
CantChangeContextSizeError string
CantChangeRenameThresholdError string
OpenCommitInBrowser string
Expand Down Expand Up @@ -1967,6 +1969,8 @@ func EnglishTranslationSet() *TranslationSet {
SortBasedOnReflog: "(based on reflog)",
SortCommits: "Commit sort order",
SortCommitsTooltip: "Change the sort order of the commits in the commit log.\n\nThe default can be changed in the config file with the key 'git.log.sortOrder'.",
CommitDisplayFormat: "Commit display format",
CommitDisplayFormatTooltip: "Change how commits are displayed in the commit log.\n\nnormal: one line per commit with hash, author, and subject\ncomfortable: two lines per commit, author and date on the second line\nspacious: like comfortable, plus an empty graph row between commits\n\nThe default can be changed in the config file with the key 'git.log.commitDisplayFormat'.",
CantChangeContextSizeError: "Cannot change context while in patch building mode because we were too lazy to support it when releasing the feature. If you really want it, please let us know!",
CantChangeRenameThresholdError: "Cannot change the rename similarity threshold while in patch building mode, because the custom patch can't cope with a rename turning into a delete and add underneath it.",
OpenCommitInBrowser: "Open commit in browser",
Expand Down