1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
package rawdiff
import (
"bufio"
"fmt"
"io"
)
// Diff represents a `git diff --raw` entry.
type Diff struct {
// The naming of the fields below follows the "RAW DIFF FORMAT" of `git
// diff`.
SrcMode string
DstMode string
SrcSHA string
DstSHA string
Status string
SrcPath string
DstPath string // optional!
}
// Parser is a parser for the "-z" variant of the "RAW DIFF FORMAT"
// documented in `git help diff`.
type Parser struct {
r *bufio.Reader
}
// NewParser returns a new Parser instance. The reader must contain
// output from `git diff --raw -z`.
func NewParser(r io.Reader) *Parser {
return &Parser{r: bufio.NewReader(r)}
}
// NextDiff returns the next raw diff. If there are no more diffs, the
// error is io.EOF.
func (p *Parser) NextDiff() (*Diff, error) {
c, err := p.r.ReadByte()
if err != nil {
return nil, err
}
if c != ':' {
return nil, fmt.Errorf("expected leading colon in raw diff line")
}
d := &Diff{}
for _, field := range []*string{&d.SrcMode, &d.DstMode, &d.SrcSHA, &d.DstSHA} {
if *field, err = p.readStringChop(' '); err != nil {
return nil, err
}
}
for _, field := range []*string{&d.Status, &d.SrcPath} {
if *field, err = p.readStringChop(0); err != nil {
return nil, err
}
}
if len(d.Status) > 0 && (d.Status[0] == 'C' || d.Status[0] == 'R') {
if d.DstPath, err = p.readStringChop(0); err != nil {
return nil, err
}
}
return d, nil
}
// readStringChop combines bufio.Reader.ReadString with removing the
// trailing delimiter.
func (p *Parser) readStringChop(delim byte) (string, error) {
s, err := p.r.ReadString(delim)
if err != nil {
return "", fmt.Errorf("read raw diff: %w", err)
}
return s[:len(s)-1], nil
}
|