8 type TextDiff
[]TextChunk
10 // A TextChunk specifies an edit to a section of a file:
11 // the text beginning at Line, which should be exactly Old,
12 // is to be replaced with New.
13 type TextChunk
struct {
19 func ParseTextDiff(raw
[]byte) (TextDiff
, os
.Error
) {
20 // Copy raw so it is safe to keep references to slices.
21 _
, chunks
:= sections(raw
, "@@ -")
23 diff
:= make(TextDiff
, len(chunks
))
24 for i
, raw
:= range chunks
{
27 // Parse start line: @@ -oldLine,oldCount +newLine,newCount @@ junk
28 chunk
:= splitLines(raw
)
29 chunkHeader
:= chunk
[0]
31 var oldLine
, oldCount
, newLine
, newCount
int
33 if oldLine
, s
, ok
= atoi(s
, "@@ -", 10); !ok
{
35 return nil, SyntaxError("unexpected chunk header line: " + string(chunkHeader
))
37 if len(s
) == 0 || s
[0] != ',' {
39 } else if oldCount
, s
, ok
= atoi(s
, ",", 10); !ok
{
42 if newLine
, s
, ok
= atoi(s
, " +", 10); !ok
{
45 if len(s
) == 0 || s
[0] != ',' {
47 } else if newCount
, s
, ok
= atoi(s
, ",", 10); !ok
{
50 if !hasPrefix(s
, " @@") {
54 // Special case: for created or deleted files, the empty half
55 // is given as starting at line 0. Translate to line 1.
56 if oldCount
== 0 && oldLine
== 0 {
59 if newCount
== 0 && newLine
== 0 {
63 // Count lines in text
64 var dropOldNL
, dropNewNL
bool
68 for _
, l
:= range chunk
{
69 if nold
== oldCount
&& nnew
== newCount
&& (len(l
) == 0 || l
[0] != '\\') {
70 if len(bytes
.TrimSpace(l
)) != 0 {
71 return nil, SyntaxError("too many chunk lines")
76 return nil, SyntaxError("empty chunk line")
87 if _
, ok
:= skip(l
, "\\ No newline at end of file"); ok
{
97 return nil, SyntaxError("message `\\ No newline at end of file' out of context")
103 return nil, SyntaxError("unexpected chunk line: " + string(l
))
108 // Does it match the header?
109 if nold
!= oldCount || nnew
!= newCount
{
110 return nil, SyntaxError("chunk header does not match line count: " + string(chunkHeader
))
112 if oldLine
+delta
!= newLine
{
113 return nil, SyntaxError("chunk delta is out of sync with previous chunks")
118 var old
, new bytes
.Buffer
121 for _
, l
:= range chunk
{
122 if nold
== oldCount
&& nnew
== newCount
{
141 c
.Old
= c
.Old
[0 : len(c
.Old
)-1]
144 c
.New
= c
.New
[0 : len(c
.New
)-1]
150 var ErrPatchFailure
= os
.NewError("patch did not apply cleanly")
152 // Apply applies the changes listed in the diff
153 // to the data, returning the new version.
154 func (d TextDiff
) Apply(data
[]byte) ([]byte, os
.Error
) {
157 for _
, c
:= range d
{
160 prefix
, data
, ok
= getLine(data
, c
.Line
-line
)
161 if !ok ||
!bytes
.HasPrefix(data
, c
.Old
) {
162 return nil, ErrPatchFailure
165 data
= data
[len(c
.Old
):]
167 line
= c
.Line
+ bytes
.Count(c
.Old
, newline
)
170 return buf
.Bytes(), nil