Add new algo to generate diff for line range
Remove old algo used for cutting big diffs (it was very buggy)
This commit is contained in:
parent
73b325c5d8
commit
3cd5ee4e42
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -258,6 +259,128 @@ func (diff *Diff) NumFiles() int {
|
||||||
return len(diff.Files)
|
return len(diff.Files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
|
||||||
|
var hunkRegex = regexp.MustCompile(`^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@`)
|
||||||
|
|
||||||
|
func isHeader(lof string) bool {
|
||||||
|
return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) string {
|
||||||
|
if line == 0 || numbersOfLine == 0 {
|
||||||
|
// no line or num of lines => no diff
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(originalDiff)
|
||||||
|
hunk := make([]string, 0)
|
||||||
|
// begin is the start of the hunk containing searched line
|
||||||
|
// end is the end of the hunk ...
|
||||||
|
// currentLine is the line number on the side of the searched line (differentiated by old)
|
||||||
|
// otherLine is the line number on the opposite side of the searched line (differentiated by old)
|
||||||
|
var begin, end, currentLine, otherLine int64
|
||||||
|
var headerLines int
|
||||||
|
for scanner.Scan() {
|
||||||
|
lof := scanner.Text()
|
||||||
|
// Add header to enable parsing
|
||||||
|
if isHeader(lof) {
|
||||||
|
hunk = append(hunk, lof)
|
||||||
|
headerLines++
|
||||||
|
}
|
||||||
|
if currentLine > line {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Detect "hunk" with contains commented lof
|
||||||
|
if strings.HasPrefix(lof, "@@") {
|
||||||
|
// Already got our hunk. End of hunk detected!
|
||||||
|
if len(hunk) > headerLines {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
groups := hunkRegex.FindStringSubmatch(lof)
|
||||||
|
if old {
|
||||||
|
begin = com.StrTo(groups[1]).MustInt64()
|
||||||
|
end = com.StrTo(groups[2]).MustInt64()
|
||||||
|
// init otherLine with begin of opposite side
|
||||||
|
otherLine = com.StrTo(groups[3]).MustInt64()
|
||||||
|
} else {
|
||||||
|
begin = com.StrTo(groups[3]).MustInt64()
|
||||||
|
end = com.StrTo(groups[4]).MustInt64()
|
||||||
|
// init otherLine with begin of opposite side
|
||||||
|
otherLine = com.StrTo(groups[1]).MustInt64()
|
||||||
|
}
|
||||||
|
end += begin // end is for real only the number of lines in hunk
|
||||||
|
// lof is between begin and end
|
||||||
|
if begin <= line && end >= line {
|
||||||
|
hunk = append(hunk, lof)
|
||||||
|
currentLine = begin
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if len(hunk) > headerLines {
|
||||||
|
hunk = append(hunk, lof)
|
||||||
|
// Count lines in context
|
||||||
|
switch lof[0] {
|
||||||
|
case '+':
|
||||||
|
if !old {
|
||||||
|
currentLine++
|
||||||
|
} else {
|
||||||
|
otherLine++
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
if old {
|
||||||
|
currentLine++
|
||||||
|
} else {
|
||||||
|
otherLine++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
currentLine++
|
||||||
|
otherLine++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hunk)-headerLines < numbersOfLine {
|
||||||
|
// No need to cut the hunk => return existing hunk
|
||||||
|
return strings.Join(hunk, "\n")
|
||||||
|
}
|
||||||
|
var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64
|
||||||
|
if old {
|
||||||
|
oldBegin = currentLine
|
||||||
|
newBegin = otherLine
|
||||||
|
} else {
|
||||||
|
oldBegin = otherLine
|
||||||
|
newBegin = currentLine
|
||||||
|
}
|
||||||
|
// headers + hunk header
|
||||||
|
newHunk := make([]string, headerLines)
|
||||||
|
// transfer existing headers
|
||||||
|
for idx, lof := range hunk[:numbersOfLine] {
|
||||||
|
newHunk[idx] = lof
|
||||||
|
}
|
||||||
|
// transfer last n lines
|
||||||
|
for _, lof := range hunk[len(hunk)-numbersOfLine-1:] {
|
||||||
|
newHunk = append(newHunk, lof)
|
||||||
|
}
|
||||||
|
// calculate newBegin, ... by counting lines
|
||||||
|
for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- {
|
||||||
|
switch hunk[i][0] {
|
||||||
|
case '+':
|
||||||
|
newBegin--
|
||||||
|
newNumOfLines++
|
||||||
|
case '-':
|
||||||
|
oldBegin--
|
||||||
|
oldNumOfLines++
|
||||||
|
default:
|
||||||
|
oldBegin--
|
||||||
|
newBegin--
|
||||||
|
newNumOfLines++
|
||||||
|
oldNumOfLines++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// construct the new hunk header
|
||||||
|
newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
|
||||||
|
oldBegin, oldNumOfLines, newBegin, newNumOfLines)
|
||||||
|
return strings.Join(newHunk, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
const cmdDiffHead = "diff --git "
|
const cmdDiffHead = "diff --git "
|
||||||
|
|
||||||
// ParsePatch builds a Diff object from a io.Reader and some
|
// ParsePatch builds a Diff object from a io.Reader and some
|
||||||
|
|
@ -335,7 +458,6 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
|
||||||
if curFileLinesCount >= maxLines {
|
if curFileLinesCount >= maxLines {
|
||||||
curFile.IsIncomplete = true
|
curFile.IsIncomplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case line[0] == ' ':
|
case line[0] == ' ':
|
||||||
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||||
|
|
@ -36,6 +37,42 @@ func TestDiffToHTML(t *testing.T) {
|
||||||
}, DiffLineDel))
|
}, DiffLineDel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exampleDiff = `diff --git a/README.md b/README.md
|
||||||
|
--- a/README.md
|
||||||
|
+++ b/README.md
|
||||||
|
@@ -1,3 +1,6 @@
|
||||||
|
# gitea-github-migrator
|
||||||
|
+
|
||||||
|
+ Build Status
|
||||||
|
- Latest Release
|
||||||
|
Docker Pulls
|
||||||
|
+ cut off
|
||||||
|
+ cut off`
|
||||||
|
|
||||||
|
func TestCutDiffAroundLine(t *testing.T) {
|
||||||
|
result := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3)
|
||||||
|
resultByLine := strings.Split(result, "\n")
|
||||||
|
assert.Len(t, resultByLine, 7)
|
||||||
|
// Check if headers got transferred
|
||||||
|
assert.Equal(t, "diff --git a/README.md b/README.md", resultByLine[0])
|
||||||
|
assert.Equal(t, "--- a/README.md", resultByLine[1])
|
||||||
|
assert.Equal(t, "+++ b/README.md", resultByLine[2])
|
||||||
|
// Check if hunk header is calculated correctly
|
||||||
|
assert.Equal(t, "@@ -2,2 +3,2 @@", resultByLine[3])
|
||||||
|
// Check if line got transferred
|
||||||
|
assert.Equal(t, "+ Build Status", resultByLine[4])
|
||||||
|
|
||||||
|
// Must be same result as before since old line 3 == new line 5
|
||||||
|
newResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3)
|
||||||
|
assert.Equal(t, result, newResult, "Must be same result as before since old line 3 == new line 5")
|
||||||
|
|
||||||
|
newResult = CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 300)
|
||||||
|
assert.Equal(t, exampleDiff, newResult)
|
||||||
|
|
||||||
|
emptyResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 0)
|
||||||
|
assert.Empty(t, emptyResult)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDiff_LoadComments(t *testing.T) {
|
func TestDiff_LoadComments(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
issue := AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
|
issue := AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)
|
||||||
|
|
|
||||||
|
|
@ -400,6 +400,10 @@ func (c *Comment) AsDiff() (*Diff, error) {
|
||||||
if len(diff.Files) == 0 {
|
if len(diff.Files) == 0 {
|
||||||
return nil, fmt.Errorf("no file found for comment ID: %d", c.ID)
|
return nil, fmt.Errorf("no file found for comment ID: %d", c.ID)
|
||||||
}
|
}
|
||||||
|
secs := diff.Files[0].Sections
|
||||||
|
if len(secs) == 0 {
|
||||||
|
return nil, fmt.Errorf("no sections found for comment ID: %d", c.ID)
|
||||||
|
}
|
||||||
return diff, nil
|
return diff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -755,6 +759,7 @@ func CreateCodeComment(doer *User, repo *Repository, issue *Issue, content, tree
|
||||||
if err := GetRawDiffForFile(gitRepo.Path, pr.MergeBase, headCommitID, RawDiffNormal, treePath, patchBuf); err != nil {
|
if err := GetRawDiffForFile(gitRepo.Path, pr.MergeBase, headCommitID, RawDiffNormal, treePath, patchBuf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
patch := CutDiffAroundLine(strings.NewReader(patchBuf.String()), int64((&Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
|
||||||
|
|
||||||
return CreateComment(&CreateCommentOptions{
|
return CreateComment(&CreateCommentOptions{
|
||||||
Type: CommentTypeCode,
|
Type: CommentTypeCode,
|
||||||
|
|
@ -766,7 +771,7 @@ func CreateCodeComment(doer *User, repo *Repository, issue *Issue, content, tree
|
||||||
TreePath: treePath,
|
TreePath: treePath,
|
||||||
CommitSHA: commit.ID.String(),
|
CommitSHA: commit.ID.String(),
|
||||||
ReviewID: reviewID,
|
ReviewID: reviewID,
|
||||||
Patch: patchBuf.String(),
|
Patch: patch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user