Fix a couple of CommentAsPatch issues. (#14804)

* CutDiffAroundLine makes the incorrect assumption that `---` and `+++` always represent part of the header of a diff.

This PR adds a flag to its parsing to prevent this problem and adds a streaming parsing technique to CutDiffAroundLine using an io.pipe instead of just sending data to an unbounded buffer.

Fix #14711

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Handle unquoted comment patch files

When making comment patches unfortunately the patch does not always quote the filename
This makes the diff --git header ambiguous again.

This PR finally adds handling for ambiguity in to parse patch

Fix #14812

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add in testing for no error

There is no way currently for CutDiffAroundLine in this test to cause an
error however, it should still be tested.

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2021-02-27 18:46:14 +00:00 committed by GitHub
parent 904a26c57c
commit 3d8b5ad5f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 270 additions and 41 deletions

View File

@ -125,30 +125,39 @@ var hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+
const cmdDiffHead = "diff --git " const cmdDiffHead = "diff --git "
func isHeader(lof string) bool { func isHeader(lof string, inHunk bool) bool {
return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++") return strings.HasPrefix(lof, cmdDiffHead) || (!inHunk && (strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")))
} }
// CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown // CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown
// it also recalculates hunks and adds the appropriate headers to the new diff. // it also recalculates hunks and adds the appropriate headers to the new diff.
// Warning: Only one-file diffs are allowed. // Warning: Only one-file diffs are allowed.
func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) string { func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) (string, error) {
if line == 0 || numbersOfLine == 0 { if line == 0 || numbersOfLine == 0 {
// no line or num of lines => no diff // no line or num of lines => no diff
return "" return "", nil
} }
scanner := bufio.NewScanner(originalDiff) scanner := bufio.NewScanner(originalDiff)
hunk := make([]string, 0) hunk := make([]string, 0)
// begin is the start of the hunk containing searched line // begin is the start of the hunk containing searched line
// end is the end of the hunk ... // end is the end of the hunk ...
// currentLine is the line number on the side of the searched line (differentiated by old) // 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) // otherLine is the line number on the opposite side of the searched line (differentiated by old)
var begin, end, currentLine, otherLine int64 var begin, end, currentLine, otherLine int64
var headerLines int var headerLines int
inHunk := false
for scanner.Scan() { for scanner.Scan() {
lof := scanner.Text() lof := scanner.Text()
// Add header to enable parsing // Add header to enable parsing
if isHeader(lof) {
if isHeader(lof, inHunk) {
if strings.HasPrefix(lof, cmdDiffHead) {
inHunk = false
}
hunk = append(hunk, lof) hunk = append(hunk, lof)
headerLines++ headerLines++
} }
@ -157,6 +166,7 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
} }
// Detect "hunk" with contains commented lof // Detect "hunk" with contains commented lof
if strings.HasPrefix(lof, "@@") { if strings.HasPrefix(lof, "@@") {
inHunk = true
// Already got our hunk. End of hunk detected! // Already got our hunk. End of hunk detected!
if len(hunk) > headerLines { if len(hunk) > headerLines {
break break
@ -213,15 +223,19 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
} }
} }
} }
err := scanner.Err()
if err != nil {
return "", err
}
// No hunk found // No hunk found
if currentLine == 0 { if currentLine == 0 {
return "" return "", nil
} }
// headerLines + hunkLine (1) = totalNonCodeLines // headerLines + hunkLine (1) = totalNonCodeLines
if len(hunk)-headerLines-1 <= numbersOfLine { if len(hunk)-headerLines-1 <= numbersOfLine {
// No need to cut the hunk => return existing hunk // No need to cut the hunk => return existing hunk
return strings.Join(hunk, "\n") return strings.Join(hunk, "\n"), nil
} }
var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64 var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64
if old { if old {
@ -256,5 +270,5 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
// construct the new hunk header // construct the new hunk header
newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@", newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
oldBegin, oldNumOfLines, newBegin, newNumOfLines) oldBegin, oldNumOfLines, newBegin, newNumOfLines)
return strings.Join(newHunk, "\n") return strings.Join(newHunk, "\n"), nil
} }

View File

