| 92 | } |
| 93 | |
| 94 | func (g *gitTracker) GitResolve(ctx context.Context, repository, path string) (string, error) { |
| 95 | cfn := func() (*git.Repository, error) { |
| 96 | return clone(ctx, repository) |
| 97 | } |
| 98 | rfn, _ := g.repositories.LoadOrStore(repository, sync.OnceValues(cfn)) |
| 99 | |
| 100 | r, err := rfn.(func() (*git.Repository, error))() |
| 101 | if err != nil { |
| 102 | return "", err |
| 103 | } |
| 104 | |
| 105 | commits, err := r.Log(&git.LogOptions{ |
| 106 | FileName: &path, |
| 107 | Order: git.LogOrderCommitterTime, |
| 108 | }) |
| 109 | if err != nil { |
| 110 | return "", err |
| 111 | } |
| 112 | defer commits.Close() |
| 113 | |
| 114 | var c *object.Commit |
| 115 | outer: |
| 116 | for { |
| 117 | c, err = commits.Next() |
| 118 | if err != nil { |
| 119 | if errors.Is(err, io.EOF) { |
| 120 | return "", fmt.Errorf("unable to find any commits for path: %q", path) |
| 121 | } |
| 122 | return "", err |
| 123 | } |
| 124 | |
| 125 | if c == nil { |
| 126 | break |
| 127 | } |
| 128 | |
| 129 | noParents := c.NumParents() |
| 130 | if noParents == 0 { |
| 131 | // initial commit |
| 132 | if _, err := c.File(path); err != nil && err != object.ErrFileNotFound { |
| 133 | return "", err |
| 134 | } |
| 135 | |
| 136 | return c.ID().String(), nil |
| 137 | } else if noParents == 1 { |
| 138 | // first commit with only one parent is a non-merge commit, even though |
| 139 | // a merge commit is a valid commit id for a change on the path we want |
| 140 | // to filter out the merge commits as the default UIs in GitHub |
| 141 | // (GitLab?) do not show the merge commits in the file history views |
| 142 | parent, err := c.Parent(0) |
| 143 | if err != nil { |
| 144 | return "", err |
| 145 | } |
| 146 | // we get commits that didn't change the path, so filter to only |
| 147 | // those that did |
| 148 | p, _ := parent.Patch(c) |
| 149 | for _, f := range p.FilePatches() { |
| 150 | from, to := f.Files() |
| 151 | if (from != nil && to != nil && from.Path() == path) || (to != nil && to.Path() == path) { |