-
Notifications
You must be signed in to change notification settings - Fork 27
Description
Description
gitdiff.Parse incorrectly rejects a valid unified diff containing multiple hunks,
returning the error:
The patch is generated by standard git diff and is well-formed — all hunk headers
have correct line counts that match the actual content.
Reproduction
package main
import (
"fmt"
"strings"
"github.com/bluekeyes/go-gitdiff/gitdiff"
)
const patch = `diff --git a/foo.go b/foo.go
index 7551f3d..856e345 100644
--- a/foo.go
+++ b/foo.go
@@ -669,13 +669,34 @@ func oldFunc() {
line1
line2
line3
- old line
+ new line 1
+ new line 2
+ new line 3
+ new line 4
+ new line 5
+ new line 6
+ new line 7
+ new line 8
+ new line 9
+ new line 10
+ new line 11
+ new line 12
+ new line 13
+ new line 14
+ new line 15
+ new line 16
+ new line 17
+ new line 18
+ new line 19
+ new line 20
line5
line6
line7
line8
line9
line10
line11
line12
@@ -684,6 +705,24 @@ func anotherFunc() {
ctx1
ctx2
ctx3
+ added1
+ added2
+ added3
+ added4
+ added5
+ added6
+ added7
+ added8
+ added9
+ added10
+ added11
+ added12
+ added13
+ added14
+ added15
+ added16
+ added17
+ added18
ctx4
ctx5
ctx6
`
func main() {
files, _, err := gitdiff.Parse(strings.NewReader(patch))
fmt.Println("files:", files)
fmt.Println("err:", err)
}Expected: parse succeeds, returns 1 file with 2 hunks
Actual:
files: []
err: gitdiff: line 41: fragment header miscounts lines: -1 old, -1 new
Root Cause Analysis
In text.go, ParseTextChunk uses the loop condition oldLines > 0 || newLines > 0
to consume lines. When the first hunk has significantly more + lines than - lines
(i.e. newLines is much larger than oldLines), oldLines reaches 0 first.
At that point the loop continues consuming lines to drain newLines, but it reads
into the second hunk's content. The second hunk's context lines are counted as +
lines of the first hunk, causing newLines to go negative and the final check:
if oldLines != 0 || newLines != 0 {
return p.Errorf(-hdr, "fragment header miscounts lines: %+d old, %+d new", -oldLines, -newLines)
}fires with -1 old, -1 new.
The fix should stop consuming lines when a new hunk header (@@) or a new file
header (diff --git) is encountered, even if the counters haven't reached zero yet.
Environment
- go-gitdiff version: v0.8.1
- Go version: go1.20