1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-07-24 20:19:39 +02:00

chore: refactor LineBlame (#8419)

- Refactor arguments of the function to make more sense.
  - `path` can be inferred from `repo` receiver.
  - `line` can be `uint64`.
- The two calls to this function check for specific errors, do this error checking in the function.
- The ID of a object format is not 40 in the case of SHA256, get the object format and use the correct length.
- Add test coverage for `LineBlame`, notably it checks for the errors that can legitimately happen.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8419
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
Gusted 2025-07-05 16:31:53 +02:00 committed by Gusted
parent d8c5083c6f
commit 5f514a6e4d
3 changed files with 88 additions and 14 deletions

View file

@ -4,20 +4,46 @@
package git
import (
"errors"
"fmt"
"regexp"
)
var (
ErrBlameFileDoesNotExist = errors.New("the blamed file does not exist")
ErrBlameFileNotEnoughLines = errors.New("the blamed file has not enough lines")
notEnoughLinesRe = regexp.MustCompile(`^fatal: file .+ has only \d+ lines?\n$`)
)
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, _, err := NewCommand(repo.Ctx, "blame").
func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, error) {
res, _, gitErr := NewCommand(repo.Ctx, "blame").
AddOptionFormat("-L %d,%d", line, line).
AddOptionValues("-p", revision).
AddDashesAndList(file).RunStdString(&RunOpts{Dir: path})
AddDashesAndList(file).RunStdString(&RunOpts{Dir: repo.Path})
if gitErr != nil {
stdErr := gitErr.Stderr()
if stdErr == fmt.Sprintf("fatal: no such path %s in %s\n", file, revision) {
return nil, ErrBlameFileDoesNotExist
}
if notEnoughLinesRe.MatchString(stdErr) {
return nil, ErrBlameFileNotEnoughLines
}
return nil, gitErr
}
objectFormat, err := repo.GetObjectFormat()
if err != nil {
return nil, err
}
if len(res) < 40 {
return nil, fmt.Errorf("invalid result of blame: %s", res)
objectIDLen := objectFormat.FullLength()
if len(res) < objectIDLen {
return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res)
}
return repo.GetCommit(res[:40])
return repo.GetCommit(res[:objectIDLen])
}