git/diff.go (view raw)
1package git
2
3import (
4 "fmt"
5 "log"
6 "strings"
7
8 "github.com/bluekeyes/go-gitdiff/gitdiff"
9 "github.com/go-git/go-git/v5/plumbing/object"
10)
11
12type TextFragment struct {
13 Header string
14 Lines []gitdiff.Line
15}
16
17type Diff struct {
18 Name struct {
19 Old string
20 New string
21 }
22 TextFragments []TextFragment
23 IsBinary bool
24 IsNew bool
25 IsDelete bool
26}
27
28// A nicer git diff representation.
29type NiceDiff struct {
30 Commit struct {
31 Message string
32 Author object.Signature
33 This string
34 Parent string
35 }
36 Stat struct {
37 FilesChanged int
38 Insertions int
39 Deletions int
40 }
41 Diff []Diff
42}
43
44func (g *GitRepo) Diff() (*NiceDiff, error) {
45 c, err := g.r.CommitObject(g.h)
46 if err != nil {
47 return nil, fmt.Errorf("commit object: %w", err)
48 }
49
50 patch := &object.Patch{}
51 commitTree, err := c.Tree()
52 parent := &object.Commit{}
53 if err == nil {
54 parentTree := &object.Tree{}
55 if c.NumParents() != 0 {
56 parent, err = c.Parents().Next()
57 if err == nil {
58 parentTree, err = parent.Tree()
59 if err == nil {
60 patch, err = parentTree.Patch(commitTree)
61 if err != nil {
62 return nil, fmt.Errorf("patch: %w", err)
63 }
64 }
65 }
66 } else {
67 patch, err = parentTree.Patch(commitTree)
68 if err != nil {
69 return nil, fmt.Errorf("patch: %w", err)
70 }
71 }
72 }
73
74 diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String()))
75 if err != nil {
76 log.Println(err)
77 }
78
79 nd := NiceDiff{}
80 nd.Commit.This = c.Hash.String()
81
82 if parent.Hash.IsZero() {
83 nd.Commit.Parent = ""
84 } else {
85 nd.Commit.Parent = parent.Hash.String()
86 }
87 nd.Commit.Author = c.Author
88 nd.Commit.Message = c.Message
89
90 for _, d := range diffs {
91 ndiff := Diff{}
92 ndiff.Name.New = d.NewName
93 ndiff.Name.Old = d.OldName
94 ndiff.IsBinary = d.IsBinary
95 ndiff.IsNew = d.IsNew
96 ndiff.IsDelete = d.IsDelete
97
98 for _, tf := range d.TextFragments {
99 ndiff.TextFragments = append(ndiff.TextFragments, TextFragment{
100 Header: tf.Header(),
101 Lines: tf.Lines,
102 })
103 for _, l := range tf.Lines {
104 switch l.Op {
105 case gitdiff.OpAdd:
106 nd.Stat.Insertions += 1
107 case gitdiff.OpDelete:
108 nd.Stat.Deletions += 1
109 }
110 }
111 }
112
113 nd.Diff = append(nd.Diff, ndiff)
114 }
115
116 nd.Stat.FilesChanged = len(diffs)
117
118 return &nd, nil
119}