Thread ignoreExternalDiff through the remaining diff-cmd builders

Prep for extending the focused main view's raw-diff fallback beyond the files
panel. ShowCmdObj, ShowFileDiffCmdObj, ShowStashEntryCmdObj and DiffCmdObj gain
the ignoreExternalDiff arg WorktreeFileDiffCmdObj already has — forcing git's own
coloured diff regardless of a configured external diff command. All callers pass
false, so behaviour is unchanged.

Also lift the pty-vs-command task choice out of the files controller into
types.NewMainViewDiffTask(WithPrefix), so the commit/stash/patch-building panels
(whose diffs are built in the diff helper, which can't reach the staging helper)
can select the same raw-fallback task.

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

View file

@ -240,10 +240,10 @@ func (self *CommitCommands) AmendHeadCmdObj() *oscommands.CmdObj {
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscommands.CmdObj {
func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string, ignoreExternalDiff bool) *oscommands.CmdObj {
cmdArgs := NewGitCmd("show").
Config("diff.noprefix=false").
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), true).
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), !ignoreExternalDiff).
Arg("--submodule").
Arg("--color=" + self.diffRendererConfigManager.GetColorArg()).
Arg("--stat").

View file

@ -341,7 +341,7 @@ func TestCommitShowCmdObj(t *testing.T) {
}
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: &config.AppState{}, runner: runner, repoPaths: &repoPaths})
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPaths).Run())
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPaths, false).Run())
runner.CheckForMissingCalls()
})
}

View file

@ -83,12 +83,14 @@ func (self *DiffCommands) probeEmitsMetadata(cmdObj *oscommands.CmdObj) bool {
}
// This is for generating diffs to be shown in the UI (e.g. rendering a range
// diff to the main view). It uses a custom diff renderer if one is configured.
func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj {
// diff to the main view). It uses a custom diff renderer if one is configured, unless
// ignoreExternalDiff is set (the focused main view's raw-diff fallback; keeps the
// colour, unlike a plain diff).
func (self *DiffCommands) DiffCmdObj(diffArgs []string, ignoreExternalDiff bool) *oscommands.CmdObj {
return self.cmd.New(
NewGitCmd("diff").
Config("diff.noprefix=false").
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), true).
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), !ignoreExternalDiff).
Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", self.diffRendererConfigManager.GetColorArg())).
Arg(diffArgs...).

View file

@ -80,10 +80,10 @@ func (self *StashCommands) Hash(index int) (string, error) {
return strings.Trim(hash, "\r\n"), err
}
func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj {
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreExternalDiff bool) *oscommands.CmdObj {
// "-u" is the same as "--include-untracked", but the latter fails in older git versions for some reason
cmdArgs := NewGitCmd("stash").Arg("show").
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), true).
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), !ignoreExternalDiff).
Arg("-p").
Arg("--stat").
Arg("-u").

View file

@ -174,7 +174,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
}
instance := buildStashCommands(commonDeps{userConfig: userConfig, appState: &config.AppState{}, repoPaths: &repoPaths})
cmdStr := instance.ShowStashEntryCmdObj(s.index).Args()
cmdStr := instance.ShowStashEntryCmdObj(s.index, false).Args()
assert.Equal(t, s.expected, cmdStr)
})
}

View file

@ -434,10 +434,10 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo
if previousPath != "" {
fileNames = append(fileNames, previousPath)
}
return self.ShowFileDiffCmdObj(from, to, reverse, fileNames, plain).RunWithOutput()
return self.ShowFileDiffCmdObj(from, to, reverse, fileNames, plain, false).RunWithOutput()
}
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileNames []string, plain bool) *oscommands.CmdObj {
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileNames []string, plain bool, ignoreExternalDiff bool) *oscommands.CmdObj {
colorArg := self.diffRendererConfigManager.GetColorArg()
if plain {
colorArg = "never"
@ -445,7 +445,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false").
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), !plain).
AddCommonDiffArgs(self.diffRendererConfigManager, self.UserConfig(), !plain && !ignoreExternalDiff).
Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", colorArg)).
Arg(from).

View file

