diff --git a/gitrpc/internal/parser/diff_cut.go b/gitrpc/internal/parser/diff_cut.go index fe40d3fa6..e41a8d3a0 100644 --- a/gitrpc/internal/parser/diff_cut.go +++ b/gitrpc/internal/parser/diff_cut.go @@ -103,16 +103,16 @@ func DiffCut(r io.Reader, params types.DiffCutParams) (types.HunkHeader, types.H linesBefore = linesBeforeBuf.lines() if !errors.Is(err, io.EOF) { - for i := 0; i < params.AfterLines && scanner.Scan(); i++ { - line := scanner.Text() + for i := 0; i < params.AfterLines; i++ { + line, _, err := scanHunkLine(scanner) + if err != nil { + return types.HunkHeader{}, types.Hunk{}, err + } if line == "" { break } linesAfter = append(linesAfter, line) } - if err = scanner.Err(); err != nil { - return types.HunkHeader{}, types.Hunk{}, err - } } diffCutHeaderLines := diffCutHeader @@ -185,22 +185,34 @@ const ( actionAdded diffAction = '+' ) -func scanHunkLine(scan *bufio.Scanner) (string, diffAction, error) { +func scanHunkLine(scan *bufio.Scanner) (line string, action diffAction, err error) { +again: + action = actionUnchanged + if !scan.Scan() { - return "", actionUnchanged, scan.Err() + err = scan.Err() + return } - line := scan.Text() + line = scan.Text() if line == "" { - return "", actionUnchanged, types.ErrHunkNotFound // should not happen: empty line in diff output + err = types.ErrHunkNotFound // should not happen: empty line in diff output + return + } + + action = diffAction(line[0]) + if action == '\\' { // handle the "\ No newline at end of file" line + goto again } - action := diffAction(line[0]) if action != actionRemoved && action != actionAdded && action != actionUnchanged { - return "", actionUnchanged, nil + // treat this as the end of hunk + line = "" + action = actionUnchanged + return } - return line, action, nil + return } type strCircBuf struct { diff --git a/gitrpc/internal/parser/diff_cut_test.go b/gitrpc/internal/parser/diff_cut_test.go index 9a6a330be..02e954fc9 100644 --- a/gitrpc/internal/parser/diff_cut_test.go +++ b/gitrpc/internal/parser/diff_cut_test.go @@ -142,6 +142,93 @@ func TestDiffCut(t *testing.T) { } } +func TestDiffCutNoEOLInOld(t *testing.T) { + const input = `diff --git a/test.txt b/test.txt +index 541cb64f..047d7ee2 100644 +--- a/test.txt ++++ b/test.txt +@@ -1 +1,4 @@ +-test +\ No newline at end of file ++123 ++456 ++789 +` + + hh, h, err := DiffCut( + strings.NewReader(input), + types.DiffCutParams{ + LineStart: 3, + LineStartNew: true, + LineEnd: 3, + LineEndNew: true, + BeforeLines: 1, + AfterLines: 1, + LineLimit: 100, + }, + ) + if err != nil { + t.Errorf("got error: %v", err) + return + } + + expectedHH := types.HunkHeader{OldLine: 2, OldSpan: 0, NewLine: 3, NewSpan: 1} + if expectedHH != hh { + t.Errorf("expected hunk header: %+v, but got: %+v", expectedHH, hh) + } + + expectedHunkLines := types.Hunk{ + HunkHeader: types.HunkHeader{OldLine: 2, OldSpan: 0, NewLine: 2, NewSpan: 2}, + Lines: []string{"+456", "+789"}, + } + if !reflect.DeepEqual(expectedHunkLines, h) { + t.Errorf("expected hunk header: %+v, but got: %+v", expectedHunkLines, h) + } +} + +func TestDiffCutNoEOLInNew(t *testing.T) { + const input = `diff --git a/test.txt b/test.txt +index af7864ba..541cb64f 100644 +--- a/test.txt ++++ b/test.txt +@@ -1,3 +1 @@ +-123 +-456 +-789 ++test +\ No newline at end of file +` + hh, h, err := DiffCut( + strings.NewReader(input), + types.DiffCutParams{ + LineStart: 1, + LineStartNew: true, + LineEnd: 1, + LineEndNew: true, + BeforeLines: 0, + AfterLines: 0, + LineLimit: 100, + }, + ) + if err != nil { + t.Errorf("got error: %v", err) + return + } + + expectedHH := types.HunkHeader{OldLine: 1, OldSpan: 3, NewLine: 1, NewSpan: 1} + if expectedHH != hh { + t.Errorf("expected hunk header: %+v, but got: %+v", expectedHH, hh) + } + + expectedHunkLines := types.Hunk{ + HunkHeader: types.HunkHeader{OldLine: 1, OldSpan: 3, NewLine: 1, NewSpan: 1}, + Lines: []string{"-123", "-456", "-789", "+test"}, + } + if !reflect.DeepEqual(expectedHunkLines, h) { + t.Errorf("expected hunk header: %+v, but got: %+v", expectedHunkLines, h) + } +} + func TestStrCircBuf(t *testing.T) { tests := []struct { name string