( cmd *oscommands.CmdObj, filterPath string, parseLogLine func(string) (*models.Commit, bool), )
| 9 | ) |
| 10 | |
| 11 | func loadCommits( |
| 12 | cmd *oscommands.CmdObj, |
| 13 | filterPath string, |
| 14 | parseLogLine func(string) (*models.Commit, bool), |
| 15 | ) ([]*models.Commit, error) { |
| 16 | commits := []*models.Commit{} |
| 17 | |
| 18 | var commit *models.Commit |
| 19 | var filterPaths []string |
| 20 | // A string pool that stores interned strings to reduce memory usage |
| 21 | pool := make(map[string]string) |
| 22 | |
| 23 | finishLastCommit := func() { |
| 24 | if commit != nil { |
| 25 | // Only set the filter paths if we have one that is not contained in the original |
| 26 | // filter path. When filtering on a directory, all file paths will start with that |
| 27 | // directory, so we needn't bother storing the individual paths. Likewise, if we |
| 28 | // filter on a file and the file path hasn't changed, we needn't store it either. |
| 29 | // Only if a file has been moved or renamed do we need to store the paths, but then |
| 30 | // we need them all so that we can properly render a diff for the rename. |
| 31 | if lo.SomeBy(filterPaths, func(path string) bool { |
| 32 | return !strings.HasPrefix(path, filterPath) |
| 33 | }) { |
| 34 | commit.FilterPaths = lo.Map(filterPaths, func(path string, _ int) string { |
| 35 | if v, ok := pool[path]; ok { |
| 36 | return v |
| 37 | } |
| 38 | pool[path] = path |
| 39 | return path |
| 40 | }) |
| 41 | } |
| 42 | commits = append(commits, commit) |
| 43 | commit = nil |
| 44 | filterPaths = nil |
| 45 | } |
| 46 | } |
| 47 | err := cmd.RunAndProcessLines(func(line string) (bool, error) { |
| 48 | if line == "" { |
| 49 | return false, nil |
| 50 | } |
| 51 | |
| 52 | if line[0] == '+' { |
| 53 | finishLastCommit() |
| 54 | var stop bool |
| 55 | commit, stop = parseLogLine(line[1:]) |
| 56 | if stop { |
| 57 | commit = nil |
| 58 | return true, nil |
| 59 | } |
| 60 | } else if commit != nil && filterPath != "" { |
| 61 | // We are filtering by path, and this line is the output of the --name-status flag |
| 62 | fields := strings.Split(line, "\t") |
| 63 | // We don't bother looking at the first field (it will be 'A', 'M', 'R072' or a bunch of others). |
| 64 | // All we care about is the path(s), and there will be one for 'M' and 'A', and two for 'R' or 'C', |
| 65 | // in which case we want them both so that we can show the diff between the two. |
| 66 | if len(fields) > 1 { |
| 67 | filterPaths = append(filterPaths, fields[1:]...) |
| 68 | } |
no test coverage detected