@ -176,7 +176,7 @@ 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)
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, false, false)
task := types.NewRunPtyTask(cmdObj.GetCmd())
// Keep the inclusion gutter in step with the content as this diff (re-)renders.
@ -201,7 +201,7 @@ func (self *CommitFilesController) copyDiffToClipboard(paths []string, toastMess
from, to := self.context().GetFromAndToForDiff()
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(from)
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, true)
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(from, to, reverse, paths, true, false)
diff, err := cmdObj.RunWithOutput()
if err != nil {
return err

View file

@ -353,7 +353,7 @@ func (self *FilesController) renderNonTextualConflict(node *filetree.FileNode) {
message := self.conflictResolutionHint(node.File.GetMergeStateDescription(self.c.Tr))
if node.File.ShortStatus == "DU" || node.File.ShortStatus == "UD" {
cmdObj := self.c.Git().Diff.DiffCmdObj([]string{"--base", "--", node.GetPath()})
cmdObj := self.c.Git().Diff.DiffCmdObj([]string{"--base", "--", node.GetPath()}, false)
prefix := message + "\n\n"
if node.File.ShortStatus == "DU" {
prefix += self.c.Tr.MergeConflictIncomingDiff
@ -387,7 +387,7 @@ func (self *FilesController) renderWorkingTreeDiff(node *filetree.FileNode) {
refreshOpts := types.RefreshMainOpts{
Pair: self.c.MainViewPairs().Normal,
Main: &types.ViewUpdateOpts{
Task: diffMainViewTask(renderRaw, cmdObj.GetCmd()),
Task: types.NewMainViewDiffTask(renderRaw, cmdObj.GetCmd()),
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
Title: title,
},
@ -404,7 +404,7 @@ func (self *FilesController) renderWorkingTreeDiff(node *filetree.FileNode) {
refreshOpts.Secondary = &types.ViewUpdateOpts{
Title: title,
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
Task: diffMainViewTask(renderRaw, cmdObj.GetCmd()),
Task: types.NewMainViewDiffTask(renderRaw, cmdObj.GetCmd()),
}
}

View file

@ -72,12 +72,12 @@ func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Comm
args = append(args, filterPath)
}
}
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
cmdObj := self.c.Git().Diff.DiffCmdObj(args, false)
prefix := style.FgYellow.Sprintf("%s %s-%s\n\n", self.c.Tr.ShowingDiffForRange, from.ShortRefName(), to.ShortRefName())
return types.NewRunPtyTaskWithPrefix(cmdObj.GetCmd(), prefix)
}
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.FilterPathsForCommit(commit))
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.FilterPathsForCommit(commit), false)
return types.NewRunPtyTask(cmdObj.GetCmd())
}
@ -100,7 +100,7 @@ func (self *DiffHelper) ExitDiffMode() error {
func (self *DiffHelper) RenderDiff() {
args := self.DiffArgs()
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
cmdObj := self.c.Git().Diff.DiffCmdObj(args, false)
prefix := style.FgMagenta.Sprintf(
"%s %s\n\n",
self.c.Tr.ShowingGitDiff,

View file

@ -5,7 +5,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"os/exec"
"path/filepath"
"strings"
@ -310,20 +309,6 @@ func placeOrHideInitialDiffSelection(c *ControllerCommon, mainContext *context.M
showSelectionAtLine(view, target, true)
}
// diffMainViewTask builds the task a side panel uses to render its diff into the main
// view, choosing between the normal pty task and the raw-diff fallback. When renderRaw
// is set (the focused main view needs to act on a diff the configured pager can't
// resolve, see StagingHelper.DiffMainViewShouldRenderRaw) it uses a plain command task,
// which — unlike the pty task — doesn't pipe the diff through a stdin pager (GIT_PAGER);
// the external diff command, if any, is suppressed in the cmd itself. The caller passes
// the same renderRaw to the diff-cmd builder so the two stay in step.
func diffMainViewTask(renderRaw bool, cmd *exec.Cmd) types.UpdateTask {
if renderRaw {
return types.NewRunCommandTask(cmd)
}
return types.NewRunPtyTask(cmd)
}
// updateFocusedMainViewSelectionVisibility shows or hides the focused-main-view selection
// to match what a side panel is rendering into the main view, called from the panel's
// render-to-main so the selection tracks content changes (a refresh after the last change

View file

@ -45,7 +45,7 @@ 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))
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Helpers().Diff.FilterPathsForCommit(commit), false)
task = types.NewRunPtyTask(cmdObj.GetCmd())
}

View file

@ -94,7 +94,7 @@ func (self *StashController) GetOnRenderToMain() func() {
} else {
prefix := style.FgYellow.Sprintf("%s\n\n", stashEntry.Description())
task = types.NewRunPtyTaskWithPrefix(
self.c.Git().Stash.ShowStashEntryCmdObj(stashEntry.Index).GetCmd(),
self.c.Git().Stash.ShowStashEntryCmdObj(stashEntry.Index, false).GetCmd(),
prefix,
)
}

View file

@ -98,3 +98,26 @@ func NewRunPtyTask(cmd *exec.Cmd) *RunPtyTask {
func NewRunPtyTaskWithPrefix(cmd *exec.Cmd, prefix string) *RunPtyTask {
return &RunPtyTask{Cmd: cmd, Prefix: prefix}
}
// NewMainViewDiffTask builds the task for rendering a diff into the main view,
// choosing between the normal pty task and the focused main view's raw-diff
// fallback. When renderRaw is set (the focused main view needs to act on a diff the
// configured pager can't resolve) it uses a plain command task, which — unlike the
// pty task — doesn't pipe the diff through a stdin pager (GIT_PAGER); the external
// diff command, if any, is suppressed in the cmd itself (its ignoreExternalDiff arg).
// The caller passes the same renderRaw to the diff-cmd builder so the two stay in step.
func NewMainViewDiffTask(renderRaw bool, cmd *exec.Cmd) UpdateTask {
if renderRaw {
return NewRunCommandTask(cmd)
}
return NewRunPtyTask(cmd)
}
// NewMainViewDiffTaskWithPrefix is NewMainViewDiffTask for a diff rendered with a
// leading prefix (e.g. a range-diff or stash header).
func NewMainViewDiffTaskWithPrefix(renderRaw bool, cmd *exec.Cmd, prefix string) UpdateTask {
if renderRaw {
return NewRunCommandTaskWithPrefix(cmd, prefix)
}
return NewRunPtyTaskWithPrefix(cmd, prefix)
}