mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Backport #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 * 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 * 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:
		@@ -125,30 +125,39 @@ var hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+
 | 
			
		||||
 | 
			
		||||
const cmdDiffHead = "diff --git "
 | 
			
		||||
 | 
			
		||||
func isHeader(lof string) bool {
 | 
			
		||||
	return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")
 | 
			
		||||
func isHeader(lof string, inHunk bool) bool {
 | 
			
		||||
	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
 | 
			
		||||
// it also recalculates hunks and adds the appropriate headers to the new diff.
 | 
			
		||||
// 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 {
 | 
			
		||||
		// no line or num of lines => no diff
 | 
			
		||||
		return ""
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	inHunk := false
 | 
			
		||||
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		lof := scanner.Text()
 | 
			
		||||
		// Add header to enable parsing
 | 
			
		||||
		if isHeader(lof) {
 | 
			
		||||
 | 
			
		||||
		if isHeader(lof, inHunk) {
 | 
			
		||||
			if strings.HasPrefix(lof, cmdDiffHead) {
 | 
			
		||||
				inHunk = false
 | 
			
		||||
			}
 | 
			
		||||
			hunk = append(hunk, lof)
 | 
			
		||||
			headerLines++
 | 
			
		||||
		}
 | 
			
		||||
@@ -157,6 +166,7 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
 | 
			
		||||
		}
 | 
			
		||||
		// Detect "hunk" with contains commented lof
 | 
			
		||||
		if strings.HasPrefix(lof, "@@") {
 | 
			
		||||
			inHunk = true
 | 
			
		||||
			// Already got our hunk. End of hunk detected!
 | 
			
		||||
			if len(hunk) > headerLines {
 | 
			
		||||
				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
 | 
			
		||||
	if currentLine == 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	// headerLines + hunkLine (1) = totalNonCodeLines
 | 
			
		||||
	if len(hunk)-headerLines-1 <= numbersOfLine {
 | 
			
		||||
		// 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
 | 
			
		||||
	if old {
 | 
			
		||||
@@ -256,5 +270,5 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
 | 
			
		||||
	// construct the new hunk header
 | 
			
		||||
	newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
 | 
			
		||||
		oldBegin, oldNumOfLines, newBegin, newNumOfLines)
 | 
			
		||||
	return strings.Join(newHunk, "\n")
 | 
			
		||||
	return strings.Join(newHunk, "\n"), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,28 @@ const exampleDiff = `diff --git a/README.md b/README.md
 | 
			
		||||
+ 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) {
 | 
			
		||||
	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")
 | 
			
		||||
	assert.Len(t, resultByLine, 7)
 | 
			
		||||
	// Check if headers got transferred
 | 
			
		||||
@@ -37,18 +57,50 @@ func TestCutDiffAroundLine(t *testing.T) {
 | 
			
		||||
	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)
 | 
			
		||||
	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")
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	// 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)
 | 
			
		||||
 | 
			
		||||
	// 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) {
 | 
			
		||||
@@ -69,7 +121,7 @@ func ExampleCutDiffAroundLine() {
 | 
			
		||||
 Docker Pulls
 | 
			
		||||
+ cut off
 | 
			
		||||
+ cut off`
 | 
			
		||||
	result := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3)
 | 
			
		||||
	result, _ := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3)
 | 
			
		||||
	println(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user