@ -23,8 +23,28 @@ const exampleDiff = `diff --git a/README.md b/README.md
+ cut off + cut off
+ cut off` + cut off`
const breakingDiff = `diff --git a/aaa.sql b/aaa.sql
index d8e4c92..19dc8ad 100644
--- a/aaa.sql
+++ b/aaa.sql
@@ -1,9 +1,10 @@
--some comment
--- some comment 5
+--some coment 2
+-- some comment 3
create or replace procedure test(p1 varchar2)
is
begin
---new comment
dbms_output.put_line(p1);
+--some other comment
end;
/
`
func TestCutDiffAroundLine(t *testing.T) { func TestCutDiffAroundLine(t *testing.T) {
result := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3) result, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3)
assert.NoError(t, err)
resultByLine := strings.Split(result, "\n") resultByLine := strings.Split(result, "\n")
assert.Len(t, resultByLine, 7) assert.Len(t, resultByLine, 7)
// Check if headers got transferred // Check if headers got transferred
@ -37,18 +57,50 @@ func TestCutDiffAroundLine(t *testing.T) {
assert.Equal(t, "+ Build Status", resultByLine[4]) assert.Equal(t, "+ Build Status", resultByLine[4])
// Must be same result as before since old line 3 == new line 5 // Must be same result as before since old line 3 == new line 5
newResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3) newResult, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3)
assert.NoError(t, err)
assert.Equal(t, result, newResult, "Must be same result as before since old line 3 == new line 5") 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) newResult, err = CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 300)
assert.NoError(t, err)
assert.Equal(t, exampleDiff, newResult) assert.Equal(t, exampleDiff, newResult)
emptyResult := CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 0) emptyResult, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 6, false, 0)
assert.NoError(t, err)
assert.Empty(t, emptyResult) assert.Empty(t, emptyResult)
// Line is out of scope // Line is out of scope
emptyResult = CutDiffAroundLine(strings.NewReader(exampleDiff), 434, false, 0) emptyResult, err = CutDiffAroundLine(strings.NewReader(exampleDiff), 434, false, 0)
assert.NoError(t, err)
assert.Empty(t, emptyResult) assert.Empty(t, emptyResult)
// Handle minus diffs properly
minusDiff, err := CutDiffAroundLine(strings.NewReader(breakingDiff), 2, false, 4)
assert.NoError(t, err)
expected := `diff --git a/aaa.sql b/aaa.sql
--- a/aaa.sql
+++ b/aaa.sql
@@ -1,9 +1,10 @@
--some comment
--- some comment 5
+--some coment 2`
assert.Equal(t, expected, minusDiff)
// Handle minus diffs properly
minusDiff, err = CutDiffAroundLine(strings.NewReader(breakingDiff), 3, false, 4)
assert.NoError(t, err)
expected = `diff --git a/aaa.sql b/aaa.sql
--- a/aaa.sql
+++ b/aaa.sql
@@ -1,9 +1,10 @@
--some comment
--- some comment 5
+--some coment 2
+-- some comment 3`
assert.Equal(t, expected, minusDiff)
} }
func BenchmarkCutDiffAroundLine(b *testing.B) { func BenchmarkCutDiffAroundLine(b *testing.B) {
@ -69,7 +121,7 @@ func ExampleCutDiffAroundLine() {
Docker Pulls Docker Pulls
+ cut off + cut off
+ cut off` + cut off`
result := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3) result, _ := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3)
println(result) println(result)
} }

View File

@ -6,7 +6,6 @@
package migrations package migrations
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
@ -802,13 +801,20 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
} }
var patch string var patch string
patchBuf := new(bytes.Buffer) reader, writer := io.Pipe()
if err := git.GetRepoRawDiffForFile(g.gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, comment.TreePath, patchBuf); err != nil { defer func() {
// We should ignore the error since the commit maybe removed when force push to the pull request _ = reader.Close()
log.Warn("GetRepoRawDiffForFile failed when migrating [%s, %s, %s, %s]: %v", g.gitRepo.Path, pr.MergeBase, headCommitID, comment.TreePath, err) _ = writer.Close()
} else { }()
patch = git.CutDiffAroundLine(patchBuf, int64((&models.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines) go func() {
} if err := git.GetRepoRawDiffForFile(g.gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, comment.TreePath, writer); err != nil {
// We should ignore the error since the commit maybe removed when force push to the pull request
log.Warn("GetRepoRawDiffForFile failed when migrating [%s, %s, %s, %s]: %v", g.gitRepo.Path, pr.MergeBase, headCommitID, comment.TreePath, err)
}
_ = writer.Close()
}()
patch, _ = git.CutDiffAroundLine(reader, int64((&models.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
var c = models.Comment{ var c = models.Comment{
Type: models.CommentTypeCode, Type: models.CommentTypeCode,

View File

@ -583,6 +583,7 @@ type DiffFile struct {
IsBin bool IsBin bool
IsLFSFile bool IsLFSFile bool
IsRenamed bool IsRenamed bool
IsAmbiguous bool
IsSubmodule bool IsSubmodule bool
Sections []*DiffSection Sections []*DiffSection
IsIncomplete bool IsIncomplete bool
@ -776,12 +777,32 @@ parsingLoop:
if strings.HasSuffix(line, " 160000\n") { if strings.HasSuffix(line, " 160000\n") {
curFile.IsSubmodule = true curFile.IsSubmodule = true
} }
case strings.HasPrefix(line, "rename from "):
curFile.IsRenamed = true
curFile.Type = DiffFileRename
if curFile.IsAmbiguous {
curFile.OldName = line[len("rename from ") : len(line)-1]
}
case strings.HasPrefix(line, "rename to "):
curFile.IsRenamed = true
curFile.Type = DiffFileRename
if curFile.IsAmbiguous {
curFile.Name = line[len("rename to ") : len(line)-1]
curFile.IsAmbiguous = false
}
case strings.HasPrefix(line, "copy from "): case strings.HasPrefix(line, "copy from "):
curFile.IsRenamed = true curFile.IsRenamed = true
curFile.Type = DiffFileCopy curFile.Type = DiffFileCopy
if curFile.IsAmbiguous {
curFile.OldName = line[len("copy from ") : len(line)-1]
}
case strings.HasPrefix(line, "copy to "): case strings.HasPrefix(line, "copy to "):
curFile.IsRenamed = true curFile.IsRenamed = true
curFile.Type = DiffFileCopy curFile.Type = DiffFileCopy
if curFile.IsAmbiguous {
curFile.Name = line[len("copy to ") : len(line)-1]
curFile.IsAmbiguous = false
}
case strings.HasPrefix(line, "new file"): case strings.HasPrefix(line, "new file"):
curFile.Type = DiffFileAdd curFile.Type = DiffFileAdd
curFile.IsCreated = true curFile.IsCreated = true
@ -803,9 +824,35 @@ parsingLoop:
case strings.HasPrefix(line, "Binary"): case strings.HasPrefix(line, "Binary"):
curFile.IsBin = true curFile.IsBin = true
case strings.HasPrefix(line, "--- "): case strings.HasPrefix(line, "--- "):
// Do nothing with this line // Handle ambiguous filenames
if curFile.IsAmbiguous {
if len(line) > 6 && line[4] == 'a' {
curFile.OldName = line[6 : len(line)-1]
if line[len(line)-2] == '\t' {
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
}
} else {
curFile.OldName = ""
}
}
// Otherwise do nothing with this line
case strings.HasPrefix(line, "+++ "): case strings.HasPrefix(line, "+++ "):
// Do nothing with this line // Handle ambiguous filenames
if curFile.IsAmbiguous {
if len(line) > 6 && line[4] == 'b' {
curFile.Name = line[6 : len(line)-1]
if line[len(line)-2] == '\t' {
curFile.Name = curFile.Name[:len(curFile.Name)-1]
}
if curFile.OldName == "" {
curFile.OldName = curFile.Name
}
} else {
curFile.Name = curFile.OldName
}
curFile.IsAmbiguous = false
}
// Otherwise do nothing with this line, but now switch to parsing hunks
lineBytes, isFragment, err := parseHunks(curFile, maxLines, maxLineCharacters, input) lineBytes, isFragment, err := parseHunks(curFile, maxLines, maxLineCharacters, input)
diff.TotalAddition += curFile.Addition diff.TotalAddition += curFile.Addition
diff.TotalDeletion += curFile.Deletion diff.TotalDeletion += curFile.Deletion
@ -1056,13 +1103,33 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
rd := strings.NewReader(line[len(cmdDiffHead):] + " ") rd := strings.NewReader(line[len(cmdDiffHead):] + " ")
curFile.Type = DiffFileChange curFile.Type = DiffFileChange
curFile.OldName = readFileName(rd) oldNameAmbiguity := false
curFile.Name = readFileName(rd) newNameAmbiguity := false
curFile.OldName, oldNameAmbiguity = readFileName(rd)
curFile.Name, newNameAmbiguity = readFileName(rd)
if oldNameAmbiguity && newNameAmbiguity {
curFile.IsAmbiguous = true
// OK we should bet that the oldName and the newName are the same if they can be made to be same
// So we need to start again ...
if (len(line)-len(cmdDiffHead)-1)%2 == 0 {
// diff --git a/b b/b b/b b/b b/b b/b
//
midpoint := (len(line) + len(cmdDiffHead) - 1) / 2
new, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:]
if len(new) > 2 && len(old) > 2 && new[2:] == old[2:] {
curFile.OldName = old[2:]
curFile.Name = old[2:]
}
}
}
curFile.IsRenamed = curFile.Name != curFile.OldName curFile.IsRenamed = curFile.Name != curFile.OldName
return curFile return curFile
} }
func readFileName(rd *strings.Reader) string { func readFileName(rd *strings.Reader) (string, bool) {
ambiguity := false
var name string var name string
char, _ := rd.ReadByte() char, _ := rd.ReadByte()
_ = rd.UnreadByte() _ = rd.UnreadByte()
@ -1072,9 +1139,24 @@ func readFileName(rd *strings.Reader) string {
name = name[1:] name = name[1:]
} }
} else { } else {
// This technique is potentially ambiguous it may not be possible to uniquely identify the filenames from the diff line alone
ambiguity = true
fmt.Fscanf(rd, "%s ", &name) fmt.Fscanf(rd, "%s ", &name)
char, _ := rd.ReadByte()
_ = rd.UnreadByte()
for !(char == 0 || char == '"' || char == 'b') {
var suffix string
fmt.Fscanf(rd, "%s ", &suffix)
name += " " + suffix
char, _ = rd.ReadByte()
_ = rd.UnreadByte()
}
} }
return name[2:] if len(name) < 2 {
log.Error("Unable to determine name from reader: %v", rd)
return "", true
}
return name[2:], ambiguity
} }
// GetDiffRange builds a Diff between two commits of a repository. // GetDiffRange builds a Diff between two commits of a repository.
@ -1191,6 +1273,7 @@ func CommentAsDiff(c *models.Comment) (*Diff, error) {
diff, err := ParsePatch(setting.Git.MaxGitDiffLines, diff, err := ParsePatch(setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch)) setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.Patch))
if err != nil { if err != nil {
log.Error("Unable to parse patch: %v", err)
return nil, err return nil, err
} }
if len(diff.Files) == 0 { if len(diff.Files) == 0 {

View File

@ -208,6 +208,66 @@ rename to a b/a a/file b/b file
oldFilename: "a b/file b/a a/file", oldFilename: "a b/file b/a a/file",
filename: "a b/a a/file b/b file", filename: "a b/a a/file b/b file",
}, },
{
name: "ambiguous deleted",
gitdiff: `diff --git a/b b/b b/b b/b
deleted file mode 100644
index 92e798b..0000000
--- a/b b/b` + "\t" + `
+++ /dev/null
@@ -1 +0,0 @@
-b b/b
`,
oldFilename: "b b/b",
filename: "b b/b",
addition: 0,
deletion: 1,
},
{
name: "ambiguous addition",
gitdiff: `diff --git a/b b/b b/b b/b
new file mode 100644
index 0000000..92e798b
--- /dev/null
+++ b/b b/b` + "\t" + `
@@ -0,0 +1 @@
+b b/b
`,
oldFilename: "b b/b",
filename: "b b/b",
addition: 1,
deletion: 0,
},
{
name: "rename",
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
similarity index 100%
rename from b b/b b/b b/b b/b
rename to b
`,
oldFilename: "b b/b b/b b/b b/b",
filename: "b",
},
{
name: "ambiguous 1",
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
similarity index 100%
rename from b b/b b/b b/b b/b
rename to b
`,
oldFilename: "b b/b b/b b/b b/b",
filename: "b",
},
{
name: "ambiguous 2",
gitdiff: `diff --git a/b b/b b/b b/b b/b b/b
similarity index 100%
rename from b b/b b/b b/b
rename to b b/b
`,
oldFilename: "b b/b b/b b/b",
filename: "b b/b",
},
{ {
name: "minuses-and-pluses", name: "minuses-and-pluses",
gitdiff: `diff --git a/minuses-and-pluses b/minuses-and-pluses gitdiff: `diff --git a/minuses-and-pluses b/minuses-and-pluses
@ -235,32 +295,32 @@ index 6961180..9ba1a00 100644
t.Run(testcase.name, func(t *testing.T) { t.Run(testcase.name, func(t *testing.T) {
got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff)) got, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
if (err != nil) != testcase.wantErr { if (err != nil) != testcase.wantErr {
t.Errorf("ParsePatch() error = %v, wantErr %v", err, testcase.wantErr) t.Errorf("ParsePatch(%q) error = %v, wantErr %v", testcase.name, err, testcase.wantErr)
return return
} }
gotMarshaled, _ := json.MarshalIndent(got, " ", " ") gotMarshaled, _ := json.MarshalIndent(got, " ", " ")
if got.NumFiles != 1 { if got.NumFiles != 1 {
t.Errorf("ParsePath() did not receive 1 file:\n%s", string(gotMarshaled)) t.Errorf("ParsePath(%q) did not receive 1 file:\n%s", testcase.name, string(gotMarshaled))
return return
} }
if got.TotalAddition != testcase.addition { if got.TotalAddition != testcase.addition {
t.Errorf("ParsePath() does not have correct totalAddition %d, wanted %d", got.TotalAddition, testcase.addition) t.Errorf("ParsePath(%q) does not have correct totalAddition %d, wanted %d", testcase.name, got.TotalAddition, testcase.addition)
} }
if got.TotalDeletion != testcase.deletion { if got.TotalDeletion != testcase.deletion {
t.Errorf("ParsePath() did not have correct totalDeletion %d, wanted %d", got.TotalDeletion, testcase.deletion) t.Errorf("ParsePath(%q) did not have correct totalDeletion %d, wanted %d", testcase.name, got.TotalDeletion, testcase.deletion)
} }
file := got.Files[0] file := got.Files[0]
if file.Addition != testcase.addition { if file.Addition != testcase.addition {
t.Errorf("ParsePath() does not have correct file addition %d, wanted %d", file.Addition, testcase.addition) t.Errorf("ParsePath(%q) does not have correct file addition %d, wanted %d", testcase.name, file.Addition, testcase.addition)
} }
if file.Deletion != testcase.deletion { if file.Deletion != testcase.deletion {
t.Errorf("ParsePath() did not have correct file deletion %d, wanted %d", file.Deletion, testcase.deletion) t.Errorf("ParsePath(%q) did not have correct file deletion %d, wanted %d", testcase.name, file.Deletion, testcase.deletion)
} }
if file.OldName != testcase.oldFilename { if file.OldName != testcase.oldFilename {
t.Errorf("ParsePath() did not have correct OldName %s, wanted %s", file.OldName, testcase.oldFilename) t.Errorf("ParsePath(%q) did not have correct OldName %q, wanted %q", testcase.name, file.OldName, testcase.oldFilename)
} }
if file.Name != testcase.filename { if file.Name != testcase.filename {
t.Errorf("ParsePath() did not have correct Name %s, wanted %s", file.Name, testcase.filename) t.Errorf("ParsePath(%q) did not have correct Name %q, wanted %q", testcase.name, file.Name, testcase.filename)
} }
}) })
} }

View File

@ -6,13 +6,14 @@
package pull package pull
import ( import (
"bytes"
"fmt" "fmt"
"io"
"regexp" "regexp"
"strings" "strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -179,11 +180,24 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models
if len(commitID) == 0 { if len(commitID) == 0 {
commitID = headCommitID commitID = headCommitID
} }
patchBuf := new(bytes.Buffer) reader, writer := io.Pipe()
if err := git.GetRepoRawDiffForFile(gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, treePath, patchBuf); err != nil { defer func() {
return nil, fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", gitRepo.Path, pr.MergeBase, headCommitID, treePath, err) _ = reader.Close()
_ = writer.Close()
}()
go func() {
if err := git.GetRepoRawDiffForFile(gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, treePath, writer); err != nil {
_ = writer.CloseWithError(fmt.Errorf("GetRawDiffForLine[%s, %s, %s, %s]: %v", gitRepo.Path, pr.MergeBase, headCommitID, treePath, err))
return
}
_ = writer.Close()
}()
patch, err = git.CutDiffAroundLine(reader, int64((&models.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
if err != nil {
log.Error("Error whilst generating patch: %v", err)
return nil, err
} }
patch = git.CutDiffAroundLine(patchBuf, int64((&models.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines)
} }
return models.CreateComment(&models.CreateCommentOptions{ return models.CreateComment(&models.CreateCommentOptions{
Type: models.CommentTypeCode, Type: models.CommentTypeCode,