1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-08-24 19:39:16 +02:00

Add a user config for using git's external diff command for paging (#4832)

### PR Description

This is similar to using lazygit's `Git.Paging.ExternalDiffCommand`
config, except that the command is configured in git. This can be done
either with git's `diff.external` config, or through `.gitattributes`,
so it gives a bit more flexibility.

We could consider removing the `Git.Paging.ExternalDiffCommand` config
now, because its functionality is covered by the new config. I decided
to keep it though, because I don't want to make this a breaking change,
and also because some users might want to have the external diff command
only in lazygit but not on the command line.
This commit is contained in:
Stefan Haller
2025-08-20 10:42:37 +02:00
committed by GitHub
10 changed files with 52 additions and 5 deletions

View File

@@ -306,6 +306,9 @@ git:
# e.g. 'difft --color=always' # e.g. 'difft --color=always'
externalDiffCommand: "" externalDiffCommand: ""
# If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
useExternalDiffGitConfig: false
# Config relating to committing # Config relating to committing
commit: commit:
# If true, pass '--signoff' flag when committing # If true, pass '--signoff' flag when committing

View File

@@ -74,3 +74,13 @@ git:
paging: paging:
externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off
``` ```
Instead of setting this command in lazygit's `externalDiffCommand` config, you can also tell lazygit to use the external diff command that is configured in git itself (`diff.external`), by using
```yaml
git:
paging:
useExternalDiffGitConfig: true
```
This can be useful if you also want to use it for diffs on the command line, and it also has the advantage that you can configure it per file type in `.gitattributes`; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.

View File

@@ -257,10 +257,11 @@ func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscom
contextSize := self.UserConfig().Git.DiffContextSize contextSize := self.UserConfig().Git.DiffContextSize
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
cmdArgs := NewGitCmd("show"). cmdArgs := NewGitCmd("show").
Config("diff.noprefix=false"). Config("diff.noprefix=false").
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd). ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff"). ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule"). Arg("--submodule").
Arg("--color="+self.UserConfig().Git.Paging.ColorArg). Arg("--color="+self.UserConfig().Git.Paging.ColorArg).
Arg(fmt.Sprintf("--unified=%d", contextSize)). Arg(fmt.Sprintf("--unified=%d", contextSize)).

View File

