Skip to content
21 changes: 17 additions & 4 deletions services/gitdiff/gitdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -1481,21 +1481,25 @@ func SyncUserSpecificDiff(ctx context.Context, userID int64, pull *issues_model.
filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState)
outer:
for _, diffFile := range diff.Files {
fileViewedState := review.UpdatedFiles[diffFile.GetDiffFileName()]
filename := diffFile.GetDiffFileName()
fileViewedState := review.UpdatedFiles[filename]

// Check whether it was previously detected that the file has changed since the last review
Comment thread
silverwind marked this conversation as resolved.
Outdated
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 {
for i, changedFile := range changedFiles {
diffFile.HasChangedSinceLastReview = filename == changedFile
if diffFile.HasChangedSinceLastReview {
filesChangedSinceLastDiff[filename] = pull_model.HasChanged

// File changes are processed, no need to check them again
changedFiles[i] = changedFiles[len(changedFiles)-1]
changedFiles = changedFiles[:len(changedFiles)-1]

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
}
}
Expand All @@ -1505,6 +1509,15 @@ outer:
}
}

// All changed files still present at this point aren't part of the diff anymore, this occurs
// when a file was modified in a previous commit of the diff and the modification got reverted afterwards.
// Marking the files as unviewed to prevent errors where a non-existing file has a view state
for _, changedFile := range changedFiles {
if _, ok := review.UpdatedFiles[changedFile]; ok {
filesChangedSinceLastDiff[changedFile] = pull_model.Unviewed
}
}

if len(filesChangedSinceLastDiff) > 0 {
// 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.
Expand Down
55 changes: 55 additions & 0 deletions services/gitdiff/gitdiff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"

issues_model "code.gitea.io/gitea/models/issues"
pull_model "code.gitea.io/gitea/models/pull"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
Expand Down Expand Up @@ -1143,3 +1144,57 @@ func TestHighlightCodeLines(t *testing.T) {
}, ret)
})
}

func TestSyncUserSpecificDiff_UpdatedFiles(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 7})
assert.NoError(t, pull.LoadBaseRepo(t.Context()))

stdin := `commit refs/heads/branch1
committer test <test@example.com> 1772749114 +0000
data 7
revert
from 1978192d98bb1b65e11c2cf37da854fbf94bffd6
D test10.txt`
require.NoError(t, gitcmd.NewCommand("fast-import").WithDir(pull.BaseRepo.RepoPath()).WithStdinBytes([]byte(stdin)).Run(t.Context()))

gitRepo, err := git.OpenRepository(t.Context(), pull.BaseRepo.RepoPath())
assert.NoError(t, err)
defer gitRepo.Close()

firstReviewCommit := "1978192d98bb1b65e11c2cf37da854fbf94bffd6"
firstReviewUpdatedFiles := map[string]pull_model.ViewedState{
"test1.txt": pull_model.Viewed,
"test10.txt": pull_model.Viewed,
}

_, err = pull_model.UpdateReviewState(t.Context(), user.ID, pull.ID, firstReviewCommit, firstReviewUpdatedFiles)
assert.NoError(t, err)
firstReview, err := pull_model.GetNewestReviewState(t.Context(), user.ID, pull.ID)
assert.NoError(t, err)
assert.NotNil(t, firstReview)
assert.Equal(t, firstReviewUpdatedFiles, firstReview.UpdatedFiles)
assert.Equal(t, 2, firstReview.GetViewedFileCount())

secondReviewCommit := "ec334573ae49726c78c6ff793138fd5334f31695"
secondReviewUpdatedFiles := map[string]pull_model.ViewedState{
"test1.txt": pull_model.Viewed,
"test10.txt": pull_model.Unviewed,
}

opts := &DiffOptions{
AfterCommitID: secondReviewCommit,
BeforeCommitID: pull.MergeBase,
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
}
diff, err := GetDiffForAPI(t.Context(), gitRepo, opts)
assert.NoError(t, err)
secondReview, err := SyncUserSpecificDiff(t.Context(), user.ID, pull, gitRepo, diff, opts)
assert.NoError(t, err)
assert.NotNil(t, secondReview)
assert.Equal(t, secondReviewUpdatedFiles, secondReview.UpdatedFiles)
assert.Equal(t, 1, secondReview.GetViewedFileCount())
}