Mark commits which changed a file among the last X commits

See https://github.com/jesseduffield/lazygit/issues/2653
This commit is contained in:
Jesse Duffield 2023-05-21 12:31:17 +10:00
parent 6f535d71c9
commit 7efe433f1c
9 changed files with 145 additions and 62 deletions

View file

@ -65,6 +65,8 @@ gui:
splitDiff: 'auto' # one of 'auto' | 'always'
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
border: 'single' # one of 'single' | 'double' | 'rounded' | 'hidden'
# For marking recent commits which changed the selected file
experimentalMarkCommitsWhichChangedFile: false
git:
paging:
colorArg: always

View file

@ -3,6 +3,7 @@ package git_commands
import (
"fmt"
"strings"
"time"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
@ -250,3 +251,19 @@ func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, erro
}
return self.GetCommitMessage(formattedHash)
}
// Returns hashes of recent commits which changed the given file
// Note: This does not look for the last X commits to change a file, instead
// it looks among the last X commits and see which of them happened to have changed the file.
// This is more efficient.
func (self *CommitCommands) GetRecentCommitsWhichChangedFile(path string) []string {
t := time.Now()
// Checking last X commits. Funnily this seems to actually consider more than the last
// X, perhaps because of topological sorting.
cmdStr := NewGitCmd("log").Arg("HEAD~50..HEAD", "--pretty=%H", "--", self.cmd.Quote(path)).
ToString()
hashes, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
self.Log.Warn(fmt.Sprintf("GetRecentCommitsWhichChangedFile took %s", time.Since(t)))
return strings.Split(strings.TrimSpace(hashes), "\n")
}

View file

