mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	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.  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:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user