1package git
2
3import (
4 "bytes"
5 "fmt"
6 "log"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "runtime"
11 "strings"
12 "testing"
13 "time"
14
15 fixtures "github.com/go-git/go-git-fixtures/v4"
16 "github.com/go-git/go-git/v5/plumbing"
17 "github.com/go-git/go-git/v5/plumbing/cache"
18 "github.com/go-git/go-git/v5/plumbing/object"
19 "github.com/go-git/go-git/v5/plumbing/storer"
20 "github.com/go-git/go-git/v5/storage/filesystem"
21 "github.com/go-git/go-git/v5/storage/memory"
22 "github.com/stretchr/testify/assert"
23 "github.com/stretchr/testify/require"
24
25 "github.com/ProtonMail/go-crypto/openpgp"
26 "github.com/ProtonMail/go-crypto/openpgp/armor"
27 "github.com/ProtonMail/go-crypto/openpgp/errors"
28 "github.com/go-git/go-billy/v5/memfs"
29 "github.com/go-git/go-billy/v5/util"
30)
31
32func (s *WorktreeSuite) TestCommitEmptyOptions() {
33 fs := memfs.New()
34 r, err := Init(memory.NewStorage(), fs)
35 s.NoError(err)
36
37 w, err := r.Worktree()
38 s.NoError(err)
39
40 util.WriteFile(fs, "foo", []byte("foo"), 0644)
41
42 _, err = w.Add("foo")
43 s.NoError(err)
44
45 hash, err := w.Commit("foo", &CommitOptions{})
46 s.NoError(err)
47 s.False(hash.IsZero())
48
49 commit, err := r.CommitObject(hash)
50 s.NoError(err)
51 s.NotEqual("", commit.Author.Name)
52}
53
54func (s *WorktreeSuite) TestCommitInitial() {
55 expected := plumbing.NewHash("98c4ac7c29c913f7461eae06e024dc18e80d23a4")
56
57 fs := memfs.New()
58 storage := memory.NewStorage()
59
60 r, err := Init(storage, fs)
61 s.NoError(err)
62
63 w, err := r.Worktree()
64 s.NoError(err)
65
66 util.WriteFile(fs, "foo", []byte("foo"), 0644)
67
68 _, err = w.Add("foo")
69 s.NoError(err)
70
71 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
72 s.Equal(expected, hash)
73 s.NoError(err)
74
75 assertStorageStatus(s, r, 1, 1, 1, expected)
76}
77
78func (s *WorktreeSuite) TestNothingToCommit() {
79 expected := plumbing.NewHash("838ea833ce893e8555907e5ef224aa076f5e274a")
80
81 r, err := Init(memory.NewStorage(), memfs.New())
82 s.NoError(err)
83
84 w, err := r.Worktree()
85 s.NoError(err)
86
87 hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()})
88 s.Equal(plumbing.ZeroHash, hash)
89 s.ErrorIs(err, ErrEmptyCommit)
90
91 hash, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true})
92 s.Equal(expected, hash)
93 s.NoError(err)
94}
95
96func (s *WorktreeSuite) TestNothingToCommitNonEmptyRepo() {
97 fs := memfs.New()
98 r, err := Init(memory.NewStorage(), fs)
99 s.NoError(err)
100
101 w, err := r.Worktree()
102 s.NoError(err)
103
104 err = util.WriteFile(fs, "foo", []byte("foo"), 0644)
105 s.NoError(err)
106
107 w.Add("foo")
108 _, err = w.Commit("previous commit\n", &CommitOptions{Author: defaultSignature()})
109 s.NoError(err)
110
111 hash, err := w.Commit("failed empty commit\n", &CommitOptions{Author: defaultSignature()})
112 s.Equal(plumbing.ZeroHash, hash)
113 s.ErrorIs(err, ErrEmptyCommit)
114
115 _, err = w.Commit("enable empty commits\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true})
116 s.NoError(err)
117}
118
119func (s *WorktreeSuite) TestRemoveAndCommitToMakeEmptyRepo() {
120 fs := memfs.New()
121 r, err := Init(memory.NewStorage(), fs)
122 s.NoError(err)
123
124 w, err := r.Worktree()
125 s.NoError(err)
126
127 err = util.WriteFile(fs, "foo", []byte("foo"), 0644)
128 s.NoError(err)
129
130 _, err = w.Add("foo")
131 s.NoError(err)
132
133 _, err = w.Commit("Add in Repo\n", &CommitOptions{Author: defaultSignature()})
134 s.NoError(err)
135
136 err = fs.Remove("foo")
137 s.NoError(err)
138
139 _, err = w.Add("foo")
140 s.NoError(err)
141
142 _, err = w.Commit("Remove foo\n", &CommitOptions{Author: defaultSignature()})
143 s.NoError(err)
144}
145
146func (s *WorktreeSuite) TestCommitParent() {
147 expected := plumbing.NewHash("ef3ca05477530b37f48564be33ddd48063fc7a22")
148
149 fs := memfs.New()
150 w := &Worktree{
151 r: s.Repository,
152 Filesystem: fs,
153 }
154
155 err := w.Checkout(&CheckoutOptions{})
156 s.NoError(err)
157
158 err = util.WriteFile(fs, "foo", []byte("foo"), 0644)
159 s.NoError(err)
160
161 _, err = w.Add("foo")
162 s.NoError(err)
163
164 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
165 s.Equal(expected, hash)
166 s.NoError(err)
167
168 assertStorageStatus(s, s.Repository, 13, 11, 10, expected)
169}
170
171func (s *WorktreeSuite) TestCommitAmendWithoutChanges() {
172 fs := memfs.New()
173 w := &Worktree{
174 r: s.Repository,
175 Filesystem: fs,
176 }
177
178 err := w.Checkout(&CheckoutOptions{})
179 s.NoError(err)
180
181 err = util.WriteFile(fs, "foo", []byte("foo"), 0644)
182 s.NoError(err)
183
184 _, err = w.Add("foo")
185 s.NoError(err)
186
187 prevHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
188 s.NoError(err)
189
190 amendedHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), Amend: true})
191 s.NoError(err)
192
193 headRef, err := w.r.Head()
194 s.NoError(err)
195
196 s.Equal(headRef.Hash(), amendedHash)
197 s.Equal(prevHash, amendedHash)
198
199 commit, err := w.r.CommitObject(headRef.Hash())
200 s.NoError(err)
201 s.Equal("foo\n", commit.Message)
202
203 assertStorageStatus(s, s.Repository, 13, 11, 10, amendedHash)
204}
205
206func (s *WorktreeSuite) TestCommitAmendWithChanges() {
207 fs := memfs.New()
208 w := &Worktree{
209 r: s.Repository,
210 Filesystem: fs,
211 }
212
213 err := w.Checkout(&CheckoutOptions{})
214 s.NoError(err)
215
216 util.WriteFile(fs, "foo", []byte("foo"), 0644)
217
218 _, err = w.Add("foo")
219 s.NoError(err)
220
221 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
222 s.NoError(err)
223
224 util.WriteFile(fs, "bar", []byte("bar"), 0644)
225
226 _, err = w.Add("bar")
227 s.NoError(err)
228
229 amendedHash, err := w.Commit("bar\n", &CommitOptions{Amend: true})
230 s.NoError(err)
231
232 headRef, err := w.r.Head()
233 s.NoError(err)
234
235 s.Equal(headRef.Hash(), amendedHash)
236
237 commit, err := w.r.CommitObject(headRef.Hash())
238 s.NoError(err)
239 s.Equal("bar\n", commit.Message)
240 s.Equal(1, commit.NumParents())
241
242 stats, err := commit.Stats()
243 s.NoError(err)
244 s.Len(stats, 2)
245 s.Equal(object.FileStat{
246 Name: "bar",
247 Addition: 1,
248 }, stats[0])
249 s.Equal(object.FileStat{
250 Name: "foo",
251 Addition: 1,
252 }, stats[1])
253
254 assertStorageStatus(s, s.Repository, 14, 12, 11, amendedHash)
255}
256
257func (s *WorktreeSuite) TestCommitAmendNothingToCommit() {
258 fs := memfs.New()
259 w := &Worktree{
260 r: s.Repository,
261 Filesystem: fs,
262 }
263
264 err := w.Checkout(&CheckoutOptions{})
265 s.NoError(err)
266
267 err = util.WriteFile(fs, "foo", []byte("foo"), 0644)
268 s.NoError(err)
269
270 _, err = w.Add("foo")
271 s.NoError(err)
272
273 prevHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
274 s.NoError(err)
275
276 _, err = w.Commit("bar\n", &CommitOptions{Author: defaultSignature(), AllowEmptyCommits: true})
277 s.NoError(err)
278
279 amendedHash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), Amend: true})
280 s.T().Log(prevHash, amendedHash)
281 s.ErrorIs(err, ErrEmptyCommit)
282 s.Equal(plumbing.ZeroHash, amendedHash)
283}
284
285func TestCount(t *testing.T) {
286 f := fixtures.Basic().One()
287 r := NewRepositoryWithEmptyWorktree(f)
288
289 iter, err := r.CommitObjects()
290 require.NoError(t, err)
291
292 count := 0
293 iter.ForEach(func(c *object.Commit) error {
294 count++
295 return nil
296 })
297 assert.Equal(t, 9, count, "commits mismatch")
298
299 trees, err := r.TreeObjects()
300 require.NoError(t, err)
301
302 count = 0
303 trees.ForEach(func(c *object.Tree) error {
304 count++
305 return nil
306 })
307 assert.Equal(t, 12, count, "trees mismatch")
308
309 blobs, err := r.BlobObjects()
310 require.NoError(t, err)
311
312 count = 0
313 blobs.ForEach(func(c *object.Blob) error {
314 count++
315 return nil
316 })
317 assert.Equal(t, 10, count, "blobs mismatch")
318
319 objects, err := r.Objects()
320 require.NoError(t, err)
321
322 count = 0
323 objects.ForEach(func(c object.Object) error {
324 count++
325 return nil
326 })
327 assert.Equal(t, 31, count, "objects mismatch")
328}
329
330func TestAddAndCommitWithSkipStatus(t *testing.T) {
331 expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b")
332
333 f := fixtures.Basic().One()
334 fs := memfs.New()
335 r := NewRepositoryWithEmptyWorktree(f)
336 w := &Worktree{
337 r: r,
338 Filesystem: fs,
339 }
340
341 err := w.Checkout(&CheckoutOptions{})
342 require.NoError(t, err)
343
344 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644)
345 util.WriteFile(fs, "foo", []byte("foo"), 0644)
346
347 err = w.AddWithOptions(&AddOptions{
348 Path: "foo",
349 SkipStatus: true,
350 })
351 require.NoError(t, err)
352
353 hash, err := w.Commit("commit foo only\n", &CommitOptions{
354 Author: defaultSignature(),
355 })
356
357 assert.Equal(t, expected.String(), hash.String())
358 require.NoError(t, err)
359
360 assertStorage(t, r, 13, 11, 10, expected)
361}
362
363func assertStorage(
364 t *testing.T, r *Repository,
365 treesCount, blobCount, commitCount int, head plumbing.Hash,
366) {
367 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject)
368 require.NoError(t, err)
369 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject)
370 require.NoError(t, err)
371 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
372 require.NoError(t, err)
373
374 assert.Equal(t, treesCount, lenIterEncodedObjects(trees), "trees count mismatch")
375 assert.Equal(t, blobCount, lenIterEncodedObjects(blobs), "blobs count mismatch")
376 assert.Equal(t, commitCount, lenIterEncodedObjects(commits), "commits count mismatch")
377
378 ref, err := r.Head()
379 require.NoError(t, err)
380 assert.Equal(t, head.String(), ref.Hash().String())
381}
382
383func (s *WorktreeSuite) TestAddAndCommitWithSkipStatusPathNotModified() {
384 expected := plumbing.NewHash("375a3808ffde7f129cdd3c8c252fd0fe37cfd13b")
385 expected2 := plumbing.NewHash("8691273baf8f6ee2cccfc05e910552c04d02d472")
386
387 fs := memfs.New()
388 w := &Worktree{
389 r: s.Repository,
390 Filesystem: fs,
391 }
392
393 err := w.Checkout(&CheckoutOptions{})
394 s.NoError(err)
395
396 util.WriteFile(fs, "foo", []byte("foo"), 0644)
397
398 status, err := w.Status()
399 s.NoError(err)
400 foo := status.File("foo")
401 s.Equal(Untracked, foo.Staging)
402 s.Equal(Untracked, foo.Worktree)
403
404 err = w.AddWithOptions(&AddOptions{
405 Path: "foo",
406 SkipStatus: true,
407 })
408 s.NoError(err)
409
410 status, err = w.Status()
411 s.NoError(err)
412 foo = status.File("foo")
413 s.Equal(Added, foo.Staging)
414 s.Equal(Unmodified, foo.Worktree)
415
416 hash, err := w.Commit("commit foo only\n", &CommitOptions{All: true,
417 Author: defaultSignature(),
418 })
419 s.Equal(expected, hash)
420 s.NoError(err)
421
422 commit1, err := w.r.CommitObject(hash)
423 s.NoError(err)
424
425 status, err = w.Status()
426 s.NoError(err)
427 foo = status.File("foo")
428 s.Equal(Untracked, foo.Staging)
429 s.Equal(Untracked, foo.Worktree)
430
431 assertStorageStatus(s, s.Repository, 13, 11, 10, expected)
432
433 err = w.AddWithOptions(&AddOptions{
434 Path: "foo",
435 SkipStatus: true,
436 })
437 s.NoError(err)
438
439 status, err = w.Status()
440 s.NoError(err)
441 foo = status.File("foo")
442 s.Equal(Untracked, foo.Staging)
443 s.Equal(Untracked, foo.Worktree)
444
445 hash, err = w.Commit("commit with no changes\n", &CommitOptions{
446 Author: defaultSignature(),
447 AllowEmptyCommits: true,
448 })
449 s.Equal(expected2, hash)
450 s.NoError(err)
451
452 commit2, err := w.r.CommitObject(hash)
453 s.NoError(err)
454
455 status, err = w.Status()
456 s.NoError(err)
457 foo = status.File("foo")
458 s.Equal(Untracked, foo.Staging)
459 s.Equal(Untracked, foo.Worktree)
460
461 patch, err := commit2.Patch(commit1)
462 s.NoError(err)
463 files := patch.FilePatches()
464 s.Nil(files)
465
466 assertStorageStatus(s, s.Repository, 13, 11, 11, expected2)
467}
468
469func (s *WorktreeSuite) TestCommitAll() {
470 expected := plumbing.NewHash("aede6f8c9c1c7ec9ca8d287c64b8ed151276fa28")
471
472 fs := memfs.New()
473 w := &Worktree{
474 r: s.Repository,
475 Filesystem: fs,
476 }
477
478 err := w.Checkout(&CheckoutOptions{})
479 s.NoError(err)
480
481 util.WriteFile(fs, "LICENSE", []byte("foo"), 0644)
482 util.WriteFile(fs, "foo", []byte("foo"), 0644)
483
484 hash, err := w.Commit("foo\n", &CommitOptions{
485 All: true,
486 Author: defaultSignature(),
487 })
488
489 s.Equal(expected, hash)
490 s.NoError(err)
491
492 assertStorageStatus(s, s.Repository, 13, 11, 10, expected)
493}
494
495func (s *WorktreeSuite) TestRemoveAndCommitAll() {
496 expected := plumbing.NewHash("907cd576c6ced2ecd3dab34a72bf9cf65944b9a9")
497
498 fs := memfs.New()
499 w := &Worktree{
500 r: s.Repository,
501 Filesystem: fs,
502 }
503
504 err := w.Checkout(&CheckoutOptions{})
505 s.NoError(err)
506
507 util.WriteFile(fs, "foo", []byte("foo"), 0644)
508 _, err = w.Add("foo")
509 s.NoError(err)
510
511 _, errFirst := w.Commit("Add in Repo\n", &CommitOptions{
512 Author: defaultSignature(),
513 })
514 s.Nil(errFirst)
515
516 errRemove := fs.Remove("foo")
517 s.Nil(errRemove)
518
519 hash, errSecond := w.Commit("Remove foo\n", &CommitOptions{
520 All: true,
521 Author: defaultSignature(),
522 })
523 s.Nil(errSecond)
524
525 s.Equal(expected, hash)
526 s.NoError(err)
527
528 assertStorageStatus(s, s.Repository, 13, 11, 11, expected)
529}
530
531func (s *WorktreeSuite) TestCommitSign() {
532 fs := memfs.New()
533 storage := memory.NewStorage()
534
535 r, err := Init(storage, fs)
536 s.NoError(err)
537
538 w, err := r.Worktree()
539 s.NoError(err)
540
541 util.WriteFile(fs, "foo", []byte("foo"), 0644)
542
543 _, err = w.Add("foo")
544 s.NoError(err)
545
546 key := commitSignKey(s.T(), true)
547 hash, err := w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
548 s.NoError(err)
549
550 // Verify the commit.
551 pks := new(bytes.Buffer)
552 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil)
553 s.NoError(err)
554
555 err = key.Serialize(pkw)
556 s.NoError(err)
557 err = pkw.Close()
558 s.NoError(err)
559
560 expectedCommit, err := r.CommitObject(hash)
561 s.NoError(err)
562 actual, err := expectedCommit.Verify(pks.String())
563 s.NoError(err)
564 s.Equal(key.PrimaryKey, actual.PrimaryKey)
565}
566
567func (s *WorktreeSuite) TestCommitSignBadKey() {
568 fs := memfs.New()
569 storage := memory.NewStorage()
570
571 r, err := Init(storage, fs)
572 s.NoError(err)
573
574 w, err := r.Worktree()
575 s.NoError(err)
576
577 util.WriteFile(fs, "foo", []byte("foo"), 0644)
578
579 _, err = w.Add("foo")
580 s.NoError(err)
581
582 key := commitSignKey(s.T(), false)
583 _, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature(), SignKey: key})
584 s.ErrorIs(err, errors.InvalidArgumentError("signing key is encrypted"))
585}
586
587func (s *WorktreeSuite) TestCommitTreeSort() {
588 fs := s.TemporalFilesystem()
589
590 st := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
591 _, err := Init(st, nil)
592 s.NoError(err)
593
594 r, _ := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{
595 URL: fs.Root(),
596 })
597
598 w, err := r.Worktree()
599 s.NoError(err)
600
601 mfs := w.Filesystem
602
603 err = mfs.MkdirAll("delta", 0755)
604 s.NoError(err)
605
606 for _, p := range []string{"delta_last", "Gamma", "delta/middle", "Beta", "delta-first", "alpha"} {
607 util.WriteFile(mfs, p, []byte("foo"), 0644)
608 _, err = w.Add(p)
609 s.NoError(err)
610 }
611
612 _, err = w.Commit("foo\n", &CommitOptions{
613 All: true,
614 Author: defaultSignature(),
615 })
616 s.NoError(err)
617
618 err = r.Push(&PushOptions{})
619 s.NoError(err)
620
621 cmd := exec.Command("git", "fsck")
622 cmd.Dir = fs.Root()
623 cmd.Env = os.Environ()
624 buf := &bytes.Buffer{}
625 cmd.Stderr = buf
626 cmd.Stdout = buf
627
628 err = cmd.Run()
629
630 s.NoError(err, fmt.Sprintf("%s", buf.Bytes()))
631}
632
633// https://github.com/go-git/go-git/pull/224
634func (s *WorktreeSuite) TestJustStoreObjectsNotAlreadyStored() {
635 fs := s.TemporalFilesystem()
636
637 fsDotgit, err := fs.Chroot(".git") // real fs to get modified timestamps
638 s.NoError(err)
639 storage := filesystem.NewStorage(fsDotgit, cache.NewObjectLRUDefault())
640
641 r, err := Init(storage, fs)
642 s.NoError(err)
643
644 w, err := r.Worktree()
645 s.NoError(err)
646
647 // Step 1: Write LICENSE
648 util.WriteFile(fs, "LICENSE", []byte("license"), 0644)
649 hLicense, err := w.Add("LICENSE")
650 s.NoError(err)
651 s.Equal(plumbing.NewHash("0484eba0d41636ba71fa612c78559cd6c3006cde"), hLicense)
652
653 hash, err := w.Commit("commit 1\n", &CommitOptions{
654 All: true,
655 Author: defaultSignature(),
656 })
657 s.NoError(err)
658 s.Equal(plumbing.NewHash("7a7faee4630d2664a6869677cc8ab614f3fd4a18"), hash)
659
660 infoLicense, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde"))
661 s.NoError(err) // checking objects file exists
662
663 // Step 2: Write foo.
664 time.Sleep(5 * time.Millisecond) // uncool, but we need to get different timestamps...
665 util.WriteFile(fs, "foo", []byte("foo"), 0644)
666 hFoo, err := w.Add("foo")
667 s.NoError(err)
668 s.Equal(plumbing.NewHash("19102815663d23f8b75a47e7a01965dcdc96468c"), hFoo)
669
670 hash, err = w.Commit("commit 2\n", &CommitOptions{
671 All: true,
672 Author: defaultSignature(),
673 })
674 s.NoError(err)
675 s.Equal(plumbing.NewHash("97c0c5177e6ac57d10e8ea0017f2d39b91e2b364"), hash)
676
677 // Step 3: Check
678 // There is no need to overwrite the object of LICENSE, because its content
679 // was not changed. Just a write on the object of foo is required. This behaviour
680 // is fixed by #224 and tested by comparing the timestamps of the stored objects.
681 infoFoo, err := fsDotgit.Stat(filepath.Join("objects", "19", "102815663d23f8b75a47e7a01965dcdc96468c"))
682 s.NoError(err) // checking objects file exists
683 s.True(infoLicense.ModTime().Before(infoFoo.ModTime())) // object of foo has another/greaterThan timestamp than LICENSE
684
685 infoLicenseSecond, err := fsDotgit.Stat(filepath.Join("objects", "04", "84eba0d41636ba71fa612c78559cd6c3006cde"))
686 s.NoError(err)
687
688 log.Printf("comparing mod time: %v == %v on %v (%v)", infoLicenseSecond.ModTime(), infoLicense.ModTime(), runtime.GOOS, runtime.GOARCH)
689 s.Equal(infoLicense.ModTime(), infoLicenseSecond.ModTime()) // object of LICENSE should have the same timestamp because no additional write operation was performed
690}
691
692func (s *WorktreeSuite) TestCommitInvalidCharactersInAuthorInfos() {
693 f := fixtures.Basic().One()
694 s.Repository = NewRepositoryWithEmptyWorktree(f)
695
696 expected := plumbing.NewHash("e8eecef2524c3a37cf0f0996603162f81e0373f1")
697
698 fs := memfs.New()
699 storage := memory.NewStorage()
700
701 r, err := Init(storage, fs)
702 s.NoError(err)
703
704 w, err := r.Worktree()
705 s.NoError(err)
706
707 util.WriteFile(fs, "foo", []byte("foo"), 0644)
708
709 _, err = w.Add("foo")
710 s.NoError(err)
711
712 hash, err := w.Commit("foo\n", &CommitOptions{Author: invalidSignature()})
713 s.Equal(expected, hash)
714 s.NoError(err)
715
716 assertStorageStatus(s, r, 1, 1, 1, expected)
717
718 // Check HEAD commit contains author informations with '<', '>' and '\n' stripped
719 lr, err := r.Log(&LogOptions{})
720 s.NoError(err)
721
722 commit, err := lr.Next()
723 s.NoError(err)
724
725 s.Equal("foo bad", commit.Author.Name)
726 s.Equal("badfoo@foo.foo", commit.Author.Email)
727
728}
729
730func assertStorageStatus(
731 s *WorktreeSuite, r *Repository,
732 treesCount, blobCount, commitCount int, head plumbing.Hash,
733) {
734 trees, err := r.Storer.IterEncodedObjects(plumbing.TreeObject)
735 s.NoError(err)
736 blobs, err := r.Storer.IterEncodedObjects(plumbing.BlobObject)
737 s.NoError(err)
738 commits, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
739 s.NoError(err)
740
741 s.Equal(treesCount, lenIterEncodedObjects(trees))
742 s.Equal(blobCount, lenIterEncodedObjects(blobs))
743 s.Equal(commitCount, lenIterEncodedObjects(commits))
744
745 ref, err := r.Head()
746 s.NoError(err)
747 s.Equal(head, ref.Hash())
748}
749
750func lenIterEncodedObjects(iter storer.EncodedObjectIter) int {
751 count := 0
752 iter.ForEach(func(plumbing.EncodedObject) error {
753 count++
754 return nil
755 })
756
757 return count
758}
759
760func defaultSignature() *object.Signature {
761 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200")
762 return &object.Signature{
763 Name: "foo",
764 Email: "foo@foo.foo",
765 When: when,
766 }
767}
768
769func invalidSignature() *object.Signature {
770 when, _ := time.Parse(object.DateFormat, "Thu May 04 00:03:43 2017 +0200")
771 return &object.Signature{
772 Name: "foo <bad>\n",
773 Email: "<bad>\nfoo@foo.foo",
774 When: when,
775 }
776}
777
778func commitSignKey(t *testing.T, decrypt bool) *openpgp.Entity {
779 s := strings.NewReader(armoredKeyRing)
780 es, err := openpgp.ReadArmoredKeyRing(s)
781 assert.NoError(t, err)
782
783 assert.Len(t, es, 1)
784 assert.Len(t, es[0].Identities, 1)
785 _, ok := es[0].Identities["foo bar <foo@foo.foo>"]
786 assert.True(t, ok)
787
788 key := es[0]
789 if decrypt {
790 err = key.PrivateKey.Decrypt([]byte(keyPassphrase))
791 assert.NoError(t, err)
792 }
793
794 return key
795}
796
797const armoredKeyRing = `
798-----BEGIN PGP PRIVATE KEY BLOCK-----
799
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
852=gIM9
853-----END PGP PRIVATE KEY BLOCK-----
854`
855
856const keyPassphrase = "abcdef0123456789"