mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Allow to mark files in a PR as viewed (#19007)
Users can now mark files in PRs as viewed, resulting in them not being shown again by default when they reopen the PR again.
This commit is contained in:
		@@ -22,6 +22,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	pull_model "code.gitea.io/gitea/models/pull"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/analyze"
 | 
			
		||||
	"code.gitea.io/gitea/modules/charset"
 | 
			
		||||
@@ -602,25 +603,27 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) Dif
 | 
			
		||||
 | 
			
		||||
// DiffFile represents a file diff.
 | 
			
		||||
type DiffFile struct {
 | 
			
		||||
	Name                    string
 | 
			
		||||
	OldName                 string
 | 
			
		||||
	Index                   int
 | 
			
		||||
	Addition, Deletion      int
 | 
			
		||||
	Type                    DiffFileType
 | 
			
		||||
	IsCreated               bool
 | 
			
		||||
	IsDeleted               bool
 | 
			
		||||
	IsBin                   bool
 | 
			
		||||
	IsLFSFile               bool
 | 
			
		||||
	IsRenamed               bool
 | 
			
		||||
	IsAmbiguous             bool
 | 
			
		||||
	IsSubmodule             bool
 | 
			
		||||
	Sections                []*DiffSection
 | 
			
		||||
	IsIncomplete            bool
 | 
			
		||||
	IsIncompleteLineTooLong bool
 | 
			
		||||
	IsProtected             bool
 | 
			
		||||
	IsGenerated             bool
 | 
			
		||||
	IsVendored              bool
 | 
			
		||||
	Language                string
 | 
			
		||||
	Name                      string
 | 
			
		||||
	OldName                   string
 | 
			
		||||
	Index                     int
 | 
			
		||||
	Addition, Deletion        int
 | 
			
		||||
	Type                      DiffFileType
 | 
			
		||||
	IsCreated                 bool
 | 
			
		||||
	IsDeleted                 bool
 | 
			
		||||
	IsBin                     bool
 | 
			
		||||
	IsLFSFile                 bool
 | 
			
		||||
	IsRenamed                 bool
 | 
			
		||||
	IsAmbiguous               bool
 | 
			
		||||
	IsSubmodule               bool
 | 
			
		||||
	Sections                  []*DiffSection
 | 
			
		||||
	IsIncomplete              bool
 | 
			
		||||
	IsIncompleteLineTooLong   bool
 | 
			
		||||
	IsProtected               bool
 | 
			
		||||
	IsGenerated               bool
 | 
			
		||||
	IsVendored                bool
 | 
			
		||||
	IsViewed                  bool // User specific
 | 
			
		||||
	HasChangedSinceLastReview bool // User specific
 | 
			
		||||
	Language                  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetType returns type of diff file.
 | 
			
		||||
@@ -663,6 +666,18 @@ func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID,
 | 
			
		||||
	return tailSection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDiffFileName returns the name of the diff file, or its old name in case it was deleted
 | 
			
		||||
func (diffFile *DiffFile) GetDiffFileName() string {
 | 
			
		||||
	if diffFile.Name == "" {
 | 
			
		||||
		return diffFile.OldName
 | 
			
		||||
	}
 | 
			
		||||
	return diffFile.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (diffFile *DiffFile) ShouldBeHidden() bool {
 | 
			
		||||
	return diffFile.IsGenerated || diffFile.IsViewed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCommitFileLineCount(commit *git.Commit, filePath string) int {
 | 
			
		||||
	blob, err := commit.GetBlobByPath(filePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -677,10 +692,12 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int {
 | 
			
		||||
 | 
			
		||||
// Diff represents a difference between two git trees.
 | 
			
		||||
type Diff struct {
 | 
			
		||||
	Start, End                             string
 | 
			
		||||
	NumFiles, TotalAddition, TotalDeletion int
 | 
			
		||||
	Files                                  []*DiffFile
 | 
			
		||||
	IsIncomplete                           bool
 | 
			
		||||
	Start, End                   string
 | 
			
		||||
	NumFiles                     int
 | 
			
		||||
	TotalAddition, TotalDeletion int
 | 
			
		||||
	Files                        []*DiffFile
 | 
			
		||||
	IsIncomplete                 bool
 | 
			
		||||
	NumViewedFiles               int // user-specific
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadComments loads comments into each line
 | 
			
		||||
@@ -1497,6 +1514,70 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
 | 
			
		||||
	return diff, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SyncAndGetUserSpecificDiff is like GetDiff, except that user specific data such as which files the given user has already viewed on the given PR will also be set
 | 
			
		||||
// Additionally, the database asynchronously is updated if files have changed since the last review
 | 
			
		||||
func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *models.PullRequest, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
 | 
			
		||||
	diff, err := GetDiff(gitRepo, opts, files...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	review, err := pull_model.GetNewestReviewState(ctx, userID, pull.ID)
 | 
			
		||||
	if err != nil || review == nil || review.UpdatedFiles == nil {
 | 
			
		||||
		return diff, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	latestCommit := opts.AfterCommitID
 | 
			
		||||
	if latestCommit == "" {
 | 
			
		||||
		latestCommit = pull.HeadBranch // opts.AfterCommitID is preferred because it handles PRs from forks correctly and the branch name doesn't
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return diff, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState)
 | 
			
		||||
outer:
 | 
			
		||||
	for _, diffFile := range diff.Files {
 | 
			
		||||
		fileViewedState := review.UpdatedFiles[diffFile.GetDiffFileName()]
 | 
			
		||||
 | 
			
		||||
		// Check whether it was previously detected that the file has changed since the last review
 | 
			
		||||
		if fileViewedState == pull_model.HasChanged {
 | 
			
		||||
			diffFile.HasChangedSinceLastReview = true
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		filename := diffFile.GetDiffFileName()
 | 
			
		||||
 | 
			
		||||
		// Check explicitly whether the file has changed since the last review
 | 
			
		||||
		for _, changedFile := range changedFiles {
 | 
			
		||||
			diffFile.HasChangedSinceLastReview = filename == changedFile
 | 
			
		||||
			if diffFile.HasChangedSinceLastReview {
 | 
			
		||||
				filesChangedSinceLastDiff[filename] = pull_model.HasChanged
 | 
			
		||||
				continue outer // We don't want to check if the file is viewed here as that would fold the file, which is in this case unwanted
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Check whether the file has already been viewed
 | 
			
		||||
		if fileViewedState == pull_model.Viewed {
 | 
			
		||||
			diffFile.IsViewed = true
 | 
			
		||||
			diff.NumViewedFiles++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Explicitly store files that have changed in the database, if any is present at all.
 | 
			
		||||
	// This has the benefit that the "Has Changed" attribute will be present as long as the user does not explicitly mark this file as viewed, so it will even survive a page reload after marking another file as viewed.
 | 
			
		||||
	// On the other hand, this means that even if a commit reverting an unseen change is committed, the file will still be seen as changed.
 | 
			
		||||
	if len(filesChangedSinceLastDiff) > 0 {
 | 
			
		||||
		err := pull_model.UpdateReviewState(ctx, review.UserID, review.PullID, review.CommitSHA, filesChangedSinceLastDiff)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Warn("Could not update review for user %d, pull %d, commit %s and the changed files %v: %v", review.UserID, review.PullID, review.CommitSHA, filesChangedSinceLastDiff, err)
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return diff, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommentAsDiff returns c.Patch as *Diff
 | 
			
		||||
func CommentAsDiff(c *models.Comment) (*Diff, error) {
 | 
			
		||||
	diff, err := ParsePatch(setting.Git.MaxGitDiffLines,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user