Skip to content

Commit

Permalink
Read previous info from git blame (#28306) (#28310)
Browse files Browse the repository at this point in the history
Backport #28306 by @KN4CK3R

Fixes #28280

Reads the `previous` info from the `git blame` output instead of
calculating it afterwards.

Co-authored-by: KN4CK3R <[email protected]>
  • Loading branch information
GiteaBot and KN4CK3R authored Dec 1, 2023
1 parent 4f5122a commit e15fe85
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 54 deletions.
37 changes: 25 additions & 12 deletions modules/git/blame.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import (
"io"
"os"
"regexp"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)

// BlamePart represents block of blame - continuous lines with one sha
type BlamePart struct {
Sha string
Lines []string
Sha string
Lines []string
PreviousSha string
PreviousPath string
}

// BlameReader returns part of file blame one by one
Expand All @@ -43,30 +46,38 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
var blamePart *BlamePart

if r.lastSha != nil {
blamePart = &BlamePart{*r.lastSha, make([]string, 0)}
blamePart = &BlamePart{
Sha: *r.lastSha,
Lines: make([]string, 0),
}
}

var line []byte
var lineBytes []byte
var isPrefix bool
var err error

for err != io.EOF {
line, isPrefix, err = r.bufferedReader.ReadLine()
lineBytes, isPrefix, err = r.bufferedReader.ReadLine()
if err != nil && err != io.EOF {
return blamePart, err
}

if len(line) == 0 {
if len(lineBytes) == 0 {
// isPrefix will be false
continue
}

lines := shaLineRegex.FindSubmatch(line)
line := string(lineBytes)

lines := shaLineRegex.FindStringSubmatch(line)
if lines != nil {
sha1 := string(lines[1])
sha1 := lines[1]

if blamePart == nil {
blamePart = &BlamePart{sha1, make([]string, 0)}
blamePart = &BlamePart{
Sha: sha1,
Lines: make([]string, 0),
}
}

if blamePart.Sha != sha1 {
Expand All @@ -81,9 +92,11 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
return blamePart, nil
}
} else if line[0] == '\t' {
code := line[1:]

blamePart.Lines = append(blamePart.Lines, string(code))
blamePart.Lines = append(blamePart.Lines, line[1:])
} else if strings.HasPrefix(line, "previous ") {
parts := strings.SplitN(line[len("previous "):], " ", 2)
blamePart.PreviousSha = parts[0]
blamePart.PreviousPath = parts[1]
}

// need to munch to end of line...
Expand Down
28 changes: 16 additions & 12 deletions modules/git/blame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ func TestReadingBlameOutput(t *testing.T) {

parts := []*BlamePart{
{
"72866af952e98d02a73003501836074b286a78f6",
[]string{
Sha: "72866af952e98d02a73003501836074b286a78f6",
Lines: []string{
"# test_repo",
"Test repository for testing migration from github to gitea",
},
},
{
"f32b0a9dfd09a60f616f29158f772cedd89942d2",
[]string{"", "Do not make any changes to this repo it is used for unit testing"},
Sha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
PreviousSha: "72866af952e98d02a73003501836074b286a78f6",
PreviousPath: "README.md",
},
}

Expand Down Expand Up @@ -64,16 +66,18 @@ func TestReadingBlameOutput(t *testing.T) {

full := []*BlamePart{
{
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
[]string{"line", "line"},
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
Lines: []string{"line", "line"},
},
{
"45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
[]string{"changed line"},
Sha: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
Lines: []string{"changed line"},
PreviousSha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
PreviousPath: "blame.txt",
},
{
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
[]string{"line", "line", ""},
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
Lines: []string{"line", "line", ""},
},
}

Expand All @@ -89,8 +93,8 @@ func TestReadingBlameOutput(t *testing.T) {
Bypass: false,
Parts: []*BlamePart{
{
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
[]string{"line", "line", "changed line", "line", "line", ""},
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
Lines: []string{"line", "line", "changed line", "line", "line", ""},
},
},
},
Expand Down
38 changes: 8 additions & 30 deletions routers/web/repo/blame.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ func RefBlame(ctx *context.Context) {
return
}

commitNames, previousCommits := processBlameParts(ctx, result.Parts)
commitNames := processBlameParts(ctx, result.Parts)
if ctx.Written() {
return
}

renderBlame(ctx, result.Parts, commitNames, previousCommits)
renderBlame(ctx, result.Parts, commitNames)

ctx.HTML(http.StatusOK, tplRepoHome)
}
Expand Down Expand Up @@ -185,12 +185,9 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error {
return nil
}

func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) {
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[string]*user_model.UserCommit {
// store commit data by SHA to look up avatar info etc
commitNames := make(map[string]*user_model.UserCommit)
// previousCommits contains links from SHA to parent SHA,
// if parent also contains the current TreePath.
previousCommits := make(map[string]string)
// and as blameParts can reference the same commits multiple
// times, we cache the lookup work locally
commits := make([]*git.Commit, 0, len(blameParts))
Expand All @@ -214,29 +211,11 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
} else {
ctx.ServerError("Repo.GitRepo.GetCommit", err)
}
return nil, nil
return nil
}
commitCache[sha] = commit
}

// find parent commit
if commit.ParentCount() > 0 {
psha := commit.Parents[0]
previousCommit, ok := commitCache[psha.String()]
if !ok {
previousCommit, _ = commit.Parent(0)
if previousCommit != nil {
commitCache[psha.String()] = previousCommit
}
}
// only store parent commit ONCE, if it has the file
if previousCommit != nil {
if haz1, _ := previousCommit.HasFile(ctx.Repo.TreePath); haz1 {
previousCommits[commit.ID.String()] = previousCommit.ID.String()
}
}
}

commits = append(commits, commit)
}

Expand All @@ -245,10 +224,10 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
commitNames[c.ID.String()] = c
}

return commitNames, previousCommits
return commitNames
}

func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit, previousCommits map[string]string) {
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit) {
repoLink := ctx.Repo.RepoLink

language := ""
Expand Down Expand Up @@ -295,7 +274,6 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
}

commit := commitNames[part.Sha]
previousSha := previousCommits[part.Sha]
if index == 0 {
// Count commit number
commitCnt++
Expand All @@ -313,8 +291,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
br.Avatar = gotemplate.HTML(avatar)
br.RepoLink = repoLink
br.PartSha = part.Sha
br.PreviousSha = previousSha
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
br.PreviousSha = part.PreviousSha
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(part.PreviousSha), util.PathEscapeSegments(part.PreviousPath))
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
br.CommitMessage = commit.CommitMessage
br.CommitSince = commitSince
Expand Down

0 comments on commit e15fe85

Please sign in to comment.