Support .git-blame-ignore-revs file (#26395)

Closes #26329

This PR adds the ability to ignore revisions specified in the
`.git-blame-ignore-revs` file in the root of the repository.


![grafik](9e91be0c-6e9c-431c-bbe9-5f80154251c8)

The banner is displayed in this case. I intentionally did not add a UI
way to bypass the ignore file (same behaviour as Github) but you can add
`?bypass-blame-ignore=true` to the url manually.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
KN4CK3R 2023-09-16 19:42:34 +02:00 committed by GitHub
parent c766140dad
commit ed64f1c2b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 306 additions and 52 deletions

View file

@ -8,9 +8,9 @@ import (
gotemplate "html/template"
"net/http"
"net/url"
"strconv"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context"
@ -45,10 +45,6 @@ func RefBlame(ctx *context.Context) {
return
}
userName := ctx.Repo.Owner.Name
repoName := ctx.Repo.Repository.Name
commitID := ctx.Repo.CommitID
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
treeLink := branchLink
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
@ -101,26 +97,16 @@ func RefBlame(ctx *context.Context) {
return
}
blameReader, err := git.CreateBlameReader(ctx, repo_model.RepoPath(userName, repoName), commitID, fileName)
bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore"))
result, err := performBlame(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Commit, fileName, bypassBlameIgnore)
if err != nil {
ctx.NotFound("CreateBlameReader", err)
return
}
defer blameReader.Close()
blameParts := make([]git.BlamePart, 0)
for {
blamePart, err := blameReader.NextPart()
if err != nil {
ctx.NotFound("NextPart", err)
return
}
if blamePart == nil {
break
}
blameParts = append(blameParts, *blamePart)
}
ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs
ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile
// Get Topics of this repo
renderRepoTopics(ctx)
@ -128,16 +114,77 @@ func RefBlame(ctx *context.Context) {
return
}
commitNames, previousCommits := processBlameParts(ctx, blameParts)
commitNames, previousCommits := processBlameParts(ctx, result.Parts)
if ctx.Written() {
return
}
renderBlame(ctx, blameParts, commitNames, previousCommits)
renderBlame(ctx, result.Parts, commitNames, previousCommits)
ctx.HTML(http.StatusOK, tplRepoHome)
}
type blameResult struct {
Parts []git.BlamePart
UsesIgnoreRevs bool
FaultyIgnoreRevsFile bool
}
func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
blameReader, err := git.CreateBlameReader(ctx, repoPath, commit, file, bypassBlameIgnore)
if err != nil {
return nil, err
}
r := &blameResult{}
if err := fillBlameResult(blameReader, r); err != nil {
_ = blameReader.Close()
return nil, err
}
err = blameReader.Close()
if err != nil {
if len(r.Parts) == 0 && r.UsesIgnoreRevs {
// try again without ignored revs
blameReader, err = git.CreateBlameReader(ctx, repoPath, commit, file, true)
if err != nil {
return nil, err
}
r := &blameResult{
FaultyIgnoreRevsFile: true,
}
if err := fillBlameResult(blameReader, r); err != nil {
_ = blameReader.Close()
return nil, err
}
return r, blameReader.Close()
}
return nil, err
}
return r, nil
}
func fillBlameResult(br *git.BlameReader, r *blameResult) error {
r.UsesIgnoreRevs = br.UsesIgnoreRevs()
r.Parts = make([]git.BlamePart, 0, 5)
for {
blamePart, err := br.NextPart()
if err != nil {
return fmt.Errorf("BlameReader.NextPart failed: %w", err)
}
if blamePart == nil {
break
}
r.Parts = append(r.Parts, *blamePart)
}
return nil
}
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) {
// store commit data by SHA to look up avatar info etc
commitNames := make(map[string]*user_model.UserCommit)