@ -27,33 +27,34 @@ type RefresherConfig struct {
}
type GuiConfig struct {
AuthorColors map[string]string `yaml:"authorColors"`
BranchColors map[string]string `yaml:"branchColors"`
ScrollHeight int `yaml:"scrollHeight"`
ScrollPastBottom bool `yaml:"scrollPastBottom"`
MouseEvents bool `yaml:"mouseEvents"`
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
SkipStashWarning bool `yaml:"skipStashWarning"`
SidePanelWidth float64 `yaml:"sidePanelWidth"`
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
Language string `yaml:"language"`
TimeFormat string `yaml:"timeFormat"`
Theme ThemeConfig `yaml:"theme"`
CommitLength CommitLengthConfig `yaml:"commitLength"`
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
ShowListFooter bool `yaml:"showListFooter"`
ShowFileTree bool `yaml:"showFileTree"`
ShowRandomTip bool `yaml:"showRandomTip"`
ShowCommandLog bool `yaml:"showCommandLog"`
ShowBottomLine bool `yaml:"showBottomLine"`
ShowIcons bool `yaml:"showIcons"`
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
CommandLogSize int `yaml:"commandLogSize"`
SplitDiff string `yaml:"splitDiff"`
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
WindowSize string `yaml:"windowSize"`
Border string `yaml:"border"`
AuthorColors map[string]string `yaml:"authorColors"`
BranchColors map[string]string `yaml:"branchColors"`
ScrollHeight int `yaml:"scrollHeight"`
ScrollPastBottom bool `yaml:"scrollPastBottom"`
MouseEvents bool `yaml:"mouseEvents"`
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
SkipStashWarning bool `yaml:"skipStashWarning"`
SidePanelWidth float64 `yaml:"sidePanelWidth"`
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
Language string `yaml:"language"`
TimeFormat string `yaml:"timeFormat"`
Theme ThemeConfig `yaml:"theme"`
CommitLength CommitLengthConfig `yaml:"commitLength"`
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
ShowListFooter bool `yaml:"showListFooter"`
ShowFileTree bool `yaml:"showFileTree"`
ShowRandomTip bool `yaml:"showRandomTip"`
ShowCommandLog bool `yaml:"showCommandLog"`
ShowBottomLine bool `yaml:"showBottomLine"`
ShowIcons bool `yaml:"showIcons"`
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
CommandLogSize int `yaml:"commandLogSize"`
SplitDiff string `yaml:"splitDiff"`
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
WindowSize string `yaml:"windowSize"`
Border string `yaml:"border"`
ExperimentalMarkCommitsWhichChangedFile bool `yaml:"experimentalMarkCommitsWhichChangedFile"`
}
type ThemeConfig struct {
@ -410,19 +411,20 @@ func GetDefaultConfig() *UserConfig {
UnstagedChangesColor: []string{"red"},
DefaultFgColor: []string{"default"},
},
CommitLength: CommitLengthConfig{Show: true},
SkipNoStagedFilesWarning: false,
ShowListFooter: true,
ShowCommandLog: true,
ShowBottomLine: true,
ShowFileTree: true,
ShowRandomTip: true,
ShowIcons: false,
ExperimentalShowBranchHeads: false,
CommandLogSize: 8,
SplitDiff: "auto",
SkipRewordInEditorWarning: false,
Border: "single",
CommitLength: CommitLengthConfig{Show: true},
SkipNoStagedFilesWarning: false,
ShowListFooter: true,
ShowCommandLog: true,
ShowBottomLine: true,
ShowFileTree: true,
ShowRandomTip: true,
ShowIcons: false,
ExperimentalShowBranchHeads: false,
CommandLogSize: 8,
SplitDiff: "auto",
SkipRewordInEditorWarning: false,
Border: "single",
ExperimentalMarkCommitsWhichChangedFile: false,
},
Git: GitConfig{
Paging: PagingConfig{

View file

@ -3,6 +3,7 @@ package context
import (
"log"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@ -37,20 +38,14 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
return presentation.GetCommitListDisplayStrings(
c.Common,
return getCommitsDisplayStrings(
c,
c.Model().Commits,
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
length,
shouldShowGraph(c),
c.Model().BisectInfo,
selectedCommitSha,
showYouAreHereLabel,
c.Model().BisectInfo,
)
}
@ -159,3 +154,31 @@ func shouldShowGraph(c *ContextCommon) bool {
log.Fatalf("Unknown value for git.log.showGraph: %s. Expected one of: 'always', 'never', 'when-maximised'", value)
return false
}
func getCommitsDisplayStrings(
c *ContextCommon,
commits []*models.Commit,
startIdx int,
length int,
selectedCommitSha string,
showYouAreHereLabel bool,
bisectInfo *git_commands.BisectInfo,
) [][]string {
return presentation.GetCommitListDisplayStrings(
c.Common,
commits,
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
length,
shouldShowGraph(c),
bisectInfo,
showYouAreHereLabel,
c.State().GetRepoState().GetRecentCommitsWhichChangedFile(),
c.UserConfig.Gui.ExperimentalMarkCommitsWhichChangedFile,
)
}

View file

@ -5,7 +5,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -40,20 +39,14 @@ func NewSubCommitsContext(
selectedCommitSha = selectedCommit.Sha
}
}
return presentation.GetCommitListDisplayStrings(
c.Common,
return getCommitsDisplayStrings(
c,
c.Model().SubCommits,
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
length,
shouldShowGraph(c),
git_commands.NewNullBisectInfo(),
selectedCommitSha,
false,
git_commands.NewNullBisectInfo(),
)
}

View file

@ -9,6 +9,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type FilesController struct {
@ -192,6 +193,16 @@ func (self *FilesController) GetOnRenderToMain() func() error {
}
}
if self.c.UserConfig.Gui.ExperimentalMarkCommitsWhichChangedFile {
go utils.Safe(func() {
recentCommitsWhichChangedFile := self.c.Git().Commit.GetRecentCommitsWhichChangedFile(node.GetPath())
self.c.OnUIThread(func() error {
self.c.State().GetRepoState().SetRecentCommitsWhichChangedFile(recentCommitsWhichChangedFile)
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
})
})
}
self.c.Helpers().MergeConflicts.ResetMergeState()
pair := self.c.MainViewPairs().Normal

View file

@ -220,6 +220,8 @@ type GuiRepoState struct {
ScreenMode types.WindowMaximisation
CurrentPopupOpts *types.CreatePopupPanelOpts
RecentCommitsWhichChangedFile []string
}
var _ types.IRepoStateAccessor = new(GuiRepoState)
@ -268,6 +270,14 @@ func (self *GuiRepoState) GetSplitMainPanel() bool {
return self.SplitMainPanel
}
func (self *GuiRepoState) SetRecentCommitsWhichChangedFile(value []string) {
self.RecentCommitsWhichChangedFile = value
}
func (self *GuiRepoState) GetRecentCommitsWhichChangedFile() []string {
return self.RecentCommitsWhichChangedFile
}
type searchingState struct {
view *gocui.View
isSearching bool

View file

@ -16,6 +16,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/kyokomi/emoji/v2"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
@ -48,6 +49,8 @@ func GetCommitListDisplayStrings(
showGraph bool,
bisectInfo *git_commands.BisectInfo,
showYouAreHereLabel bool,
recentCommitsWhichChangedFile []string,
markRecentFileChanges bool,
) [][]string {
mutex.Lock()
defer mutex.Unlock()
@ -113,6 +116,8 @@ func GetCommitListDisplayStrings(
bisectStatus,
bisectInfo,
isYouAreHereCommit,
recentCommitsWhichChangedFile,
markRecentFileChanges,
))
}
return lines
@ -259,6 +264,8 @@ func displayCommit(
bisectStatus BisectStatus,
bisectInfo *git_commands.BisectInfo,
isYouAreHereCommit bool,
recentCommitsWhichChangedFile []string,
markRecentFileChanges bool,
) []string {
shaColor := getShaColor(commit, diffName, cherryPickedCommitShaSet, bisectStatus, bisectInfo)
bisectString := getBisectStatusText(bisectStatus, bisectInfo)
@ -299,7 +306,23 @@ func displayCommit(
cols := make([]string, 0, 7)
if icons.IsIconEnabled() {
cols = append(cols, shaColor.Sprint(icons.IconForCommit(commit)))
if markRecentFileChanges && lo.SomeBy(recentCommitsWhichChangedFile, func(sha string) bool {
return utils.ShortSha(sha) == utils.ShortSha(commit.Sha)
}) {
cols = append(cols, style.FgDefault.Sprint(">"))
} else {
cols = append(cols, shaColor.Sprint(icons.IconForCommit(commit)))
}
} else {
if markRecentFileChanges {
if lo.SomeBy(recentCommitsWhichChangedFile, func(sha string) bool {
return utils.ShortSha(sha) == utils.ShortSha(commit.Sha)
}) {
cols = append(cols, style.FgDefault.Sprint(">"))
} else {
cols = append(cols, " ")
}
}
}
cols = append(cols, shaColor.Sprint(commit.ShortSha()))
cols = append(cols, bisectString)

View file

@ -254,6 +254,8 @@ type IRepoStateAccessor interface {
IsSearching() bool
SetSplitMainPanel(bool)
GetSplitMainPanel() bool
SetRecentCommitsWhichChangedFile([]string)
GetRecentCommitsWhichChangedFile() []string
}
// startup stages so we don't need to load everything at once