add new diff mode

WIP

WIP

WIP

WIP

WIP

WIP

WIP
This commit is contained in:
Jesse Duffield 2020-03-29 14:34:17 +11:00
parent 33d287d2f0
commit 40fbce91ce
38 changed files with 465 additions and 136 deletions

View file

@ -90,6 +90,7 @@ Default path for the config file:
undo: 'z' undo: 'z'
redo: '<c-z>' redo: '<c-z>'
filteringMenu: <c-s> filteringMenu: <c-s>
diffingMenu: '\'
status: status:
checkForUpdate: 'u' checkForUpdate: 'u'
recentRepos: '<enter>' recentRepos: '<enter>'

View file

@ -1021,11 +1021,6 @@ func (c *GitCommand) ResetSoft(ref string) error {
return c.OSCommand.RunCommand("git reset --soft " + ref) return c.OSCommand.RunCommand("git reset --soft " + ref)
} }
// DiffCommits show diff between commits
func (c *GitCommand) DiffCommits(sha1, sha2 string) (string, error) {
return c.OSCommand.RunCommandWithOutput("git diff --color=%s --stat -p %s %s", c.colorArg(), sha1, sha2)
}
// CreateFixupCommit creates a commit that fixes up a previous commit // CreateFixupCommit creates a commit that fixes up a previous commit
func (c *GitCommand) CreateFixupCommit(sha string) error { func (c *GitCommand) CreateFixupCommit(sha string) error {
return c.OSCommand.RunCommand("git commit --fixup=%s", sha) return c.OSCommand.RunCommand("git commit --fixup=%s", sha)

View file

@ -5,3 +5,7 @@ type RemoteBranch struct {
Name string Name string
RemoteName string RemoteName string
} }
func (r *RemoteBranch) FullName() string {
return r.RemoteName + "/" + r.Name
}

View file

@ -1,7 +1,13 @@
package commands package commands
import "fmt"
// StashEntry : A git stash entry // StashEntry : A git stash entry
type StashEntry struct { type StashEntry struct {
Index int Index int
Name string Name string
} }
func (s *StashEntry) RefName() string {
return fmt.Sprintf("stash@{%d}", s.Index)
}

View file

@ -321,6 +321,7 @@ keybinding:
undo: 'z' undo: 'z'
redo: '<c-z>' redo: '<c-z>'
filteringMenu: <c-s> filteringMenu: <c-s>
diffingMenu: '\'
status: status:
checkForUpdate: 'u' checkForUpdate: 'u'
recentRepos: '<enter>' recentRepos: '<enter>'

View file

@ -42,6 +42,10 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
branch := gui.getSelectedBranch() branch := gui.getSelectedBranch()
v.FocusPoint(0, gui.State.Panels.Branches.SelectedLine) v.FocusPoint(0, gui.State.Panels.Branches.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(branch.Name), gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
) )
@ -85,7 +89,7 @@ func (gui *Gui) renderLocalBranchesWithSelection() error {
branchesView := gui.getBranchesView() branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches)) gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL) displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Diff.Ref)
gui.renderDisplayStrings(branchesView, displayStrings) gui.renderDisplayStrings(branchesView, displayStrings)
if gui.g.CurrentView() == branchesView { if gui.g.CurrentView() == branchesView {
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil { if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {

View file

@ -7,7 +7,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/presentation"
) )
func (gui *Gui) getSelectedCommitFile(g *gocui.Gui) *commands.CommitFile { func (gui *Gui) getSelectedCommitFile() *commands.CommitFile {
selectedLine := gui.State.Panels.CommitFiles.SelectedLine selectedLine := gui.State.Panels.CommitFiles.SelectedLine
if selectedLine == -1 { if selectedLine == -1 {
return nil return nil
@ -34,7 +34,7 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error {
gui.handleEscapeLineByLinePanel() gui.handleEscapeLineByLinePanel()
} }
commitFile := gui.getSelectedCommitFile(g) commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil
@ -99,7 +99,7 @@ func (gui *Gui) refreshCommitFilesView() error {
return err return err
} }
commit := gui.getSelectedCommit(gui.g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return nil return nil
} }
@ -113,14 +113,14 @@ func (gui *Gui) refreshCommitFilesView() error {
gui.refreshSelectedLine(&gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles)) gui.refreshSelectedLine(&gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles))
commitsFileView := gui.getCommitFilesView() commitsFileView := gui.getCommitFilesView()
displayStrings := presentation.GetCommitFileListDisplayStrings(gui.State.CommitFiles) displayStrings := presentation.GetCommitFileListDisplayStrings(gui.State.CommitFiles, gui.State.Diff.Ref)
gui.renderDisplayStrings(commitsFileView, displayStrings) gui.renderDisplayStrings(commitsFileView, displayStrings)
return gui.handleCommitFileSelect(gui.g, commitsFileView) return gui.handleCommitFileSelect(gui.g, commitsFileView)
} }
func (gui *Gui) handleOpenOldCommitFile(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleOpenOldCommitFile(g *gocui.Gui, v *gocui.View) error {
file := gui.getSelectedCommitFile(g) file := gui.getSelectedCommitFile()
return gui.openFile(file.Name) return gui.openFile(file.Name)
} }
@ -129,7 +129,7 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
commitFile := gui.getSelectedCommitFile(g) commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil
@ -167,7 +167,7 @@ func (gui *Gui) startPatchManager() error {
diffMap[commitFile.Name] = commitText diffMap[commitFile.Name] = commitText
} }
commit := gui.getSelectedCommit(gui.g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return errors.New("No commit selected") return errors.New("No commit selected")
} }
@ -185,7 +185,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
return err return err
} }
commitFile := gui.getSelectedCommitFile(gui.g) commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil

View file

