forked from tangled.org/core
this repo has no description
at master 3.5 kB view raw
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}