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