Skip to content

Commit 754774d

Browse files
author
Yuan Ting
committed
Add patch (for github patches) parser.
1 parent 438167e commit 754774d

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

gitdiff/gitdiff.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gitdiff
33
import (
44
"fmt"
55
"os"
6+
"strings"
67
)
78

89
// File describes changes to a single file. It can be either a text file or a
@@ -37,6 +38,18 @@ type File struct {
3738
ReverseBinaryFragment *BinaryFragment
3839
}
3940

41+
func (f *File) HasFunc(match func(string)bool) bool {
42+
for _, fragment := range f.TextFragments {
43+
funcnames := fragment.FuncNames()
44+
for _, fname := range funcnames {
45+
if match(fname) {
46+
return true
47+
}
48+
}
49+
}
50+
return false
51+
}
52+
4053
// TextFragment describes changed lines starting at a specific line in a text file.
4154
type TextFragment struct {
4255
Comment string
@@ -61,6 +74,27 @@ func (f *TextFragment) Header() string {
6174
return fmt.Sprintf("@@ -%d,%d +%d,%d @@ %s", f.OldPosition, f.OldLines, f.NewPosition, f.NewLines, f.Comment)
6275
}
6376

77+
func (f *TextFragment) FuncNames() []string {
78+
funcnames := []string{}
79+
addname := func(line string) {
80+
if strings.HasPrefix(line, "func") {
81+
def := strings.TrimRight(line, " {")
82+
if strings.Count(def, "(") == 1 {
83+
funcnames = append(funcnames, def[5:])
84+
} else {
85+
low := strings.Index(def, ")") + 2
86+
hig := strings.Index(def[low:], "(") + low
87+
funcnames = append(funcnames, def[low:hig])
88+
}
89+
}
90+
}
91+
addname(f.Comment)
92+
for _, line := range f.Lines {
93+
addname(line.Line)
94+
}
95+
return funcnames
96+
}
97+
6498
// Line is a line in a text fragment.
6599
type Line struct {
66100
Op LineOp

gitdiff/parser.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,109 @@ import (
77
"bufio"
88
"fmt"
99
"io"
10+
"strings"
1011
)
1112

13+
type Commit struct {
14+
CommitSha string
15+
Content string
16+
}
17+
18+
func EmitCommit(content string) (Commit, bool) {
19+
tokens := strings.Split(content, " ")
20+
if len(tokens) < 2 {
21+
return Commit{}, false
22+
}
23+
return Commit{
24+
CommitSha: tokens[1],
25+
Content: content,
26+
}, true
27+
}
28+
29+
func (c Commit) Equal(rhs Commit) bool {
30+
return c.CommitSha == rhs.CommitSha
31+
}
32+
33+
34+
type Patches struct {
35+
FilesToSha map[*File]string
36+
ShaToFiles map[string][]*File
37+
ShaToCommit map[string]Commit
38+
}
39+
40+
func (p *Patches) AppendFile(cmt Commit, file *File) {
41+
p.FilesToSha[file] = cmt.CommitSha
42+
p.ShaToFiles[cmt.CommitSha] = append(p.ShaToFiles[cmt.CommitSha], file)
43+
p.ShaToCommit[cmt.CommitSha] = cmt
44+
}
45+
46+
func (p *Patches) GetFiles(sha string) ([]*File, bool) {
47+
files, ok := p.ShaToFiles[sha]
48+
return files, ok
49+
}
50+
51+
func (p *Patches) GetCommit(file *File) (Commit, bool) {
52+
sha, ok := p.FilesToSha[file]
53+
if !ok {
54+
return Commit{}, false
55+
}
56+
cmt, ok := p.ShaToCommit[sha]
57+
if !ok {
58+
return Commit{}, false
59+
}
60+
return cmt, ok
61+
}
62+
63+
func ParsePatch(r io.Reader) ([]*File, *Patches, error) {
64+
p := newParser(r)
65+
patches := &Patches{
66+
FilesToSha: make(map[*File]string),
67+
ShaToFiles: make(map[string][]*File),
68+
ShaToCommit: make(map[string]Commit),
69+
}
70+
if err := p.Next(); err != nil {
71+
if err == io.EOF {
72+
return nil, patches, nil
73+
}
74+
return nil, patches, err
75+
}
76+
77+
var files []*File
78+
lastCommit := Commit{}
79+
for {
80+
file, pre, err := p.ParseNextFileHeader()
81+
if err != nil {
82+
return files, patches, err
83+
}
84+
if file == nil {
85+
break
86+
}
87+
88+
for _, fn := range []func(*File) (int, error){
89+
p.ParseTextFragments,
90+
p.ParseBinaryFragments,
91+
} {
92+
n, err := fn(file)
93+
if err != nil {
94+
return files, patches, err
95+
}
96+
if n > 0 {
97+
break
98+
}
99+
}
100+
101+
files = append(files, file)
102+
if cmt, ok := EmitCommit(pre); ok {
103+
patches.AppendFile(cmt, file)
104+
lastCommit = cmt
105+
} else {
106+
patches.AppendFile(lastCommit, file)
107+
}
108+
}
109+
110+
return files, patches, nil
111+
}
112+
12113
// Parse parses a patch with changes to one or more files. Any content before
13114
// the first file is returned as the second value. If an error occurs while
14115
// parsing, it returns all files parsed before the error.

0 commit comments

Comments
 (0)