@ -12,7 +12,7 @@ import (
// list panel functions // list panel functions
func (gui *Gui) getSelectedCommit(g *gocui.Gui) *commands.Commit { func (gui *Gui) getSelectedCommit() *commands.Commit {
selectedLine := gui.State.Panels.Commits.SelectedLine selectedLine := gui.State.Panels.Commits.SelectedLine
if selectedLine == -1 { if selectedLine == -1 {
return nil return nil
@ -49,16 +49,15 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
gui.getSecondaryView().Title = "Custom Patch" gui.getSecondaryView().Title = "Custom Patch"
gui.handleEscapeLineByLinePanel() gui.handleEscapeLineByLinePanel()
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch")) return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch"))
} }
v.FocusPoint(0, gui.State.Panels.Commits.SelectedLine) v.FocusPoint(0, gui.State.Panels.Commits.SelectedLine)
// if specific diff mode is on, don't show diff if gui.inDiffMode() {
if gui.State.Panels.Commits.SpecificDiffMode { return gui.renderDiff()
return nil
} }
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
@ -509,7 +508,7 @@ func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return nil return nil
} }
@ -533,7 +532,7 @@ func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) er
return err return err
} }
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return nil return nil
} }
@ -555,7 +554,7 @@ func (gui *Gui) handleTagCommit(g *gocui.Gui, v *gocui.View) error {
// TODO: bring up menu asking if you want to make a lightweight or annotated tag // TODO: bring up menu asking if you want to make a lightweight or annotated tag
// if annotated, switch to a subprocess to create the message // if annotated, switch to a subprocess to create the message
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return nil return nil
} }
@ -573,7 +572,7 @@ func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
} }
func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return nil return nil
} }
@ -587,7 +586,7 @@ func (gui *Gui) renderBranchCommitsWithSelection() error {
commitsView := gui.getCommitsView() commitsView := gui.getCommitsView()
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits)) gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
displayStrings := presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap()) displayStrings := presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap(), gui.State.Diff.Ref)
gui.renderDisplayStrings(commitsView, displayStrings) gui.renderDisplayStrings(commitsView, displayStrings)
if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" { if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" {
if err := gui.handleCommitSelect(gui.g, commitsView); err != nil { if err := gui.handleCommitSelect(gui.g, commitsView); err != nil {
@ -649,7 +648,7 @@ func (gui *Gui) handlePrevCommitsTab(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error {
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit()
if commit == nil { if commit == nil {
return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitsThisBranch")) return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitsThisBranch"))
} }

189
pkg/gui/diffing.go Normal file
View file

@ -0,0 +1,189 @@
package gui
import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
)
func (gui *Gui) inDiffMode() bool {
return gui.State.Diff.Ref != ""
}
func (gui *Gui) exitDiffMode() error {
gui.State.Diff = DiffState{}
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
}
func (gui *Gui) renderDiff() error {
filterArg := ""
if gui.inFilterMode() {
filterArg = fmt.Sprintf(" -- %s", gui.State.FilterPath)
}
cmd := gui.OSCommand.ExecutableFromString(
fmt.Sprintf("git diff --color %s %s", gui.diffStr(), filterArg),
)
if err := gui.newPtyTask("main", cmd); err != nil {
gui.Log.Error(err)
}
return nil
}
// currentDiffTerminals returns the current diff terminals of the currently selected item.
// in the case of a branch it returns both the branch and it's upstream name,
// which becomes an option when you bring up the diff menu, but when you're just
// flicking through branches it will be using the local branch name.
func (gui *Gui) currentDiffTerminals() []string {
names := []string{}
switch gui.g.CurrentView().Name() {
case "files":
file, err := gui.getSelectedFile()
if err == nil {
names = append(names, file.Name)
}
case "commitFiles":
file := gui.getSelectedCommitFile()
if file != nil {
names = append(names, file.Name)
}
case "commits":
var commit *commands.Commit
switch gui.getCommitsView().Context {
case "reflog-commits":
commit = gui.getSelectedReflogCommit()
case "branch-commits":
commit = gui.getSelectedCommit()
}
if commit != nil {
names = append(names, commit.Sha)
}
case "stash":
entry := gui.getSelectedStashEntry()
if entry != nil {
names = append(names, entry.RefName())
}
case "branches":
switch gui.getBranchesView().Context {
case "local-branches":
branch := gui.getSelectedBranch()
if branch != nil {
names = append(names, branch.Name)
if branch.UpstreamName != "" {
names = append(names, branch.UpstreamName)
}
}
case "remotes":
remote := gui.getSelectedRemote()
if remote != nil {
names = append(names, remote.Name)
}
case "remote-branches":
remoteBranch := gui.getSelectedRemoteBranch()
if remoteBranch != nil {
names = append(names, remoteBranch.FullName())
}
case "tags":
tag := gui.getSelectedTag()
if tag != nil {
names = append(names, tag.Name)
}
}
}
return names
}
func (gui *Gui) currentDiffTerminal() string {
names := gui.currentDiffTerminals()
if len(names) == 0 {
return "HEAD"
}
return names[0]
}
func (gui *Gui) diffStr() string {
left := gui.State.Diff.Ref
right := gui.currentDiffTerminal()
if gui.State.Diff.Reverse {
left, right = right, left
}
return fmt.Sprintf("%s %s", left, right)
}
func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
names := gui.currentDiffTerminals()
menuItems := []*menuItem{}
for _, name := range names {
name := name
menuItems = append(menuItems, []*menuItem{
{
displayString: fmt.Sprintf("%s %s", gui.Tr.SLocalize("diffFrom"), name),
onPress: func() error {
gui.State.Diff.Ref = name
gui.State.Diff.Reverse = false
// can scope this down based on current view but too lazy right now
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
},
},
{
displayString: fmt.Sprintf("%s %s", gui.Tr.SLocalize("diffTo"), name),
onPress: func() error {
gui.State.Diff.Ref = name
gui.State.Diff.Reverse = true
// can scope this down based on current view but too lazy right now
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
},
},
}...)
}
menuItems = append(menuItems, []*menuItem{
{
displayString: gui.Tr.SLocalize("enterRefToDiffFrom"),
onPress: func() error {
return gui.createPromptPanel(gui.g, v, gui.Tr.SLocalize("enteRefName"), "", func(g *gocui.Gui, promptView *gocui.View) error {
gui.State.Diff.Ref = strings.TrimSpace(promptView.Buffer())
gui.State.Diff.Reverse = false
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
})
},
},
{
displayString: gui.Tr.SLocalize("enterRefToDiffTo"),
onPress: func() error {
return gui.createPromptPanel(gui.g, v, gui.Tr.SLocalize("enteRefName"), "", func(g *gocui.Gui, promptView *gocui.View) error {
gui.State.Diff.Ref = strings.TrimSpace(promptView.Buffer())
gui.State.Diff.Reverse = true
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
})
},
},
}...)
menuItems = append(menuItems, &menuItem{
displayString: gui.Tr.SLocalize("swapDiff"),
onPress: func() error {
gui.State.Diff.Reverse = !gui.State.Diff.Reverse
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
},
})
if gui.inDiffMode() {
menuItems = append(menuItems, &menuItem{
displayString: gui.Tr.SLocalize("exitDiffMode"),
onPress: func() error {
gui.State.Diff = DiffState{}
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
},
})
}
return gui.createMenu(gui.Tr.SLocalize("DiffingMenuTitle"), menuItems, createMenuOptions{showCancel: true})
}

View file

@ -5,7 +5,7 @@ import (
) )
func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return err return err

