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