diff --git a/docs/Custom_Pagers.md b/docs/Custom_Pagers.md index 3d596460b..f1b01b382 100644 --- a/docs/Custom_Pagers.md +++ b/docs/Custom_Pagers.md @@ -1,6 +1,6 @@ # Custom Pagers -Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `o` in the Status panel). +Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml file (which can be opened by pressing `e` in the Status panel). Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support. @@ -62,3 +62,25 @@ git: ``` If you set `useConfig: true`, lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager). + +## Using external diff commands + +Some diff tools can't work as a simple pager like the ones above do, because they need access to the entire diff, so just post-processing git's diff is not enough for them. The most notable example is probably [difftastic](https://difftastic.wilfred.me.uk). + +These can be used in lazygit by using the `externalDiffCommand` config; in the case of difftastic, that could be + +```yaml +git: + paging: + externalDiffCommand: difft --color=always +``` + +The `colorArg`, `pager`, and `useConfig` options are not used in this case. + +You can add whatever extra arguments you prefer for your difftool; for instance + +```yaml +git: + paging: + externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off +``` diff --git a/pkg/commands/git_commands/commit.go b/pkg/commands/git_commands/commit.go index ec27d66cb..c68cb0d9c 100644 --- a/pkg/commands/git_commands/commit.go +++ b/pkg/commands/git_commands/commit.go @@ -199,7 +199,10 @@ func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj { func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj { contextSize := self.UserConfig.Git.DiffContextSize + extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand cmdArgs := NewGitCmd("show"). + ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd). + ArgIfElse(extDiffCmd != "", "--ext-diff", "--no-ext-diff"). Arg("--submodule"). Arg("--color="+self.UserConfig.Git.Paging.ColorArg). Arg(fmt.Sprintf("--unified=%d", contextSize)). diff --git a/pkg/commands/git_commands/commit_test.go b/pkg/commands/git_commands/commit_test.go index d25d613b2..48fc5edc7 100644 --- a/pkg/commands/git_commands/commit_test.go +++ b/pkg/commands/git_commands/commit_test.go @@ -186,6 +186,7 @@ func TestCommitShowCmdObj(t *testing.T) { filterPath string contextSize int ignoreWhitespace bool + extDiffCmd string expected []string } @@ -195,28 +196,40 @@ func TestCommitShowCmdObj(t *testing.T) { filterPath: "", contextSize: 3, ignoreWhitespace: false, - expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"}, + extDiffCmd: "", + expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"}, }, { testName: "Default case with filter path", filterPath: "file.txt", contextSize: 3, ignoreWhitespace: false, - expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"}, + extDiffCmd: "", + expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"}, }, { testName: "Show diff with custom context size", filterPath: "", contextSize: 77, ignoreWhitespace: false, - expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"}, + extDiffCmd: "", + expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"}, }, { testName: "Show diff, ignoring whitespace", filterPath: "", contextSize: 77, ignoreWhitespace: true, - expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"}, + extDiffCmd: "", + expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"}, + }, + { + testName: "Show diff with external diff command", + filterPath: "", + contextSize: 3, + ignoreWhitespace: false, + extDiffCmd: "difft --color=always", + expected: []string{"-c", "diff.external=difft --color=always", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"}, }, } @@ -225,6 +238,7 @@ func TestCommitShowCmdObj(t *testing.T) { t.Run(s.testName, func(t *testing.T) { userConfig := config.GetDefaultConfig() userConfig.Git.DiffContextSize = s.contextSize + userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil) instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner}) diff --git a/pkg/commands/git_commands/git_command_builder.go b/pkg/commands/git_commands/git_command_builder.go index 27f2e4a6c..4aa35be5f 100644 --- a/pkg/commands/git_commands/git_command_builder.go +++ b/pkg/commands/git_commands/git_command_builder.go @@ -44,6 +44,14 @@ func (self *GitCommandBuilder) Config(value string) *GitCommandBuilder { return self } +func (self *GitCommandBuilder) ConfigIf(condition bool, ifTrue string) *GitCommandBuilder { + if condition { + self.Config(ifTrue) + } + + return self +} + // the -C arg will make git do a `cd` to the directory before doing anything else func (self *GitCommandBuilder) Dir(path string) *GitCommandBuilder { // repo path comes before the command diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go index 9a167de36..a9ebd3985 100644 --- a/pkg/commands/git_commands/working_tree.go +++ b/pkg/commands/git_commands/working_tree.go @@ -243,10 +243,13 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain contextSize := self.UserConfig.Git.DiffContextSize prevPath := node.GetPreviousPath() noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile() + extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand + useExtDiff := extDiffCmd != "" && !plain cmdArgs := NewGitCmd("diff"). + ConfigIf(useExtDiff, "diff.external="+extDiffCmd). + ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff"). Arg("--submodule"). - Arg("--no-ext-diff"). Arg(fmt.Sprintf("--unified=%d", contextSize)). Arg(fmt.Sprintf("--color=%s", colorArg)). ArgIf(ignoreWhitespace, "--ignore-all-space"). @@ -279,9 +282,13 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve colorArg = "never" } + extDiffCmd := self.UserConfig.Git.Paging.ExternalDiffCommand + useExtDiff := extDiffCmd != "" && !plain + cmdArgs := NewGitCmd("diff"). + ConfigIf(useExtDiff, "diff.external="+extDiffCmd). + ArgIfElse(useExtDiff, "--ext-diff", "--no-ext-diff"). Arg("--submodule"). - Arg("--no-ext-diff"). Arg(fmt.Sprintf("--unified=%d", contextSize)). Arg("--no-renames"). Arg(fmt.Sprintf("--color=%s", colorArg)). diff --git a/pkg/commands/git_commands/working_tree_test.go b/pkg/commands/git_commands/working_tree_test.go index 3a1f2a9da..a79631236 100644 --- a/pkg/commands/git_commands/working_tree_test.go +++ b/pkg/commands/git_commands/working_tree_test.go @@ -231,7 +231,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil), }, { testName: "cached", @@ -245,7 +245,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil), }, { testName: "plain", @@ -259,7 +259,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil), }, { testName: "File not tracked and file has no staged changes", @@ -273,7 +273,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil), }, { testName: "Default case (ignore whitespace)", @@ -287,7 +287,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: true, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil), }, { testName: "Show diff with custom context size", @@ -301,7 +301,7 @@ func TestWorkingTreeDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 17, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil), }, } @@ -343,7 +343,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), }, { testName: "Show diff with custom context size", @@ -354,7 +354,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) { ignoreWhitespace: false, contextSize: 123, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil), }, { testName: "Default case (ignore whitespace)", @@ -365,7 +365,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) { ignoreWhitespace: true, contextSize: 3, runner: oscommands.NewFakeRunner(t). - ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil), + ExpectGitArgs([]string{"diff", "--no-ext-diff", "--submodule", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil), }, } diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 43bbe05ee..0acfcfb9b 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -101,9 +101,10 @@ type GitConfig struct { } type PagingConfig struct { - ColorArg string `yaml:"colorArg"` - Pager string `yaml:"pager"` - UseConfig bool `yaml:"useConfig"` + ColorArg string `yaml:"colorArg"` + Pager string `yaml:"pager"` + UseConfig bool `yaml:"useConfig"` + ExternalDiffCommand string `yaml:"externalDiffCommand"` } type CommitConfig struct { @@ -469,9 +470,10 @@ func GetDefaultConfig() *UserConfig { }, Git: GitConfig{ Paging: PagingConfig{ - ColorArg: "always", - Pager: "", - UseConfig: false, + ColorArg: "always", + Pager: "", + UseConfig: false, + ExternalDiffCommand: "", }, Commit: CommitConfig{ SignOff: false,