@@ -256,6 +256,7 @@ func TestCommitShowCmdObj(t *testing.T) {
similarityThreshold int similarityThreshold int
ignoreWhitespace bool ignoreWhitespace bool
extDiffCmd string extDiffCmd string
useExtDiffGitConfig bool
expected []string expected []string
} }
@@ -314,6 +315,15 @@ func TestCommitShowCmdObj(t *testing.T) {
extDiffCmd: "difft --color=always", extDiffCmd: "difft --color=always",
expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"}, expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
}, },
{
testName: "Show diff using git's external diff config",
filterPaths: []string{},
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
useExtDiffGitConfig: true,
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
},
} }
for _, s := range scenarios { for _, s := range scenarios {
@@ -323,6 +333,7 @@ func TestCommitShowCmdObj(t *testing.T) {
userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace
userConfig.Git.DiffContextSize = s.contextSize userConfig.Git.DiffContextSize = s.contextSize
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil) runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
repoPaths := RepoPaths{ repoPaths := RepoPaths{

View File

@@ -21,13 +21,14 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj { func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj {
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != "" useExtDiff := extDiffCmd != ""
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
ignoreWhitespace := self.UserConfig().Git.IgnoreWhitespaceInDiffView ignoreWhitespace := self.UserConfig().Git.IgnoreWhitespaceInDiffView
return self.cmd.New( return self.cmd.New(
NewGitCmd("diff"). NewGitCmd("diff").
Config("diff.noprefix=false"). Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd). ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff"). ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule"). Arg("--submodule").
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)). Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
ArgIf(ignoreWhitespace, "--ignore-all-space"). ArgIf(ignoreWhitespace, "--ignore-all-space").

View File

@@ -82,6 +82,7 @@ func (self *StashCommands) Hash(index int) (string, error) {
func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj { func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj {
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
// "-u" is the same as "--include-untracked", but the latter fails in older git versions for some reason // "-u" is the same as "--include-untracked", but the latter fails in older git versions for some reason
cmdArgs := NewGitCmd("stash").Arg("show"). cmdArgs := NewGitCmd("stash").Arg("show").
@@ -89,7 +90,7 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj {
Arg("--stat"). Arg("--stat").
Arg("-u"). Arg("-u").
ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd). ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd).
ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff"). ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)). Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)).
Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)). Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)).
ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space"). ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").

View File

@@ -104,6 +104,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
similarityThreshold int similarityThreshold int
ignoreWhitespace bool ignoreWhitespace bool
extDiffCmd string extDiffCmd string
useExtDiffGitConfig bool
expected []string expected []string
} }
@@ -141,6 +142,15 @@ func TestStashStashEntryCmdObj(t *testing.T) {
extDiffCmd: "difft --color=always", extDiffCmd: "difft --color=always",
expected: []string{"git", "-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"}, expected: []string{"git", "-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"},
}, },
{
testName: "Show diff using git's external diff config",
index: 5,
contextSize: 3,
similarityThreshold: 50,
ignoreWhitespace: false,
useExtDiffGitConfig: true,
expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"},
},
{ {
testName: "Default case", testName: "Default case",
index: 5, index: 5,
@@ -158,6 +168,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
userConfig.Git.DiffContextSize = s.contextSize userConfig.Git.DiffContextSize = s.contextSize
userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold
userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd
userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig
repoPaths := RepoPaths{ repoPaths := RepoPaths{
worktreePath: "/path/to/worktree", worktreePath: "/path/to/worktree",
} }

View File

@@ -267,10 +267,11 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile() noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != "" && !plain useExtDiff := extDiffCmd != "" && !plain
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
cmdArgs := NewGitCmd("diff"). cmdArgs := NewGitCmd("diff").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd). ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff"). ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule"). Arg("--submodule").
Arg(fmt.Sprintf("--unified=%d", contextSize)). Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg(fmt.Sprintf("--color=%s", colorArg)). Arg(fmt.Sprintf("--color=%s", colorArg)).
@@ -304,11 +305,12 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
useExtDiff := extDiffCmd != "" && !plain useExtDiff := extDiffCmd != "" && !plain
useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig
cmdArgs := NewGitCmd("diff"). cmdArgs := NewGitCmd("diff").
Config("diff.noprefix=false"). Config("diff.noprefix=false").
ConfigIf(useExtDiff, "diff.external="+extDiffCmd). ConfigIf(useExtDiff, "diff.external="+extDiffCmd).
ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff"). ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff").
Arg("--submodule"). Arg("--submodule").
Arg(fmt.Sprintf("--unified=%d", contextSize)). Arg(fmt.Sprintf("--unified=%d", contextSize)).
Arg("--no-renames"). Arg("--no-renames").

View File

@@ -318,6 +318,8 @@ type PagingConfig struct {
Pager PagerType `yaml:"pager"` Pager PagerType `yaml:"pager"`
// e.g. 'difft --color=always' // e.g. 'difft --color=always'
ExternalDiffCommand string `yaml:"externalDiffCommand"` ExternalDiffCommand string `yaml:"externalDiffCommand"`
// If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
UseExternalDiffGitConfig bool `yaml:"useExternalDiffGitConfig"`
} }
type CommitConfig struct { type CommitConfig struct {

View File

@@ -1675,6 +1675,11 @@
"externalDiffCommand": { "externalDiffCommand": {
"type": "string", "type": "string",
"description": "e.g. 'difft --color=always'" "description": "e.g. 'difft --color=always'"
},
"useExternalDiffGitConfig": {
"type": "boolean",
"description": "If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.",
"default": false
} }
}, },
"additionalProperties": false, "additionalProperties": false,