mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	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:
		| @@ -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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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, | ||||||
|   | |||||||
| @@ -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 { | ||||||
|   | |||||||
| @@ -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) | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -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, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user