s
This commit is contained in:
+195
@@ -0,0 +1,195 @@
|
||||
package gitcmd
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DiffLineType string
|
||||
|
||||
const (
|
||||
DiffLineContext DiffLineType = "context"
|
||||
DiffLineAdd DiffLineType = "add"
|
||||
DiffLineDelete DiffLineType = "delete"
|
||||
)
|
||||
|
||||
type DiffLine struct {
|
||||
Type DiffLineType `json:"type"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type Hunk struct {
|
||||
OldStart int `json:"old_start"`
|
||||
OldCount int `json:"old_count"`
|
||||
NewStart int `json:"new_start"`
|
||||
NewCount int `json:"new_count"`
|
||||
Lines []DiffLine `json:"lines"`
|
||||
}
|
||||
|
||||
type FileChange struct {
|
||||
Path string `json:"path"`
|
||||
OldPath string `json:"old_path"`
|
||||
Status string `json:"status"`
|
||||
IsBinary bool `json:"is_binary"`
|
||||
Additions int `json:"additions"`
|
||||
Deletions int `json:"deletions"`
|
||||
Hunks []Hunk `json:"hunks"`
|
||||
}
|
||||
|
||||
type DiffResult struct {
|
||||
Files []FileChange `json:"files"`
|
||||
}
|
||||
|
||||
var hunkHeaderRe = regexp.MustCompile(`^@+ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @+`)
|
||||
|
||||
func (r *Repository) Diff(a, b string, paths []string) (*DiffResult, error) {
|
||||
cmd := NewCommand("diff", "--unified=3", "--no-color", "--no-prefix").
|
||||
AddDynamicArguments(a, b).
|
||||
WithDir(r.Path)
|
||||
if len(paths) > 0 {
|
||||
cmd.AddDashesAndList(paths...)
|
||||
}
|
||||
out, _, err := cmd.RunStdString(r.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseDiff(out), nil
|
||||
}
|
||||
|
||||
func (r *Repository) CommitDiff(sha string) (*DiffResult, error) {
|
||||
out, _, err := NewCommand("show", "--unified=3", "--no-color", "--no-prefix", "--format=").
|
||||
AddDynamicArguments(sha).
|
||||
WithDir(r.Path).
|
||||
RunStdString(r.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseDiff(out), nil
|
||||
}
|
||||
|
||||
func parseDiff(out string) *DiffResult {
|
||||
res := &DiffResult{}
|
||||
if out == "" {
|
||||
return res
|
||||
}
|
||||
|
||||
var cur *FileChange
|
||||
var curHunk *Hunk
|
||||
finishHunk := func() {
|
||||
if cur != nil && curHunk != nil {
|
||||
cur.Hunks = append(cur.Hunks, *curHunk)
|
||||
curHunk = nil
|
||||
}
|
||||
}
|
||||
finishFile := func() {
|
||||
finishHunk()
|
||||
if cur != nil {
|
||||
if cur.Status == "" {
|
||||
cur.Status = "M"
|
||||
}
|
||||
|
||||
if cur.Status != "R" {
|
||||
cur.OldPath = ""
|
||||
}
|
||||
res.Files = append(res.Files, *cur)
|
||||
cur = nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "diff --git "):
|
||||
finishFile()
|
||||
cur = &FileChange{}
|
||||
|
||||
rest := strings.TrimPrefix(line, "diff --git ")
|
||||
if parts := strings.SplitN(rest, " ", 2); len(parts) == 2 {
|
||||
cur.OldPath, cur.Path = parts[0], parts[1]
|
||||
} else {
|
||||
cur.Path = rest
|
||||
}
|
||||
case cur == nil:
|
||||
|
||||
continue
|
||||
case strings.HasPrefix(line, "new file mode"):
|
||||
cur.Status = "A"
|
||||
case strings.HasPrefix(line, "deleted file mode"):
|
||||
cur.Status = "D"
|
||||
case strings.HasPrefix(line, "rename from "):
|
||||
cur.OldPath = strings.TrimPrefix(line, "rename from ")
|
||||
cur.Status = "R"
|
||||
case strings.HasPrefix(line, "rename to "):
|
||||
cur.Path = strings.TrimPrefix(line, "rename to ")
|
||||
cur.Status = "R"
|
||||
case strings.HasPrefix(line, "copy from "):
|
||||
cur.OldPath = strings.TrimPrefix(line, "copy from ")
|
||||
cur.Status = "R"
|
||||
case strings.HasPrefix(line, "copy to "):
|
||||
cur.Path = strings.TrimPrefix(line, "copy to ")
|
||||
cur.Status = "R"
|
||||
case strings.HasPrefix(line, "similarity index"):
|
||||
if cur.Status == "" {
|
||||
cur.Status = "R"
|
||||
}
|
||||
case strings.HasPrefix(line, "Binary files "):
|
||||
cur.IsBinary = true
|
||||
case strings.HasPrefix(line, "--- "):
|
||||
p := strings.TrimPrefix(line, "--- ")
|
||||
if p != "/dev/null" {
|
||||
cur.OldPath = p
|
||||
}
|
||||
if p == "/dev/null" && cur.Status == "" {
|
||||
cur.Status = "A"
|
||||
}
|
||||
case strings.HasPrefix(line, "+++ "):
|
||||
p := strings.TrimPrefix(line, "+++ ")
|
||||
if p != "/dev/null" {
|
||||
cur.Path = p
|
||||
}
|
||||
if p == "/dev/null" && cur.Status == "" {
|
||||
cur.Status = "D"
|
||||
}
|
||||
case hunkHeaderRe.MatchString(line):
|
||||
finishHunk()
|
||||
m := hunkHeaderRe.FindStringSubmatch(line)
|
||||
os, _ := strconv.Atoi(m[1])
|
||||
oc := 1
|
||||
if m[2] != "" {
|
||||
oc, _ = strconv.Atoi(m[2])
|
||||
}
|
||||
ns, _ := strconv.Atoi(m[3])
|
||||
nc := 1
|
||||
if m[4] != "" {
|
||||
nc, _ = strconv.Atoi(m[4])
|
||||
}
|
||||
curHunk = &Hunk{OldStart: os, OldCount: oc, NewStart: ns, NewCount: nc}
|
||||
case strings.HasPrefix(line, "\\ No newline"):
|
||||
|
||||
default:
|
||||
if curHunk == nil {
|
||||
|
||||
continue
|
||||
}
|
||||
if line == "" {
|
||||
|
||||
continue
|
||||
}
|
||||
switch line[0] {
|
||||
case ' ':
|
||||
curHunk.Lines = append(curHunk.Lines, DiffLine{Type: DiffLineContext, Text: line[1:]})
|
||||
case '+':
|
||||
curHunk.Lines = append(curHunk.Lines, DiffLine{Type: DiffLineAdd, Text: line[1:]})
|
||||
cur.Additions++
|
||||
case '-':
|
||||
curHunk.Lines = append(curHunk.Lines, DiffLine{Type: DiffLineDelete, Text: line[1:]})
|
||||
cur.Deletions++
|
||||
default:
|
||||
|
||||
curHunk.Lines = append(curHunk.Lines, DiffLine{Type: DiffLineContext, Text: line})
|
||||
}
|
||||
}
|
||||
}
|
||||
finishFile()
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user