all repos

mugit @ b6e1830

🐮 git server that your cow will love

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
ui: redesign commit page, 3 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  *Commit
30
	Parents []string // list of short hashes
31
	Stat    struct {
32
		FilesChanged int
33
		Insertions   int
34
		Deletions    int
35
	}
36
}
37
38
func (g *Repo) Diff() (*NiceDiff, error) {
39
	c, err := g.r.CommitObject(g.h)
40
	if err != nil {
41
		return nil, fmt.Errorf("commit object: %w", err)
42
	}
43
44
	patch, parents, err := g.getPatch(c)
45
	if err != nil {
46
		return nil, err
47
	}
48
49
	diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String()))
50
	if err != nil {
51
		return nil, fmt.Errorf("parsing diff: %w", err)
52
	}
53
54
	nd := NiceDiff{}
55
	nd.Commit = newCommit(c)
56
	nd.Parents = parents
57
	nd.Stat.FilesChanged = len(diffs)
58
	nd.Diff = make([]Diff, len(diffs))
59
	for i, d := range diffs {
60
		diff := &nd.Diff[i]
61
		diff.Name.New = d.NewName
62
		if d.OldName != d.NewName {
63
			diff.Name.Old = d.OldName
64
		}
65
		diff.IsBinary = d.IsBinary
66
		diff.IsNew = d.IsNew
67
		diff.IsDelete = d.IsDelete
68
69
		for _, tf := range d.TextFragments {
70
			diff.TextFragments = append(diff.TextFragments, TextFragment{
71
				Header: tf.Header(),
72
				Lines:  tf.Lines,
73
			})
74
			for _, l := range tf.Lines {
75
				switch l.Op {
76
				case gitdiff.OpAdd:
77
					nd.Stat.Insertions += 1
78
				case gitdiff.OpDelete:
79
					nd.Stat.Deletions += 1
80
				}
81
			}
82
		}
83
	}
84
	return &nd, nil
85
}
86
87
func (g *Repo) getPatch(c *object.Commit) (*object.Patch, []string, error) {
88
	commitTree, err := c.Tree()
89
	if err != nil {
90
		return nil, nil, err
91
	}
92
93
	parentTree := &object.Tree{}
94
	if c.NumParents() > 0 {
95
		parent, perr := c.Parents().Next()
96
		if perr != nil {
97
			return nil, nil, perr
98
		}
99
		if parentTree, err = parent.Tree(); err != nil {
100
			return nil, nil, err
101
		}
102
	}
103
104
	patch, err := parentTree.Patch(commitTree)
105
	if err != nil {
106
		return nil, nil, fmt.Errorf("patch: %w", err)
107
	}
108
109
	parents := make([]string, len(c.ParentHashes))
110
	for i, h := range c.ParentHashes {
111
		parents[i] = newShortHash(h)
112
	}
113
114
	return patch, parents, nil
115
}