1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-21 12:16:54 +02:00
lazygit/pkg/gui/controllers/helpers/diff_helper.go
Stefan Haller 442592a149 Show diff for range selection in commits and sub-commits panel
In other views that show lists of commits (reflog and stash) it doesn't make
sense to show a range diff of selected entries because they don't form a linear
sequence, so we keep the previous behavior of showing the diff for the free end
of the selection range in those view.

The same applies to the commits view if the selection range includes rebasing
todos; these can have an arbitrary order, and a range diff doesn't make sense
for those.
2024-08-28 19:35:23 +02:00

174 lines
4.8 KiB
Go

package helpers
import (
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo"
)
type DiffHelper struct {
c *HelperCommon
}
func NewDiffHelper(c *HelperCommon) *DiffHelper {
return &DiffHelper{
c: c,
}
}
func (self *DiffHelper) DiffArgs() []string {
output := []string{"--stat", "-p", self.c.Modes().Diffing.Ref}
right := self.currentDiffTerminal()
if right != "" {
output = append(output, right)
}
if self.c.Modes().Diffing.Reverse {
output = append(output, "-R")
}
if self.c.GetAppState().IgnoreWhitespaceInDiffView {
output = append(output, "--ignore-all-space")
}
output = append(output, "--")
file := self.currentlySelectedFilename()
if file != "" {
output = append(output, file)
} else if self.c.Modes().Filtering.Active() {
output = append(output, self.c.Modes().Filtering.GetPath())
}
return output
}
// Returns an update task that can be passed to RenderToMainViews to render a
// diff for the selected commit(s). We need to pass both the selected commit
// and the refRange for a range selection. If the refRange is nil (meaning that
// either there's no range, or it can't be diffed for some reason), then we want
// to fall back to rendering the diff for the single commit.
func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Commit, refRange *types.RefRange) types.UpdateTask {
if refRange != nil {
from, to := refRange.From, refRange.To
args := []string{from.ParentRefName(), to.RefName(), "--stat", "-p"}
if self.c.GetAppState().IgnoreWhitespaceInDiffView {
args = append(args, "--ignore-all-space")
}
args = append(args, "--")
if path := self.c.Modes().Filtering.GetPath(); path != "" {
args = append(args, path)
}
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
task := types.NewRunPtyTask(cmdObj.GetCmd())
task.Prefix = style.FgYellow.Sprintf("%s %s-%s\n\n", self.c.Tr.ShowingDiffForRange, from.ShortRefName(), to.ShortRefName())
return task
}
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash, self.c.Modes().Filtering.GetPath())
return types.NewRunPtyTask(cmdObj.GetCmd())
}
func (self *DiffHelper) ExitDiffMode() error {
self.c.Modes().Diffing = diffing.New()
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}
func (self *DiffHelper) RenderDiff() error {
args := self.DiffArgs()
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
task := types.NewRunPtyTask(cmdObj.GetCmd())
task.Prefix = style.FgMagenta.Sprintf(
"%s %s\n\n",
self.c.Tr.ShowingGitDiff,
"git diff "+strings.Join(args, " "),
)
return self.c.RenderToMainViews(types.RefreshMainOpts{
Pair: self.c.MainViewPairs().Normal,
Main: &types.ViewUpdateOpts{
Title: "Diff",
SubTitle: self.IgnoringWhitespaceSubTitle(),
Task: task,
},
})
}
// 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 (self *DiffHelper) CurrentDiffTerminals() []string {
c := self.c.Context().CurrentSide()
if c.GetKey() == "" {
return nil
}
switch v := c.(type) {
case types.DiffableContext:
return v.GetDiffTerminals()
}
return nil
}
func (self *DiffHelper) currentDiffTerminal() string {
names := self.CurrentDiffTerminals()
if len(names) == 0 {
return ""
}
return names[0]
}
func (self *DiffHelper) currentlySelectedFilename() string {
currentContext := self.c.Context().Current()
switch currentContext := currentContext.(type) {
case types.IListContext:
if lo.Contains([]types.ContextKey{context.FILES_CONTEXT_KEY, context.COMMIT_FILES_CONTEXT_KEY}, currentContext.GetKey()) {
return currentContext.GetSelectedItemId()
}
}
return ""
}
func (self *DiffHelper) WithDiffModeCheck(f func() error) error {
if self.c.Modes().Diffing.Active() {
return self.RenderDiff()
}
return f()
}
func (self *DiffHelper) IgnoringWhitespaceSubTitle() string {
if self.c.GetAppState().IgnoreWhitespaceInDiffView {
return self.c.Tr.IgnoreWhitespaceDiffViewSubTitle
}
return ""
}
func (self *DiffHelper) OpenDiffToolForRef(selectedRef types.Ref) error {
to := selectedRef.RefName()
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff("")
_, err := self.c.RunSubprocess(self.c.Git().Diff.OpenDiffToolCmdObj(
git_commands.DiffToolCmdOptions{
Filepath: ".",
FromCommit: from,
ToCommit: to,
Reverse: reverse,
IsDirectory: true,
Staged: false,
}))
return err
}