summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-19 18:32:27 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-01-22 10:48:51 +1100
commit4ab5e5413944a92699a9540148666f0de26aa44b (patch)
tree58a4b648b5a4e5a6ed5f01b4e40ae5ba3c3ebad7 /pkg/gui
parentab84410b4155bcee73ead0e2a7510924575b1323 (diff)
add support for git bisect
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/bisect.go204
-rw-r--r--pkg/gui/commits_panel.go9
-rw-r--r--pkg/gui/custom_commands.go6
-rw-r--r--pkg/gui/filetree/commit_file_manager.go3
-rw-r--r--pkg/gui/filetree/file_manager.go3
-rw-r--r--pkg/gui/filetree/presentation.go (renamed from pkg/gui/presentation/files.go)47
-rw-r--r--pkg/gui/gui.go3
-rw-r--r--pkg/gui/keybindings.go8
-rw-r--r--pkg/gui/list_context_config.go3
-rw-r--r--pkg/gui/modes.go65
-rw-r--r--pkg/gui/presentation/commit_files.go49
-rw-r--r--pkg/gui/presentation/commits.go132
-rw-r--r--pkg/gui/view_helpers.go6
13 files changed, 452 insertions, 86 deletions
diff --git a/pkg/gui/bisect.go b/pkg/gui/bisect.go
new file mode 100644
index 000000000..ca00ce87f
--- /dev/null
+++ b/pkg/gui/bisect.go
@@ -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{}})
+}
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 0f63e4157..d568caf88 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -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,
},
)
diff --git a/pkg/gui/custom_commands.go b/pkg/gui/custom_commands.go
index c55b58a02..02293dd65 100644
--- a/pkg/gui/custom_commands.go
+++ b/pkg/gui/custom_commands.go
@@ -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)
}
diff --git a/pkg/gui/filetree/commit_file_manager.go b/pkg/gui/filetree/commit_file_manager.go
index e80528bcb..852a67b09 100644
--- a/pkg/gui/filetree/commit_file_manager.go
+++ b/pkg/gui/filetree/commit_file_manager.go
@@ -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)
})
}
diff --git a/pkg/gui/filetree/file_manager.go b/pkg/gui/filetree/file_manager.go
index 699e20332..b028ef961 100644
--- a/pkg/gui/filetree/file_manager.go
+++ b/pkg/gui/filetree/file_manager.go
@@ -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)
})
}
diff --git a/pkg/gui/presentation/files.go b/pkg/gui/filetree/presentation.go
index 4d00b7e84..6cecb78ad 100644
--- a/pkg/gui/presentation/files.go
+++ b/pkg/gui/filetree/presentation.go
@@ -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
+ }
+}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 50291c8bf..731e56b5b 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -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}},
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index a3e998416..584a276bb 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -910,6 +910,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
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)},
Key: gui.getKey(config.Universal.GoInto),
Handler: gui.handleViewReflogCommitFiles,
diff --git a/pkg/gui/list_context_config.go b/pkg/gui/list_context_config.go
index e5ad6c034..f05375b67 100644
--- a/pkg/gui/list_context_config.go
+++ b/pkg/gui/list_context_config.go
@@ -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) {
diff --git a/pkg/gui/modes.go b/pkg/gui/modes.go
index 1f92d1c84..b60fafc8a 100644
--- a/pkg/gui/modes.go
+++ b/pkg/gui/modes.go
@@ -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),
+ )
+}
diff --git a/pkg/gui/presentation/commit_files.go b/pkg/gui/presentation/commit_files.go
deleted file mode 100644
index 6a351be48..000000000
--- a/pkg/gui/presentation/commit_files.go
+++ /dev/null
@@ -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
- }
-}
diff --git a/pkg/gui/presentation/commits.go b/pkg/gui/presentation/commits.go
index 68cd554fa..0bc84d5e8 100644
--- a/pkg/gui/presentation/commits.go
+++ b/pkg/gui/presentation/commits.go
@@ -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 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) style.TextStyle {
+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 {
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index 3f559b400..300571ad3 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -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 {