aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/sourcegraph/go-diff
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-07-04 11:12:55 +0200
committerDmitry Vyukov <dvyukov@google.com>2020-07-04 15:05:30 +0200
commitc7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 (patch)
tree0dff0ee1f98dbfa3ad8776112053a450d176592b /vendor/github.com/sourcegraph/go-diff
parent9573094ce235bd9afe88f5da27a47dd6bcc1e13b (diff)
go.mod: vendor golangci-lint
Diffstat (limited to 'vendor/github.com/sourcegraph/go-diff')
-rw-r--r--vendor/github.com/sourcegraph/go-diff/LICENSE35
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/diff.go76
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go1059
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/diff.proto81
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/doc.go2
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/parse.go625
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/print.go140
-rw-r--r--vendor/github.com/sourcegraph/go-diff/diff/reader_util.go37
8 files changed, 2055 insertions, 0 deletions
diff --git a/vendor/github.com/sourcegraph/go-diff/LICENSE b/vendor/github.com/sourcegraph/go-diff/LICENSE
new file mode 100644
index 000000000..0733b6e5f
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/LICENSE
@@ -0,0 +1,35 @@
+Copyright (c) 2014 Sourcegraph, Inc.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+-----------------------------------------------------------------
+
+Portions adapted from python-unidiff:
+
+Copyright (c) 2012 Matias Bordese
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.go b/vendor/github.com/sourcegraph/go-diff/diff/diff.go
new file mode 100644
index 000000000..646602a6c
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.go
@@ -0,0 +1,76 @@
+package diff
+
+import "bytes"
+
+// NOTE: types are code-generated in diff.pb.go.
+
+//go:generate protoc -I../../../.. -I ../../../../github.com/gogo/protobuf/protobuf -I. --gogo_out=. diff.proto
+
+// Stat computes the number of lines added/changed/deleted in all
+// hunks in this file's diff.
+func (d *FileDiff) Stat() Stat {
+ total := Stat{}
+ for _, h := range d.Hunks {
+ total.add(h.Stat())
+ }
+ return total
+}
+
+// Stat computes the number of lines added/changed/deleted in this
+// hunk.
+func (h *Hunk) Stat() Stat {
+ lines := bytes.Split(h.Body, []byte{'\n'})
+ var last byte
+ st := Stat{}
+ for _, line := range lines {
+ if len(line) == 0 {
+ last = 0
+ continue
+ }
+ switch line[0] {
+ case '-':
+ if last == '+' {
+ st.Added--
+ st.Changed++
+ last = 0 // next line can't change this one since this is already a change
+ } else {
+ st.Deleted++
+ last = line[0]
+ }
+ case '+':
+ if last == '-' {
+ st.Deleted--
+ st.Changed++
+ last = 0 // next line can't change this one since this is already a change
+ } else {
+ st.Added++
+ last = line[0]
+ }
+ default:
+ last = 0
+ }
+ }
+ return st
+}
+
+var (
+ hunkPrefix = []byte("@@ ")
+)
+
+const hunkHeader = "@@ -%d,%d +%d,%d @@"
+
+// diffTimeParseLayout is the layout used to parse the time in unified diff file
+// header timestamps.
+// See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html.
+const diffTimeParseLayout = "2006-01-02 15:04:05 -0700"
+
+// diffTimeFormatLayout is the layout used to format (i.e., print) the time in unified diff file
+// header timestamps.
+// See https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html.
+const diffTimeFormatLayout = "2006-01-02 15:04:05.000000000 -0700"
+
+func (s *Stat) add(o Stat) {
+ s.Added += o.Added
+ s.Changed += o.Changed
+ s.Deleted += o.Deleted
+}
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go b/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go
new file mode 100644
index 000000000..2e7c27fb4
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.pb.go
@@ -0,0 +1,1059 @@
+// Code generated by protoc-gen-gogo.
+// source: diff.proto
+// DO NOT EDIT!
+
+/*
+ Package diff is a generated protocol buffer package.
+
+ It is generated from these files:
+ diff.proto
+
+ It has these top-level messages:
+ FileDiff
+ Hunk
+ Stat
+*/
+package diff
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto"
+import pbtypes "sourcegraph.com/sqs/pbtypes"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// A FileDiff represents a unified diff for a single file.
+//
+// A file unified diff has a header that resembles the following:
+//
+// --- oldname 2009-10-11 15:12:20.000000000 -0700
+// +++ newname 2009-10-11 15:12:30.000000000 -0700
+type FileDiff struct {
+ // the original name of the file
+ OrigName string `protobuf:"bytes,1,opt,name=OrigName,proto3" json:"OrigName,omitempty"`
+ // the original timestamp (nil if not present)
+ OrigTime *pbtypes.Timestamp `protobuf:"bytes,2,opt,name=OrigTime" json:"OrigTime,omitempty"`
+ // the new name of the file (often same as OrigName)
+ NewName string `protobuf:"bytes,3,opt,name=NewName,proto3" json:"NewName,omitempty"`
+ // the new timestamp (nil if not present)
+ NewTime *pbtypes.Timestamp `protobuf:"bytes,4,opt,name=NewTime" json:"NewTime,omitempty"`
+ // extended header lines (e.g., git's "new mode <mode>", "rename from <path>", etc.)
+ Extended []string `protobuf:"bytes,5,rep,name=Extended" json:"Extended,omitempty"`
+ // hunks that were changed from orig to new
+ Hunks []*Hunk `protobuf:"bytes,6,rep,name=Hunks" json:"Hunks,omitempty"`
+}
+
+func (m *FileDiff) Reset() { *m = FileDiff{} }
+func (m *FileDiff) String() string { return proto.CompactTextString(m) }
+func (*FileDiff) ProtoMessage() {}
+
+// A Hunk represents a series of changes (additions or deletions) in a file's
+// unified diff.
+type Hunk struct {
+ // starting line number in original file
+ OrigStartLine int32 `protobuf:"varint,1,opt,name=OrigStartLine,proto3" json:"OrigStartLine,omitempty"`
+ // number of lines the hunk applies to in the original file
+ OrigLines int32 `protobuf:"varint,2,opt,name=OrigLines,proto3" json:"OrigLines,omitempty"`
+ // if > 0, then the original file had a 'No newline at end of file' mark at this offset
+ OrigNoNewlineAt int32 `protobuf:"varint,3,opt,name=OrigNoNewlineAt,proto3" json:"OrigNoNewlineAt,omitempty"`
+ // starting line number in new file
+ NewStartLine int32 `protobuf:"varint,4,opt,name=NewStartLine,proto3" json:"NewStartLine,omitempty"`
+ // number of lines the hunk applies to in the new file
+ NewLines int32 `protobuf:"varint,5,opt,name=NewLines,proto3" json:"NewLines,omitempty"`
+ // optional section heading
+ Section string `protobuf:"bytes,6,opt,name=Section,proto3" json:"Section,omitempty"`
+ // 0-indexed line offset in unified file diff (including section headers); this is
+ // only set when Hunks are read from entire file diff (i.e., when ReadAllHunks is
+ // called) This accounts for hunk headers, too, so the StartPosition of the first
+ // hunk will be 1.
+ StartPosition int32 `protobuf:"varint,7,opt,name=StartPosition,proto3" json:"StartPosition,omitempty"`
+ // hunk body (lines prefixed with '-', '+', or ' ')
+ Body []byte `protobuf:"bytes,8,opt,name=Body,proto3" json:"Body,omitempty"`
+}
+
+func (m *Hunk) Reset() { *m = Hunk{} }
+func (m *Hunk) String() string { return proto.CompactTextString(m) }
+func (*Hunk) ProtoMessage() {}
+
+// A Stat is a diff stat that represents the number of lines added/changed/deleted.
+type Stat struct {
+ // number of lines added
+ Added int32 `protobuf:"varint,1,opt,name=Added,proto3" json:""`
+ // number of lines changed
+ Changed int32 `protobuf:"varint,2,opt,name=Changed,proto3" json:""`
+ // number of lines deleted
+ Deleted int32 `protobuf:"varint,3,opt,name=Deleted,proto3" json:""`
+}
+
+func (m *Stat) Reset() { *m = Stat{} }
+func (m *Stat) String() string { return proto.CompactTextString(m) }
+func (*Stat) ProtoMessage() {}
+
+func (m *FileDiff) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *FileDiff) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if len(m.OrigName) > 0 {
+ data[i] = 0xa
+ i++
+ i = encodeVarintDiff(data, i, uint64(len(m.OrigName)))
+ i += copy(data[i:], m.OrigName)
+ }
+ if m.OrigTime != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.OrigTime.Size()))
+ n1, err := m.OrigTime.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n1
+ }
+ if len(m.NewName) > 0 {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintDiff(data, i, uint64(len(m.NewName)))
+ i += copy(data[i:], m.NewName)
+ }
+ if m.NewTime != nil {
+ data[i] = 0x22
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.NewTime.Size()))
+ n2, err := m.NewTime.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n2
+ }
+ if len(m.Extended) > 0 {
+ for _, s := range m.Extended {
+ data[i] = 0x2a
+ i++
+ l = len(s)
+ for l >= 1<<7 {
+ data[i] = uint8(uint64(l)&0x7f | 0x80)
+ l >>= 7
+ i++
+ }
+ data[i] = uint8(l)
+ i++
+ i += copy(data[i:], s)
+ }
+ }
+ if len(m.Hunks) > 0 {
+ for _, msg := range m.Hunks {
+ data[i] = 0x32
+ i++
+ i = encodeVarintDiff(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ return i, nil
+}
+
+func (m *Hunk) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Hunk) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.OrigStartLine != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.OrigStartLine))
+ }
+ if m.OrigLines != 0 {
+ data[i] = 0x10
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.OrigLines))
+ }
+ if m.OrigNoNewlineAt != 0 {
+ data[i] = 0x18
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.OrigNoNewlineAt))
+ }
+ if m.NewStartLine != 0 {
+ data[i] = 0x20
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.NewStartLine))
+ }
+ if m.NewLines != 0 {
+ data[i] = 0x28
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.NewLines))
+ }
+ if len(m.Section) > 0 {
+ data[i] = 0x32
+ i++
+ i = encodeVarintDiff(data, i, uint64(len(m.Section)))
+ i += copy(data[i:], m.Section)
+ }
+ if m.StartPosition != 0 {
+ data[i] = 0x38
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.StartPosition))
+ }
+ if m.Body != nil {
+ if len(m.Body) > 0 {
+ data[i] = 0x42
+ i++
+ i = encodeVarintDiff(data, i, uint64(len(m.Body)))
+ i += copy(data[i:], m.Body)
+ }
+ }
+ return i, nil
+}
+
+func (m *Stat) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Stat) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Added != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.Added))
+ }
+ if m.Changed != 0 {
+ data[i] = 0x10
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.Changed))
+ }
+ if m.Deleted != 0 {
+ data[i] = 0x18
+ i++
+ i = encodeVarintDiff(data, i, uint64(m.Deleted))
+ }
+ return i, nil
+}
+
+func encodeFixed64Diff(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Diff(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintDiff(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *FileDiff) Size() (n int) {
+ var l int
+ _ = l
+ l = len(m.OrigName)
+ if l > 0 {
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ if m.OrigTime != nil {
+ l = m.OrigTime.Size()
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ l = len(m.NewName)
+ if l > 0 {
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ if m.NewTime != nil {
+ l = m.NewTime.Size()
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ if len(m.Extended) > 0 {
+ for _, s := range m.Extended {
+ l = len(s)
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ }
+ if len(m.Hunks) > 0 {
+ for _, e := range m.Hunks {
+ l = e.Size()
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *Hunk) Size() (n int) {
+ var l int
+ _ = l
+ if m.OrigStartLine != 0 {
+ n += 1 + sovDiff(uint64(m.OrigStartLine))
+ }
+ if m.OrigLines != 0 {
+ n += 1 + sovDiff(uint64(m.OrigLines))
+ }
+ if m.OrigNoNewlineAt != 0 {
+ n += 1 + sovDiff(uint64(m.OrigNoNewlineAt))
+ }
+ if m.NewStartLine != 0 {
+ n += 1 + sovDiff(uint64(m.NewStartLine))
+ }
+ if m.NewLines != 0 {
+ n += 1 + sovDiff(uint64(m.NewLines))
+ }
+ l = len(m.Section)
+ if l > 0 {
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ if m.StartPosition != 0 {
+ n += 1 + sovDiff(uint64(m.StartPosition))
+ }
+ if m.Body != nil {
+ l = len(m.Body)
+ if l > 0 {
+ n += 1 + l + sovDiff(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *Stat) Size() (n int) {
+ var l int
+ _ = l
+ if m.Added != 0 {
+ n += 1 + sovDiff(uint64(m.Added))
+ }
+ if m.Changed != 0 {
+ n += 1 + sovDiff(uint64(m.Changed))
+ }
+ if m.Deleted != 0 {
+ n += 1 + sovDiff(uint64(m.Deleted))
+ }
+ return n
+}
+
+func sovDiff(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozDiff(x uint64) (n int) {
+ return sovDiff(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *FileDiff) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: FileDiff: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: FileDiff: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OrigName", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.OrigName = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OrigTime", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.OrigTime == nil {
+ m.OrigTime = &pbtypes.Timestamp{}
+ }
+ if err := m.OrigTime.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NewName", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.NewName = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NewTime", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.NewTime == nil {
+ m.NewTime = &pbtypes.Timestamp{}
+ }
+ if err := m.NewTime.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 5:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Extended", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Extended = append(m.Extended, string(data[iNdEx:postIndex]))
+ iNdEx = postIndex
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Hunks", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Hunks = append(m.Hunks, &Hunk{})
+ if err := m.Hunks[len(m.Hunks)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipDiff(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthDiff
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *Hunk) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: Hunk: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Hunk: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OrigStartLine", wireType)
+ }
+ m.OrigStartLine = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.OrigStartLine |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OrigLines", wireType)
+ }
+ m.OrigLines = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.OrigLines |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field OrigNoNewlineAt", wireType)
+ }
+ m.OrigNoNewlineAt = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.OrigNoNewlineAt |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NewStartLine", wireType)
+ }
+ m.NewStartLine = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.NewStartLine |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NewLines", wireType)
+ }
+ m.NewLines = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.NewLines |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Section", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Section = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 7:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field StartPosition", wireType)
+ }
+ m.StartPosition = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.StartPosition |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 8:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthDiff
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Body = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipDiff(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthDiff
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *Stat) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: Stat: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Stat: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Added", wireType)
+ }
+ m.Added = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Added |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Changed", wireType)
+ }
+ m.Changed = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Changed |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType)
+ }
+ m.Deleted = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Deleted |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ iNdEx = preIndex
+ skippy, err := skipDiff(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthDiff
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipDiff(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthDiff
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowDiff
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipDiff(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthDiff = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowDiff = fmt.Errorf("proto: integer overflow")
+)
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.proto b/vendor/github.com/sourcegraph/go-diff/diff/diff.proto
new file mode 100644
index 000000000..8868970f6
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.proto
@@ -0,0 +1,81 @@
+syntax = "proto3";
+package diff;
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+import "sourcegraph.com/sqs/pbtypes/timestamp.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+
+// A FileDiff represents a unified diff for a single file.
+//
+// A file unified diff has a header that resembles the following:
+//
+// --- oldname 2009-10-11 15:12:20.000000000 -0700
+// +++ newname 2009-10-11 15:12:30.000000000 -0700
+message FileDiff {
+ // the original name of the file
+ string OrigName = 1;
+
+ // the original timestamp (nil if not present)
+ pbtypes.Timestamp OrigTime = 2;
+
+ // the new name of the file (often same as OrigName)
+ string NewName = 3;
+
+ // the new timestamp (nil if not present)
+ pbtypes.Timestamp NewTime = 4;
+
+ // extended header lines (e.g., git's "new mode <mode>", "rename from <path>", etc.)
+ repeated string Extended = 5;
+
+ // hunks that were changed from orig to new
+ repeated Hunk Hunks = 6;
+}
+
+
+// A Hunk represents a series of changes (additions or deletions) in a file's
+// unified diff.
+message Hunk {
+ // starting line number in original file
+ int32 OrigStartLine = 1;
+
+ // number of lines the hunk applies to in the original file
+ int32 OrigLines = 2;
+
+ // if > 0, then the original file had a 'No newline at end of file' mark at this offset
+ int32 OrigNoNewlineAt = 3;
+
+ // starting line number in new file
+ int32 NewStartLine = 4;
+
+ // number of lines the hunk applies to in the new file
+ int32 NewLines = 5;
+
+ // optional section heading
+ string Section = 6;
+
+ // 0-indexed line offset in unified file diff (including section headers); this is
+ // only set when Hunks are read from entire file diff (i.e., when ReadAllHunks is
+ // called) This accounts for hunk headers, too, so the StartPosition of the first
+ // hunk will be 1.
+ int32 StartPosition = 7;
+
+ // hunk body (lines prefixed with '-', '+', or ' ')
+ bytes Body = 8;
+}
+
+// A Stat is a diff stat that represents the number of lines added/changed/deleted.
+message Stat {
+ // number of lines added
+ int32 Added = 1 [(gogoproto.jsontag) = ""];
+
+ // number of lines changed
+ int32 Changed = 2 [(gogoproto.jsontag) = ""];
+
+ // number of lines deleted
+ int32 Deleted = 3 [(gogoproto.jsontag) = ""];
+}
+
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/doc.go b/vendor/github.com/sourcegraph/go-diff/diff/doc.go
new file mode 100644
index 000000000..12fe96a07
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/doc.go
@@ -0,0 +1,2 @@
+// Package diff provides a parser for unified diffs.
+package diff
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/parse.go b/vendor/github.com/sourcegraph/go-diff/diff/parse.go
new file mode 100644
index 000000000..1e77df48f
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/parse.go
@@ -0,0 +1,625 @@
+package diff
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "time"
+
+ "sourcegraph.com/sqs/pbtypes"
+)
+
+// ParseMultiFileDiff parses a multi-file unified diff. It returns an error if parsing failed as a whole, but does its
+// best to parse as many files in the case of per-file errors. In the case of non-fatal per-file errors, the error
+// return value is null and the Errs field in the returned MultiFileDiff is set.
+func ParseMultiFileDiff(diff []byte) ([]*FileDiff, error) {
+ return NewMultiFileDiffReader(bytes.NewReader(diff)).ReadAllFiles()
+}
+
+// NewMultiFileDiffReader returns a new MultiFileDiffReader that reads
+// a multi-file unified diff from r.
+func NewMultiFileDiffReader(r io.Reader) *MultiFileDiffReader {
+ return &MultiFileDiffReader{reader: bufio.NewReader(r)}
+}
+
+// MultiFileDiffReader reads a multi-file unified diff.
+type MultiFileDiffReader struct {
+ line int
+ offset int64
+ reader *bufio.Reader
+
+ // TODO(sqs): line and offset tracking in multi-file diffs is broken; add tests and fix
+
+ // nextFileFirstLine is a line that was read by a HunksReader that
+ // was how it determined the hunk was complete. But to determine
+ // that, it needed to read the first line of the next file. We
+ // store nextFileFirstLine so we can "give the first line back" to
+ // the next file.
+ nextFileFirstLine []byte
+}
+
+// ReadFile reads the next file unified diff (including headers and
+// all hunks) from r. If there are no more files in the diff, it
+// returns error io.EOF.
+func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) {
+ fr := &FileDiffReader{
+ line: r.line,
+ offset: r.offset,
+ reader: r.reader,
+ fileHeaderLine: r.nextFileFirstLine,
+ }
+ r.nextFileFirstLine = nil
+
+ fd, err := fr.ReadAllHeaders()
+ if err != nil {
+ switch e := err.(type) {
+ case *ParseError:
+ if e.Err == ErrNoFileHeader || e.Err == ErrExtendedHeadersEOF {
+ return nil, io.EOF
+ }
+
+ case OverflowError:
+ r.nextFileFirstLine = []byte(e)
+ return fd, nil
+
+ default:
+ return nil, err
+ }
+ }
+
+ // Before reading hunks, check to see if there are any. If there
+ // aren't any, and there's another file after this file in the
+ // diff, then the hunks reader will complain ErrNoHunkHeader. It's
+ // not easy for us to tell from that error alone if that was
+ // caused by the lack of any hunks, or a malformatted hunk, so we
+ // need to perform the check here.
+ hr := fr.HunksReader()
+ line, err := readLine(r.reader)
+ if err != nil {
+ return fd, err
+ }
+ line = bytes.TrimSuffix(line, []byte{'\n'})
+ if bytes.HasPrefix(line, hunkPrefix) {
+ hr.nextHunkHeaderLine = line
+ fd.Hunks, err = hr.ReadAllHunks()
+ r.line = fr.line
+ r.offset = fr.offset
+ if err != nil {
+ if e0, ok := err.(*ParseError); ok {
+ if e, ok := e0.Err.(*ErrBadHunkLine); ok {
+ // This just means we finished reading the hunks for the
+ // current file. See the ErrBadHunkLine doc for more info.
+ r.nextFileFirstLine = e.Line
+ return fd, nil
+ }
+ }
+ return nil, err
+ }
+ } else {
+ // There weren't any hunks, so that line we peeked ahead at
+ // actually belongs to the next file. Put it back.
+ r.nextFileFirstLine = line
+ }
+
+ return fd, nil
+}
+
+// ReadAllFiles reads all file unified diffs (including headers and all
+// hunks) remaining in r.
+func (r *MultiFileDiffReader) ReadAllFiles() ([]*FileDiff, error) {
+ var ds []*FileDiff
+ for {
+ d, err := r.ReadFile()
+ if d != nil {
+ ds = append(ds, d)
+ }
+ if err == io.EOF {
+ return ds, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+}
+
+// ParseFileDiff parses a file unified diff.
+func ParseFileDiff(diff []byte) (*FileDiff, error) {
+ return NewFileDiffReader(bytes.NewReader(diff)).Read()
+}
+
+// NewFileDiffReader returns a new FileDiffReader that reads a file
+// unified diff.
+func NewFileDiffReader(r io.Reader) *FileDiffReader {
+ return &FileDiffReader{reader: bufio.NewReader(r)}
+}
+
+// FileDiffReader reads a unified file diff.
+type FileDiffReader struct {
+ line int
+ offset int64
+ reader *bufio.Reader
+
+ // fileHeaderLine is the first file header line, set by:
+ //
+ // (1) ReadExtendedHeaders if it encroaches on a file header line
+ // (which it must to detect when extended headers are done); or
+ // (2) (*MultiFileDiffReader).ReadFile() if it encroaches on a
+ // file header line while reading the previous file's hunks (in a
+ // multi-file diff).
+ fileHeaderLine []byte
+}
+
+// Read reads a file unified diff, including headers and hunks, from r.
+func (r *FileDiffReader) Read() (*FileDiff, error) {
+ fd, err := r.ReadAllHeaders()
+ if err != nil {
+ return nil, err
+ }
+
+ fd.Hunks, err = r.HunksReader().ReadAllHunks()
+ if err != nil {
+ return nil, err
+ }
+
+ return fd, nil
+}
+
+// ReadAllHeaders reads the file headers and extended headers (if any)
+// from a file unified diff. It does not read hunks, and the returned
+// FileDiff's Hunks field is nil. To read the hunks, call the
+// (*FileDiffReader).HunksReader() method to get a HunksReader and
+// read hunks from that.
+func (r *FileDiffReader) ReadAllHeaders() (*FileDiff, error) {
+ var err error
+ fd := &FileDiff{}
+
+ fd.Extended, err = r.ReadExtendedHeaders()
+ if pe, ok := err.(*ParseError); ok && pe.Err == ErrExtendedHeadersEOF {
+ wasEmpty := handleEmpty(fd)
+ if wasEmpty {
+ return fd, nil
+ }
+ return fd, err
+ } else if _, ok := err.(OverflowError); ok {
+ handleEmpty(fd)
+ return fd, err
+ } else if err != nil {
+ return fd, err
+ }
+
+ var origTime, newTime *time.Time
+ fd.OrigName, fd.NewName, origTime, newTime, err = r.ReadFileHeaders()
+ if err != nil {
+ return nil, err
+ }
+ if origTime != nil {
+ ts := pbtypes.NewTimestamp(*origTime)
+ fd.OrigTime = &ts
+ }
+ if newTime != nil {
+ ts := pbtypes.NewTimestamp(*newTime)
+ fd.NewTime = &ts
+ }
+
+ return fd, nil
+}
+
+// HunksReader returns a new HunksReader that reads hunks from r. The
+// HunksReader's line and offset (used in error messages) is set to
+// start where the file diff header ended (which means errors have the
+// correct position information).
+func (r *FileDiffReader) HunksReader() *HunksReader {
+ return &HunksReader{
+ line: r.line,
+ offset: r.offset,
+ reader: r.reader,
+ }
+}
+
+// ReadFileHeaders reads the unified file diff header (the lines that
+// start with "---" and "+++" with the orig/new file names and
+// timestamps).
+func (r *FileDiffReader) ReadFileHeaders() (origName, newName string, origTimestamp, newTimestamp *time.Time, err error) {
+ origName, origTimestamp, err = r.readOneFileHeader([]byte("--- "))
+ if err != nil {
+ return "", "", nil, nil, err
+ }
+
+ newName, newTimestamp, err = r.readOneFileHeader([]byte("+++ "))
+ if err != nil {
+ return "", "", nil, nil, err
+ }
+
+ return origName, newName, origTimestamp, newTimestamp, nil
+}
+
+// readOneFileHeader reads one of the file headers (prefix should be
+// either "+++ " or "--- ").
+func (r *FileDiffReader) readOneFileHeader(prefix []byte) (filename string, timestamp *time.Time, err error) {
+ var line []byte
+
+ if r.fileHeaderLine == nil {
+ var err error
+ line, err = readLine(r.reader)
+ if err == io.EOF {
+ return "", nil, &ParseError{r.line, r.offset, ErrNoFileHeader}
+ } else if err != nil {
+ return "", nil, err
+ }
+ } else {
+ line = r.fileHeaderLine
+ r.fileHeaderLine = nil
+ }
+
+ if !bytes.HasPrefix(line, prefix) {
+ return "", nil, &ParseError{r.line, r.offset, ErrBadFileHeader}
+ }
+
+ r.offset += int64(len(line))
+ r.line++
+ line = line[len(prefix):]
+
+ trimmedLine := strings.TrimSpace(string(line)) // filenames that contain spaces may be terminated by a tab
+ parts := strings.SplitN(trimmedLine, "\t", 2)
+ filename = parts[0]
+ if len(parts) == 2 {
+ // Timestamp is optional, but this header has it.
+ ts, err := time.Parse(diffTimeParseLayout, parts[1])
+ if err != nil {
+ return "", nil, err
+ }
+ timestamp = &ts
+ }
+
+ return filename, timestamp, err
+}
+
+// OverflowError is returned when we have overflowed into the start
+// of the next file while reading extended headers.
+type OverflowError string
+
+func (e OverflowError) Error() string {
+ return fmt.Sprintf("overflowed into next file: %s", e)
+}
+
+// ReadExtendedHeaders reads the extended header lines, if any, from a
+// unified diff file (e.g., git's "diff --git a/foo.go b/foo.go", "new
+// mode <mode>", "rename from <path>", etc.).
+func (r *FileDiffReader) ReadExtendedHeaders() ([]string, error) {
+ var xheaders []string
+ firstLine := true
+ for {
+ var line []byte
+ if r.fileHeaderLine == nil {
+ var err error
+ line, err = readLine(r.reader)
+ if err == io.EOF {
+ return xheaders, &ParseError{r.line, r.offset, ErrExtendedHeadersEOF}
+ } else if err != nil {
+ return xheaders, err
+ }
+ } else {
+ line = r.fileHeaderLine
+ r.fileHeaderLine = nil
+ }
+
+ if bytes.HasPrefix(line, []byte("diff --git ")) {
+ if firstLine {
+ firstLine = false
+ } else {
+ return xheaders, OverflowError(line)
+ }
+ }
+ if bytes.HasPrefix(line, []byte("--- ")) {
+ // We've reached the file header.
+ r.fileHeaderLine = line // pass to readOneFileHeader (see fileHeaderLine field doc)
+ return xheaders, nil
+ }
+
+ r.line++
+ r.offset += int64(len(line))
+ xheaders = append(xheaders, string(line))
+ }
+}
+
+// handleEmpty detects when FileDiff was an empty diff and will not have any hunks
+// that follow. It updates fd fields from the parsed extended headers.
+func handleEmpty(fd *FileDiff) (wasEmpty bool) {
+ switch {
+ case (len(fd.Extended) == 3 || len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[3], "Binary files ")) &&
+ strings.HasPrefix(fd.Extended[1], "new file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "):
+
+ names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2)
+ fd.OrigName = "/dev/null"
+ fd.NewName = names[1]
+ return true
+ case (len(fd.Extended) == 3 || len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[3], "Binary files ")) &&
+ strings.HasPrefix(fd.Extended[1], "deleted file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "):
+
+ names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2)
+ fd.OrigName = names[0]
+ fd.NewName = "/dev/null"
+ return true
+ case len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[2], "rename from ") && strings.HasPrefix(fd.Extended[3], "rename to ") && strings.HasPrefix(fd.Extended[0], "diff --git "):
+ names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2)
+ fd.OrigName = names[0]
+ fd.NewName = names[1]
+ return true
+ case len(fd.Extended) == 3 && strings.HasPrefix(fd.Extended[2], "Binary files ") && strings.HasPrefix(fd.Extended[0], "diff --git "):
+ names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2)
+ fd.OrigName = names[0]
+ fd.NewName = names[1]
+ return true
+ default:
+ return false
+ }
+}
+
+var (
+ // ErrNoFileHeader is when a file unified diff has no file header
+ // (i.e., the lines that begin with "---" and "+++").
+ ErrNoFileHeader = errors.New("expected file header, got EOF")
+
+ // ErrBadFileHeader is when a file unified diff has a malformed
+ // file header (i.e., the lines that begin with "---" and "+++").
+ ErrBadFileHeader = errors.New("bad file header")
+
+ // ErrExtendedHeadersEOF is when an EOF was encountered while reading extended file headers, which means that there were no ---/+++ headers encountered before hunks (if any) began.
+ ErrExtendedHeadersEOF = errors.New("expected file header while reading extended headers, got EOF")
+)
+
+// ParseHunks parses hunks from a unified diff. The diff must consist
+// only of hunks and not include a file header; if it has a file
+// header, use ParseFileDiff.
+func ParseHunks(diff []byte) ([]*Hunk, error) {
+ r := NewHunksReader(bytes.NewReader(diff))
+ hunks, err := r.ReadAllHunks()
+ if err != nil {
+ return nil, err
+ }
+ return hunks, nil
+}
+
+// NewHunksReader returns a new HunksReader that reads unified diff hunks
+// from r.
+func NewHunksReader(r io.Reader) *HunksReader {
+ return &HunksReader{reader: bufio.NewReader(r)}
+}
+
+// A HunksReader reads hunks from a unified diff.
+type HunksReader struct {
+ line int
+ offset int64
+ hunk *Hunk
+ reader *bufio.Reader
+
+ nextHunkHeaderLine []byte
+}
+
+// ReadHunk reads one hunk from r. If there are no more hunks, it
+// returns error io.EOF.
+func (r *HunksReader) ReadHunk() (*Hunk, error) {
+ r.hunk = nil
+ lastLineFromOrig := true
+ var line []byte
+ var err error
+ for {
+ if r.nextHunkHeaderLine != nil {
+ // Use stored hunk header line that was scanned in at the
+ // completion of the previous hunk's ReadHunk.
+ line = r.nextHunkHeaderLine
+ r.nextHunkHeaderLine = nil
+ } else {
+ line, err = readLine(r.reader)
+ if err != nil {
+ if err == io.EOF && r.hunk != nil {
+ return r.hunk, nil
+ }
+ return nil, err
+ }
+ }
+
+ // Record position.
+ r.line++
+ r.offset += int64(len(line))
+
+ if r.hunk == nil {
+ // Check for presence of hunk header.
+ if !bytes.HasPrefix(line, hunkPrefix) {
+ return nil, &ParseError{r.line, r.offset, ErrNoHunkHeader}
+ }
+
+ // Parse hunk header.
+ r.hunk = &Hunk{}
+ items := []interface{}{
+ &r.hunk.OrigStartLine, &r.hunk.OrigLines,
+ &r.hunk.NewStartLine, &r.hunk.NewLines,
+ }
+ header, section, err := normalizeHeader(string(line))
+ if err != nil {
+ return nil, &ParseError{r.line, r.offset, err}
+ }
+ n, err := fmt.Sscanf(header, hunkHeader, items...)
+ if err != nil {
+ return nil, err
+ }
+ if n < len(items) {
+ return nil, &ParseError{r.line, r.offset, &ErrBadHunkHeader{header: string(line)}}
+ }
+
+ r.hunk.Section = section
+ } else {
+ // Read hunk body line.
+ if bytes.HasPrefix(line, hunkPrefix) {
+ // Saw start of new hunk, so this hunk is
+ // complete. But we've already read in the next hunk's
+ // header, so we need to be sure that the next call to
+ // ReadHunk starts with that header.
+ r.nextHunkHeaderLine = line
+
+ // Rewind position.
+ r.line--
+ r.offset -= int64(len(line))
+
+ return r.hunk, nil
+ }
+
+ if len(line) >= 1 && !linePrefix(line[0]) {
+ // Bad hunk header line. If we're reading a multi-file
+ // diff, this may be the end of the current
+ // file. Return a "rich" error that lets our caller
+ // handle that case.
+ return r.hunk, &ParseError{r.line, r.offset, &ErrBadHunkLine{Line: line}}
+ }
+ if bytes.Equal(line, []byte(noNewlineMessage)) {
+ if lastLineFromOrig {
+ // Retain the newline in the body (otherwise the
+ // diff line would be like "-a+b", where "+b" is
+ // the the next line of the new file, which is not
+ // validly formatted) but record that the orig had
+ // no newline.
+ r.hunk.OrigNoNewlineAt = int32(len(r.hunk.Body))
+ } else {
+ // Remove previous line's newline.
+ if len(r.hunk.Body) != 0 {
+ r.hunk.Body = r.hunk.Body[:len(r.hunk.Body)-1]
+ }
+ }
+ continue
+ }
+
+ if len(line) > 0 {
+ lastLineFromOrig = line[0] == '-'
+ }
+
+ r.hunk.Body = append(r.hunk.Body, line...)
+ r.hunk.Body = append(r.hunk.Body, '\n')
+ }
+ }
+}
+
+const noNewlineMessage = `\ No newline at end of file`
+
+// linePrefixes is the set of all characters a valid line in a diff
+// hunk can start with. '\' can appear in diffs when no newline is
+// present at the end of a file.
+// See: 'http://www.gnu.org/software/diffutils/manual/diffutils.html#Incomplete-Lines'
+var linePrefixes = []byte{' ', '-', '+', '\\'}
+
+// linePrefix returns true if 'c' is in 'linePrefixes'.
+func linePrefix(c byte) bool {
+ for _, p := range linePrefixes {
+ if p == c {
+ return true
+ }
+ }
+ return false
+}
+
+// normalizeHeader takes a header of the form:
+// "@@ -linestart[,chunksize] +linestart[,chunksize] @@ section"
+// and returns two strings, with the first in the form:
+// "@@ -linestart,chunksize +linestart,chunksize @@".
+// where linestart and chunksize are both integers. The second is the
+// optional section header. chunksize may be omitted from the header
+// if its value is 1. normalizeHeader returns an error if the header
+// is not in the correct format.
+func normalizeHeader(header string) (string, string, error) {
+ // Split the header into five parts: the first '@@', the two
+ // ranges, the last '@@', and the optional section.
+ pieces := strings.SplitN(header, " ", 5)
+ if len(pieces) < 4 {
+ return "", "", &ErrBadHunkHeader{header: header}
+ }
+
+ if pieces[0] != "@@" {
+ return "", "", &ErrBadHunkHeader{header: header}
+ }
+ for i := 1; i < 3; i++ {
+ if !strings.ContainsRune(pieces[i], ',') {
+ pieces[i] = pieces[i] + ",1"
+ }
+ }
+ if pieces[3] != "@@" {
+ return "", "", &ErrBadHunkHeader{header: header}
+ }
+
+ var section string
+ if len(pieces) == 5 {
+ section = pieces[4]
+ }
+ return strings.Join(pieces, " "), strings.TrimSpace(section), nil
+}
+
+// ReadAllHunks reads all remaining hunks from r. A successful call
+// returns err == nil, not err == EOF. Because ReadAllHunks is defined
+// to read until EOF, it does not treat end of file as an error to be
+// reported.
+func (r *HunksReader) ReadAllHunks() ([]*Hunk, error) {
+ var hunks []*Hunk
+ linesRead := int32(0)
+ for {
+ hunk, err := r.ReadHunk()
+ if err == io.EOF {
+ return hunks, nil
+ }
+ if hunk != nil {
+ linesRead++ // account for the hunk header line
+ hunk.StartPosition = linesRead
+ hunks = append(hunks, hunk)
+ linesRead += int32(bytes.Count(hunk.Body, []byte{'\n'}))
+ }
+ if err != nil {
+ return hunks, err
+ }
+ }
+}
+
+// A ParseError is a description of a unified diff syntax error.
+type ParseError struct {
+ Line int // Line where the error occurred
+ Offset int64 // Offset where the error occurred
+ Err error // The actual error
+}
+
+func (e *ParseError) Error() string {
+ return fmt.Sprintf("line %d, char %d: %s", e.Line, e.Offset, e.Err)
+}
+
+// ErrNoHunkHeader indicates that a unified diff hunk header was
+// expected but not found during parsing.
+var ErrNoHunkHeader = errors.New("no hunk header")
+
+// ErrBadHunkHeader indicates that a malformed unified diff hunk
+// header was encountered during parsing.
+type ErrBadHunkHeader struct {
+ header string
+}
+
+func (e *ErrBadHunkHeader) Error() string {
+ if e.header == "" {
+ return "bad hunk header"
+ }
+ return "bad hunk header: " + e.header
+}
+
+// ErrBadHunkLine is when a line not beginning with ' ', '-', '+', or
+// '\' is encountered while reading a hunk. In the context of reading
+// a single hunk or file, it is an unexpected error. In a multi-file
+// diff, however, it indicates that the current file's diff is
+// complete (and remaining diff data will describe another file
+// unified diff).
+type ErrBadHunkLine struct {
+ Line []byte
+}
+
+func (e *ErrBadHunkLine) Error() string {
+ m := "bad hunk line (does not start with ' ', '-', '+', or '\\')"
+ if len(e.Line) == 0 {
+ return m
+ }
+ return m + ": " + string(e.Line)
+}
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/print.go b/vendor/github.com/sourcegraph/go-diff/diff/print.go
new file mode 100644
index 000000000..d440cb9ad
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/print.go
@@ -0,0 +1,140 @@
+package diff
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "time"
+
+ "sourcegraph.com/sqs/pbtypes"
+)
+
+// PrintMultiFileDiff prints a multi-file diff in unified diff format.
+func PrintMultiFileDiff(ds []*FileDiff) ([]byte, error) {
+ var buf bytes.Buffer
+ for _, d := range ds {
+ diff, err := PrintFileDiff(d)
+ if err != nil {
+ return nil, err
+ }
+ if _, err := buf.Write(diff); err != nil {
+ return nil, err
+ }
+ }
+ return buf.Bytes(), nil
+}
+
+// PrintFileDiff prints a FileDiff in unified diff format.
+//
+// TODO(sqs): handle escaping whitespace/etc. chars in filenames
+func PrintFileDiff(d *FileDiff) ([]byte, error) {
+ var buf bytes.Buffer
+
+ for _, xheader := range d.Extended {
+ if _, err := fmt.Fprintln(&buf, xheader); err != nil {
+ return nil, err
+ }
+ }
+
+ if d.Hunks == nil {
+ return buf.Bytes(), nil
+ }
+
+ if err := printFileHeader(&buf, "--- ", d.OrigName, timePtr(d.OrigTime)); err != nil {
+ return nil, err
+ }
+ if err := printFileHeader(&buf, "+++ ", d.NewName, timePtr(d.NewTime)); err != nil {
+ return nil, err
+ }
+
+ ph, err := PrintHunks(d.Hunks)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, err := buf.Write(ph); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+func timePtr(ts *pbtypes.Timestamp) *time.Time {
+ if ts == nil {
+ return nil
+ }
+ t := ts.Time()
+ return &t
+}
+
+func printFileHeader(w io.Writer, prefix string, filename string, timestamp *time.Time) error {
+ if _, err := fmt.Fprint(w, prefix, filename); err != nil {
+ return err
+ }
+ if timestamp != nil {
+ if _, err := fmt.Fprint(w, "\t", timestamp.Format(diffTimeFormatLayout)); err != nil {
+ return err
+ }
+ }
+ if _, err := fmt.Fprintln(w); err != nil {
+ return err
+ }
+ return nil
+}
+
+// PrintHunks prints diff hunks in unified diff format.
+func PrintHunks(hunks []*Hunk) ([]byte, error) {
+ var buf bytes.Buffer
+ for _, hunk := range hunks {
+ _, err := fmt.Fprintf(&buf,
+ "@@ -%d,%d +%d,%d @@", hunk.OrigStartLine, hunk.OrigLines, hunk.NewStartLine, hunk.NewLines,
+ )
+ if err != nil {
+ return nil, err
+ }
+ if hunk.Section != "" {
+ _, err := fmt.Fprint(&buf, " ", hunk.Section)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if _, err := fmt.Fprintln(&buf); err != nil {
+ return nil, err
+ }
+
+ if hunk.OrigNoNewlineAt == 0 {
+ if _, err := buf.Write(hunk.Body); err != nil {
+ return nil, err
+ }
+ } else {
+ if _, err := buf.Write(hunk.Body[:hunk.OrigNoNewlineAt]); err != nil {
+ return nil, err
+ }
+ if err := printNoNewlineMessage(&buf); err != nil {
+ return nil, err
+ }
+ if _, err := buf.Write(hunk.Body[hunk.OrigNoNewlineAt:]); err != nil {
+ return nil, err
+ }
+ }
+
+ if !bytes.HasSuffix(hunk.Body, []byte{'\n'}) {
+ if _, err := fmt.Fprintln(&buf); err != nil {
+ return nil, err
+ }
+ if err := printNoNewlineMessage(&buf); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return buf.Bytes(), nil
+}
+
+func printNoNewlineMessage(w io.Writer) error {
+ if _, err := w.Write([]byte(noNewlineMessage)); err != nil {
+ return err
+ }
+ if _, err := fmt.Fprintln(w); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go
new file mode 100644
index 000000000..395fb7baf
--- /dev/null
+++ b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go
@@ -0,0 +1,37 @@
+package diff
+
+import (
+ "bufio"
+ "io"
+)
+
+// readLine is a helper that mimics the functionality of calling bufio.Scanner.Scan() and
+// bufio.Scanner.Bytes(), but without the token size limitation. It will read and return
+// the next line in the Reader with the trailing newline stripped. It will return an
+// io.EOF error when there is nothing left to read (at the start of the function call). It
+// will return any other errors it receives from the underlying call to ReadBytes.
+func readLine(r *bufio.Reader) ([]byte, error) {
+ line_, err := r.ReadBytes('\n')
+ if err == io.EOF {
+ if len(line_) == 0 {
+ return nil, io.EOF
+ }
+
+ // ReadBytes returned io.EOF, because it didn't find another newline, but there is
+ // still the remainder of the file to return as a line.
+ line := line_
+ return line, nil
+ } else if err != nil {
+ return nil, err
+ }
+ line := line_[0 : len(line_)-1]
+ return dropCR(line), nil
+}
+
+// dropCR drops a terminal \r from the data.
+func dropCR(data []byte) []byte {
+ if len(data) > 0 && data[len(data)-1] == '\r' {
+ return data[0 : len(data)-1]
+ }
+ return data
+}