Read `previous` info from git blame (#28306)
Fixes #28280 Reads the `previous` info from the `git blame` output instead of calculating it afterwards.
This commit is contained in:
parent
3618715fab
commit
14354e4f8e
|
@ -11,6 +11,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
@ -18,8 +19,10 @@ import (
|
||||||
|
|
||||||
// BlamePart represents block of blame - continuous lines with one sha
|
// BlamePart represents block of blame - continuous lines with one sha
|
||||||
type BlamePart struct {
|
type BlamePart struct {
|
||||||
Sha string
|
Sha string
|
||||||
Lines []string
|
Lines []string
|
||||||
|
PreviousSha string
|
||||||
|
PreviousPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlameReader returns part of file blame one by one
|
// BlameReader returns part of file blame one by one
|
||||||
|
@ -43,30 +46,38 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||||
var blamePart *BlamePart
|
var blamePart *BlamePart
|
||||||
|
|
||||||
if r.lastSha != nil {
|
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 isPrefix bool
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for err != io.EOF {
|
for err != io.EOF {
|
||||||
line, isPrefix, err = r.bufferedReader.ReadLine()
|
lineBytes, isPrefix, err = r.bufferedReader.ReadLine()
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return blamePart, err
|
return blamePart, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(line) == 0 {
|
if len(lineBytes) == 0 {
|
||||||
// isPrefix will be false
|
// isPrefix will be false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
lines := shaLineRegex.FindSubmatch(line)
|
line := string(lineBytes)
|
||||||
|
|
||||||
|
lines := shaLineRegex.FindStringSubmatch(line)
|
||||||
if lines != nil {
|
if lines != nil {
|
||||||
sha1 := string(lines[1])
|
sha1 := lines[1]
|
||||||
|
|
||||||
if blamePart == nil {
|
if blamePart == nil {
|
||||||
blamePart = &BlamePart{sha1, make([]string, 0)}
|
blamePart = &BlamePart{
|
||||||
|
Sha: sha1,
|
||||||
|
Lines: make([]string, 0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if blamePart.Sha != sha1 {
|
if blamePart.Sha != sha1 {
|
||||||
|
@ -81,9 +92,11 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||||
return blamePart, nil
|
return blamePart, nil
|
||||||
}
|
}
|
||||||
} else if line[0] == '\t' {
|
} else if line[0] == '\t' {
|
||||||
code := line[1:]
|
blamePart.Lines = append(blamePart.Lines, line[1:])
|
||||||
|
} else if strings.HasPrefix(line, "previous ") {
|
||||||
blamePart.Lines = append(blamePart.Lines, string(code))
|
parts := strings.SplitN(line[len("previous "):], " ", 2)
|
||||||
|
blamePart.PreviousSha = parts[0]
|
||||||
|
blamePart.PreviousPath = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to munch to end of line...
|
// need to munch to end of line...
|
||||||
|
|
|
@ -24,15 +24,17 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||||
|
|
||||||
parts := []*BlamePart{
|
parts := []*BlamePart{
|
||||||
{
|
{
|
||||||
"72866af952e98d02a73003501836074b286a78f6",
|
Sha: "72866af952e98d02a73003501836074b286a78f6",
|
||||||
[]string{
|
Lines: []string{
|
||||||
"# test_repo",
|
"# test_repo",
|
||||||
"Test repository for testing migration from github to gitea",
|
"Test repository for testing migration from github to gitea",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
Sha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||||
[]string{"", "Do not make any changes to this repo it is used for unit testing"},
|
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||||
|
PreviousSha: "72866af952e98d02a73003501836074b286a78f6",
|
||||||
|
PreviousPath: "README.md",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,16 +66,18 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||||
|
|
||||||
full := []*BlamePart{
|
full := []*BlamePart{
|
||||||
{
|
{
|
||||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||||
[]string{"line", "line"},
|
Lines: []string{"line", "line"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
Sha: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||||
[]string{"changed line"},
|
Lines: []string{"changed line"},
|
||||||
|
PreviousSha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||||
|
PreviousPath: "blame.txt",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||||
[]string{"line", "line", ""},
|
Lines: []string{"line", "line", ""},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +93,8 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||||
Bypass: false,
|
Bypass: false,
|
||||||
Parts: []*BlamePart{
|
Parts: []*BlamePart{
|
||||||
{
|
{
|
||||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||||
[]string{"line", "line", "changed line", "line", "line", ""},
|
Lines: []string{"line", "line", "changed line", "line", "line", ""},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -114,12 +114,12 @@ func RefBlame(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
commitNames, previousCommits := processBlameParts(ctx, result.Parts)
|
commitNames := processBlameParts(ctx, result.Parts)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBlame(ctx, result.Parts, commitNames, previousCommits)
|
renderBlame(ctx, result.Parts, commitNames)
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||||
}
|
}
|
||||||
|
@ -185,12 +185,9 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error {
|
||||||
return nil
|
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
|
// store commit data by SHA to look up avatar info etc
|
||||||
commitNames := make(map[string]*user_model.UserCommit)
|
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
|
// and as blameParts can reference the same commits multiple
|
||||||
// times, we cache the lookup work locally
|
// times, we cache the lookup work locally
|
||||||
commits := make([]*git.Commit, 0, len(blameParts))
|
commits := make([]*git.Commit, 0, len(blameParts))
|
||||||
|
@ -214,29 +211,11 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("Repo.GitRepo.GetCommit", err)
|
ctx.ServerError("Repo.GitRepo.GetCommit", err)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
commitCache[sha] = commit
|
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)
|
commits = append(commits, commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,10 +224,10 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||||
commitNames[c.ID.String()] = c
|
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
|
repoLink := ctx.Repo.RepoLink
|
||||||
|
|
||||||
language := ""
|
language := ""
|
||||||
|
@ -295,7 +274,6 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||||
}
|
}
|
||||||
|
|
||||||
commit := commitNames[part.Sha]
|
commit := commitNames[part.Sha]
|
||||||
previousSha := previousCommits[part.Sha]
|
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
// Count commit number
|
// Count commit number
|
||||||
commitCnt++
|
commitCnt++
|
||||||
|
@ -313,8 +291,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||||
br.Avatar = gotemplate.HTML(avatar)
|
br.Avatar = gotemplate.HTML(avatar)
|
||||||
br.RepoLink = repoLink
|
br.RepoLink = repoLink
|
||||||
br.PartSha = part.Sha
|
br.PartSha = part.Sha
|
||||||
br.PreviousSha = previousSha
|
br.PreviousSha = part.PreviousSha
|
||||||
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
|
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.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
|
||||||
br.CommitMessage = commit.CommitMessage
|
br.CommitMessage = commit.CommitMessage
|
||||||
br.CommitSince = commitSince
|
br.CommitSince = commitSince
|
||||||
|
|
Loading…
Reference in New Issue