Extend the raw-diff fallback to the commit/stash/patch-building panels

Wire the remaining diff panels — local commits, sub-commits, commit files,
stash, and reflog — into the focused main view's raw-diff fallback, the same way
the files panel already is: each computes DiffMainViewShouldRenderRaw and renders
its diff raw (no pager) when focused under a pager whose output we can't resolve,
so its selection stays toggleable into a custom patch (or, for reflog, navigable
and copyable).

The commit-diff panels build their diff through the diff helper, which can't
reach the staging helper, so the panel computes renderRaw and passes it in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-06-20 08:35:30 +02:00
parent 10f41bed50
commit ae1ab5ca1e
8 changed files with 86 additions and 13 deletions

View file

@ -176,8 +176,9 @@ func (self *CommitFilesController) GetOnRenderToMain() func() {
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
paths := self.pathsForDiff(node)
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, false, false)
task := types.NewRunPtyTask(cmdObj.GetCmd())
renderRaw := self.c.Helpers().Staging.DiffMainViewShouldRenderRaw()
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, false, renderRaw)
task := types.NewMainViewDiffTask(renderRaw, cmdObj.GetCmd())
// Keep the inclusion gutter in step with the content as this diff (re-)renders.
// It's a no-op unless the main view is focused and a patch is being built (see

View file

@ -52,7 +52,11 @@ func (self *DiffHelper) DiffArgs() []string {
// 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 {
// GetUpdateTaskForRenderingCommitsDiff builds the task for showing a commit's (or a
// commit range's) diff in the main view. renderRaw bypasses the pager for the focused
// main view's raw-diff fallback (see StagingHelper.DiffMainViewShouldRenderRaw); the
// calling panel computes it, since this helper can't reach the staging helper.
func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Commit, refRange *types.RefRange, renderRaw bool) types.UpdateTask {
if refRange != nil {
from, to := refRange.From, refRange.To
args := []string{from.ParentRefName(), to.RefName(), "--stat", "-p"}
@ -72,13 +76,13 @@ func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Comm
args = append(args, filterPath)
}
}
cmdObj := self.c.Git().Diff.DiffCmdObj(args, false)
cmdObj := self.c.Git().Diff.DiffCmdObj(args, renderRaw)
prefix := style.FgYellow.Sprintf("%s %s-%s\n\n", self.c.Tr.ShowingDiffForRange, from.ShortRefName(), to.ShortRefName())
return types.NewRunPtyTaskWithPrefix(cmdObj.GetCmd(), prefix)
return types.NewMainViewDiffTaskWithPrefix(renderRaw, cmdObj.GetCmd(), prefix)
}
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.FilterPathsForCommit(commit), false)
return types.NewRunPtyTask(cmdObj.GetCmd())
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.FilterPathsForCommit(commit), renderRaw)
return types.NewMainViewDiffTask(renderRaw, cmdObj.GetCmd())
}
func (self *DiffHelper) FilterPathsForCommit(commit *models.Commit) []string {

View file

@ -703,7 +703,8 @@ func (self *LocalCommitsController) GetOnRenderToMain() func() {
self.c.Tr.ExecCommandHere + "\n\n" + commit.Name)
} else {
refRange := self.context().GetSelectedRefRangeForDiffFiles()
task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange)
renderRaw := self.c.Helpers().Staging.DiffMainViewShouldRenderRaw()
task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange, renderRaw)
}
// Keep the inclusion gutter in step with the content as this diff

View file

@ -45,9 +45,10 @@ func (self *ReflogCommitsController) GetOnRenderToMain() func() {
if commit == nil {
task = types.NewRenderStringTask("No reflog history")
} else {
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Helpers().Diff.FilterPathsForCommit(commit), false)
renderRaw := self.c.Helpers().Staging.DiffMainViewShouldRenderRaw()
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Helpers().Diff.FilterPathsForCommit(commit), renderRaw)
task = types.NewRunPtyTask(cmdObj.GetCmd())
task = types.NewMainViewDiffTask(renderRaw, cmdObj.GetCmd())
}
self.c.RenderToMainViews(types.RefreshMainOpts{

View file

@ -93,8 +93,10 @@ func (self *StashController) GetOnRenderToMain() func() {
task = types.NewRenderStringTask(self.c.Tr.NoStashEntries)
} else {
prefix := style.FgYellow.Sprintf("%s\n\n", stashEntry.Description())
task = types.NewRunPtyTaskWithPrefix(
self.c.Git().Stash.ShowStashEntryCmdObj(stashEntry.Index, false).GetCmd(),
renderRaw := self.c.Helpers().Staging.DiffMainViewShouldRenderRaw()
task = types.NewMainViewDiffTaskWithPrefix(
renderRaw,
self.c.Git().Stash.ShowStashEntryCmdObj(stashEntry.Index, renderRaw).GetCmd(),
prefix,
)
}

View file

@ -46,7 +46,8 @@ func (self *SubCommitsController) GetOnRenderToMain() func() {
task = types.NewRenderStringTask("No commits")
} else {
refRange := self.context().GetSelectedRefRangeForDiffFiles()
task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange)
renderRaw := self.c.Helpers().Staging.DiffMainViewShouldRenderRaw()
task = self.c.Helpers().Diff.GetUpdateTaskForRenderingCommitsDiff(commit, refRange, renderRaw)
}
// Keep the inclusion gutter in step with the content as this diff

View file

@ -0,0 +1,62 @@
package patch_building
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var BuildFromMainViewWithUnsupportedPager = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Build a custom patch from a commit's focused main view under a pager that restructures the diff without emitting metadata; it falls back to the raw diff so the selection is still toggleable",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false
// `cat -n` numbers every line, which the buffer parser can't resolve, and cat
// emits no metadata, so the focused main view must fall back to the raw diff.
cfg.GetUserConfig().Git.DiffRenderers = []config.DiffRendererConfig{
{Command: "cat -n"},
}
},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "one\ntwo\nthree\nfour\nfive\n")
shell.Commit("first commit")
shell.UpdateFileAndAdd("file1", "one\ntwo\nTHREE\nfour\nfive\n")
shell.Commit("update")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("update").IsSelected(),
Contains("first commit"),
)
// While browsing, the main view shows the pager's output (numbered lines).
t.Views().Main().Content(Contains(" +THREE"))
// Focusing the main view falls back to the raw diff, so the change line is
// resolved and the selection is toggleable into a custom patch.
t.Views().Commits().Press(keys.Universal.FocusMainView)
t.Views().Main().
IsFocused().
SelectedLines(
Contains("-three"),
).
Press(keys.Main.ToggleSelectHunk).
SelectedLines(
Contains("-three"),
Contains("+THREE"),
).
PressPrimaryAction()
t.Views().Information().Content(Contains("Building patch"))
t.Views().Secondary().
ContainsLines(
Contains("-three"),
Contains("+THREE"),
)
},
})

View file

@ -362,6 +362,7 @@ var tests = []*components.IntegrationTest{
patch_building.ApplyWithModifiedFileConflict,
patch_building.ApplyWithModifiedFileNoConflict,
patch_building.BuildFromMainView,
patch_building.BuildFromMainViewWithUnsupportedPager,
patch_building.BuildFromWholeCommitMainView,
patch_building.BuildMultiFileFromWholeCommitMainView,
patch_building.CopyRenamedFileDiff,