Hand an external diff command to git through the environment

An external diff command rides on the git command as a diff.external
config, which means it is fixed when the command is built. That is
before the layout pass, so the width the renderer is to lay out for
isn't known yet, and the command can't be told about it.

Pass it as GIT_EXTERNAL_DIFF from the render instead, where the width
is known and where a stdin filter is already handed to git the same
way. git ranks the variable exactly as it ranks the config, behind a
per-path diff driver from .gitattributes, so a repository that defines
one still gets it (verified on git 2.22.5 and 2.55). An empty command
means the user wants their own git config to apply, so leave the
variable unset for that; git takes it being set at all as an
instruction.

The renderer command leaves the git arguments, so it also leaves the key
that says which diff a render is of. Cycling between two external diff
renderers now keeps the view's place, the way cycling between two stdin
filters already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-09-25 11:31:59 +02:00
co-authored by Claude Opus 5
parent f06b881f55
commit f496f17452
4 changed files with 17 additions and 7 deletions
+1 -1
View File
@@ -312,7 +312,7 @@ func TestCommitShowCmdObj(t *testing.T) {
similarityThreshold: 50,
ignoreWhitespace: false,
diffRendererConfig: &config.DiffRendererConfig{Type: "extDiff", Command: "difft --color=always"},
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--unified=3", "--find-renames=50%", "--submodule", "--color=always", "--stat", "--decorate", "-p", "1234567890", "--"},
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--ext-diff", "--unified=3", "--find-renames=50%", "--submodule", "--color=always", "--stat", "--decorate", "-p", "1234567890", "--"},
},
{
testName: "Show diff using git's external diff config",
@@ -125,11 +125,9 @@ func (self *GitCommandBuilder) GitDirIf(condition bool, path string) *GitCommand
func (self *GitCommandBuilder) AddCommonDiffArgs(diffRendererConfigManager *config.DiffRendererConfigManager, userConfig *config.UserConfig, forUI bool) *GitCommandBuilder {
contextSize := userConfig.Git.DiffContextSize
extDiffCmd := diffRendererConfigManager.GetExternalDiffCommand(contextSize)
useExtDiff := forUI && diffRendererConfigManager.GetDiffRendererType() == config.DiffRendererType_ExtDiff
return self.
ConfigIf(forUI && extDiffCmd != "", "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
ArgIf(forUI && userConfig.Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
+1 -1
View File
@@ -139,7 +139,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
similarityThreshold: 50,
ignoreWhitespace: false,
diffRendererConfig: &config.DiffRendererConfig{Type: "extDiff", Command: "difft --color=always"},
expected: []string{"git", "-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "stash", "show", "--ext-diff", "--unified=3", "--find-renames=50%", "-p", "--stat", "-u", "--color=always", "refs/stash@{5}"},
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "--ext-diff", "--unified=3", "--find-renames=50%", "-p", "--stat", "-u", "--color=always", "refs/stash@{5}"},
},
{
testName: "Show diff using git's external diff config",
+15 -3
View File
@@ -84,10 +84,12 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
// Run the pty after layout so that it gets the correct size
gui.afterLayout(func() error {
// Need to get the width and the pager command again because the layout might have
// changed the size of the view
// Need to get the width and the renderer command again because the layout
// might have changed the size of the view
width = view.InnerWidth()
pager := gui.stateAccessor.GetDiffRendererConfigManager().GetStdinFilterCommand(width)
diffRendererConfigManager := gui.stateAccessor.GetDiffRendererConfigManager()
pager := diffRendererConfigManager.GetStdinFilterCommand(width)
externalDiff := diffRendererConfigManager.GetExternalDiffCommand(gui.c.UserConfig().Git.DiffContextSize)
cmdStr := strings.Join(cmd.Args, " ")
@@ -100,6 +102,16 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
cmd.Env = append(cmd.Env, "GIT_PAGER="+pager)
// An external diff command is named to git here, in the environment,
// because the width it renders at is only known after the layout, and
// the command's arguments were settled before it. An empty command
// means the user wants git's own diff.external config to apply, so
// leave the variable unset in that case; git takes it being set at all
// as an instruction, however little it says.
if externalDiff != "" {
cmd.Env = append(cmd.Env, "GIT_EXTERNAL_DIFF="+externalDiff)
}
manager := gui.getManager(view)
// Size the pty from the view's dimensions here, on the UI thread; the