master
  1// Package gitdiff parses and applies patches generated by Git. It supports
  2// line-oriented text patches, binary patches, and can also parse standard
  3// unified diffs generated by other tools.
  4package gitdiff
  5
  6import (
  7	"bufio"
  8	"fmt"
  9	"io"
 10)
 11
 12// Parse parses a patch with changes to one or more files. Any content before
 13// the first file is returned as the second value. If an error occurs while
 14// parsing, it returns all files parsed before the error.
 15//
 16// Parse expects to receive a single patch. If the input may contain multiple
 17// patches (for example, if it is an mbox file), callers should split it into
 18// individual patches and call Parse on each one.
 19func Parse(r io.Reader) ([]*File, string, error) {
 20	p := newParser(r)
 21
 22	if err := p.Next(); err != nil {
 23		if err == io.EOF {
 24			return nil, "", nil
 25		}
 26		return nil, "", err
 27	}
 28
 29	var preamble string
 30	var files []*File
 31	for {
 32		file, pre, err := p.ParseNextFileHeader()
 33		if err != nil {
 34			return files, preamble, err
 35		}
 36		if len(files) == 0 {
 37			preamble = pre
 38		}
 39		if file == nil {
 40			break
 41		}
 42
 43		for _, fn := range []func(*File) (int, error){
 44			p.ParseTextFragments,
 45			p.ParseBinaryFragments,
 46		} {
 47			n, err := fn(file)
 48			if err != nil {
 49				return files, preamble, err
 50			}
 51			if n > 0 {
 52				break
 53			}
 54		}
 55
 56		files = append(files, file)
 57	}
 58
 59	return files, preamble, nil
 60}
 61
 62// TODO(bkeyes): consider exporting the parser type with configuration
 63// this would enable OID validation, p-value guessing, and prefix stripping
 64// by allowing users to set or override defaults
 65
 66// parser invariants:
 67// - methods that parse objects:
 68//     - start with the parser on the first line of the first object
 69//     - if returning nil, do not advance
 70//     - if returning an error, do not advance past the object
 71//     - if returning an object, advance to the first line after the object
 72// - any exported parsing methods must initialize the parser by calling Next()
 73
 74type stringReader interface {
 75	ReadString(delim byte) (string, error)
 76}
 77
 78type parser struct {
 79	r stringReader
 80
 81	eof    bool
 82	lineno int64
 83	lines  [3]string
 84}
 85
 86func newParser(r io.Reader) *parser {
 87	if r, ok := r.(stringReader); ok {
 88		return &parser{r: r}
 89	}
 90	return &parser{r: bufio.NewReader(r)}
 91}
 92
 93// Next advances the parser by one line. It returns any error encountered while
 94// reading the line, including io.EOF when the end of stream is reached.
 95func (p *parser) Next() error {
 96	if p.eof {
 97		return io.EOF
 98	}
 99
100	if p.lineno == 0 {
101		// on first call to next, need to shift in all lines
102		for i := 0; i < len(p.lines)-1; i++ {
103			if err := p.shiftLines(); err != nil && err != io.EOF {
104				return err
105			}
106		}
107	}
108
109	err := p.shiftLines()
110	if err != nil && err != io.EOF {
111		return err
112	}
113
114	p.lineno++
115	if p.lines[0] == "" {
116		p.eof = true
117		return io.EOF
118	}
119	return nil
120}
121
122func (p *parser) shiftLines() (err error) {
123	for i := 0; i < len(p.lines)-1; i++ {
124		p.lines[i] = p.lines[i+1]
125	}
126	p.lines[len(p.lines)-1], err = p.r.ReadString('\n')
127	return
128}
129
130// Line returns a line from the parser without advancing it. A delta of 0
131// returns the current line, while higher deltas return read-ahead lines. It
132// returns an empty string if the delta is higher than the available lines,
133// either because of the buffer size or because the parser reached the end of
134// the input. Valid lines always contain at least a newline character.
135func (p *parser) Line(delta uint) string {
136	return p.lines[delta]
137}
138
139// Errorf generates an error and appends the current line information.
140func (p *parser) Errorf(delta int64, msg string, args ...interface{}) error {
141	return fmt.Errorf("gitdiff: line %d: %s", p.lineno+delta, fmt.Sprintf(msg, args...))
142}