View file

@ -17,7 +17,7 @@ import (
// list panel functions // list panel functions
func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) { func (gui *Gui) getSelectedFile() (*commands.File, error) {
selectedLine := gui.State.Panels.Files.SelectedLine selectedLine := gui.State.Panels.Files.SelectedLine
if selectedLine == -1 { if selectedLine == -1 {
return &commands.File{}, gui.Errors.ErrNoFiles return &commands.File{}, gui.Errors.ErrNoFiles
@ -27,7 +27,7 @@ func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
} }
func (gui *Gui) selectFile(alreadySelected bool) error { func (gui *Gui) selectFile(alreadySelected bool) error {
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return err return err
@ -39,12 +39,6 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
gui.getFilesView().FocusPoint(0, gui.State.Panels.Files.SelectedLine) gui.getFilesView().FocusPoint(0, gui.State.Panels.Files.SelectedLine)
if file.HasInlineMergeConflicts {
gui.getMainView().Title = gui.Tr.SLocalize("MergeConflictsTitle")
gui.State.SplitMainPanel = false
return gui.refreshMergePanel()
}
if !alreadySelected { if !alreadySelected {
if err := gui.resetOrigin(gui.getMainView()); err != nil { if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err return err
@ -54,6 +48,16 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
} }
} }
if gui.inDiffMode() {
return gui.renderDiff()
}
if file.HasInlineMergeConflicts {
gui.getMainView().Title = gui.Tr.SLocalize("MergeConflictsTitle")
gui.State.SplitMainPanel = false
return gui.refreshMergePanel()
}
if file.HasStagedChanges && file.HasUnstagedChanges { if file.HasStagedChanges && file.HasUnstagedChanges {
gui.State.SplitMainPanel = true gui.State.SplitMainPanel = true
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges") gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
@ -89,7 +93,7 @@ func (gui *Gui) refreshFiles() error {
gui.State.RefreshingFilesMutex.Unlock() gui.State.RefreshingFilesMutex.Unlock()
}() }()
selectedFile, _ := gui.getSelectedFile(gui.g) selectedFile, _ := gui.getSelectedFile()
filesView := gui.getFilesView() filesView := gui.getFilesView()
if filesView == nil { if filesView == nil {
@ -101,11 +105,11 @@ func (gui *Gui) refreshFiles() error {
} }
gui.g.Update(func(g *gocui.Gui) error { gui.g.Update(func(g *gocui.Gui) error {
displayStrings := presentation.GetFileListDisplayStrings(gui.State.Files) displayStrings := presentation.GetFileListDisplayStrings(gui.State.Files, gui.State.Diff.Ref)
gui.renderDisplayStrings(filesView, displayStrings) gui.renderDisplayStrings(filesView, displayStrings)
if g.CurrentView() == filesView || (g.CurrentView() == gui.getMainView() && g.CurrentView().Context == "merging") { if g.CurrentView() == filesView || (g.CurrentView() == gui.getMainView() && g.CurrentView().Context == "merging") {
newSelectedFile, _ := gui.getSelectedFile(gui.g) newSelectedFile, _ := gui.getSelectedFile()
alreadySelected := newSelectedFile.Name == selectedFile.Name alreadySelected := newSelectedFile.Name == selectedFile.Name
return gui.selectFile(alreadySelected) return gui.selectFile(alreadySelected)
} }
@ -140,7 +144,7 @@ func (gui *Gui) trackedFiles() []*commands.File {
} }
func (gui *Gui) stageSelectedFile(g *gocui.Gui) error { func (gui *Gui) stageSelectedFile(g *gocui.Gui) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
return err return err
} }
@ -152,7 +156,7 @@ func (gui *Gui) handleEnterFile(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error { func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error {
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return err return err
@ -173,7 +177,7 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
} }
func (gui *Gui) handleFilePress(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleFilePress(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err == gui.Errors.ErrNoFiles { if err == gui.Errors.ErrNoFiles {
return nil return nil
@ -237,7 +241,7 @@ func (gui *Gui) handleStageAll(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
return gui.surfaceError(err) return gui.surfaceError(err)
} }
@ -345,7 +349,7 @@ func (gui *Gui) editFile(filename string) error {
} }
func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
return gui.surfaceError(err) return gui.surfaceError(err)
} }
@ -354,7 +358,7 @@ func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
return gui.surfaceError(err) return gui.surfaceError(err)
} }
@ -379,7 +383,7 @@ func (gui *Gui) refreshStateFiles() error {
} }
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
item, err := gui.getSelectedFile(g) item, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return "", err return "", err
@ -489,7 +493,7 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return gui.surfaceError(err) return gui.surfaceError(err)

View file

@ -7,16 +7,20 @@ import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
) )
func (gui *Gui) handleCreateScopingMenuPanel(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
fileName := "" fileName := ""
switch v.Name() { switch v.Name() {
case "files": case "files":
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err == nil { if err == nil {
fileName = file.Name fileName = file.Name
} }
case "commitFiles": case "commitFiles":
file := gui.getSelectedCommitFile(gui.g) file := gui.getSelectedCommitFile()
if file != nil { if file != nil {
fileName = file.Name fileName = file.Name
} }

View file

@ -144,6 +144,14 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
} }
} }
if gui.inDiffMode() {
if width-cx <= len(gui.Tr.SLocalize("(reset)")) {
return gui.exitDiffMode()
} else {
return nil
}
}
if cx <= len(gui.Tr.SLocalize("Donate")) { if cx <= len(gui.Tr.SLocalize("Donate")) {
return gui.OSCommand.OpenLink("https://github.com/sponsors/jesseduffield") return gui.OSCommand.OpenLink("https://github.com/sponsors/jesseduffield")
} }

View file

@ -136,9 +136,8 @@ type tagsPanelState struct {
} }
type commitPanelState struct { type commitPanelState struct {
SelectedLine int SelectedLine int
SpecificDiffMode bool LimitCommits bool
LimitCommits bool
} }
type reflogCommitPanelState struct { type reflogCommitPanelState struct {
@ -187,8 +186,8 @@ const (
// if ref is blank we're not diffing anything // if ref is blank we're not diffing anything
type DiffState struct { type DiffState struct {
Ref string Ref string
Left bool Reverse bool
} }
type guiState struct { type guiState struct {
@ -231,10 +230,12 @@ type guiState struct {
} }
func (gui *Gui) resetState() { func (gui *Gui) resetState() {
// we carry over the filter path // we carry over the filter path and diff state
prevFilterPath := "" prevFilterPath := ""
prevDiff := DiffState{}
if gui.State != nil { if gui.State != nil {
prevFilterPath = gui.State.FilterPath prevFilterPath = gui.State.FilterPath
prevDiff = gui.State.Diff
} }
gui.State = &guiState{ gui.State = &guiState{
@ -266,6 +267,7 @@ func (gui *Gui) resetState() {
SideView: nil, SideView: nil,
Ptmx: nil, Ptmx: nil,
FilterPath: prevFilterPath, FilterPath: prevFilterPath,
Diff: prevDiff,
} }
} }

View file

@ -978,9 +978,16 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
ViewName: "", ViewName: "",
Key: gui.getKey("universal.filteringMenu"), Key: gui.getKey("universal.filteringMenu"),
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleCreateScopingMenuPanel, Handler: gui.handleCreateFilteringMenuPanel,
Description: gui.Tr.SLocalize("openScopingMenu"), Description: gui.Tr.SLocalize("openScopingMenu"),
}, },
{
ViewName: "",
Key: gui.getKey("universal.diffingMenu"),
Modifier: gocui.ModNone,
Handler: gui.handleCreateDiffingMenuPanel,
Description: gui.Tr.SLocalize("openDiffingMenu"),
},
{ {
ViewName: "secondary", ViewName: "secondary",
Key: gocui.MouseWheelUp, Key: gocui.MouseWheelUp,

View file

@ -150,7 +150,9 @@ func (gui *Gui) layout(g *gocui.Gui) error {
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate")) donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate"))
information = donate + " " + information information = donate + " " + information
} }
if gui.inFilterMode() { if gui.inDiffMode() {
information = utils.ColoredString(fmt.Sprintf("%s %s %s", gui.Tr.SLocalize("showingGitDiff"), gui.diffStr(), utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)), color.FgMagenta, color.Bold)
} else if gui.inFilterMode() {
information = utils.ColoredString(fmt.Sprintf("%s '%s' %s", gui.Tr.SLocalize("filteringBy"), gui.State.FilterPath, utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)), color.FgRed, color.Bold) information = utils.ColoredString(fmt.Sprintf("%s '%s' %s", gui.Tr.SLocalize("filteringBy"), gui.State.FilterPath, utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)), color.FgRed, color.Bold)
} else if len(gui.State.CherryPickedCommits) > 0 { } else if len(gui.State.CherryPickedCommits) > 0 {
information = utils.ColoredString(fmt.Sprintf("%d commits copied", len(gui.State.CherryPickedCommits)), color.FgCyan) information = utils.ColoredString(fmt.Sprintf("%d commits copied", len(gui.State.CherryPickedCommits)), color.FgCyan)

View file

@ -104,7 +104,7 @@ func (gui *Gui) isIndexToDelete(i int, conflict commands.Conflict, pick string)
} }
func (gui *Gui) resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick string) error { func (gui *Gui) resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick string) error {
gitFile, err := gui.getSelectedFile(g) gitFile, err := gui.getSelectedFile()
if err != nil { if err != nil {
return err return err
} }
@ -130,7 +130,7 @@ func (gui *Gui) resolveConflict(g *gocui.Gui, conflict commands.Conflict, pick s
} }
func (gui *Gui) pushFileSnapshot(g *gocui.Gui) error { func (gui *Gui) pushFileSnapshot(g *gocui.Gui) error {
gitFile, err := gui.getSelectedFile(g) gitFile, err := gui.getSelectedFile()
if err != nil { if err != nil {
return err return err
} }
@ -147,7 +147,7 @@ func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
prevContent := gui.State.Panels.Merging.EditHistory.Pop().(string) prevContent := gui.State.Panels.Merging.EditHistory.Pop().(string)
gitFile, err := gui.getSelectedFile(g) gitFile, err := gui.getSelectedFile()
if err != nil { if err != nil {
return err return err
} }

View file

@ -16,7 +16,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
gui.getSecondaryView().Title = "Custom Patch" gui.getSecondaryView().Title = "Custom Patch"
// get diff from commit file that's currently selected // get diff from commit file that's currently selected
commitFile := gui.getSelectedCommitFile(gui.g) commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil
@ -56,7 +56,7 @@ func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error
} }
// add range of lines to those set for the file // add range of lines to those set for the file
commitFile := gui.getSelectedCommitFile(gui.g) commitFile := gui.getSelectedCommitFile()
if commitFile == nil { if commitFile == nil {
gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles"))
return nil return nil

View file

@ -30,7 +30,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu(g *gocui.Gui, v *gocui.View) error
}, },
} }
selectedCommit := gui.getSelectedCommit(gui.g) selectedCommit := gui.getSelectedCommit()
if selectedCommit != nil && gui.GitCommand.PatchManager.CommitSha != selectedCommit.Sha { if selectedCommit != nil && gui.GitCommand.PatchManager.CommitSha != selectedCommit.Sha {
// adding this option to index 1 // adding this option to index 1
menuItems = append( menuItems = append(

View file

@ -10,23 +10,29 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bool) [][]string { func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bool, diffName string) [][]string {
lines := make([][]string, len(branches)) lines := make([][]string, len(branches))
for i := range branches { for i := range branches {
lines[i] = getBranchDisplayStrings(branches[i], fullDescription) diffed := branches[i].Name == diffName
lines[i] = getBranchDisplayStrings(branches[i], fullDescription, diffed)
} }
return lines return lines
} }
// getBranchDisplayStrings returns the display string of branch // getBranchDisplayStrings returns the display string of branch
func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string { func getBranchDisplayStrings(b *commands.Branch, fullDescription bool, diffed bool) []string {
displayName := b.Name displayName := b.Name
if b.DisplayName != "" { if b.DisplayName != "" {
displayName = b.DisplayName displayName = b.DisplayName
} }
coloredName := utils.ColoredString(displayName, GetBranchColor(b.Name))
nameColorAttr := GetBranchColor(b.Name)
if diffed {
nameColorAttr = theme.DiffTerminalColor
}
coloredName := utils.ColoredString(displayName, nameColorAttr)
if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" { if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" {
trackColor := color.FgYellow trackColor := color.FgYellow
if b.Pushables == "0" && b.Pullables == "0" { if b.Pushables == "0" && b.Pullables == "0" {

View file

@ -6,21 +6,23 @@ import (
"github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/theme"
) )
func GetCommitFileListDisplayStrings(branches []*commands.CommitFile) [][]string { func GetCommitFileListDisplayStrings(commitFiles []*commands.CommitFile, diffName string) [][]string {
lines := make([][]string, len(branches)) lines := make([][]string, len(commitFiles))
for i := range branches { for i := range commitFiles {
lines[i] = getCommitFileDisplayStrings(branches[i]) diffed := commitFiles[i].Name == diffName
lines[i] = getCommitFileDisplayStrings(commitFiles[i], diffed)
} }
return lines return lines
} }
// getCommitFileDisplayStrings returns the display string of branch // getCommitFileDisplayStrings returns the display string of branch
func getCommitFileDisplayStrings(f *commands.CommitFile) []string { func getCommitFileDisplayStrings(f *commands.CommitFile, diffed bool) []string {
yellow := color.New(color.FgYellow) yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen) green := color.New(color.FgGreen)
defaultColor := color.New(theme.DefaultTextColor) defaultColor := color.New(theme.DefaultTextColor)
diffTerminalColor := color.New(theme.DiffTerminalColor)
var colour *color.Color var colour *color.Color
switch f.Status { switch f.Status {
@ -31,5 +33,8 @@ func getCommitFileDisplayStrings(f *commands.CommitFile) []string {
case commands.PART: case commands.PART:
colour = yellow colour = yellow
} }
if diffed {
colour = diffTerminalColor
}
return []string{colour.Sprint(f.DisplayString)} return []string{colour.Sprint(f.DisplayString)}
} }

View file

@ -9,10 +9,10 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool) [][]string { func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string) [][]string {
lines := make([][]string, len(commits)) lines := make([][]string, len(commits))
var displayFunc func(*commands.Commit, map[string]bool) []string var displayFunc func(*commands.Commit, map[string]bool, bool) []string
if fullDescription { if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForCommit displayFunc = getFullDescriptionDisplayStringsForCommit
} else { } else {
@ -20,20 +20,21 @@ func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription boo
} }
for i := range commits { for i := range commits {
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap) diffed := commits[i].Sha == diffName
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed)
} }
return lines return lines
} }
func getFullDescriptionDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool) []string { func getFullDescriptionDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
red := color.New(color.FgRed) red := color.New(color.FgRed)
yellow := color.New(color.FgYellow) yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen) green := color.New(color.FgGreen)
blue := color.New(color.FgBlue) blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan) cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor) defaultColor := color.New(theme.DefaultTextColor)
// magenta := color.New(color.FgMagenta) diffedColor := color.New(theme.DiffTerminalColor)
// for some reason, setting the background to blue pads out the other commits // for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature, // horizontally. For the sake of accessibility I'm considering this a feature,
@ -56,16 +57,12 @@ func getFullDescriptionDisplayStringsForCommit(c *commands.Commit, cherryPickedC
shaColor = defaultColor shaColor = defaultColor
} }
if cherryPickedCommitShaMap[c.Sha] { if diffed {
shaColor = diffedColor
} else if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied shaColor = copied
} }
// for _, entry := range diffEntries {
// if c.Sha == entry.Sha {
// shaColor = magenta
// }
// }
tagString := "" tagString := ""
secondColumnString := blue.Sprint(utils.UnixToDate(c.UnixTimestamp)) secondColumnString := blue.Sprint(utils.UnixToDate(c.UnixTimestamp))
if c.Action != "" { if c.Action != "" {
@ -80,14 +77,14 @@ func getFullDescriptionDisplayStringsForCommit(c *commands.Commit, cherryPickedC
return []string{shaColor.Sprint(c.ShortSha()), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)} return []string{shaColor.Sprint(c.ShortSha()), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
} }
func getDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool) []string { func getDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
red := color.New(color.FgRed) red := color.New(color.FgRed)
yellow := color.New(color.FgYellow) yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen) green := color.New(color.FgGreen)
blue := color.New(color.FgBlue) blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan) cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor) defaultColor := color.New(theme.DefaultTextColor)
// magenta := color.New(color.FgMagenta) diffedColor := color.New(theme.DiffTerminalColor)
// for some reason, setting the background to blue pads out the other commits // for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature, // horizontally. For the sake of accessibility I'm considering this a feature,
@ -110,16 +107,12 @@ func getDisplayStringsForCommit(c *commands.Commit, cherryPickedCommitShaMap map
shaColor = defaultColor shaColor = defaultColor
} }
if cherryPickedCommitShaMap[c.Sha] { if diffed {
shaColor = diffedColor
} else if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied shaColor = copied
} }
// for _, entry := range diffEntries {
// if c.Sha == entry.Sha {
// shaColor = magenta
// }
// }
actionString := "" actionString := ""
tagString := "" tagString := ""
if c.Action != "" { if c.Action != "" {

View file

@ -3,34 +3,42 @@ package presentation
import ( import (
"github.com/fatih/color" "github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
) )
func GetFileListDisplayStrings(files []*commands.File) [][]string { func GetFileListDisplayStrings(files []*commands.File, diffName string) [][]string {
lines := make([][]string, len(files)) lines := make([][]string, len(files))
for i := range files { for i := range files {
lines[i] = getFileDisplayStrings(files[i]) diffed := files[i].Name == diffName
lines[i] = getFileDisplayStrings(files[i], diffed)
} }
return lines return lines
} }
// getFileDisplayStrings returns the display string of branch // getFileDisplayStrings returns the display string of branch
func getFileDisplayStrings(f *commands.File) []string { func getFileDisplayStrings(f *commands.File, diffed bool) []string {
// potentially inefficient to be instantiating these color // potentially inefficient to be instantiating these color
// objects with each render // objects with each render
red := color.New(color.FgRed) red := color.New(color.FgRed)
green := color.New(color.FgGreen) green := color.New(color.FgGreen)
diffColor := color.New(theme.DiffTerminalColor)
if !f.Tracked && !f.HasStagedChanges { if !f.Tracked && !f.HasStagedChanges {
return []string{red.Sprint(f.DisplayString)} return []string{red.Sprint(f.DisplayString)}
} }
output := green.Sprint(f.DisplayString[0:1]) output := green.Sprint(f.DisplayString[0:1])
output += red.Sprint(f.DisplayString[1:3]) output += red.Sprint(f.DisplayString[1:3])
if f.HasUnstagedChanges {
output += red.Sprint(f.Name) var restColor *color.Color
if diffed {
restColor = diffColor
} else if f.HasUnstagedChanges {
restColor = red
} else { } else {
output += green.Sprint(f.Name) restColor = green
} }
output += restColor.Sprint(f.Name)
return []string{output} return []string{output}
} }

View file

@ -7,10 +7,10 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetReflogCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool) [][]string { func GetReflogCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool, diffName string) [][]string {
lines := make([][]string, len(commits)) lines := make([][]string, len(commits))
var displayFunc func(*commands.Commit) []string var displayFunc func(*commands.Commit, bool) []string
if fullDescription { if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForReflogCommit displayFunc = getFullDescriptionDisplayStringsForReflogCommit
} else { } else {
@ -18,23 +18,27 @@ func GetReflogCommitListDisplayStrings(commits []*commands.Commit, fullDescripti
} }
for i := range commits { for i := range commits {
lines[i] = displayFunc(commits[i]) diffed := commits[i].Sha == diffName
lines[i] = displayFunc(commits[i], diffed)
} }
return lines return lines
} }
func getFullDescriptionDisplayStringsForReflogCommit(c *commands.Commit) []string { func getFullDescriptionDisplayStringsForReflogCommit(c *commands.Commit, diffed bool) []string {
defaultColor := color.New(theme.DefaultTextColor) colorAttr := theme.DefaultTextColor
if diffed {
colorAttr = theme.DiffTerminalColor
}
return []string{ return []string{
utils.ColoredString(c.ShortSha(), color.FgBlue), utils.ColoredString(c.ShortSha(), color.FgBlue),
utils.ColoredString(utils.UnixToDate(c.UnixTimestamp), color.FgMagenta), utils.ColoredString(utils.UnixToDate(c.UnixTimestamp), color.FgMagenta),
defaultColor.Sprint(c.Name), utils.ColoredString(c.Name, colorAttr),
} }
} }
func getDisplayStringsForReflogCommit(c *commands.Commit) []string { func getDisplayStringsForReflogCommit(c *commands.Commit, diffed bool) []string {
defaultColor := color.New(theme.DefaultTextColor) defaultColor := color.New(theme.DefaultTextColor)
return []string{utils.ColoredString(c.ShortSha(), color.FgBlue), defaultColor.Sprint(c.Name)} return []string{utils.ColoredString(c.ShortSha(), color.FgBlue), defaultColor.Sprint(c.Name)}

View file

@ -2,22 +2,29 @@ package presentation
import ( import (
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetRemoteBranchListDisplayStrings(branches []*commands.RemoteBranch) [][]string { func GetRemoteBranchListDisplayStrings(branches []*commands.RemoteBranch, diffName string) [][]string {
lines := make([][]string, len(branches)) lines := make([][]string, len(branches))
for i := range branches { for i := range branches {
lines[i] = getRemoteBranchDisplayStrings(branches[i]) diffed := branches[i].FullName() == diffName
lines[i] = getRemoteBranchDisplayStrings(branches[i], diffed)
} }
return lines return lines
} }
// getRemoteBranchDisplayStrings returns the display string of branch // getRemoteBranchDisplayStrings returns the display string of branch
func getRemoteBranchDisplayStrings(b *commands.RemoteBranch) []string { func getRemoteBranchDisplayStrings(b *commands.RemoteBranch, diffed bool) []string {
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name)) nameColorAttr := GetBranchColor(b.Name)
if diffed {
nameColorAttr = theme.DiffTerminalColor
}
displayName := utils.ColoredString(b.Name, nameColorAttr)
return []string{displayName} return []string{displayName}
} }

View file

@ -5,22 +5,29 @@ import (
"github.com/fatih/color" "github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetRemoteListDisplayStrings(remotes []*commands.Remote) [][]string { func GetRemoteListDisplayStrings(remotes []*commands.Remote, diffName string) [][]string {
lines := make([][]string, len(remotes)) lines := make([][]string, len(remotes))
for i := range remotes { for i := range remotes {
lines[i] = getRemoteDisplayStrings(remotes[i]) diffed := remotes[i].Name == diffName
lines[i] = getRemoteDisplayStrings(remotes[i], diffed)
} }
return lines return lines
} }
// getRemoteDisplayStrings returns the display string of branch // getRemoteDisplayStrings returns the display string of branch
func getRemoteDisplayStrings(r *commands.Remote) []string { func getRemoteDisplayStrings(r *commands.Remote, diffed bool) []string {
branchCount := len(r.Branches) branchCount := len(r.Branches)
return []string{r.Name, utils.ColoredString(fmt.Sprintf("%d branches", branchCount), color.FgBlue)} nameColorAttr := theme.DefaultTextColor
if diffed {
nameColorAttr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(r.Name, nameColorAttr), utils.ColoredString(fmt.Sprintf("%d branches", branchCount), color.FgBlue)}
} }

View file

@ -2,19 +2,26 @@ package presentation
import ( import (
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetStashEntryListDisplayStrings(stashEntries []*commands.StashEntry) [][]string { func GetStashEntryListDisplayStrings(stashEntries []*commands.StashEntry, diffName string) [][]string {
lines := make([][]string, len(stashEntries)) lines := make([][]string, len(stashEntries))
for i := range stashEntries { for i := range stashEntries {
lines[i] = getStashEntryDisplayStrings(stashEntries[i]) diffed := stashEntries[i].RefName() == diffName
lines[i] = getStashEntryDisplayStrings(stashEntries[i], diffed)
} }
return lines return lines
} }
// getStashEntryDisplayStrings returns the display string of branch // getStashEntryDisplayStrings returns the display string of branch
func getStashEntryDisplayStrings(s *commands.StashEntry) []string { func getStashEntryDisplayStrings(s *commands.StashEntry, diffed bool) []string {
return []string{s.Name} attr := theme.DefaultTextColor
if diffed {
attr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(s.Name, attr)}
} }

View file

@ -2,19 +2,26 @@ package presentation
import ( import (
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func GetTagListDisplayStrings(tags []*commands.Tag) [][]string { func GetTagListDisplayStrings(tags []*commands.Tag, diffName string) [][]string {
lines := make([][]string, len(tags)) lines := make([][]string, len(tags))
for i := range tags { for i := range tags {
lines[i] = getTagDisplayStrings(tags[i]) diffed := tags[i].Name == diffName
lines[i] = getTagDisplayStrings(tags[i], diffed)
} }
return lines return lines
} }
// getTagDisplayStrings returns the display string of branch // getTagDisplayStrings returns the display string of branch
func getTagDisplayStrings(t *commands.Tag) []string { func getTagDisplayStrings(t *commands.Tag, diffed bool) []string {
return []string{t.Name} attr := theme.DefaultTextColor
if diffed {
attr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(t.Name, attr)}
} }

View file

@ -38,6 +38,12 @@ func (gui *Gui) quit(v *gocui.View) error {
if gui.State.Updating { if gui.State.Updating {
return gui.createUpdateQuitConfirmation(gui.g, v) return gui.createUpdateQuitConfirmation(gui.g, v)
} }
if gui.inDiffMode() {
return gui.exitDiffMode()
}
if gui.inFilterMode() {
return gui.exitFilterMode()
}
if gui.Config.GetUserConfig().GetBool("confirmOnQuit") { if gui.Config.GetUserConfig().GetBool("confirmOnQuit") {
return gui.createConfirmationPanel(gui.g, v, true, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error { return gui.createConfirmationPanel(gui.g, v, true, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit return gocui.ErrQuit

View file

@ -37,6 +37,10 @@ func (gui *Gui) handleReflogCommitSelect(g *gocui.Gui, v *gocui.View) error {
} }
v.FocusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine) v.FocusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath), gui.GitCommand.ShowCmdStr(commit.Sha, gui.State.FilterPath),
) )
@ -100,7 +104,7 @@ func (gui *Gui) renderReflogCommitsWithSelection() error {
commitsView := gui.getCommitsView() commitsView := gui.getCommitsView()
gui.refreshSelectedLine(&gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.FilteredReflogCommits)) gui.refreshSelectedLine(&gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.FilteredReflogCommits))
displayStrings := presentation.GetReflogCommitListDisplayStrings(gui.State.FilteredReflogCommits, gui.State.ScreenMode != SCREEN_NORMAL) displayStrings := presentation.GetReflogCommitListDisplayStrings(gui.State.FilteredReflogCommits, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Diff.Ref)
gui.renderDisplayStrings(commitsView, displayStrings) gui.renderDisplayStrings(commitsView, displayStrings)
if gui.g.CurrentView() == commitsView && commitsView.Context == "reflog-commits" { if gui.g.CurrentView() == commitsView && commitsView.Context == "reflog-commits" {
if err := gui.handleReflogCommitSelect(gui.g, commitsView); err != nil { if err := gui.handleReflogCommitSelect(gui.g, commitsView); err != nil {

View file

@ -32,7 +32,6 @@ func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Title = "Remote Branch" gui.getMainView().Title = "Remote Branch"
remote := gui.getSelectedRemote()
remoteBranch := gui.getSelectedRemoteBranch() remoteBranch := gui.getSelectedRemoteBranch()
if remoteBranch == nil { if remoteBranch == nil {
return gui.newStringTask("main", "No branches for this remote") return gui.newStringTask("main", "No branches for this remote")
@ -40,10 +39,12 @@ func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error {
v.FocusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine) v.FocusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine)
branchName := fmt.Sprintf("%s/%s", remote.Name, remoteBranch.Name) if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(branchName), gui.GitCommand.GetBranchGraphCmdStr(remoteBranch.FullName()),
) )
if err := gui.newCmdTask("main", cmd); err != nil { if err := gui.newCmdTask("main", cmd); err != nil {
gui.Log.Error(err) gui.Log.Error(err)
@ -60,7 +61,7 @@ func (gui *Gui) renderRemoteBranchesWithSelection() error {
branchesView := gui.getBranchesView() branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.RemoteBranches.SelectedLine, len(gui.State.RemoteBranches)) gui.refreshSelectedLine(&gui.State.Panels.RemoteBranches.SelectedLine, len(gui.State.RemoteBranches))
displayStrings := presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches) displayStrings := presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches, gui.State.Diff.Ref)
gui.renderDisplayStrings(branchesView, displayStrings) gui.renderDisplayStrings(branchesView, displayStrings)
if gui.g.CurrentView() == branchesView && branchesView.Context == "remote-branches" { if gui.g.CurrentView() == branchesView && branchesView.Context == "remote-branches" {
if err := gui.handleRemoteBranchSelect(gui.g, branchesView); err != nil { if err := gui.handleRemoteBranchSelect(gui.g, branchesView); err != nil {
@ -76,7 +77,7 @@ func (gui *Gui) handleCheckoutRemoteBranch(g *gocui.Gui, v *gocui.View) error {
if remoteBranch == nil { if remoteBranch == nil {
return nil return nil
} }
if err := gui.handleCheckoutRef(remoteBranch.RemoteName+"/"+remoteBranch.Name, handleCheckoutRefOptions{}); err != nil { if err := gui.handleCheckoutRef(remoteBranch.FullName(), handleCheckoutRefOptions{}); err != nil {
return err return err
} }
return gui.switchBranchesPanelContext("local-branches") return gui.switchBranchesPanelContext("local-branches")
@ -117,7 +118,7 @@ func (gui *Gui) handleSetBranchUpstream(g *gocui.Gui, v *gocui.View) error {
"SetUpstreamMessage", "SetUpstreamMessage",
Teml{ Teml{
"checkedOut": checkedOutBranch.Name, "checkedOut": checkedOutBranch.Name,
"selected": selectedBranch.RemoteName + "/" + selectedBranch.Name, "selected": selectedBranch.FullName(),
}, },
) )

View file

@ -41,6 +41,10 @@ func (gui *Gui) handleRemoteSelect(g *gocui.Gui, v *gocui.View) error {
} }
v.FocusPoint(0, gui.State.Panels.Remotes.SelectedLine) v.FocusPoint(0, gui.State.Panels.Remotes.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
return gui.newStringTask("main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n"))) return gui.newStringTask("main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
} }
@ -80,7 +84,7 @@ func (gui *Gui) renderRemotesWithSelection() error {
gui.refreshSelectedLine(&gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes)) gui.refreshSelectedLine(&gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes))
displayStrings := presentation.GetRemoteListDisplayStrings(gui.State.Remotes) displayStrings := presentation.GetRemoteListDisplayStrings(gui.State.Remotes, gui.State.Diff.Ref)
gui.renderDisplayStrings(branchesView, displayStrings) gui.renderDisplayStrings(branchesView, displayStrings)
if gui.g.CurrentView() == branchesView && branchesView.Context == "remotes" { if gui.g.CurrentView() == branchesView && branchesView.Context == "remotes" {

View file

@ -19,7 +19,7 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
return err return err
} }
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoFiles { if err != gui.Errors.ErrNoFiles {
return err return err
@ -125,7 +125,7 @@ func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) applySelection(reverse bool) error { func (gui *Gui) applySelection(reverse bool) error {
state := gui.State.Panels.LineByLine state := gui.State.Panels.LineByLine
file, err := gui.getSelectedFile(gui.g) file, err := gui.getSelectedFile()
if err != nil { if err != nil {
return err return err
} }

View file

@ -8,7 +8,7 @@ import (
// list panel functions // list panel functions
func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry { func (gui *Gui) getSelectedStashEntry() *commands.StashEntry {
selectedLine := gui.State.Panels.Stash.SelectedLine selectedLine := gui.State.Panels.Stash.SelectedLine
if selectedLine == -1 { if selectedLine == -1 {
return nil return nil
@ -30,12 +30,16 @@ func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Title = "Stash" gui.getMainView().Title = "Stash"
stashEntry := gui.getSelectedStashEntry(v) stashEntry := gui.getSelectedStashEntry()
if stashEntry == nil { if stashEntry == nil {
return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries")) return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries"))
} }
v.FocusPoint(0, gui.State.Panels.Stash.SelectedLine) v.FocusPoint(0, gui.State.Panels.Stash.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index), gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
) )
@ -53,7 +57,7 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
stashView := gui.getStashView() stashView := gui.getStashView()
displayStrings := presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries) displayStrings := presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries, gui.State.Diff.Ref)
gui.renderDisplayStrings(stashView, displayStrings) gui.renderDisplayStrings(stashView, displayStrings)
return gui.resetOrigin(stashView) return gui.resetOrigin(stashView)
@ -78,7 +82,7 @@ func (gui *Gui) handleStashDrop(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error { func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error {
stashEntry := gui.getSelectedStashEntry(v) stashEntry := gui.getSelectedStashEntry()
if stashEntry == nil { if stashEntry == nil {
errorMessage := gui.Tr.TemplateLocalize( errorMessage := gui.Tr.TemplateLocalize(
"NoStashTo", "NoStashTo",

View file

@ -97,6 +97,10 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Title = "" gui.getMainView().Title = ""
if gui.inDiffMode() {
return gui.renderDiff()
}
magenta := color.New(color.FgMagenta) magenta := color.New(color.FgMagenta)
dashboardString := strings.Join( dashboardString := strings.Join(

View file

@ -36,6 +36,10 @@ func (gui *Gui) handleTagSelect(g *gocui.Gui, v *gocui.View) error {
} }
v.FocusPoint(0, gui.State.Panels.Tags.SelectedLine) v.FocusPoint(0, gui.State.Panels.Tags.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
cmd := gui.OSCommand.ExecutableFromString( cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.GetBranchGraphCmdStr(tag.Name), gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
) )
@ -65,7 +69,7 @@ func (gui *Gui) renderTagsWithSelection() error {
branchesView := gui.getBranchesView() branchesView := gui.getBranchesView()
gui.refreshSelectedLine(&gui.State.Panels.Tags.SelectedLine, len(gui.State.Tags)) gui.refreshSelectedLine(&gui.State.Panels.Tags.SelectedLine, len(gui.State.Tags))
displayStrings := presentation.GetTagListDisplayStrings(gui.State.Tags) displayStrings := presentation.GetTagListDisplayStrings(gui.State.Tags, gui.State.Diff.Ref)
gui.renderDisplayStrings(branchesView, displayStrings) gui.renderDisplayStrings(branchesView, displayStrings)
if gui.g.CurrentView() == branchesView && branchesView.Context == "tags" { if gui.g.CurrentView() == branchesView && branchesView.Context == "tags" {
if err := gui.handleTagSelect(gui.g, branchesView); err != nil { if err := gui.handleTagSelect(gui.g, branchesView); err != nil {

View file

@ -1107,6 +1107,36 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{ }, &i18n.Message{
ID: "MustExitFilterModePrompt", ID: "MustExitFilterModePrompt",
Other: "Command not available in filtered mode. Exit filtered mode?", Other: "Command not available in filtered mode. Exit filtered mode?",
}, &i18n.Message{
ID: "diffFrom",
Other: "diff from",
}, &i18n.Message{
ID: "diffTo",
Other: "diff to",
}, &i18n.Message{
ID: "enterRefToDiffFrom",
Other: "enter ref to diff from",
}, &i18n.Message{
ID: "enterRefToDiffTo",
Other: "enter ref to diff to",
}, &i18n.Message{
ID: "enteRefName",
Other: "enter ref:",
}, &i18n.Message{
ID: "exitDiffMode",
Other: "exit diff mode",
}, &i18n.Message{
ID: "DiffingMenuTitle",
Other: "Diffing",
}, &i18n.Message{
ID: "swapDiff",
Other: "reverse diff direction",
}, &i18n.Message{
ID: "openDiffingMenu",
Other: "open diff menu",
}, &i18n.Message{
ID: "showingGitDiff",
Other: "showing git diff:",
}, },
) )
} }

View file

@ -27,6 +27,8 @@ var (
OptionsFgColor color.Attribute OptionsFgColor color.Attribute
OptionsColor gocui.Attribute OptionsColor gocui.Attribute
DiffTerminalColor = color.FgMagenta
) )
// UpdateTheme updates all theme variables // UpdateTheme updates all theme variables