mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-06 22:33:07 +02:00
When filtering for a file path we use the --follow option for "git log", so it will follow renames of the file, which is great. However, if you then selected one of the commits before a rename, you didn't see a diff, because we would pass the original filter path to the "git show" call. To fix this, call git log with the --name-status option when filtering by path, so that each commit reports which file paths are touched in this commit; remember these in the commit object, so that we can pass them to the "git show" call later. Be careful not to store too many such paths unnecessarily. When filtering by folder rather than file, all these paths will necessarily be inside the original filter path, so detect this and don't store them in this case. There is some unfortunate code duplication between loading commits and loading reflog commits, which I am too lazy to clean up right now.
75 lines
2.3 KiB
Go
75 lines
2.3 KiB
Go
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
|
|
}
|