forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package patchutil
2
3import (
4 "bytes"
5 "fmt"
6 "strings"
7
8 "github.com/bluekeyes/go-gitdiff/gitdiff"
9)
10
11type Line struct {
12 LineNumber int64
13 Content string
14 IsUnknown bool
15}
16
17func NewLineAt(lineNumber int64, content string) Line {
18 return Line{
19 LineNumber: lineNumber,
20 Content: content,
21 IsUnknown: false,
22 }
23}
24
25type Image struct {
26 File string
27 Data []*Line
28}
29
30func (r *Image) String() string {
31 var i, j int64
32 var b strings.Builder
33 for {
34 i += 1
35
36 if int(j) >= (len(r.Data)) {
37 break
38 }
39
40 if r.Data[j].LineNumber == i {
41 // b.WriteString(fmt.Sprintf("%d:", r.Data[j].LineNumber))
42 b.WriteString(r.Data[j].Content)
43 j += 1
44 } else {
45 //b.WriteString(fmt.Sprintf("%d:\n", i))
46 b.WriteString("\n")
47 }
48 }
49
50 return b.String()
51}
52
53func (r *Image) AddLine(line *Line) {
54 r.Data = append(r.Data, line)
55}
56
57// rebuild the original file from a patch
58func CreatePreImage(file *gitdiff.File) Image {
59 rf := Image{
60 File: bestName(file),
61 }
62
63 for _, fragment := range file.TextFragments {
64 position := fragment.OldPosition
65 for _, line := range fragment.Lines {
66 switch line.Op {
67 case gitdiff.OpContext:
68 rl := NewLineAt(position, line.Line)
69 rf.Data = append(rf.Data, &rl)
70 position += 1
71 case gitdiff.OpDelete:
72 rl := NewLineAt(position, line.Line)
73 rf.Data = append(rf.Data, &rl)
74 position += 1
75 case gitdiff.OpAdd:
76 // do nothing here
77 }
78 }
79 }
80
81 return rf
82}
83
84// rebuild the revised file from a patch
85func CreatePostImage(file *gitdiff.File) Image {
86 rf := Image{
87 File: bestName(file),
88 }
89
90 for _, fragment := range file.TextFragments {
91 position := fragment.NewPosition
92 for _, line := range fragment.Lines {
93 switch line.Op {
94 case gitdiff.OpContext:
95 rl := NewLineAt(position, line.Line)
96 rf.Data = append(rf.Data, &rl)
97 position += 1
98 case gitdiff.OpAdd:
99 rl := NewLineAt(position, line.Line)
100 rf.Data = append(rf.Data, &rl)
101 position += 1
102 case gitdiff.OpDelete:
103 // do nothing here
104 }
105 }
106 }
107
108 return rf
109}
110
111type MergeError struct {
112 msg string
113 mismatchingLine int64
114}
115
116func (m MergeError) Error() string {
117 return fmt.Sprintf("%s: %v", m.msg, m.mismatchingLine)
118}
119
120// best effort merging of two reconstructed files
121func (this *Image) Merge(other *Image) (*Image, error) {
122 mergedFile := Image{}
123
124 var i, j int64
125
126 for int(i) < len(this.Data) || int(j) < len(other.Data) {
127 if int(i) >= len(this.Data) {
128 // first file is done; the rest of the lines from file 2 can go in
129 mergedFile.AddLine(other.Data[j])
130 j++
131 continue
132 }
133
134 if int(j) >= len(other.Data) {
135 // first file is done; the rest of the lines from file 2 can go in
136 mergedFile.AddLine(this.Data[i])
137 i++
138 continue
139 }
140
141 line1 := this.Data[i]
142 line2 := other.Data[j]
143
144 if line1.LineNumber == line2.LineNumber {
145 if line1.Content != line2.Content {
146 return nil, MergeError{
147 msg: "mismatching lines, this patch might have undergone rebase",
148 mismatchingLine: line1.LineNumber,
149 }
150 } else {
151 mergedFile.AddLine(line1)
152 }
153 i++
154 j++
155 } else if line1.LineNumber < line2.LineNumber {
156 mergedFile.AddLine(line1)
157 i++
158 } else {
159 mergedFile.AddLine(line2)
160 j++
161 }
162 }
163
164 return &mergedFile, nil
165}
166
167func (r *Image) Apply(patch *gitdiff.File) (string, error) {
168 original := r.String()
169 var buffer bytes.Buffer
170 reader := strings.NewReader(original)
171
172 err := gitdiff.Apply(&buffer, reader, patch)
173 if err != nil {
174 return "", err
175 }
176
177 return buffer.String(), nil
178}