mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-25 18:55:28 -06:00
add support for git bisect
This commit is contained in:
204
pkg/gui/bisect.go
Normal file
204
pkg/gui/bisect.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleOpenBisectMenu() error {
|
||||
if ok, err := gui.validateNotInFilterMode(); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
// no shame in getting this directly rather than using the cached value
|
||||
// given how cheap it is to obtain
|
||||
info := gui.Git.Bisect.GetInfo()
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
if info.Started() {
|
||||
return gui.openMidBisectMenu(info, commit)
|
||||
} else {
|
||||
return gui.openStartBisectMenu(info, commit)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) openMidBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
|
||||
// if there is not yet a 'current' bisect commit, or if we have
|
||||
// selected the current commit, we need to jump to the next 'current' commit
|
||||
// after we perform a bisect action. The reason we don't unconditionally jump
|
||||
// is that sometimes the user will want to go and mark a few commits as skipped
|
||||
// in a row and they wouldn't want to be jumped back to the current bisect
|
||||
// commit each time.
|
||||
// Originally we were allowing the user to, from the bisect menu, select whether
|
||||
// they were talking about the selected commit or the current bisect commit,
|
||||
// and that was a bit confusing (and required extra keypresses).
|
||||
selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
|
||||
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf(gui.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.BisectMark)
|
||||
if err := gui.Git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.afterMark(selectCurrentAfter)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: fmt.Sprintf(gui.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.BisectMark)
|
||||
if err := gui.Git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.afterMark(selectCurrentAfter)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: fmt.Sprintf(gui.Tr.Bisect.Skip, commit.ShortSha()),
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.BisectSkip)
|
||||
if err := gui.Git.Bisect.Skip(commit.Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.afterMark(selectCurrentAfter)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.Bisect.ResetOption,
|
||||
onPress: func() error {
|
||||
return gui.resetBisect()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(
|
||||
gui.Tr.Bisect.BisectMenuTitle,
|
||||
menuItems,
|
||||
createMenuOptions{showCancel: true},
|
||||
)
|
||||
}
|
||||
|
||||
func (gui *Gui) openStartBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
|
||||
return gui.createMenu(
|
||||
gui.Tr.Bisect.BisectMenuTitle,
|
||||
[]*menuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf(gui.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StartBisect)
|
||||
if err := gui.Git.Bisect.Start(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.Git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.postBisectCommandRefresh()
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: fmt.Sprintf(gui.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StartBisect)
|
||||
if err := gui.Git.Bisect.Start(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.Git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.postBisectCommandRefresh()
|
||||
},
|
||||
},
|
||||
},
|
||||
createMenuOptions{showCancel: true},
|
||||
)
|
||||
}
|
||||
|
||||
func (gui *Gui) resetBisect() error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.Bisect.ResetTitle,
|
||||
prompt: gui.Tr.Bisect.ResetPrompt,
|
||||
handleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.ResetBisect)
|
||||
if err := gui.Git.Bisect.Reset(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.postBisectCommandRefresh()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) showBisectCompleteMessage(candidateShas []string) error {
|
||||
prompt := gui.Tr.Bisect.CompletePrompt
|
||||
if len(candidateShas) > 1 {
|
||||
prompt = gui.Tr.Bisect.CompletePromptIndeterminate
|
||||
}
|
||||
|
||||
formattedCommits, err := gui.Git.Commit.GetCommitsOneline(candidateShas)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.Bisect.CompleteTitle,
|
||||
prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
||||
handleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.ResetBisect)
|
||||
if err := gui.Git.Bisect.Reset(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.postBisectCommandRefresh()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) afterMark(selectCurrent bool) error {
|
||||
done, candidateShas, err := gui.Git.Bisect.IsDone()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if err := gui.postBisectCommandRefresh(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
if done {
|
||||
return gui.showBisectCompleteMessage(candidateShas)
|
||||
}
|
||||
|
||||
if selectCurrent {
|
||||
gui.selectCurrentBisectCommit()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) selectCurrentBisectCommit() {
|
||||
info := gui.Git.Bisect.GetInfo()
|
||||
if info.GetCurrentSha() != "" {
|
||||
// find index of commit with that sha, move cursor to that.
|
||||
for i, commit := range gui.State.Commits {
|
||||
if commit.Sha == info.GetCurrentSha() {
|
||||
gui.State.Contexts.BranchCommits.GetPanelState().SetSelectedLineIdx(i)
|
||||
_ = gui.State.Contexts.BranchCommits.HandleFocus()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) postBisectCommandRefresh() error {
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{}})
|
||||
}
|
||||
@@ -116,12 +116,19 @@ func (gui *Gui) refreshCommitsWithLimit() error {
|
||||
gui.Mutexes.BranchCommitsMutex.Lock()
|
||||
defer gui.Mutexes.BranchCommitsMutex.Unlock()
|
||||
|
||||
refName := "HEAD"
|
||||
bisectInfo := gui.Git.Bisect.GetInfo()
|
||||
gui.State.BisectInfo = bisectInfo
|
||||
if bisectInfo.Started() {
|
||||
refName = bisectInfo.StartSha()
|
||||
}
|
||||
|
||||
commits, err := gui.Git.Loaders.Commits.GetCommits(
|
||||
loaders.GetCommitsOptions{
|
||||
Limit: gui.State.Panels.Commits.LimitCommits,
|
||||
FilterPath: gui.State.Modes.Filtering.GetPath(),
|
||||
IncludeRebaseCommits: true,
|
||||
RefName: "HEAD",
|
||||
RefName: refName,
|
||||
All: gui.State.ShowWholeGitGraph,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -254,7 +254,11 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
}
|
||||
return gui.WithWaitingStatus(loadingText, func() error {
|
||||
gui.logAction(gui.Tr.Actions.CustomCommand)
|
||||
err := gui.OSCommand.Cmd.NewShell(cmdStr).Run()
|
||||
cmdObj := gui.OSCommand.Cmd.NewShell(cmdStr)
|
||||
if customCommand.Stream {
|
||||
cmdObj.StreamOutput()
|
||||
}
|
||||
err := cmdObj.Run()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package filetree
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -114,6 +113,6 @@ func (m *CommitFileManager) Render(diffName string, patchManager *patch.PatchMan
|
||||
status = patch.PART
|
||||
}
|
||||
|
||||
return presentation.GetCommitFileLine(castN.NameAtDepth(depth), diffName, castN.File, status)
|
||||
return getCommitFileLine(castN.NameAtDepth(depth), diffName, castN.File, status)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -136,6 +135,6 @@ func (m *FileManager) Render(diffName string, submoduleConfigs []*models.Submodu
|
||||
|
||||
return renderAux(m.tree, m.collapsedPaths, "", -1, func(n INode, depth int) string {
|
||||
castN := n.(*FileNode)
|
||||
return presentation.GetFileLine(castN.GetHasUnstagedChanges(), castN.GetHasStagedChanges(), castN.NameAtDepth(depth), diffName, submoduleConfigs, castN.File)
|
||||
return getFileLine(castN.GetHasUnstagedChanges(), castN.GetHasStagedChanges(), castN.NameAtDepth(depth), diffName, submoduleConfigs, castN.File)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package presentation
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, diffName string, submoduleConfigs []*models.SubmoduleConfig, file *models.File) string {
|
||||
// TODO: move this back into presentation package and fix the import cycle
|
||||
|
||||
func getFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, diffName string, submoduleConfigs []*models.SubmoduleConfig, file *models.File) string {
|
||||
// potentially inefficient to be instantiating these color
|
||||
// objects with each render
|
||||
partiallyModifiedColor := style.FgYellow
|
||||
@@ -51,3 +54,43 @@ func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, di
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func getCommitFileLine(name string, diffName string, commitFile *models.CommitFile, status patch.PatchStatus) string {
|
||||
var colour style.TextStyle
|
||||
if diffName == name {
|
||||
colour = theme.DiffTerminalColor
|
||||
} else {
|
||||
switch status {
|
||||
case patch.WHOLE:
|
||||
colour = style.FgGreen
|
||||
case patch.PART:
|
||||
colour = style.FgYellow
|
||||
case patch.UNSELECTED:
|
||||
colour = theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
name = utils.EscapeSpecialChars(name)
|
||||
if commitFile == nil {
|
||||
return colour.Sprint(name)
|
||||
}
|
||||
|
||||
return getColorForChangeStatus(commitFile.ChangeStatus).Sprint(commitFile.ChangeStatus) + " " + colour.Sprint(name)
|
||||
}
|
||||
|
||||
func getColorForChangeStatus(changeStatus string) style.TextStyle {
|
||||
switch changeStatus {
|
||||
case "A":
|
||||
return style.FgGreen
|
||||
case "M", "R":
|
||||
return style.FgYellow
|
||||
case "D":
|
||||
return style.FgRed
|
||||
case "C":
|
||||
return style.FgCyan
|
||||
case "T":
|
||||
return style.FgMagenta
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
@@ -309,6 +310,7 @@ type guiState struct {
|
||||
RemoteBranches []*models.RemoteBranch
|
||||
Tags []*models.Tag
|
||||
MenuItems []*menuItem
|
||||
BisectInfo *git_commands.BisectInfo
|
||||
Updating bool
|
||||
Panels *panelStates
|
||||
SplitMainPanel bool
|
||||
@@ -394,6 +396,7 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
|
||||
FilteredReflogCommits: make([]*models.Commit, 0),
|
||||
ReflogCommits: make([]*models.Commit, 0),
|
||||
StashEntries: make([]*models.StashEntry, 0),
|
||||
BisectInfo: gui.Git.Bisect.GetInfo(),
|
||||
Panels: &panelStates{
|
||||
// TODO: work out why some of these are -1 and some are 0. Last time I checked there was a good reason but I'm less certain now
|
||||
Files: &filePanelState{listPanelState{SelectedLineIdx: -1}},
|
||||
|
||||
@@ -908,6 +908,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Handler: gui.handleOpenCommitInBrowser,
|
||||
Description: gui.Tr.LcOpenCommitInBrowser,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{string(BRANCH_COMMITS_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Commits.ViewBisectOptions),
|
||||
Handler: gui.handleOpenBisectMenu,
|
||||
Description: gui.Tr.LcViewBisectOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{string(REFLOG_COMMITS_CONTEXT_KEY)},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
@@ -177,6 +178,7 @@ func (gui *Gui) branchCommitsListContext() IListContext {
|
||||
startIdx,
|
||||
length,
|
||||
gui.shouldShowGraph(),
|
||||
gui.State.BisectInfo,
|
||||
)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
@@ -218,6 +220,7 @@ func (gui *Gui) subCommitsListContext() IListContext {
|
||||
startIdx,
|
||||
length,
|
||||
gui.shouldShowGraph(),
|
||||
git_commands.NewNullBisectInfo(),
|
||||
)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
@@ -16,11 +18,13 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.Diffing.Active,
|
||||
description: func() string {
|
||||
return style.FgMagenta.Sprintf(
|
||||
"%s %s %s",
|
||||
gui.Tr.LcShowingGitDiff,
|
||||
"git diff "+gui.diffStr(),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
return gui.withResetButton(
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
gui.Tr.LcShowingGitDiff,
|
||||
"git diff "+gui.diffStr(),
|
||||
),
|
||||
style.FgMagenta,
|
||||
)
|
||||
},
|
||||
reset: gui.exitDiffMode,
|
||||
@@ -28,22 +32,20 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.Git.Patch.PatchManager.Active,
|
||||
description: func() string {
|
||||
return style.FgYellow.SetBold().Sprintf(
|
||||
"%s %s",
|
||||
gui.Tr.LcBuildingPatch,
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
return gui.withResetButton(gui.Tr.LcBuildingPatch, style.FgYellow.SetBold())
|
||||
},
|
||||
reset: gui.handleResetPatch,
|
||||
},
|
||||
{
|
||||
isActive: gui.State.Modes.Filtering.Active,
|
||||
description: func() string {
|
||||
return style.FgRed.SetBold().Sprintf(
|
||||
"%s '%s' %s",
|
||||
gui.Tr.LcFilteringBy,
|
||||
gui.State.Modes.Filtering.GetPath(),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
return gui.withResetButton(
|
||||
fmt.Sprintf(
|
||||
"%s '%s'",
|
||||
gui.Tr.LcFilteringBy,
|
||||
gui.State.Modes.Filtering.GetPath(),
|
||||
),
|
||||
style.FgRed,
|
||||
)
|
||||
},
|
||||
reset: gui.exitFilterMode,
|
||||
@@ -51,10 +53,12 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.CherryPicking.Active,
|
||||
description: func() string {
|
||||
return style.FgCyan.Sprintf(
|
||||
"%d commits copied %s",
|
||||
len(gui.State.Modes.CherryPicking.CherryPickedCommits),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
return gui.withResetButton(
|
||||
fmt.Sprintf(
|
||||
"%d commits copied",
|
||||
len(gui.State.Modes.CherryPicking.CherryPickedCommits),
|
||||
),
|
||||
style.FgCyan,
|
||||
)
|
||||
},
|
||||
reset: gui.exitCherryPickingMode,
|
||||
@@ -65,13 +69,28 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
},
|
||||
description: func() string {
|
||||
workingTreeState := gui.Git.Status.WorkingTreeState()
|
||||
return style.FgYellow.Sprintf(
|
||||
"%s %s",
|
||||
formatWorkingTreeState(workingTreeState),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
return gui.withResetButton(
|
||||
formatWorkingTreeState(workingTreeState), style.FgYellow,
|
||||
)
|
||||
},
|
||||
reset: gui.abortMergeOrRebaseWithConfirm,
|
||||
},
|
||||
{
|
||||
isActive: func() bool {
|
||||
return gui.State.BisectInfo.Started()
|
||||
},
|
||||
description: func() string {
|
||||
return gui.withResetButton("bisecting", style.FgGreen)
|
||||
},
|
||||
reset: gui.resetBisect,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) withResetButton(content string, textStyle style.TextStyle) string {
|
||||
return textStyle.Sprintf(
|
||||
"%s %s",
|
||||
content,
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFile, status patch.PatchStatus) string {
|
||||
var colour style.TextStyle
|
||||
if diffName == name {
|
||||
colour = theme.DiffTerminalColor
|
||||
} else {
|
||||
switch status {
|
||||
case patch.WHOLE:
|
||||
colour = style.FgGreen
|
||||
case patch.PART:
|
||||
colour = style.FgYellow
|
||||
case patch.UNSELECTED:
|
||||
colour = theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
name = utils.EscapeSpecialChars(name)
|
||||
if commitFile == nil {
|
||||
return colour.Sprint(name)
|
||||
}
|
||||
|
||||
return getColorForChangeStatus(commitFile.ChangeStatus).Sprint(commitFile.ChangeStatus) + " " + colour.Sprint(name)
|
||||
}
|
||||
|
||||
func getColorForChangeStatus(changeStatus string) style.TextStyle {
|
||||
switch changeStatus {
|
||||
case "A":
|
||||
return style.FgGreen
|
||||
case "M", "R":
|
||||
return style.FgYellow
|
||||
case "D":
|
||||
return style.FgRed
|
||||
case "C":
|
||||
return style.FgCyan
|
||||
case "T":
|
||||
return style.FgMagenta
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/authors"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/graph"
|
||||
@@ -21,6 +22,14 @@ type pipeSetCacheKey struct {
|
||||
var pipeSetCache = make(map[pipeSetCacheKey][][]*graph.Pipe)
|
||||
var mutex sync.Mutex
|
||||
|
||||
type BisectProgress int
|
||||
|
||||
const (
|
||||
BeforeNewCommit BisectProgress = iota
|
||||
InbetweenCommits
|
||||
AfterOldCommit
|
||||
)
|
||||
|
||||
func GetCommitListDisplayStrings(
|
||||
commits []*models.Commit,
|
||||
fullDescription bool,
|
||||
@@ -31,6 +40,7 @@ func GetCommitListDisplayStrings(
|
||||
startIdx int,
|
||||
length int,
|
||||
showGraph bool,
|
||||
bisectInfo *git_commands.BisectInfo,
|
||||
) [][]string {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
@@ -77,12 +87,94 @@ func GetCommitListDisplayStrings(
|
||||
}
|
||||
|
||||
lines := make([][]string, 0, len(filteredCommits))
|
||||
bisectProgress := BeforeNewCommit
|
||||
var bisectStatus BisectStatus
|
||||
for i, commit := range filteredCommits {
|
||||
lines = append(lines, displayCommit(commit, cherryPickedCommitShaMap, diffName, parseEmoji, getGraphLine(i), fullDescription))
|
||||
bisectStatus, bisectProgress = getBisectStatus(commit.Sha, bisectInfo, bisectProgress)
|
||||
lines = append(lines, displayCommit(
|
||||
commit,
|
||||
cherryPickedCommitShaMap,
|
||||
diffName,
|
||||
parseEmoji,
|
||||
getGraphLine(i),
|
||||
fullDescription,
|
||||
bisectStatus,
|
||||
bisectInfo,
|
||||
))
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
func getBisectStatus(commitSha string, bisectInfo *git_commands.BisectInfo, bisectProgress BisectProgress) (BisectStatus, BisectProgress) {
|
||||
if !bisectInfo.Started() {
|
||||
return BisectStatusNone, bisectProgress
|
||||
}
|
||||
|
||||
if bisectInfo.GetCurrentSha() == commitSha {
|
||||
return BisectStatusCurrent, bisectProgress
|
||||
}
|
||||
|
||||
status, ok := bisectInfo.Status(commitSha)
|
||||
if ok {
|
||||
switch status {
|
||||
case git_commands.BisectStatusNew:
|
||||
return BisectStatusNew, InbetweenCommits
|
||||
case git_commands.BisectStatusOld:
|
||||
return BisectStatusOld, AfterOldCommit
|
||||
case git_commands.BisectStatusSkipped:
|
||||
return BisectStatusSkipped, bisectProgress
|
||||
}
|
||||
} else {
|
||||
if bisectProgress == InbetweenCommits {
|
||||
return BisectStatusCandidate, bisectProgress
|
||||
} else {
|
||||
return BisectStatusNone, bisectProgress
|
||||
}
|
||||
}
|
||||
|
||||
// should never land here
|
||||
return BisectStatusNone, bisectProgress
|
||||
}
|
||||
|
||||
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("?")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func displayCommit(
|
||||
commit *models.Commit,
|
||||
cherryPickedCommitShaMap map[string]bool,
|
||||
@@ -90,9 +182,11 @@ func displayCommit(
|
||||
parseEmoji bool,
|
||||
graphLine string,
|
||||
fullDescription bool,
|
||||
bisectStatus BisectStatus,
|
||||
bisectInfo *git_commands.BisectInfo,
|
||||
) []string {
|
||||
|
||||
shaColor := getShaColor(commit, diffName, cherryPickedCommitShaMap)
|
||||
shaColor := getShaColor(commit, diffName, cherryPickedCommitShaMap, bisectStatus, bisectInfo)
|
||||
bisectString := getBisectStatusText(bisectStatus, bisectInfo)
|
||||
|
||||
actionString := ""
|
||||
if commit.Action != "" {
|
||||
@@ -122,6 +216,7 @@ func displayCommit(
|
||||
|
||||
cols := make([]string, 0, 5)
|
||||
cols = append(cols, shaColor.Sprint(commit.ShortSha()))
|
||||
cols = append(cols, bisectString)
|
||||
if fullDescription {
|
||||
cols = append(cols, style.FgBlue.Sprint(utils.UnixToDate(commit.UnixTimestamp)))
|
||||
}
|
||||
@@ -133,10 +228,39 @@ func displayCommit(
|
||||
)
|
||||
|
||||
return cols
|
||||
|
||||
}
|
||||
|
||||
func getShaColor(commit *models.Commit, diffName string, cherryPickedCommitShaMap map[string]bool) style.TextStyle {
|
||||
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
|
||||
}
|
||||
|
||||
// shouldn't land here
|
||||
return style.FgWhite
|
||||
}
|
||||
|
||||
func getShaColor(
|
||||
commit *models.Commit,
|
||||
diffName string,
|
||||
cherryPickedCommitShaMap map[string]bool,
|
||||
bisectStatus BisectStatus,
|
||||
bisectInfo *git_commands.BisectInfo,
|
||||
) style.TextStyle {
|
||||
if bisectInfo.Started() {
|
||||
return getBisectStatusColor(bisectStatus)
|
||||
}
|
||||
|
||||
diffed := commit.Sha == diffName
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch commit.Status {
|
||||
|
||||
@@ -28,6 +28,8 @@ const (
|
||||
REMOTES
|
||||
STATUS
|
||||
SUBMODULES
|
||||
// not actually a view. Will refactor this later
|
||||
BISECT_INFO
|
||||
)
|
||||
|
||||
func getScopeNames(scopes []RefreshableView) []string {
|
||||
@@ -105,12 +107,12 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
f := func() {
|
||||
var scopeMap map[RefreshableView]bool
|
||||
if len(options.scope) == 0 {
|
||||
scopeMap = arrToMap([]RefreshableView{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS})
|
||||
scopeMap = arrToMap([]RefreshableView{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS, BISECT_INFO})
|
||||
} else {
|
||||
scopeMap = arrToMap(options.scope)
|
||||
}
|
||||
|
||||
if scopeMap[COMMITS] || scopeMap[BRANCHES] || scopeMap[REFLOG] {
|
||||
if scopeMap[COMMITS] || scopeMap[BRANCHES] || scopeMap[REFLOG] || scopeMap[BISECT_INFO] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
|
||||
Reference in New Issue
Block a user