mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 01:34:27 +00:00 
			
		
		
		
	Backport #28306 by @KN4CK3R Fixes #28280 Reads the `previous` info from the `git blame` output instead of calculating it afterwards. Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
This commit is contained in:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user