mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-06 22:33:07 +02:00
Show diff for renamed file when filtering by path (#4750)
- **PR Description** When filtering by file path for a file that was renamed at some point, we show all commits even before the rename, which is great; however when you selected a commit before the rename, the diff was empty. The commit for the rename itself would show the file as added rather than renamed. This PR fixes that; along the way, we fix the filtered reflog display, which is supposed to filter by path like the commits view does, but this didn't work. Fixes #4510.
This commit is contained in:
@ -253,7 +253,7 @@ func (self *CommitCommands) AmendHeadCmdObj() *oscommands.CmdObj {
|
|||||||
return self.cmd.New(cmdArgs)
|
return self.cmd.New(cmdArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) *oscommands.CmdObj {
|
func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscommands.CmdObj {
|
||||||
contextSize := self.UserConfig().Git.DiffContextSize
|
contextSize := self.UserConfig().Git.DiffContextSize
|
||||||
|
|
||||||
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand
|
||||||
@ -270,7 +270,8 @@ func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) *oscomman
|
|||||||
Arg(hash).
|
Arg(hash).
|
||||||
ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space").
|
||||||
Arg(fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold)).
|
Arg(fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold)).
|
||||||
ArgIf(filterPath != "", "--", filterPath).
|
Arg("--").
|
||||||
|
Arg(filterPaths...).
|
||||||
Dir(self.repoPaths.worktreePath).
|
Dir(self.repoPaths.worktreePath).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
|
@ -89,11 +89,13 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
|||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
var realCommits []*models.Commit
|
||||||
commit := self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != "")
|
realCommits, logErr = loadCommits(self.getLogCmd(opts), opts.FilterPath, func(line string) (*models.Commit, bool) {
|
||||||
commits = append(commits, commit)
|
return self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != ""), false
|
||||||
return false, nil
|
|
||||||
})
|
})
|
||||||
|
if logErr == nil {
|
||||||
|
commits = append(commits, realCommits...)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var ancestor string
|
var ancestor string
|
||||||
@ -292,7 +294,10 @@ func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, tod
|
|||||||
|
|
||||||
fullCommits := map[string]*models.Commit{}
|
fullCommits := map[string]*models.Commit{}
|
||||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit := self.extractCommitFromLine(hashPool, line, false)
|
if line == "" || line[0] != '+' {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
commit := self.extractCommitFromLine(hashPool, line[1:], false)
|
||||||
fullCommits[commit.Hash()] = commit
|
fullCommits[commit.Hash()] = commit
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
@ -599,7 +604,7 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
|
|||||||
Arg("--abbrev=40").
|
Arg("--abbrev=40").
|
||||||
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
|
ArgIf(opts.FilterAuthor != "", "--author="+opts.FilterAuthor).
|
||||||
ArgIf(opts.Limit, "-300").
|
ArgIf(opts.Limit, "-300").
|
||||||
ArgIf(opts.FilterPath != "", "--follow").
|
ArgIf(opts.FilterPath != "", "--follow", "--name-status").
|
||||||
Arg("--no-show-signature").
|
Arg("--no-show-signature").
|
||||||
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
|
ArgIf(opts.RefToShowDivergenceFrom != "", "--left-right").
|
||||||
Arg("--").
|
Arg("--").
|
||||||
@ -609,4 +614,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) *oscommands.CmdObj {
|
|||||||
return self.cmd.New(cmdArgs).DontLog()
|
return self.cmd.New(cmdArgs).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s`
|
const prettyFormat = `--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s`
|
||||||
|
@ -15,16 +15,16 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commitsOutput = strings.ReplaceAll(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode
|
var commitsOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode
|
||||||
b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|origin/better-tests|e94e8fc5b6fab4cb755f|>|fix logging
|
+b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164|1640824515|Jesse Duffield|jessedduffield@gmail.com|origin/better-tests|e94e8fc5b6fab4cb755f|>|fix logging
|
||||||
e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|>|refactor
|
+e94e8fc5b6fab4cb755f29f1bdb3ee5e001df35c|1640823749|Jesse Duffield|jessedduffield@gmail.com|tag: 123, tag: 456|d8084cd558925eb7c9c3|>|refactor
|
||||||
d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|>|WIP
|
+d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffield@gmail.com||65f910ebd85283b5cce9|>|WIP
|
||||||
65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|>|WIP
|
+65f910ebd85283b5cce9bf67d03d3f1a9ea3813a|1640821275|Jesse Duffield|jessedduffield@gmail.com||26c07b1ab33860a1a759|>|WIP
|
||||||
26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|>|WIP
|
+26c07b1ab33860a1a7591a0638f9925ccf497ffa|1640750752|Jesse Duffield|jessedduffield@gmail.com||3d4470a6c072208722e5|>|WIP
|
||||||
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|>|WIP
|
+3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|>|WIP
|
||||||
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|>|refactoring the config struct`, "|", "\x00")
|
+053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|>|refactoring the config struct`, "|", "\x00")
|
||||||
|
|
||||||
var singleCommitOutput = strings.ReplaceAll(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode`, "|", "\x00")
|
var singleCommitOutput = strings.ReplaceAll(`+0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com|HEAD -> better-tests|b21997d6b4cbdf84b149|>|better typing for rebase mode`, "|", "\x00")
|
||||||
|
|
||||||
func TestGetCommits(t *testing.T) {
|
func TestGetCommits(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
@ -44,7 +44,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommitOpts: []models.NewCommitOpts{},
|
expectedCommitOpts: []models.NewCommitOpts{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -55,7 +55,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", RefForPushedStatus: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "refs/heads/mybranch", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommitOpts: []models.NewCommitOpts{},
|
expectedCommitOpts: []models.NewCommitOpts{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -69,7 +69,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||||
// here it's testing which of the configured main branches have an upstream
|
// here it's testing which of the configured main branches have an upstream
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||||
@ -205,7 +205,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
// here it's testing which of the configured main branches exist; neither does
|
// here it's testing which of the configured main branches exist; neither does
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||||
@ -241,7 +241,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||||
// here it's testing which of the configured main branches exist
|
// here it's testing which of the configured main branches exist
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||||
@ -276,7 +276,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
|
||||||
|
|
||||||
expectedCommitOpts: []models.NewCommitOpts{},
|
expectedCommitOpts: []models.NewCommitOpts{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@ -287,7 +287,7 @@ func TestGetCommits(t *testing.T) {
|
|||||||
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
|
opts: GetCommitsOptions{RefName: "HEAD", RefForPushedStatus: "mybranch", FilterPath: "src"},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
ExpectGitArgs([]string{"merge-base", "mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--follow", "--no-show-signature", "--", "src"}, "", nil),
|
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:+%H%x00%at%x00%aN%x00%ae%x00%D%x00%P%x00%m%x00%s", "--abbrev=40", "--follow", "--name-status", "--no-show-signature", "--", "src"}, "", nil),
|
||||||
|
|
||||||
expectedCommitOpts: []models.NewCommitOpts{},
|
expectedCommitOpts: []models.NewCommitOpts{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
|
74
pkg/commands/git_commands/commit_loading_shared.go
Normal file
74
pkg/commands/git_commands/commit_loading_shared.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package git_commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadCommits(
|
||||||
|
cmd *oscommands.CmdObj,
|
||||||
|
filterPath string,
|
||||||
|
parseLogLine func(string) (*models.Commit, bool),
|
||||||
|
) ([]*models.Commit, error) {
|
||||||
|
commits := []*models.Commit{}
|
||||||
|
|
||||||
|
var commit *models.Commit
|
||||||
|
var filterPaths []string
|
||||||
|
// A string pool that stores interned strings to reduce memory usage
|
||||||
|
pool := make(map[string]string)
|
||||||
|
|
||||||
|
finishLastCommit := func() {
|
||||||
|
if commit != nil {
|
||||||
|
// Only set the filter paths if we have one that is not contained in the original
|
||||||
|
// filter path. When filtering on a directory, all file paths will start with that
|
||||||
|
// directory, so we needn't bother storing the individual paths. Likewise, if we
|
||||||
|
// filter on a file and the file path hasn't changed, we needn't store it either.
|
||||||
|
// Only if a file has been moved or renamed do we need to store the paths, but then
|
||||||
|
// we need them all so that we can properly render a diff for the rename.
|
||||||
|
if lo.SomeBy(filterPaths, func(path string) bool {
|
||||||
|
return !strings.HasPrefix(path, filterPath)
|
||||||
|
}) {
|
||||||
|
commit.FilterPaths = lo.Map(filterPaths, func(path string, _ int) string {
|
||||||
|
if v, ok := pool[path]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
pool[path] = path
|
||||||
|
return path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
commits = append(commits, commit)
|
||||||
|
commit = nil
|
||||||
|
filterPaths = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := cmd.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
|
if line == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if line[0] == '+' {
|
||||||
|
finishLastCommit()
|
||||||
|
var stop bool
|
||||||
|
commit, stop = parseLogLine(line[1:])
|
||||||
|
if stop {
|
||||||
|
commit = nil
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
} else if commit != nil && filterPath != "" {
|
||||||
|
// We are filtering by path, and this line is the output of the --name-status flag
|
||||||
|
fields := strings.Split(line, "\t")
|
||||||
|
// We don't bother looking at the first field (it will be 'A', 'M', 'R072' or a bunch of others).
|
||||||
|
// All we care about is the path(s), and there will be one for 'M' and 'A', and two for 'R' or 'C',
|
||||||
|
// in which case we want them both so that we can show the diff between the two.
|
||||||
|
if len(fields) > 1 {
|
||||||
|
filterPaths = append(filterPaths, fields[1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
finishLastCommit()
|
||||||
|
return commits, err
|
||||||
|
}
|
@ -251,7 +251,7 @@ func TestCommitCreateAmendCommit(t *testing.T) {
|
|||||||
func TestCommitShowCmdObj(t *testing.T) {
|
func TestCommitShowCmdObj(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
filterPath string
|
filterPaths []string
|
||||||
contextSize uint64
|
contextSize uint64
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
ignoreWhitespace bool
|
ignoreWhitespace bool
|
||||||
@ -262,16 +262,16 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "Default case without filter path",
|
testName: "Default case without filter path",
|
||||||
filterPath: "",
|
filterPaths: []string{},
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
similarityThreshold: 50,
|
similarityThreshold: 50,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
extDiffCmd: "",
|
extDiffCmd: "",
|
||||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
|
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Default case with filter path",
|
testName: "Default case with filter path",
|
||||||
filterPath: "file.txt",
|
filterPaths: []string{"file.txt"},
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
similarityThreshold: 50,
|
similarityThreshold: 50,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
@ -280,39 +280,39 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom context size",
|
testName: "Show diff with custom context size",
|
||||||
filterPath: "",
|
filterPaths: []string{},
|
||||||
contextSize: 77,
|
contextSize: 77,
|
||||||
similarityThreshold: 50,
|
similarityThreshold: 50,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
extDiffCmd: "",
|
extDiffCmd: "",
|
||||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%"},
|
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with custom similarity threshold",
|
testName: "Show diff with custom similarity threshold",
|
||||||
filterPath: "",
|
filterPaths: []string{},
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
similarityThreshold: 33,
|
similarityThreshold: 33,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
extDiffCmd: "",
|
extDiffCmd: "",
|
||||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%"},
|
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%", "--"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff, ignoring whitespace",
|
testName: "Show diff, ignoring whitespace",
|
||||||
filterPath: "",
|
filterPaths: []string{},
|
||||||
contextSize: 77,
|
contextSize: 77,
|
||||||
similarityThreshold: 50,
|
similarityThreshold: 50,
|
||||||
ignoreWhitespace: true,
|
ignoreWhitespace: true,
|
||||||
extDiffCmd: "",
|
extDiffCmd: "",
|
||||||
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%"},
|
expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%", "--"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "Show diff with external diff command",
|
testName: "Show diff with external diff command",
|
||||||
filterPath: "",
|
filterPaths: []string{},
|
||||||
contextSize: 3,
|
contextSize: 3,
|
||||||
similarityThreshold: 50,
|
similarityThreshold: 50,
|
||||||
ignoreWhitespace: false,
|
ignoreWhitespace: false,
|
||||||
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%", "--"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +330,7 @@ func TestCommitShowCmdObj(t *testing.T) {
|
|||||||
}
|
}
|
||||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: &config.AppState{}, runner: runner, repoPaths: &repoPaths})
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: &config.AppState{}, runner: runner, repoPaths: &repoPaths})
|
||||||
|
|
||||||
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath).Run())
|
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPaths).Run())
|
||||||
runner.CheckForMissingCalls()
|
runner.CheckForMissingCalls()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -25,24 +25,22 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
|
|||||||
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
||||||
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
|
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
|
||||||
func (self *ReflogCommitLoader) GetReflogCommits(hashPool *utils.StringPool, lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
|
func (self *ReflogCommitLoader) GetReflogCommits(hashPool *utils.StringPool, lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
|
||||||
commits := make([]*models.Commit, 0)
|
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("log").
|
cmdArgs := NewGitCmd("log").
|
||||||
Config("log.showSignature=false").
|
Config("log.showSignature=false").
|
||||||
Arg("-g").
|
Arg("-g").
|
||||||
Arg("--abbrev=40").
|
Arg("--format=+%H%x00%ct%x00%gs%x00%P").
|
||||||
Arg("--format=%h%x00%ct%x00%gs%x00%P").
|
|
||||||
ArgIf(filterAuthor != "", "--author="+filterAuthor).
|
ArgIf(filterAuthor != "", "--author="+filterAuthor).
|
||||||
ArgIf(filterPath != "", "--follow", "--", filterPath).
|
ArgIf(filterPath != "", "--follow", "--name-status", "--", filterPath).
|
||||||
ToArgv()
|
ToArgv()
|
||||||
|
|
||||||
cmdObj := self.cmd.New(cmdArgs).DontLog()
|
cmdObj := self.cmd.New(cmdArgs).DontLog()
|
||||||
|
|
||||||
onlyObtainedNewReflogCommits := false
|
onlyObtainedNewReflogCommits := false
|
||||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
|
||||||
|
commits, err := loadCommits(cmdObj, filterPath, func(line string) (*models.Commit, bool) {
|
||||||
commit, ok := self.parseLine(hashPool, line)
|
commit, ok := self.parseLine(hashPool, line)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the unix timestamp here is the timestamp of the COMMIT, not the reflog entry itself,
|
// note that the unix timestamp here is the timestamp of the COMMIT, not the reflog entry itself,
|
||||||
@ -52,11 +50,10 @@ func (self *ReflogCommitLoader) GetReflogCommits(hashPool *utils.StringPool, las
|
|||||||
if lastReflogCommit != nil && self.sameReflogCommit(commit, lastReflogCommit) {
|
if lastReflogCommit != nil && self.sameReflogCommit(commit, lastReflogCommit) {
|
||||||
onlyObtainedNewReflogCommits = true
|
onlyObtainedNewReflogCommits = true
|
||||||
// after this point we already have these reflogs loaded so we'll simply return the new ones
|
// after this point we already have these reflogs loaded so we'll simply return the new ones
|
||||||
return true, nil
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
commits = append(commits, commit)
|
return commit, false
|
||||||
return false, nil
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
@ -14,11 +14,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reflogOutput = strings.ReplaceAll(`c3c4b66b64c97ffeecde|1643150483|checkout: moving from A to B|51baa8c1
|
var reflogOutput = strings.ReplaceAll(`+c3c4b66b64c97ffeecde|1643150483|checkout: moving from A to B|51baa8c1
|
||||||
c3c4b66b64c97ffeecde|1643150483|checkout: moving from B to A|51baa8c1
|
+c3c4b66b64c97ffeecde|1643150483|checkout: moving from B to A|51baa8c1
|
||||||
c3c4b66b64c97ffeecde|1643150483|checkout: moving from A to B|51baa8c1
|
+c3c4b66b64c97ffeecde|1643150483|checkout: moving from A to B|51baa8c1
|
||||||
c3c4b66b64c97ffeecde|1643150483|checkout: moving from master to A|51baa8c1
|
+c3c4b66b64c97ffeecde|1643150483|checkout: moving from master to A|51baa8c1
|
||||||
f4ddf2f0d4be4ccc7efa|1643149435|checkout: moving from A to master|51baa8c1
|
+f4ddf2f0d4be4ccc7efa|1643149435|checkout: moving from A to master|51baa8c1
|
||||||
`, "|", "\x00")
|
`, "|", "\x00")
|
||||||
|
|
||||||
func TestGetReflogCommits(t *testing.T) {
|
func TestGetReflogCommits(t *testing.T) {
|
||||||
@ -39,7 +39,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "no reflog entries",
|
testName: "no reflog entries",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P"}, "", nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P"}, "", nil),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
expectedCommitOpts: []models.NewCommitOpts{},
|
expectedCommitOpts: []models.NewCommitOpts{},
|
||||||
@ -49,7 +49,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "some reflog entries",
|
testName: "some reflog entries",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
expectedCommitOpts: []models.NewCommitOpts{
|
expectedCommitOpts: []models.NewCommitOpts{
|
||||||
@ -95,7 +95,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "some reflog entries where last commit is given",
|
testName: "some reflog entries where last commit is given",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
@ -119,7 +119,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "when passing filterPath",
|
testName: "when passing filterPath",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P", "--follow", "--", "path"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P", "--follow", "--name-status", "--", "path"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
@ -144,7 +144,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "when passing filterAuthor",
|
testName: "when passing filterAuthor",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
@ -169,7 +169,7 @@ func TestGetReflogCommits(t *testing.T) {
|
|||||||
{
|
{
|
||||||
testName: "when command returns error",
|
testName: "when command returns error",
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%P"}, "", errors.New("haha")),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--format=+%H%x00%ct%x00%gs%x00%P"}, "", errors.New("haha")),
|
||||||
|
|
||||||
lastReflogCommit: nil,
|
lastReflogCommit: nil,
|
||||||
filterPath: "",
|
filterPath: "",
|
||||||
|
@ -54,6 +54,10 @@ type Commit struct {
|
|||||||
// Hashes of parent commits (will be multiple if it's a merge commit)
|
// Hashes of parent commits (will be multiple if it's a merge commit)
|
||||||
parents []*string
|
parents []*string
|
||||||
|
|
||||||
|
// When filtering by path, this contains the paths that were changed in this
|
||||||
|
// commit; nil when not filtering by path.
|
||||||
|
FilterPaths []string
|
||||||
|
|
||||||
Status CommitStatus
|
Status CommitStatus
|
||||||
Action todo.TodoCommand
|
Action todo.TodoCommand
|
||||||
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
|
Divergence Divergence // set to DivergenceNone unless we are showing the divergence view
|
||||||
|
@ -90,12 +90,6 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
modeHelper,
|
modeHelper,
|
||||||
)
|
)
|
||||||
|
|
||||||
setSubCommits := func(commits []*models.Commit) {
|
|
||||||
gui.Mutexes.SubCommitsMutex.Lock()
|
|
||||||
defer gui.Mutexes.SubCommitsMutex.Unlock()
|
|
||||||
|
|
||||||
gui.State.Model.SubCommits = commits
|
|
||||||
}
|
|
||||||
gui.helpers = &helpers.Helpers{
|
gui.helpers = &helpers.Helpers{
|
||||||
Refs: refsHelper,
|
Refs: refsHelper,
|
||||||
Host: helpers.NewHostHelper(helperCommon),
|
Host: helpers.NewHostHelper(helperCommon),
|
||||||
@ -135,7 +129,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
),
|
),
|
||||||
Search: searchHelper,
|
Search: searchHelper,
|
||||||
Worktree: worktreeHelper,
|
Worktree: worktreeHelper,
|
||||||
SubCommits: helpers.NewSubCommitsHelper(helperCommon, refreshHelper, setSubCommits),
|
SubCommits: helpers.NewSubCommitsHelper(helperCommon, refreshHelper),
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.CustomCommandsClient = custom_commands.NewClient(
|
gui.CustomCommandsClient = custom_commands.NewClient(
|
||||||
|
@ -57,18 +57,41 @@ func (self *DiffHelper) GetUpdateTaskForRenderingCommitsDiff(commit *models.Comm
|
|||||||
from, to := refRange.From, refRange.To
|
from, to := refRange.From, refRange.To
|
||||||
args := []string{from.ParentRefName(), to.RefName(), "--stat", "-p"}
|
args := []string{from.ParentRefName(), to.RefName(), "--stat", "-p"}
|
||||||
args = append(args, "--")
|
args = append(args, "--")
|
||||||
if path := self.c.Modes().Filtering.GetPath(); path != "" {
|
if filterPath := self.c.Modes().Filtering.GetPath(); filterPath != "" {
|
||||||
args = append(args, path)
|
// If both refs are commits, filter by the union of their paths. This is useful for
|
||||||
|
// example when diffing a range of commits in filter-by-path mode across a rename.
|
||||||
|
fromCommit, ok1 := from.(*models.Commit)
|
||||||
|
toCommit, ok2 := to.(*models.Commit)
|
||||||
|
if ok1 && ok2 {
|
||||||
|
paths := append(self.FilterPathsForCommit(fromCommit), self.FilterPathsForCommit(toCommit)...)
|
||||||
|
args = append(args, lo.Uniq(paths)...)
|
||||||
|
} else {
|
||||||
|
// If either ref is not a commit (which is possible in sticky diff mode, when
|
||||||
|
// diffing against a branch or tag), we just filter by the filter path; that's the
|
||||||
|
// best we can do in this case.
|
||||||
|
args = append(args, filterPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
|
cmdObj := self.c.Git().Diff.DiffCmdObj(args)
|
||||||
prefix := style.FgYellow.Sprintf("%s %s-%s\n\n", self.c.Tr.ShowingDiffForRange, from.ShortRefName(), to.ShortRefName())
|
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.NewRunPtyTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Modes().Filtering.GetPath())
|
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.FilterPathsForCommit(commit))
|
||||||
return types.NewRunPtyTask(cmdObj.GetCmd())
|
return types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *DiffHelper) FilterPathsForCommit(commit *models.Commit) []string {
|
||||||
|
filterPath := self.c.Modes().Filtering.GetPath()
|
||||||
|
if filterPath != "" {
|
||||||
|
if len(commit.FilterPaths) > 0 {
|
||||||
|
return commit.FilterPaths
|
||||||
|
}
|
||||||
|
return []string{filterPath}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *DiffHelper) ExitDiffMode() error {
|
func (self *DiffHelper) ExitDiffMode() error {
|
||||||
self.c.Modes().Diffing = diffing.New()
|
self.c.Modes().Diffing = diffing.New()
|
||||||
self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
|
@ -449,21 +449,8 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
|
|||||||
self.c.Mutexes().RefreshingBranchesMutex.Lock()
|
self.c.Mutexes().RefreshingBranchesMutex.Lock()
|
||||||
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
|
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
|
||||||
|
|
||||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
|
||||||
if self.c.Modes().Filtering.Active() && self.c.UserConfig().Git.LocalBranchSortOrder == "recency" {
|
|
||||||
// in filter mode we filter our reflog commits to just those containing the path
|
|
||||||
// however we need all the reflog entries to populate the recencies of our branches
|
|
||||||
// which allows us to order them correctly. So if we're filtering we'll just
|
|
||||||
// manually load all the reflog commits here
|
|
||||||
var err error
|
|
||||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(self.c.Model().HashPool, nil, "", "")
|
|
||||||
if err != nil {
|
|
||||||
self.c.Log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(
|
branches, err := self.c.Git().Loaders.BranchLoader.Load(
|
||||||
reflogCommits,
|
self.c.Model().ReflogCommits,
|
||||||
self.c.Model().MainBranches,
|
self.c.Model().MainBranches,
|
||||||
self.c.Model().Branches,
|
self.c.Model().Branches,
|
||||||
loadBehindCounts,
|
loadBehindCounts,
|
||||||
@ -620,12 +607,13 @@ func (self *RefreshHelper) refreshReflogCommits() error {
|
|||||||
// pulling state into its own variable in case it gets swapped out for another state
|
// pulling state into its own variable in case it gets swapped out for another state
|
||||||
// and we get an out of bounds exception
|
// and we get an out of bounds exception
|
||||||
model := self.c.Model()
|
model := self.c.Model()
|
||||||
var lastReflogCommit *models.Commit
|
|
||||||
if len(model.ReflogCommits) > 0 {
|
|
||||||
lastReflogCommit = model.ReflogCommits[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
|
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
|
||||||
|
var lastReflogCommit *models.Commit
|
||||||
|
if filterPath == "" && filterAuthor == "" && len(*stateCommits) > 0 {
|
||||||
|
lastReflogCommit = (*stateCommits)[0]
|
||||||
|
}
|
||||||
|
|
||||||
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
||||||
GetReflogCommits(self.c.Model().HashPool, lastReflogCommit, filterPath, filterAuthor)
|
GetReflogCommits(self.c.Model().HashPool, lastReflogCommit, filterPath, filterAuthor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,18 +11,15 @@ type SubCommitsHelper struct {
|
|||||||
c *HelperCommon
|
c *HelperCommon
|
||||||
|
|
||||||
refreshHelper *RefreshHelper
|
refreshHelper *RefreshHelper
|
||||||
setSubCommits func([]*models.Commit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubCommitsHelper(
|
func NewSubCommitsHelper(
|
||||||
c *HelperCommon,
|
c *HelperCommon,
|
||||||
refreshHelper *RefreshHelper,
|
refreshHelper *RefreshHelper,
|
||||||
setSubCommits func([]*models.Commit),
|
|
||||||
) *SubCommitsHelper {
|
) *SubCommitsHelper {
|
||||||
return &SubCommitsHelper{
|
return &SubCommitsHelper{
|
||||||
c: c,
|
c: c,
|
||||||
refreshHelper: refreshHelper,
|
refreshHelper: refreshHelper,
|
||||||
setSubCommits: setSubCommits,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,3 +70,10 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
|
|||||||
self.c.Context().Push(self.c.Contexts().SubCommits, types.OnFocusOpts{})
|
self.c.Context().Push(self.c.Contexts().SubCommits, types.OnFocusOpts{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SubCommitsHelper) setSubCommits(commits []*models.Commit) {
|
||||||
|
self.c.Mutexes().SubCommitsMutex.Lock()
|
||||||
|
defer self.c.Mutexes().SubCommitsMutex.Unlock()
|
||||||
|
|
||||||
|
self.c.Model().SubCommits = commits
|
||||||
|
}
|
||||||
|
@ -45,7 +45,7 @@ func (self *ReflogCommitsController) GetOnRenderToMain() func() {
|
|||||||
if commit == nil {
|
if commit == nil {
|
||||||
task = types.NewRenderStringTask("No reflog history")
|
task = types.NewRenderStringTask("No reflog history")
|
||||||
} else {
|
} else {
|
||||||
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Modes().Filtering.GetPath())
|
cmdObj := self.c.Git().Commit.ShowCmdObj(commit.Hash(), self.c.Helpers().Diff.FilterPathsForCommit(commit))
|
||||||
|
|
||||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func (self *UndoController) reflogRedo() error {
|
|||||||
// Though we might support this later, hence the use of the CURRENT_REBASE action kind.
|
// Though we might support this later, hence the use of the CURRENT_REBASE action kind.
|
||||||
func (self *UndoController) parseReflogForActions(onUserAction func(counter int, action reflogAction) (bool, error)) error {
|
func (self *UndoController) parseReflogForActions(onUserAction func(counter int, action reflogAction) (bool, error)) error {
|
||||||
counter := 0
|
counter := 0
|
||||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
reflogCommits := self.c.Model().ReflogCommits
|
||||||
rebaseFinishCommitHash := ""
|
rebaseFinishCommitHash := ""
|
||||||
var action *reflogAction
|
var action *reflogAction
|
||||||
for reflogCommitIdx, reflogCommit := range reflogCommits {
|
for reflogCommitIdx, reflogCommit := range reflogCommits {
|
||||||
|
@ -289,10 +289,11 @@ type Model struct {
|
|||||||
Worktrees []*models.Worktree
|
Worktrees []*models.Worktree
|
||||||
|
|
||||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||||
// when in filtering mode we only include the ones that match the given path
|
// When in filtering mode we only include the ones that match the given path
|
||||||
FilteredReflogCommits []*models.Commit
|
FilteredReflogCommits []*models.Commit
|
||||||
// ReflogCommits are the ones used by the branches panel to obtain recency values
|
// ReflogCommits are the ones used by the branches panel to obtain recency values,
|
||||||
// if we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
|
// and for the undo functionality.
|
||||||
|
// If we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
|
||||||
// one and the same
|
// one and the same
|
||||||
ReflogCommits []*models.Commit
|
ReflogCommits []*models.Commit
|
||||||
|
|
||||||
|
@ -235,6 +235,10 @@ func (self *Shell) DeleteFileAndAdd(fileName string) *Shell {
|
|||||||
GitAdd(fileName)
|
GitAdd(fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Shell) RenameFileInGit(oldName string, newName string) *Shell {
|
||||||
|
return self.RunCommand([]string{"git", "mv", oldName, newName})
|
||||||
|
}
|
||||||
|
|
||||||
// creates commits 01, 02, 03, ..., n with a new file in each
|
// creates commits 01, 02, 03, ..., n with a new file in each
|
||||||
// The reason for padding with zeroes is so that it's easier to do string
|
// The reason for padding with zeroes is so that it's easier to do string
|
||||||
// matches on the commit messages when there are many of them
|
// matches on the commit messages when there are many of them
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
package filter_by_path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ShowDiffsForRenamedFile = NewIntegrationTest(NewIntegrationTestArgs{
|
||||||
|
Description: "Filter commits by file path for a file that was renamed, and verify that it shows the diffs correctly",
|
||||||
|
ExtraCmdArgs: []string{},
|
||||||
|
Skip: false,
|
||||||
|
SetupConfig: func(config *config.AppConfig) {
|
||||||
|
},
|
||||||
|
SetupRepo: func(shell *Shell) {
|
||||||
|
shell.CreateFileAndAdd("oldFile", "a\nb\nc\n")
|
||||||
|
shell.Commit("add old file")
|
||||||
|
shell.UpdateFileAndAdd("oldFile", "x\nb\nc\n")
|
||||||
|
shell.Commit("update old file")
|
||||||
|
shell.CreateFileAndAdd("unrelatedFile", "content of unrelated file\n")
|
||||||
|
shell.Commit("add unrelated file")
|
||||||
|
shell.RenameFileInGit("oldFile", "newFile")
|
||||||
|
shell.Commit("rename file")
|
||||||
|
shell.UpdateFileAndAdd("newFile", "y\nb\nc\n")
|
||||||
|
shell.UpdateFileAndAdd("unrelatedFile", "updated content of unrelated file\n")
|
||||||
|
shell.Commit("update both files")
|
||||||
|
},
|
||||||
|
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||||
|
t.Views().Commits().
|
||||||
|
Focus().
|
||||||
|
Lines(
|
||||||
|
Contains("update both files").IsSelected(),
|
||||||
|
Contains("rename file"),
|
||||||
|
Contains("add unrelated file"),
|
||||||
|
Contains("update old file"),
|
||||||
|
Contains("add old file"),
|
||||||
|
).
|
||||||
|
PressEnter()
|
||||||
|
|
||||||
|
t.Views().CommitFiles().
|
||||||
|
IsFocused().
|
||||||
|
Lines(
|
||||||
|
Equals("▼ /").IsSelected(),
|
||||||
|
Equals(" M newFile"),
|
||||||
|
Equals(" M unrelatedFile"),
|
||||||
|
).
|
||||||
|
SelectNextItem().
|
||||||
|
Press(keys.Universal.FilteringMenu)
|
||||||
|
|
||||||
|
t.ExpectPopup().Menu().Title(Equals("Filtering")).
|
||||||
|
Select(Contains("Filter by 'newFile'")).Confirm()
|
||||||
|
|
||||||
|
t.Views().Commits().
|
||||||
|
IsFocused().
|
||||||
|
Lines(
|
||||||
|
Contains("update both files").IsSelected(),
|
||||||
|
Contains("rename file"),
|
||||||
|
Contains("update old file"),
|
||||||
|
Contains("add old file"),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Views().Main().ContainsLines(
|
||||||
|
Equals(" update both files"),
|
||||||
|
Equals("---"),
|
||||||
|
Equals(" newFile | 2 +-"),
|
||||||
|
Equals(" 1 file changed, 1 insertion(+), 1 deletion(-)"),
|
||||||
|
Equals(""),
|
||||||
|
Equals("diff --git a/newFile b/newFile"),
|
||||||
|
Contains("index"),
|
||||||
|
Equals("--- a/newFile"),
|
||||||
|
Equals("+++ b/newFile"),
|
||||||
|
Equals("@@ -1,3 +1,3 @@"),
|
||||||
|
Equals("-x"),
|
||||||
|
Equals("+y"),
|
||||||
|
Equals(" b"),
|
||||||
|
Equals(" c"),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Views().Commits().SelectNextItem()
|
||||||
|
|
||||||
|
t.Views().Main().ContainsLines(
|
||||||
|
Equals(" rename file"),
|
||||||
|
Equals("---"),
|
||||||
|
Equals(" oldFile => newFile | 0"),
|
||||||
|
Equals(" 1 file changed, 0 insertions(+), 0 deletions(-)"),
|
||||||
|
Equals(""),
|
||||||
|
Equals("diff --git a/oldFile b/newFile"),
|
||||||
|
Equals("similarity index 100%"),
|
||||||
|
Equals("rename from oldFile"),
|
||||||
|
Equals("rename to newFile"),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Views().Commits().SelectNextItem()
|
||||||
|
|
||||||
|
t.Views().Main().ContainsLines(
|
||||||
|
Equals(" update old file"),
|
||||||
|
Equals("---"),
|
||||||
|
Equals(" oldFile | 2 +-"),
|
||||||
|
Equals(" 1 file changed, 1 insertion(+), 1 deletion(-)"),
|
||||||
|
Equals(""),
|
||||||
|
Equals("diff --git a/oldFile b/oldFile"),
|
||||||
|
Contains("index"),
|
||||||
|
Equals("--- a/oldFile"),
|
||||||
|
Equals("+++ b/oldFile"),
|
||||||
|
Equals("@@ -1,3 +1,3 @@"),
|
||||||
|
Equals("-a"),
|
||||||
|
Equals("+x"),
|
||||||
|
Equals(" b"),
|
||||||
|
Equals(" c"),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Views().Commits().
|
||||||
|
Press(keys.Universal.RangeSelectUp).
|
||||||
|
Press(keys.Universal.RangeSelectUp)
|
||||||
|
|
||||||
|
t.Views().Main().ContainsLines(
|
||||||
|
Contains("Showing diff for range"),
|
||||||
|
Equals(""),
|
||||||
|
Equals(" oldFile => newFile | 2 +-"),
|
||||||
|
Equals(" 1 file changed, 1 insertion(+), 1 deletion(-)"),
|
||||||
|
Equals(""),
|
||||||
|
Equals("diff --git a/oldFile b/newFile"),
|
||||||
|
Equals("similarity index 66%"),
|
||||||
|
Equals("rename from oldFile"),
|
||||||
|
Equals("rename to newFile"),
|
||||||
|
Contains("index"),
|
||||||
|
Equals("--- a/oldFile"),
|
||||||
|
Equals("+++ b/newFile"),
|
||||||
|
Equals("@@ -1,3 +1,3 @@"),
|
||||||
|
Equals("-a"),
|
||||||
|
Equals("+y"),
|
||||||
|
Equals(" b"),
|
||||||
|
Equals(" c"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
@ -235,6 +235,7 @@ var tests = []*components.IntegrationTest{
|
|||||||
filter_by_path.CliArg,
|
filter_by_path.CliArg,
|
||||||
filter_by_path.KeepSameCommitSelectedOnExit,
|
filter_by_path.KeepSameCommitSelectedOnExit,
|
||||||
filter_by_path.SelectFile,
|
filter_by_path.SelectFile,
|
||||||
|
filter_by_path.ShowDiffsForRenamedFile,
|
||||||
filter_by_path.TypeFile,
|
filter_by_path.TypeFile,
|
||||||
interactive_rebase.AdvancedInteractiveRebase,
|
interactive_rebase.AdvancedInteractiveRebase,
|
||||||
interactive_rebase.AmendCommitWithConflict,
|
interactive_rebase.AmendCommitWithConflict,
|
||||||
|
Reference in New Issue
Block a user