fix: multiline comment invalidation (#12950)
Found issues during the process of invalidation of a multiline comment (link to #12582): * Update a line in the middle of the comment * Update/Delete the last line of the comment No problem with: * Deleting a line in the middle of the comment * Update/Delete the first line of the comment I added all these cases in the pull_review_test.go ### Tests for Go changes - I added test coverage for Go changes... - [X] in their respective `*_test.go` for unit tests. - I ran... - [X] `make pr-go` before pushing Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12950 Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
This commit is contained in:
committed by
Mathieu Fenniak
parent
c2dcdc9ba7
commit
3dc2b52b5f
@@ -897,8 +897,43 @@ func (c *Comment) CheckLineRangeValid(ctx context.Context, repo *repo_model.Repo
|
||||
}
|
||||
anchorResolvedLine := anchorBlame.LineNumber
|
||||
|
||||
// A line that no longer resolves is treated as "modified but present" and tolerated; only a
|
||||
// resolved line landing at an unexpected offset (lines inserted/removed inside the range) is a break.
|
||||
// Catch a line changed by a later commit by comparing each range line's current content
|
||||
// to the comment's Patch (its content at creation; lines the PR itself changed already match it).
|
||||
// The Patch uses the comment's line coordinates, so trust it only
|
||||
// when the anchor's recorded content equals its current content — otherwise fall back below.
|
||||
if expected := git.PatchRightSideContent(c.Patch); len(expected) > 0 {
|
||||
if headLines, ok := c.headFileLines(gitRepo, currentHead, anchorBlame.FilePath); ok {
|
||||
lineAt := func(n uint64) (string, bool) {
|
||||
if n >= 1 && n <= uint64(len(headLines)) {
|
||||
return headLines[n-1], true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
anchorExpected, hasAnchor := expected[int64(c.UnsignedLine())]
|
||||
anchorCurrent, hasCurrent := lineAt(anchorResolvedLine)
|
||||
if hasAnchor && hasCurrent && anchorExpected == anchorCurrent {
|
||||
trusted := true
|
||||
for i := int64(1); i <= c.ExtraLinesCount; i++ {
|
||||
exp, okExp := expected[int64(c.UnsignedLine())+i]
|
||||
cur, okCur := lineAt(anchorResolvedLine + uint64(i))
|
||||
if !okExp || !okCur {
|
||||
trusted = false // mapping incomplete -> fall back to the offset-only check
|
||||
break
|
||||
}
|
||||
if exp != cur {
|
||||
return "invalid", nil // a range line was changed after the comment was made
|
||||
}
|
||||
}
|
||||
if trusted {
|
||||
return "valid", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback (offset-only): a line that no longer resolves is treated as "modified but present"
|
||||
// and tolerated; only a resolved line landing at an unexpected offset (lines inserted/removed
|
||||
// inside the range) is a break.
|
||||
startLine := c.UnsignedLine()
|
||||
for i := int64(1); i <= c.ExtraLinesCount; i++ {
|
||||
blame, err := c.resolveLineAtHead(gitRepo, startLine+uint64(i), currentHead)
|
||||
@@ -919,6 +954,25 @@ func (c *Comment) CheckLineRangeValid(ctx context.Context, repo *repo_model.Repo
|
||||
return resultJSON == "valid", nil
|
||||
}
|
||||
|
||||
// headFileLines reads the content of treePath at the given commit and returns its lines
|
||||
// (dropping the trailing empty element produced by a final newline). ok is false when the
|
||||
// file can't be read at head.
|
||||
func (c *Comment) headFileLines(gitRepo *git.Repository, head, treePath string) (lines []string, ok bool) {
|
||||
commit, err := gitRepo.GetCommit(head)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
content, err := commit.GetFileContent(treePath, -1)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
lines = strings.Split(content, "\n")
|
||||
if n := len(lines); n > 0 && lines[n-1] == "" {
|
||||
lines = lines[:n-1]
|
||||
}
|
||||
return lines, true
|
||||
}
|
||||
|
||||
// CodeCommentLink returns the url to a comment in code
|
||||
func (c *Comment) CodeCommentLink(ctx context.Context) string {
|
||||
err := c.LoadIssue(ctx)
|
||||
|
||||
Reference in New Issue
Block a user