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:
Jonas Franz 2018-07-06 15:24:07 +02:00
parent 73b325c5d8
commit 3cd5ee4e42
No known key found for this signature in database
GPG Key ID: 506AEEBE80BEDECD
3 changed files with 166 additions and 2 deletions

View File

@ -14,6 +14,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"regexp"
"sort"
"strconv"
"strings"
@ -258,6 +259,128 @@ func (diff *Diff) NumFiles() int {
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 "
// 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 {
curFile.IsIncomplete = true
}
switch {
case line[0] == ' ':
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}

View File

@ -2,6 +2,7 @@ package models
import (
"html/template"
"strings"
"testing"
dmp "github.com/sergi/go-diff/diffmatchpatch"
@ -36,6 +37,42 @@ func TestDiffToHTML(t *testing.T) {
}, 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) {
assert.NoError(t, PrepareTestDatabase())
issue := AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue)

View File

@ -400,6 +400,10 @@ func (c *Comment) AsDiff() (*Diff, error) {
if len(diff.Files) == 0 {
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
}
@ -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 {
return nil, err
}
patch := CutDiffAroundLine(strings.NewReader(patchBuf.String()), int64((&Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
return CreateComment(&CreateCommentOptions{
Type: CommentTypeCode,
@ -766,7 +771,7 @@ func CreateCodeComment(doer *User, repo *Repository, issue *Issue, content, tree
TreePath: treePath,
CommitSHA: commit.ID.String(),
ReviewID: reviewID,
Patch: patchBuf.String(),
Patch: patch,
})
}