+168
types/commit.go
+168
types/commit.go
···
1
+
package types
2
+
3
+
import (
4
+
"bytes"
5
+
"encoding/json"
6
+
"fmt"
7
+
"maps"
8
+
"strings"
9
+
10
+
"github.com/go-git/go-git/v5/plumbing"
11
+
"github.com/go-git/go-git/v5/plumbing/object"
12
+
)
13
+
14
+
type Commit struct {
15
+
// hash of the commit object.
16
+
Hash plumbing.Hash `json:"hash,omitempty"`
17
+
18
+
// author is the original author of the commit.
19
+
Author object.Signature `json:"author"`
20
+
21
+
// committer is the one performing the commit, might be different from author.
22
+
Committer object.Signature `json:"committer"`
23
+
24
+
// message is the commit message, contains arbitrary text.
25
+
Message string `json:"message"`
26
+
27
+
// treehash is the hash of the root tree of the commit.
28
+
Tree string `json:"tree"`
29
+
30
+
// parents are the hashes of the parent commits of the commit.
31
+
ParentHashes []plumbing.Hash `json:"parent_hashes,omitempty"`
32
+
33
+
// pgpsignature is the pgp signature of the commit.
34
+
PGPSignature string `json:"pgp_signature,omitempty"`
35
+
36
+
// mergetag is the embedded tag object when a merge commit is created by
37
+
// merging a signed tag.
38
+
MergeTag string `json:"merge_tag,omitempty"`
39
+
40
+
// changeid is a unique identifier for the change (e.g., gerrit change-id).
41
+
ChangeId string `json:"change_id,omitempty"`
42
+
43
+
// extraheaders contains additional headers not captured by other fields.
44
+
ExtraHeaders map[string][]byte `json:"extra_headers,omitempty"`
45
+
46
+
// deprecated: kept for backwards compatibility with old json format.
47
+
This string `json:"this,omitempty"`
48
+
49
+
// deprecated: kept for backwards compatibility with old json format.
50
+
Parent string `json:"parent,omitempty"`
51
+
}
52
+
53
+
// types.Commit is an unify two commit structs:
54
+
// - git.object.Commit from
55
+
// - types.NiceDiff.commit
56
+
//
57
+
// to do this in backwards compatible fashion, we define the base struct
58
+
// to use the same fields as NiceDiff.Commit, and then we also unmarshal
59
+
// the struct fields from go-git structs, this custom unmarshal makes sense
60
+
// of both representations and unifies them to have maximal data in either
61
+
// form.
62
+
func (c *Commit) UnmarshalJSON(data []byte) error {
63
+
type Alias Commit
64
+
65
+
aux := &struct {
66
+
*object.Commit
67
+
*Alias
68
+
}{
69
+
Alias: (*Alias)(c),
70
+
}
71
+
72
+
if err := json.Unmarshal(data, aux); err != nil {
73
+
return err
74
+
}
75
+
76
+
c.FromGoGitCommit(aux.Commit)
77
+
78
+
return nil
79
+
}
80
+
81
+
// fill in as much of Commit as possible from the given go-git commit
82
+
func (c *Commit) FromGoGitCommit(gc *object.Commit) {
83
+
if gc == nil {
84
+
return
85
+
}
86
+
87
+
if c.Hash.IsZero() {
88
+
c.Hash = gc.Hash
89
+
}
90
+
if c.This == "" {
91
+
c.This = gc.Hash.String()
92
+
}
93
+
if isEmptySignature(c.Author) {
94
+
c.Author = gc.Author
95
+
}
96
+
if isEmptySignature(c.Committer) {
97
+
c.Committer = gc.Committer
98
+
}
99
+
if c.Message == "" {
100
+
c.Message = gc.Message
101
+
}
102
+
if c.Tree == "" {
103
+
c.Tree = gc.TreeHash.String()
104
+
}
105
+
if c.PGPSignature == "" {
106
+
c.PGPSignature = gc.PGPSignature
107
+
}
108
+
if c.MergeTag == "" {
109
+
c.MergeTag = gc.MergeTag
110
+
}
111
+
112
+
if len(c.ParentHashes) == 0 {
113
+
c.ParentHashes = gc.ParentHashes
114
+
}
115
+
if c.Parent == "" && len(gc.ParentHashes) > 0 {
116
+
c.Parent = gc.ParentHashes[0].String()
117
+
}
118
+
119
+
if len(c.ExtraHeaders) == 0 {
120
+
c.ExtraHeaders = make(map[string][]byte)
121
+
maps.Copy(c.ExtraHeaders, gc.ExtraHeaders)
122
+
}
123
+
124
+
if c.ChangeId == "" {
125
+
if v, ok := gc.ExtraHeaders["change-id"]; ok {
126
+
c.ChangeId = string(v)
127
+
}
128
+
}
129
+
}
130
+
131
+
func isEmptySignature(s object.Signature) bool {
132
+
return s.Email == "" && s.Name == "" && s.When.IsZero()
133
+
}
134
+
135
+
// produce a verifiable payload from this commit's metadata
136
+
func (c *Commit) Payload() string {
137
+
author := bytes.NewBuffer([]byte{})
138
+
c.Author.Encode(author)
139
+
140
+
committer := bytes.NewBuffer([]byte{})
141
+
c.Committer.Encode(committer)
142
+
143
+
payload := strings.Builder{}
144
+
145
+
fmt.Fprintf(&payload, "tree %s\n", c.Tree)
146
+
147
+
if len(c.ParentHashes) > 0 {
148
+
for _, p := range c.ParentHashes {
149
+
fmt.Fprintf(&payload, "parent %s\n", p.String())
150
+
}
151
+
} else {
152
+
// present for backwards compatibility
153
+
fmt.Fprintf(&payload, "parent %s\n", c.Parent)
154
+
}
155
+
156
+
fmt.Fprintf(&payload, "author %s\n", author.String())
157
+
fmt.Fprintf(&payload, "committer %s\n", committer.String())
158
+
159
+
if c.ChangeId != "" {
160
+
fmt.Fprintf(&payload, "change-id %s\n", c.ChangeId)
161
+
} else if v, ok := c.ExtraHeaders["change-id"]; ok {
162
+
fmt.Fprintf(&payload, "change-id %s\n", string(v))
163
+
}
164
+
165
+
fmt.Fprintf(&payload, "\n%s", c.Message)
166
+
167
+
return payload.String()
168
+
}