all repos

mugit @ 99ee247

🐮 git server that your cow will love

mugit/internal/git/diff.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
git: dont expose internal go-git types, 4 months ago
1
package git
2
3
import (
4
	"fmt"
5
	"strings"
6
7
	"github.com/bluekeyes/go-gitdiff/gitdiff"
8
	"github.com/go-git/go-git/v5/plumbing/object"
9
)
10
11
type TextFragment struct {
12
	Header string
13
	Lines  []gitdiff.Line
14
}
15
16
type Diff struct {
17
	Name struct {
18
		Old string
19
		New string
20
	}
21
	TextFragments []TextFragment
22
	IsBinary      bool
23
	IsNew         bool
24
	IsDelete      bool
25
}
26
27
type NiceDiff struct {
28
	Diff   []Diff
29
	Commit struct {
30
		Commit
31
		This   string
32
		Parent string
33
	}
34
	Stat struct {
35
		FilesChanged int
36
		Insertions   int
37
		Deletions    int
38
	}
39
}
40
41
func (g *Repo) 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, parent, err := g.getPatch(c)
48
	if err != nil {
49
		return nil, err
50
	}
51
52
	diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String()))
53
	if err != nil {
54
		return nil, fmt.Errorf("parsing diff: %w", err)
55
	}
56
57
	nd := NiceDiff{}
58
	nd.Commit.Message = c.Message
59
	nd.Commit.Hash = c.Hash.String()
60
	nd.Commit.HashShort = c.Hash.String()[:8]
61
	nd.Commit.AuthorEmail = c.Author.Email
62
	nd.Commit.AuthorName = c.Author.Name
63
	nd.Commit.This = c.Hash.String()
64
	nd.Commit.Parent = getParentHash(parent)
65
	nd.Stat.FilesChanged = len(diffs)
66
67
	nd.Diff = make([]Diff, len(diffs))
68
	for i, d := range diffs {
69
		diff := &nd.Diff[i]
70
		diff.Name.New = d.NewName
71
		if d.OldName != d.NewName {
72
			diff.Name.Old = d.OldName
73
		}
74
		diff.IsBinary = d.IsBinary
75
		diff.IsNew = d.IsNew
76
		diff.IsDelete = d.IsDelete
77
78
		for _, tf := range d.TextFragments {
79
			diff.TextFragments = append(diff.TextFragments, TextFragment{
80
				Header: tf.Header(),
81
				Lines:  tf.Lines,
82
			})
83
			for _, l := range tf.Lines {
84
				switch l.Op {
85
				case gitdiff.OpAdd:
86
					nd.Stat.Insertions += 1
87
				case gitdiff.OpDelete:
88
					nd.Stat.Deletions += 1
89
				}
90
			}
91
		}
92
	}
93
	return &nd, nil
94
}
95
96
func (g *Repo) getPatch(c *object.Commit) (*object.Patch, *object.Commit, error) {
97
	commitTree, err := c.Tree()
98
	if err != nil {
99
		return nil, nil, err
100
	}
101
102
	var parentTree *object.Tree
103
	var parent *object.Commit
104
105
	if c.NumParents() != 0 {
106
		parent, err = c.Parents().Next()
107
		if err != nil {
108
			return nil, nil, err
109
		}
110
		parentTree, err = parent.Tree()
111
		if err != nil {
112
			return nil, nil, err
113
		}
114
	} else {
115
		parentTree = &object.Tree{}
116
	}
117
118
	patch, err := parentTree.Patch(commitTree)
119
	if err != nil {
120
		return nil, nil, fmt.Errorf("patch: %w", err)
121
	}
122
123
	return patch, parent, nil
124
}
125
126
func getParentHash(parent *object.Commit) string {
127
	if parent == nil || parent.Hash.IsZero() {
128
		return ""
129
	}
130
	return parent.Hash.String()
131
}