1package git
2
3import (
4 "bytes"
5 "strings"
6 "time"
7
8 "golang.org/x/crypto/openpgp"
9 "golang.org/x/crypto/openpgp/armor"
10 "golang.org/x/crypto/openpgp/errors"
11 "gopkg.in/src-d/go-git.v4/plumbing"
12 "gopkg.in/src-d/go-git.v4/plumbing/object"
13 "gopkg.in/src-d/go-git.v4/plumbing/storer"
14 "gopkg.in/src-d/go-git.v4/storage/memory"
15
16 . "gopkg.in/check.v1"
17 "gopkg.in/src-d/go-billy.v4/memfs"
18 "gopkg.in/src-d/go-billy.v4/util"
19)
20
21func (s *WorktreeSuite) TestCommitInvalidOptions(c *C) {
22 r, err := Init(memory.NewStorage(), memfs.New())
23 c.Assert(err, IsNil)
24
25 w, err := r.Worktree()
26 c.Assert(err, IsNil)
27
28 hash, err := w.Commit("", &CommitOptions{})
29 c.Assert(err, Equals, ErrMissingAuthor)
30 c.Assert(hash.IsZero(), Equals, true)
31}
32
33func (s *WorktreeSuite) TestCommitInitial(c *C) {
34 expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4")
35
36 fs := memfs.New()
37 storage := memory.NewStorage()
38
39 r, err := Init(storage, fs)
40 c.Assert(err, IsNil)
41
42 w, err := r.Worktree()
43 c.Assert(err, IsNil)
44
45 util.WriteFile(fs, "foo", []byte("foo"), 0644)
46
47 _, err = w.Add("foo")
48 c.Assert(err, IsNil)
49
50 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
51 c.Assert(hash, Equals, expected)
52 c.Assert(err, IsNil)
53
54 assertStorageStatus(c, r, 1, 1, 1, expected)
55}
56
57func (s *WorktreeSuite) TestCommitParent(c *C) {
58 expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22")
59
60 fs := memfs.New()
61 w := &Worktree{
62 r: s.Repository,
63 Filesystem: fs,
64 }
65
66 err := w.Checkout(&CheckoutOptions{})
67 c.Assert(err, IsNil)
68
69 util.WriteFile(fs, "foo", []byte("foo"), 0644)
70
71 _, err = w.Add("foo")
72 c.Assert(err, IsNil)
73
74 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
75 c.Assert(hash, Equals, expected)
76 c.Assert(err, IsNil)
77
78 assertStorageStatus(c, s.Repository, 13, 11, 10, expected)
79}
80
81func (s *WorktreeSuite) TestCommitAll(c *C) {
82 expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28")
83
84 fs := memfs.New()
85 w := &Worktree{
86 r: s.Repository,
87 Filesystem: fs,
88 }
89
90 err := w.Checkout(&CheckoutOptions{})
91 c.Assert(err, IsNil)
92
93 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644)
94 util.WriteFile(fs, "foo", []byte("foo"), 0644)
95
96 hash, err := w.Commit("foo\n", &CommitOptions{
97 All: true,
98 Author: defaultSignature(),
99 })
100
101 c.Assert(hash, Equals, expected)
102 c.Assert(err, IsNil)
103
104 assertStorageStatus(c, s.Repository, 13, 11, 10, expected)
105}
106
107func (s *WorktreeSuite) TestRemoveAndCommitAll(c *C) {
108 expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9")
109
110 fs := memfs.New()
111 w := &Worktree{
112 r: s.Repository,
113 Filesystem: fs,
114 }
115
116 err := w.Checkout(&CheckoutOptions{})
117 c.Assert(err, IsNil)
118
119 util.WriteFile(fs, "foo", []byte("foo"), 0644)
120 _, err = w.Add("foo")
121 c.Assert(err, IsNil)
122
123 _, errFirst := w.Commit("Add in Repo\n", &CommitOptions{
124 Author: defaultSignature(),
125 })
126 c.Assert(errFirst, IsNil)
127
128 errRemove := fs.Remove("foo")
129 c.Assert(errRemove, IsNil)
130
131 hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{
132 All: true,
133 Author: defaultSignature(),
134 })
135 c.Assert(errSecond, IsNil)
136
137 c.Assert(hash, Equals, expected)
138 c.Assert(err, IsNil)
139
140 assertStorageStatus(c, s.Repository, 13, 11, 11, expected)
141}
142
143func (s *WorktreeSuite) TestCommitSign(c *C) {
144 fs := memfs.New()
145 storage := memory.NewStorage()
146
147 r, err := Init(storage, fs)
148 c.Assert(err, IsNil)
149
150 w, err := r.Worktree()
151 c.Assert(err, IsNil)
152
153 util.WriteFile(fs, "foo", []byte("foo"), 0644)
154
155 _, err = w.Add("foo")
156 c.Assert(err, IsNil)
157
158 key := commitSignKey(c, true)
159 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
160 c.Assert(err, IsNil)
161
162 // Verify the commit.
163 pks := new(bytes.Buffer)
164 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil)
165 c.Assert(err, IsNil)
166
167 err = key.Serialize(pkw)
168 c.Assert(err, IsNil)
169 err = pkw.Close()
170 c.Assert(err, IsNil)
171
172 expectedCommit, err := r.CommitObject(hash)
173 c.Assert(err, IsNil)
174 actual, err := expectedCommit.Verify(pks.String())
175 c.Assert(err, IsNil)
176 c.Assert(actual.PrimaryKey, DeepEquals, key.PrimaryKey)
177}
178
179func (s *WorktreeSuite) TestCommitSignBadKey(c *C) {
180 fs := memfs.New()
181 storage := memory.NewStorage()
182
183 r, err := Init(storage, fs)
184 c.Assert(err, IsNil)
185
186 w, err := r.Worktree()
187 c.Assert(err, IsNil)
188
189 util.WriteFile(fs, "foo", []byte("foo"), 0644)
190
191 _, err = w.Add("foo")
192 c.Assert(err, IsNil)
193
194 key := commitSignKey(c, false)
195 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
196 c.Assert(err, Equals, errors.InvalidArgumentError("signing key is encrypted"))
197}
198
199func assertStorageStatus(
200 c *C, r *Repository,
201 treesCount, blobCount, commitCount int, head plumbing.Hash,
202) {
203 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject)
204 c.Assert(err, IsNil)
205 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject)
206 c.Assert(err, IsNil)
207 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
208 c.Assert(err, IsNil)
209
210 c.Assert(lenIterEncodedObjects(trees), Equals, treesCount)
211 c.Assert(lenIterEncodedObjects(blobs), Equals, blobCount)
212 c.Assert(lenIterEncodedObjects(commits), Equals, commitCount)
213
214 ref, err := r.Head()
215 c.Assert(err, IsNil)
216 c.Assert(ref.Hash(), Equals, head)
217}
218
219func lenIterEncodedObjects(iter storer.EncodedObjectIter) int {
220 count := 0
221 iter.ForEach(func(plumbing.EncodedObject) error {
222 count++
223 return nil
224 })
225
226 return count
227}
228
229func defaultSignature() *object.Signature {
230 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200")
231 return &object.Signature{
232 Name: "foo",
233 Email: "foo@foo.foo",
234 When: when,
235 }
236}
237
238func commitSignKey(c *C, decrypt bool) *openpgp.Entity {
239 s := strings.NewReader(armoredKeyRing)
240 es, err := openpgp.ReadArmoredKeyRing(s)
241 c.Assert(err, IsNil)
242
243 c.Assert(es, HasLen, 1)
244 c.Assert(es[0].Identities, HasLen, 1)
245 _, ok := es[0].Identities["foo bar <foo@foo.foo>"]
246 c.Assert(ok, Equals, true)
247
248 key := es[0]
249 if decrypt {
250 err = key.PrivateKey.Decrypt([]byte(keyPassphrase))
251 c.Assert(err, IsNil)
252 }
253
254 return key
255}
256
257const armoredKeyRing = `
258-----BEGIN PGP PRIVATE KEY BLOCK-----
259
260lQdGBFt2OHgBEADQpRmFm9X9xBfUljVs1B24MXWRHcEP5tx2k6Cp90sSz/ZOJcxH
261RjzYuXjpkE7g/PaZxAMVS1PptJip/w1/+5l2gZ7RmzU/e3hKe4vALHzKMVp8t7Ta
2620e2K3STxapCr9FNITjQRGOhnFwqiYoPCf9u5Iy8uszDH7HHnBZx+Nvbl95dDvmMs
263aFUKMeaoFD19iwEdRu6gJo7YIWF/8zwHi49neKigisGKh5PI0KUYeRPydXeCZIKQ
264ofdk+CPUS4r3dVhxTMYeHn/Vrep3blEA45E7KJ+TESmKkwliEgdjJwaVkUfJhBkb
265p2pMPKwbxLma9GCJBimOkehFv8/S+xn/xrLSsTxeOCIzMp3I5vgjR5QfONq5kuB1
266qbr8rDpSCHmTd7tzixFA0tVPBsvToA5Cz2MahJ+vmouusiWq/2YzGNE4zlzezNZ1
2673dgsVJm67xUSs0qY5ipKzButCFSKnaj1hLNR1NsUd0NPrVBTGblxULLuD99GhoXk
268/pcM5dCGTUX7XIarSFTEgBNQytpmfgt1Xbw2ErmlAdiFb4/5uBdbsVFAjglBvRI5
269VhFXr7mUd+XR/23aRczdAnp+Zg7VvyaJQi0ZwEj7VvLzpSAneVrxEcnuc2MBkUgT
270TN/Z5LYqC93nr6vB7+HMwoBZ8hBAkO4rTKYQl3eMUSkIsE45CqI7Hz0eXQARAQAB
271/gcDAqG5KzRnSp/38h4JKzJhSBRyyBPrgpYqR6ivFABzPUPJjO0gqRYzx/C+HJyl
272z+QED0WH+sW8Ns4PkAgNWZ+225fzSssavLcPwjncy9pzcV+7bc76cFb77fSve+1D
273LxhpzN58q03cSXPoamcDD7yY8GYYkAquLDZw+eRQ57BbBrNjXyfpGkBmtULymLqZ
274SgkuV5we7//lRPDIuPk+9lszJXBUW3k5e32CR47B/hI6Pu0DTlN9VesAEmXRNsi9
275YlRiO74nGPQPEWGjnEUQ++W8ip0CzoSrmPhrdGQlSR+SBEbBCuXz1lsj7D9cBxwH
276qHgwhYKvWz/gaY702+i/S1Cu/PjEpY3WPC5oSSNSSgypD8uSpcb4s2LffIegJNck
277e1AuiovG6u/3QXPot0jHhdy+Qwe+oaJfSEBGQ4fD4W6GbPxwOIQGgXV0bRaeHYgL
278iUWbN3rTLLVfDJKVo2ahvqZ7i4whfMuu1gGWQ4OEizrCDqp0x48HchEOC+T1eP3T
279Zjth2YMtzZdXlpt5HNKeaY6ZP+NWILwvOQBd3UtNpeaCNhUa0YyB7GD/k7tZnCIZ
280aNyF/DpnRrSQ9mAOffVn2NDGUv+01LnhIfa2tJes9XPmTc6ASrn/RGE9xH0X7wBD
281HfAdGhHgbkzwNeYkQvSh1WyWj5C0Sq7X70dIYdcO81i5MMtlJrzrlB5/YCFVWSxt
2827/EqwMBT3g9mkjAqo6beHxI1Hukn9rt9A6+MU64r0/cB+mVZuiBDoU/+KIiXBWiE
283F/C1n/BO115WoWG35vj5oH+syuv3lRuPaz8GxoffcT+FUkmevZO1/BjEAABAwMS1
284nlB4y6xMJ0i2aCB2kp7ThDOOeTIQpdvtDLqRtQsVTpk73AEuDeKmULJnE2+Shi7v
285yrNj1CPiBdYzz8jBDJYQH87iFQrro7VQNZzMMxpMWXQOZYWidHuBz4TgJJ0ll0JN
286KwLtqv5wdf2zG8zNli0Dz+JwiwQ1kXDcA03rxHBCFALvkdIX0KUvTaTSV7OJ65VI
287rcIwB5fSZgRE7m/9RjBGq/U+n4Kw+vlfpL7UeECJM0N7l8ekgTqqKv2Czu29eTjF
288QOnpQtjgsWVpOnHKpQUfCN1Nxg8H1ytH9HQwLn+cGjm/yK55yIK+03X/vSs2m2qz
2892zDhWlgvHLsDOEQkNsuOIvLkNM6Hv3MLTldknC+vMla34fYqpHfV1phL4npVByMW
290CFOOzLa3qCoBXIGWvtnDx06r/8apHnt256G2X0iuRWWK+XpatMjmriZnj8vyGdIg
291TZ1sNXnuFKMcXYMIvLANZXz4Rabbe6tTJ+BUVkbCGja4Z9iwmYvga77Mr2vjhtwi
292CesRpcz6gR6U5fLddRZXyzKGxC3uQzokc9RtTuRNgSBZQ0oki++d6sr0+jOb54Mr
293wfcMbMgpkQK0IJsMoOxzPLU8s6rISJvFi4IQ2dPYog17GS7Kjb1IGjGUxNKVHiIE
294Is9wB+6bB51ZUUwc0zDSkuS6EaXLLVzmS7a3TOkVzu6J760TDVLL2+PDYkkBUP6O
295SA2yeHirpyMma9QII1sw3xcKH/kDeyWigiB1VDKQpuq1PP98lYjQwAbe3Xrpy2FO
296L/v6dSOJ+imgxD4osT0SanGkZEwPqJFvs6BI9Af8q9ia0xfK3Iu6F2F8JxmG1YiR
297tUm9kCu3X/fNyE08G2sxD8QzGP9VS529nEDRBqkAgY6EHTpRKhPer9QrkUnqEyDZ
2984s7RPcJW+cII/FPW8mSMgTqxFtTZgqNaqPPLevrTnTYTdrW/RkEs1mm0FWZvbyBi
299YXIgPGZvb0Bmb28uZm9vPokCVAQTAQgAPhYhBJICM5a3zdmD+nRGF3grx+nZaj4C
300BQJbdjh4AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEHgrx+nZ
301aj4CTyUP/2+4k4hXkkBrEeD0yDpmR/FrAgCOZ3iRWca9bJwKtV0hW0HSztlPEfng
302wkwBmmyrnDevA+Ur4/hsBoTzfL4Fzo4OQDg2PZpSpIAHC1m/SQMN/s188RM8eK+Q
303JBtinAo2IDoZyBi5Ar4rVNXrRpgvzwOLm15kpuPp15wxO+4gYOkNIT06yUrDNh3J
304ccXmgZoVD54JmvKrEXscqX71/1NkaUhwZfFALN3+TVXUUdv1icQUJtxNBc29arwM
305LuPuj9XAm5XJaVXDfsJyGu4aj4g6AJDXjVW1d2MgXv1rMRud7CGuX2PmO3CUUua9
306cUaavop5AmtF/+IsHae9qRt8PiMGTebV8IZ3Z6DZeOYDnfJVOXoIUcrAvX3LoImc
307ephBdZ0KmYvaxlDrjtWAvmD6sPgwSvjLiXTmbmAkjRBXCVve4THf05kVUMcr8tmz
308Il8LB+Dri2TfanBKykf6ulH0p2MHgSGQbYA5MuSp+soOitD5YvCxM7o/O0frrfit
309p/O8mPerMEaYF1+3QbF5ApJkXCmjFCj71EPwXEDcl3VIGc+zA49oNjZMMmCcX2Gc
310JyKTWizfuRBGeG5VhCCmTQQjZHPMVO255mdzsPkb6ZHEnolDapY6QXccV5x05XqD
311sObFTy6iwEITdGmxN40pNE3WbhYGqOoXb8iRIG2hURv0gfG1/iI0
312=8g3t
313-----END PGP PRIVATE KEY BLOCK-----
314`
315
316const keyPassphrase = "abcdef0123456789"