1package git
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "os/exec"
11 "os/user"
12 "path"
13 "path/filepath"
14 "regexp"
15 "strings"
16 "testing"
17 "time"
18
19 fixtures "github.com/go-git/go-git-fixtures/v4"
20 "github.com/stretchr/testify/assert"
21 "github.com/stretchr/testify/suite"
22
23 "github.com/ProtonMail/go-crypto/openpgp"
24 "github.com/ProtonMail/go-crypto/openpgp/armor"
25 openpgperr "github.com/ProtonMail/go-crypto/openpgp/errors"
26
27 "github.com/go-git/go-git/v5/config"
28 "github.com/go-git/go-git/v5/plumbing"
29 "github.com/go-git/go-git/v5/plumbing/cache"
30 "github.com/go-git/go-git/v5/plumbing/object"
31 "github.com/go-git/go-git/v5/plumbing/protocol/packp"
32 "github.com/go-git/go-git/v5/plumbing/storer"
33 "github.com/go-git/go-git/v5/plumbing/transport"
34 "github.com/go-git/go-git/v5/storage"
35 "github.com/go-git/go-git/v5/storage/filesystem"
36 "github.com/go-git/go-git/v5/storage/memory"
37
38 "github.com/go-git/go-billy/v5"
39 "github.com/go-git/go-billy/v5/memfs"
40 "github.com/go-git/go-billy/v5/osfs"
41 "github.com/go-git/go-billy/v5/util"
42)
43
44type RepositorySuite struct {
45 suite.Suite
46 BaseSuite
47}
48
49func TestRepositorySuite(t *testing.T) {
50 suite.Run(t, new(RepositorySuite))
51}
52
53func (s *RepositorySuite) TestInit() {
54 r, err := Init(memory.NewStorage(), memfs.New())
55 s.NoError(err)
56 s.NotNil(r)
57
58 cfg, err := r.Config()
59 s.NoError(err)
60 s.False(cfg.Core.IsBare)
61
62 // check the HEAD to see what the default branch is
63 createCommit(s, r)
64 ref, err := r.Head()
65 s.NoError(err)
66 s.Equal(plumbing.Master.String(), ref.Name().String())
67}
68
69func (s *RepositorySuite) TestInitWithOptions() {
70 r, err := InitWithOptions(memory.NewStorage(), memfs.New(), InitOptions{
71 DefaultBranch: "refs/heads/foo",
72 })
73 s.NoError(err)
74 s.NotNil(r)
75 createCommit(s, r)
76
77 ref, err := r.Head()
78 s.NoError(err)
79 s.Equal("refs/heads/foo", ref.Name().String())
80
81}
82
83func (s *RepositorySuite) TestInitWithInvalidDefaultBranch() {
84 _, err := InitWithOptions(memory.NewStorage(), memfs.New(), InitOptions{
85 DefaultBranch: "foo",
86 })
87 s.NotNil(err)
88}
89
90func createCommit(s *RepositorySuite, r *Repository) plumbing.Hash {
91 // Create a commit so there is a HEAD to check
92 wt, err := r.Worktree()
93 s.NoError(err)
94
95 rm, err := wt.Filesystem.Create("foo.txt")
96 s.NoError(err)
97
98 _, err = rm.Write([]byte("foo text"))
99 s.NoError(err)
100
101 _, err = wt.Add("foo.txt")
102 s.NoError(err)
103
104 author := object.Signature{
105 Name: "go-git",
106 Email: "go-git@fake.local",
107 When: time.Now(),
108 }
109
110 h, err := wt.Commit("test commit message", &CommitOptions{
111 All: true,
112 Author: &author,
113 Committer: &author,
114 AllowEmptyCommits: true,
115 })
116 s.NoError(err)
117 return h
118}
119
120func (s *RepositorySuite) TestInitNonStandardDotGit() {
121 dir, err := os.MkdirTemp("", "")
122 s.NoError(err)
123
124 fs := osfs.New(dir)
125 dot, _ := fs.Chroot("storage")
126 st := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
127
128 wt, _ := fs.Chroot("worktree")
129 r, err := Init(st, wt)
130 s.NoError(err)
131 s.NotNil(r)
132
133 f, err := fs.Open(fs.Join("worktree", ".git"))
134 s.NoError(err)
135 defer func() { _ = f.Close() }()
136
137 all, err := io.ReadAll(f)
138 s.NoError(err)
139 s.Equal(string(all), fmt.Sprintf("gitdir: %s\n", filepath.Join("..", "storage")))
140
141 cfg, err := r.Config()
142 s.NoError(err)
143 s.Equal(cfg.Core.Worktree, filepath.Join("..", "worktree"))
144}
145
146func (s *RepositorySuite) TestInitStandardDotGit() {
147 dir, err := os.MkdirTemp("", "")
148 s.NoError(err)
149
150 fs := osfs.New(dir)
151 dot, _ := fs.Chroot(".git")
152 st := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
153
154 r, err := Init(st, fs)
155 s.NoError(err)
156 s.NotNil(r)
157
158 l, err := fs.ReadDir(".git")
159 s.NoError(err)
160 s.True(len(l) > 0)
161
162 cfg, err := r.Config()
163 s.NoError(err)
164 s.Equal("", cfg.Core.Worktree)
165}
166
167func (s *RepositorySuite) TestInitBare() {
168 r, err := Init(memory.NewStorage(), nil)
169 s.NoError(err)
170 s.NotNil(r)
171
172 cfg, err := r.Config()
173 s.NoError(err)
174 s.True(cfg.Core.IsBare)
175}
176
177func (s *RepositorySuite) TestInitAlreadyExists() {
178 st := memory.NewStorage()
179
180 r, err := Init(st, nil)
181 s.NoError(err)
182 s.NotNil(r)
183
184 r, err = Init(st, nil)
185 s.ErrorIs(err, ErrRepositoryAlreadyExists)
186 s.Nil(r)
187}
188
189func (s *RepositorySuite) TestOpen() {
190 st := memory.NewStorage()
191
192 r, err := Init(st, memfs.New())
193 s.NoError(err)
194 s.NotNil(r)
195
196 r, err = Open(st, memfs.New())
197 s.NoError(err)
198 s.NotNil(r)
199}
200
201func (s *RepositorySuite) TestOpenBare() {
202 st := memory.NewStorage()
203
204 r, err := Init(st, nil)
205 s.NoError(err)
206 s.NotNil(r)
207
208 r, err = Open(st, nil)
209 s.NoError(err)
210 s.NotNil(r)
211}
212
213func (s *RepositorySuite) TestOpenBareMissingWorktree() {
214 st := memory.NewStorage()
215
216 r, err := Init(st, memfs.New())
217 s.NoError(err)
218 s.NotNil(r)
219
220 r, err = Open(st, nil)
221 s.NoError(err)
222 s.NotNil(r)
223}
224
225func (s *RepositorySuite) TestOpenNotExists() {
226 r, err := Open(memory.NewStorage(), nil)
227 s.ErrorIs(err, ErrRepositoryNotExists)
228 s.Nil(r)
229}
230
231func (s *RepositorySuite) TestClone() {
232 r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
233 URL: s.GetBasicLocalRepositoryURL(),
234 })
235
236 s.NoError(err)
237
238 remotes, err := r.Remotes()
239 s.NoError(err)
240 s.Len(remotes, 1)
241}
242
243func (s *RepositorySuite) TestCloneContext() {
244 ctx, cancel := context.WithCancel(context.Background())
245 cancel()
246
247 r, err := CloneContext(ctx, memory.NewStorage(), nil, &CloneOptions{
248 URL: s.GetBasicLocalRepositoryURL(),
249 })
250
251 s.NotNil(r)
252 s.ErrorIs(err, context.Canceled)
253}
254
255func (s *RepositorySuite) TestCloneMirror() {
256 r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
257 URL: fixtures.Basic().One().URL,
258 Mirror: true,
259 })
260
261 s.NoError(err)
262
263 refs, err := r.References()
264 var count int
265 refs.ForEach(func(r *plumbing.Reference) error { s.T().Log(r); count++; return nil })
266 s.NoError(err)
267 // 6 refs total from github.com/git-fixtures/basic.git:
268 // - HEAD
269 // - refs/heads/master
270 // - refs/heads/branch
271 // - refs/pull/1/head
272 // - refs/pull/2/head
273 // - refs/pull/2/merge
274 s.Equal(6, count)
275
276 cfg, err := r.Config()
277 s.NoError(err)
278
279 s.True(cfg.Core.IsBare)
280 s.Nil(cfg.Remotes[DefaultRemoteName].Validate())
281 s.True(cfg.Remotes[DefaultRemoteName].Mirror)
282}
283
284func (s *RepositorySuite) TestCloneWithTags() {
285 url := s.GetLocalRepositoryURL(
286 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
287 )
288
289 r, err := Clone(memory.NewStorage(), nil, &CloneOptions{URL: url, Tags: NoTags})
290 s.NoError(err)
291
292 remotes, err := r.Remotes()
293 s.NoError(err)
294 s.Len(remotes, 1)
295
296 i, err := r.References()
297 s.NoError(err)
298
299 var count int
300 i.ForEach(func(r *plumbing.Reference) error { count++; return nil })
301
302 s.Equal(3, count)
303}
304
305func (s *RepositorySuite) TestCloneSparse() {
306 fs := memfs.New()
307 r, err := Clone(memory.NewStorage(), fs, &CloneOptions{
308 URL: s.GetBasicLocalRepositoryURL(),
309 NoCheckout: true,
310 })
311 s.NoError(err)
312
313 w, err := r.Worktree()
314 s.NoError(err)
315
316 sparseCheckoutDirectories := []string{"go", "json", "php"}
317 s.NoError(w.Checkout(&CheckoutOptions{
318 Branch: "refs/heads/master",
319 SparseCheckoutDirectories: sparseCheckoutDirectories,
320 }))
321
322 fis, err := fs.ReadDir(".")
323 s.NoError(err)
324 for _, fi := range fis {
325 s.True(fi.IsDir())
326 var oneOfSparseCheckoutDirs bool
327
328 for _, sparseCheckoutDirectory := range sparseCheckoutDirectories {
329 if strings.HasPrefix(fi.Name(), sparseCheckoutDirectory) {
330 oneOfSparseCheckoutDirs = true
331 }
332 }
333 s.True(oneOfSparseCheckoutDirs)
334 }
335}
336
337func (s *RepositorySuite) TestCreateRemoteAndRemote() {
338 r, _ := Init(memory.NewStorage(), nil)
339 remote, err := r.CreateRemote(&config.RemoteConfig{
340 Name: "foo",
341 URLs: []string{"http://foo/foo.git"},
342 })
343
344 s.NoError(err)
345 s.Equal("foo", remote.Config().Name)
346
347 alt, err := r.Remote("foo")
348 s.NoError(err)
349 s.NotSame(remote, alt)
350 s.Equal("foo", alt.Config().Name)
351}
352
353func (s *RepositorySuite) TestCreateRemoteInvalid() {
354 r, _ := Init(memory.NewStorage(), nil)
355 remote, err := r.CreateRemote(&config.RemoteConfig{})
356
357 s.ErrorIs(err, config.ErrRemoteConfigEmptyName)
358 s.Nil(remote)
359}
360
361func (s *RepositorySuite) TestCreateRemoteAnonymous() {
362 r, _ := Init(memory.NewStorage(), nil)
363 remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{
364 Name: "anonymous",
365 URLs: []string{"http://foo/foo.git"},
366 })
367
368 s.NoError(err)
369 s.Equal("anonymous", remote.Config().Name)
370}
371
372func (s *RepositorySuite) TestCreateRemoteAnonymousInvalidName() {
373 r, _ := Init(memory.NewStorage(), nil)
374 remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{
375 Name: "not_anonymous",
376 URLs: []string{"http://foo/foo.git"},
377 })
378
379 s.ErrorIs(err, ErrAnonymousRemoteName)
380 s.Nil(remote)
381}
382
383func (s *RepositorySuite) TestCreateRemoteAnonymousInvalid() {
384 r, _ := Init(memory.NewStorage(), nil)
385 remote, err := r.CreateRemoteAnonymous(&config.RemoteConfig{})
386
387 s.ErrorIs(err, config.ErrRemoteConfigEmptyName)
388 s.Nil(remote)
389}
390
391func (s *RepositorySuite) TestDeleteRemote() {
392 r, _ := Init(memory.NewStorage(), nil)
393 _, err := r.CreateRemote(&config.RemoteConfig{
394 Name: "foo",
395 URLs: []string{"http://foo/foo.git"},
396 })
397
398 s.NoError(err)
399
400 err = r.DeleteRemote("foo")
401 s.NoError(err)
402
403 alt, err := r.Remote("foo")
404 s.ErrorIs(err, ErrRemoteNotFound)
405 s.Nil(alt)
406}
407
408func (s *RepositorySuite) TestEmptyCreateBranch() {
409 r, _ := Init(memory.NewStorage(), nil)
410 err := r.CreateBranch(&config.Branch{})
411
412 s.NotNil(err)
413}
414
415func (s *RepositorySuite) TestInvalidCreateBranch() {
416 r, _ := Init(memory.NewStorage(), nil)
417 err := r.CreateBranch(&config.Branch{
418 Name: "-foo",
419 })
420
421 s.NotNil(err)
422}
423
424func (s *RepositorySuite) TestCreateBranchAndBranch() {
425 r, _ := Init(memory.NewStorage(), nil)
426 testBranch := &config.Branch{
427 Name: "foo",
428 Remote: "origin",
429 Merge: "refs/heads/foo",
430 }
431 err := r.CreateBranch(testBranch)
432
433 s.NoError(err)
434 cfg, err := r.Config()
435 s.NoError(err)
436 s.Len(cfg.Branches, 1)
437 branch := cfg.Branches["foo"]
438 s.Equal(testBranch.Name, branch.Name)
439 s.Equal(testBranch.Remote, branch.Remote)
440 s.Equal(testBranch.Merge, branch.Merge)
441
442 branch, err = r.Branch("foo")
443 s.NoError(err)
444 s.Equal(testBranch.Name, branch.Name)
445 s.Equal(testBranch.Remote, branch.Remote)
446 s.Equal(testBranch.Merge, branch.Merge)
447}
448
449func (s *RepositorySuite) TestMergeFF() {
450 r, err := Init(memory.NewStorage(), memfs.New())
451 s.NoError(err)
452 s.NotNil(r)
453
454 createCommit(s, r)
455 createCommit(s, r)
456 createCommit(s, r)
457 lastCommit := createCommit(s, r)
458
459 wt, err := r.Worktree()
460 s.NoError(err)
461
462 targetBranch := plumbing.NewBranchReferenceName("foo")
463 err = wt.Checkout(&CheckoutOptions{
464 Hash: lastCommit,
465 Create: true,
466 Branch: targetBranch,
467 })
468 s.NoError(err)
469
470 createCommit(s, r)
471 fooHash := createCommit(s, r)
472
473 // Checkout the master branch so that we can try to merge foo into it.
474 err = wt.Checkout(&CheckoutOptions{
475 Branch: plumbing.Master,
476 })
477 s.NoError(err)
478
479 head, err := r.Head()
480 s.NoError(err)
481 s.Equal(lastCommit, head.Hash())
482
483 targetRef := plumbing.NewHashReference(targetBranch, fooHash)
484 s.NotNil(targetRef)
485
486 err = r.Merge(*targetRef, MergeOptions{
487 Strategy: FastForwardMerge,
488 })
489 s.NoError(err)
490
491 head, err = r.Head()
492 s.NoError(err)
493 s.Equal(fooHash, head.Hash())
494}
495
496func (s *RepositorySuite) TestMergeFF_Invalid() {
497 r, err := Init(memory.NewStorage(), memfs.New())
498 s.NoError(err)
499 s.NotNil(r)
500
501 // Keep track of the first commit, which will be the
502 // reference to create the target branch so that we
503 // can simulate a non-ff merge.
504 firstCommit := createCommit(s, r)
505 createCommit(s, r)
506 createCommit(s, r)
507 lastCommit := createCommit(s, r)
508
509 wt, err := r.Worktree()
510 s.NoError(err)
511
512 targetBranch := plumbing.NewBranchReferenceName("foo")
513 err = wt.Checkout(&CheckoutOptions{
514 Hash: firstCommit,
515 Create: true,
516 Branch: targetBranch,
517 })
518
519 s.NoError(err)
520
521 createCommit(s, r)
522 h := createCommit(s, r)
523
524 // Checkout the master branch so that we can try to merge foo into it.
525 err = wt.Checkout(&CheckoutOptions{
526 Branch: plumbing.Master,
527 })
528 s.NoError(err)
529
530 head, err := r.Head()
531 s.NoError(err)
532 s.Equal(lastCommit, head.Hash())
533
534 targetRef := plumbing.NewHashReference(targetBranch, h)
535 s.NotNil(targetRef)
536
537 err = r.Merge(*targetRef, MergeOptions{
538 Strategy: MergeStrategy(10),
539 })
540 s.ErrorIs(err, ErrUnsupportedMergeStrategy)
541
542 // Failed merge operations must not change HEAD.
543 head, err = r.Head()
544 s.NoError(err)
545 s.Equal(lastCommit, head.Hash())
546
547 err = r.Merge(*targetRef, MergeOptions{})
548 s.ErrorIs(err, ErrFastForwardMergeNotPossible)
549
550 head, err = r.Head()
551 s.NoError(err)
552 s.Equal(lastCommit, head.Hash())
553}
554
555func (s *RepositorySuite) TestCreateBranchUnmarshal() {
556 r, _ := Init(memory.NewStorage(), nil)
557
558 expected := []byte(`[core]
559 bare = true
560[remote "foo"]
561 url = http://foo/foo.git
562 fetch = +refs/heads/*:refs/remotes/foo/*
563[branch "foo"]
564 remote = origin
565 merge = refs/heads/foo
566[branch "master"]
567 remote = origin
568 merge = refs/heads/master
569`)
570
571 _, err := r.CreateRemote(&config.RemoteConfig{
572 Name: "foo",
573 URLs: []string{"http://foo/foo.git"},
574 })
575 s.NoError(err)
576 testBranch1 := &config.Branch{
577 Name: "master",
578 Remote: "origin",
579 Merge: "refs/heads/master",
580 }
581 testBranch2 := &config.Branch{
582 Name: "foo",
583 Remote: "origin",
584 Merge: "refs/heads/foo",
585 }
586 err = r.CreateBranch(testBranch1)
587 s.NoError(err)
588 err = r.CreateBranch(testBranch2)
589 s.NoError(err)
590
591 cfg, err := r.Config()
592 s.NoError(err)
593 marshaled, err := cfg.Marshal()
594 s.NoError(err)
595 s.Equal(string(marshaled), string(expected))
596}
597
598func (s *RepositorySuite) TestBranchInvalid() {
599 r, _ := Init(memory.NewStorage(), nil)
600 branch, err := r.Branch("foo")
601
602 s.NotNil(err)
603 s.Nil(branch)
604}
605
606func (s *RepositorySuite) TestCreateBranchInvalid() {
607 r, _ := Init(memory.NewStorage(), nil)
608 err := r.CreateBranch(&config.Branch{})
609
610 s.NotNil(err)
611
612 testBranch := &config.Branch{
613 Name: "foo",
614 Remote: "origin",
615 Merge: "refs/heads/foo",
616 }
617 err = r.CreateBranch(testBranch)
618 s.NoError(err)
619 err = r.CreateBranch(testBranch)
620 s.NotNil(err)
621}
622
623func (s *RepositorySuite) TestDeleteBranch() {
624 r, _ := Init(memory.NewStorage(), nil)
625 testBranch := &config.Branch{
626 Name: "foo",
627 Remote: "origin",
628 Merge: "refs/heads/foo",
629 }
630 err := r.CreateBranch(testBranch)
631
632 s.NoError(err)
633
634 err = r.DeleteBranch("foo")
635 s.NoError(err)
636
637 b, err := r.Branch("foo")
638 s.ErrorIs(err, ErrBranchNotFound)
639 s.Nil(b)
640
641 err = r.DeleteBranch("foo")
642 s.ErrorIs(err, ErrBranchNotFound)
643}
644
645func (s *RepositorySuite) TestPlainInit() {
646 dir, err := os.MkdirTemp("", "")
647 s.NoError(err)
648
649 r, err := PlainInit(dir, true)
650 s.NoError(err)
651 s.NotNil(r)
652
653 cfg, err := r.Config()
654 s.NoError(err)
655 s.True(cfg.Core.IsBare)
656}
657
658func (s *RepositorySuite) TestPlainInitWithOptions() {
659 dir, err := os.MkdirTemp("", "")
660 s.NoError(err)
661
662 r, err := PlainInitWithOptions(dir, &PlainInitOptions{
663 InitOptions: InitOptions{
664 DefaultBranch: "refs/heads/foo",
665 },
666 Bare: false,
667 })
668 s.NoError(err)
669 s.NotNil(r)
670
671 cfg, err := r.Config()
672 s.NoError(err)
673 s.False(cfg.Core.IsBare)
674
675 createCommit(s, r)
676
677 ref, err := r.Head()
678 s.NoError(err)
679 s.Equal("refs/heads/foo", ref.Name().String())
680}
681
682func (s *RepositorySuite) TestPlainInitAlreadyExists() {
683 dir, err := os.MkdirTemp("", "")
684 s.NoError(err)
685
686 r, err := PlainInit(dir, true)
687 s.NoError(err)
688 s.NotNil(r)
689
690 r, err = PlainInit(dir, true)
691 s.ErrorIs(err, ErrRepositoryAlreadyExists)
692 s.Nil(r)
693}
694
695func (s *RepositorySuite) TestPlainOpen() {
696 dir, err := os.MkdirTemp("", "")
697 s.NoError(err)
698
699 r, err := PlainInit(dir, false)
700 s.NoError(err)
701 s.NotNil(r)
702
703 r, err = PlainOpen(dir)
704 s.NoError(err)
705 s.NotNil(r)
706}
707
708func (s *RepositorySuite) TestPlainOpenTildePath() {
709 dir, clean := s.TemporalHomeDir()
710 defer clean()
711
712 r, err := PlainInit(dir, false)
713 s.NoError(err)
714 s.NotNil(r)
715
716 currentUser, err := user.Current()
717 s.NoError(err)
718 // remove domain for windows
719 username := currentUser.Username[strings.Index(currentUser.Username, "\\")+1:]
720
721 homes := []string{"~/", "~" + username + "/"}
722 for _, home := range homes {
723 path := strings.Replace(dir, strings.Split(dir, ".tmp")[0], home, 1)
724
725 r, err = PlainOpen(path)
726 s.NoError(err)
727 s.NotNil(r)
728 }
729}
730
731func (s *RepositorySuite) TestPlainOpenBare() {
732 dir, err := os.MkdirTemp("", "")
733 s.NoError(err)
734
735 r, err := PlainInit(dir, true)
736 s.NoError(err)
737 s.NotNil(r)
738
739 r, err = PlainOpen(dir)
740 s.NoError(err)
741 s.NotNil(r)
742}
743
744func (s *RepositorySuite) TestPlainOpenNotBare() {
745 dir, err := os.MkdirTemp("", "")
746 s.NoError(err)
747
748 r, err := PlainInit(dir, false)
749 s.NoError(err)
750 s.NotNil(r)
751
752 r, err = PlainOpen(filepath.Join(dir, ".git"))
753 s.NoError(err)
754 s.NotNil(r)
755}
756
757func (s *RepositorySuite) testPlainOpenGitFile(f func(string, string) string) {
758 fs := s.TemporalFilesystem()
759
760 dir, err := util.TempDir(fs, "", "plain-open")
761 s.NoError(err)
762
763 r, err := PlainInit(fs.Join(fs.Root(), dir), true)
764 s.NoError(err)
765 s.NotNil(r)
766
767 altDir, err := util.TempDir(fs, "", "plain-open")
768 s.NoError(err)
769
770 err = util.WriteFile(fs, fs.Join(altDir, ".git"),
771 []byte(f(fs.Join(fs.Root(), dir), fs.Join(fs.Root(), altDir))),
772 0o644,
773 )
774
775 s.NoError(err)
776
777 r, err = PlainOpen(fs.Join(fs.Root(), altDir))
778 s.NoError(err)
779 s.NotNil(r)
780}
781
782func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFile() {
783 s.testPlainOpenGitFile(func(dir, altDir string) string {
784 return fmt.Sprintf("gitdir: %s\n", dir)
785 })
786}
787
788func (s *RepositorySuite) TestPlainOpenBareAbsoluteGitDirFileNoEOL() {
789 s.testPlainOpenGitFile(func(dir, altDir string) string {
790 return fmt.Sprintf("gitdir: %s", dir)
791 })
792}
793
794func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFile() {
795 s.testPlainOpenGitFile(func(dir, altDir string) string {
796 dir, err := filepath.Rel(altDir, dir)
797 s.NoError(err)
798 return fmt.Sprintf("gitdir: %s\n", dir)
799 })
800}
801
802func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileNoEOL() {
803 s.testPlainOpenGitFile(func(dir, altDir string) string {
804 dir, err := filepath.Rel(altDir, dir)
805 s.NoError(err)
806 return fmt.Sprintf("gitdir: %s\n", dir)
807 })
808}
809
810func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileTrailingGarbage() {
811 fs := s.TemporalFilesystem()
812
813 dir, err := util.TempDir(fs, "", "")
814 s.NoError(err)
815
816 r, err := PlainInit(dir, true)
817 s.NoError(err)
818 s.NotNil(r)
819
820 altDir, err := util.TempDir(fs, "", "")
821 s.NoError(err)
822
823 err = util.WriteFile(fs, fs.Join(altDir, ".git"),
824 []byte(fmt.Sprintf("gitdir: %s\nTRAILING", fs.Join(fs.Root(), altDir))),
825 0o644,
826 )
827 s.NoError(err)
828
829 r, err = PlainOpen(altDir)
830 s.ErrorIs(err, ErrRepositoryNotExists)
831 s.Nil(r)
832}
833
834func (s *RepositorySuite) TestPlainOpenBareRelativeGitDirFileBadPrefix() {
835 fs := s.TemporalFilesystem()
836
837 dir, err := util.TempDir(fs, "", "")
838 s.NoError(err)
839
840 r, err := PlainInit(fs.Join(fs.Root(), dir), true)
841 s.NoError(err)
842 s.NotNil(r)
843
844 altDir, err := util.TempDir(fs, "", "")
845 s.NoError(err)
846
847 err = util.WriteFile(fs, fs.Join(altDir, ".git"), []byte(
848 fmt.Sprintf("xgitdir: %s\n", fs.Join(fs.Root(), dir)),
849 ), 0o644)
850
851 s.NoError(err)
852
853 r, err = PlainOpen(fs.Join(fs.Root(), altDir))
854 s.ErrorContains(err, "gitdir")
855 s.Nil(r)
856}
857
858func (s *RepositorySuite) TestPlainOpenNotExists() {
859 r, err := PlainOpen("/not-exists/")
860 s.ErrorIs(err, ErrRepositoryNotExists)
861 s.Nil(r)
862}
863
864func (s *RepositorySuite) TestPlainOpenDetectDotGit() {
865 fs := s.TemporalFilesystem()
866
867 dir, err := util.TempDir(fs, "", "")
868 s.NoError(err)
869
870 subdir := filepath.Join(dir, "a", "b")
871 err = fs.MkdirAll(subdir, 0755)
872 s.NoError(err)
873
874 file := fs.Join(subdir, "file.txt")
875 f, err := fs.Create(file)
876 s.NoError(err)
877 f.Close()
878
879 r, err := PlainInit(fs.Join(fs.Root(), dir), false)
880 s.NoError(err)
881 s.NotNil(r)
882
883 opt := &PlainOpenOptions{DetectDotGit: true}
884 r, err = PlainOpenWithOptions(fs.Join(fs.Root(), subdir), opt)
885 s.NoError(err)
886 s.NotNil(r)
887
888 r, err = PlainOpenWithOptions(fs.Join(fs.Root(), file), opt)
889 s.NoError(err)
890 s.NotNil(r)
891
892 optnodetect := &PlainOpenOptions{DetectDotGit: false}
893 r, err = PlainOpenWithOptions(fs.Join(fs.Root(), file), optnodetect)
894 s.NotNil(err)
895 s.Nil(r)
896}
897
898func (s *RepositorySuite) TestPlainOpenNotExistsDetectDotGit() {
899 dir, err := os.MkdirTemp("", "")
900 s.NoError(err)
901
902 opt := &PlainOpenOptions{DetectDotGit: true}
903 r, err := PlainOpenWithOptions(dir, opt)
904 s.ErrorIs(err, ErrRepositoryNotExists)
905 s.Nil(r)
906}
907
908func (s *RepositorySuite) TestPlainClone() {
909 dir, err := os.MkdirTemp("", "")
910 s.NoError(err)
911
912 r, err := PlainClone(dir, false, &CloneOptions{
913 URL: s.GetBasicLocalRepositoryURL(),
914 })
915
916 s.NoError(err)
917
918 remotes, err := r.Remotes()
919 s.NoError(err)
920 s.Len(remotes, 1)
921 cfg, err := r.Config()
922 s.NoError(err)
923 s.Len(cfg.Branches, 1)
924 s.Equal("master", cfg.Branches["master"].Name)
925}
926
927func (s *RepositorySuite) TestPlainCloneBareAndShared() {
928 dir, err := os.MkdirTemp("", "")
929 s.NoError(err)
930
931 remote := s.GetBasicLocalRepositoryURL()
932
933 r, err := PlainClone(dir, true, &CloneOptions{
934 URL: remote,
935 Shared: true,
936 })
937 s.NoError(err)
938
939 altpath := path.Join(dir, "objects", "info", "alternates")
940 _, err = os.Stat(altpath)
941 s.NoError(err)
942
943 data, err := os.ReadFile(altpath)
944 s.NoError(err)
945
946 line := path.Join(remote, GitDirName, "objects") + "\n"
947 s.Equal(line, string(data))
948
949 cfg, err := r.Config()
950 s.NoError(err)
951 s.Len(cfg.Branches, 1)
952 s.Equal("master", cfg.Branches["master"].Name)
953}
954
955func (s *RepositorySuite) TestPlainCloneShared() {
956 dir, err := os.MkdirTemp("", "")
957 s.NoError(err)
958
959 remote := s.GetBasicLocalRepositoryURL()
960
961 r, err := PlainClone(dir, false, &CloneOptions{
962 URL: remote,
963 Shared: true,
964 })
965 s.NoError(err)
966
967 altpath := path.Join(dir, GitDirName, "objects", "info", "alternates")
968 _, err = os.Stat(altpath)
969 s.NoError(err)
970
971 data, err := os.ReadFile(altpath)
972 s.NoError(err)
973
974 line := path.Join(remote, GitDirName, "objects") + "\n"
975 s.Equal(line, string(data))
976
977 cfg, err := r.Config()
978 s.NoError(err)
979 s.Len(cfg.Branches, 1)
980 s.Equal("master", cfg.Branches["master"].Name)
981}
982
983func (s *RepositorySuite) TestPlainCloneSharedHttpShouldReturnError() {
984 dir, err := os.MkdirTemp("", "")
985 s.NoError(err)
986
987 remote := "http://somerepo"
988
989 _, err = PlainClone(dir, false, &CloneOptions{
990 URL: remote,
991 Shared: true,
992 })
993 s.ErrorIs(err, ErrAlternatePathNotSupported)
994}
995
996func (s *RepositorySuite) TestPlainCloneSharedHttpsShouldReturnError() {
997 dir, err := os.MkdirTemp("", "")
998 s.NoError(err)
999
1000 remote := "https://somerepo"
1001
1002 _, err = PlainClone(dir, false, &CloneOptions{
1003 URL: remote,
1004 Shared: true,
1005 })
1006 s.ErrorIs(err, ErrAlternatePathNotSupported)
1007}
1008
1009func (s *RepositorySuite) TestPlainCloneSharedSSHShouldReturnError() {
1010 dir, err := os.MkdirTemp("", "")
1011 s.NoError(err)
1012
1013 remote := "ssh://somerepo"
1014
1015 _, err = PlainClone(dir, false, &CloneOptions{
1016 URL: remote,
1017 Shared: true,
1018 })
1019 s.ErrorIs(err, ErrAlternatePathNotSupported)
1020}
1021
1022func (s *RepositorySuite) TestPlainCloneWithRemoteName() {
1023 dir, err := os.MkdirTemp("", "")
1024 s.NoError(err)
1025
1026 r, err := PlainClone(dir, false, &CloneOptions{
1027 URL: s.GetBasicLocalRepositoryURL(),
1028 RemoteName: "test",
1029 })
1030
1031 s.NoError(err)
1032
1033 remote, err := r.Remote("test")
1034 s.NoError(err)
1035 s.NotNil(remote)
1036}
1037
1038func (s *RepositorySuite) TestPlainCloneOverExistingGitDirectory() {
1039 dir, err := os.MkdirTemp("", "")
1040 s.NoError(err)
1041
1042 r, err := PlainInit(dir, false)
1043 s.NotNil(r)
1044 s.NoError(err)
1045
1046 r, err = PlainClone(dir, false, &CloneOptions{
1047 URL: s.GetBasicLocalRepositoryURL(),
1048 })
1049 s.Nil(r)
1050 s.ErrorIs(err, ErrRepositoryAlreadyExists)
1051}
1052
1053func (s *RepositorySuite) TestPlainCloneContextCancel() {
1054 ctx, cancel := context.WithCancel(context.Background())
1055 cancel()
1056
1057 dir, err := os.MkdirTemp("", "")
1058 s.NoError(err)
1059
1060 r, err := PlainCloneContext(ctx, dir, false, &CloneOptions{
1061 URL: s.GetBasicLocalRepositoryURL(),
1062 })
1063
1064 s.NotNil(r)
1065 s.ErrorIs(err, context.Canceled)
1066}
1067
1068func (s *RepositorySuite) TestPlainCloneContextNonExistentWithExistentDir() {
1069 ctx, cancel := context.WithCancel(context.Background())
1070 defer cancel()
1071
1072 fs := s.TemporalFilesystem()
1073
1074 dir, err := util.TempDir(fs, "", "")
1075 s.NoError(err)
1076
1077 r, err := PlainCloneContext(ctx, dir, false, &CloneOptions{
1078 URL: "incorrectOnPurpose",
1079 })
1080 s.NotNil(r)
1081 s.ErrorIs(err, transport.ErrRepositoryNotFound)
1082
1083 _, err = fs.Stat(dir)
1084 s.False(os.IsNotExist(err))
1085
1086 names, err := fs.ReadDir(dir)
1087 s.NoError(err)
1088 s.Len(names, 0)
1089}
1090
1091func (s *RepositorySuite) TestPlainCloneContextNonExistentWithNonExistentDir() {
1092 ctx, cancel := context.WithCancel(context.Background())
1093 defer cancel()
1094
1095 fs := s.TemporalFilesystem()
1096
1097 tmpDir, err := util.TempDir(fs, "", "")
1098 s.NoError(err)
1099
1100 repoDir := filepath.Join(tmpDir, "repoDir")
1101
1102 r, err := PlainCloneContext(ctx, repoDir, false, &CloneOptions{
1103 URL: "incorrectOnPurpose",
1104 })
1105 s.NotNil(r)
1106 s.ErrorIs(err, transport.ErrRepositoryNotFound)
1107
1108 _, err = fs.Stat(repoDir)
1109 s.True(os.IsNotExist(err))
1110}
1111
1112func (s *RepositorySuite) TestPlainCloneContextNonExistentWithNotDir() {
1113 ctx, cancel := context.WithCancel(context.Background())
1114 cancel()
1115
1116 fs := s.TemporalFilesystem()
1117
1118 tmpDir, err := util.TempDir(fs, "", "")
1119 s.NoError(err)
1120
1121 repoDir := fs.Join(tmpDir, "repoDir")
1122
1123 f, err := fs.Create(repoDir)
1124 s.NoError(err)
1125 s.Nil(f.Close())
1126
1127 r, err := PlainCloneContext(ctx, fs.Join(fs.Root(), repoDir), false, &CloneOptions{
1128 URL: "incorrectOnPurpose",
1129 })
1130 s.Nil(r)
1131 s.ErrorContains(err, "not a directory")
1132
1133 fi, err := fs.Stat(repoDir)
1134 s.NoError(err)
1135 s.False(fi.IsDir())
1136}
1137
1138func (s *RepositorySuite) TestPlainCloneContextNonExistentWithNotEmptyDir() {
1139 ctx, cancel := context.WithCancel(context.Background())
1140 defer cancel()
1141
1142 fs := s.TemporalFilesystem()
1143
1144 tmpDir, err := util.TempDir(fs, "", "")
1145 s.NoError(err)
1146
1147 repoDir := filepath.Join(tmpDir, "repoDir")
1148 err = fs.MkdirAll(repoDir, 0777)
1149 s.NoError(err)
1150
1151 dummyFile := filepath.Join(repoDir, "dummyFile")
1152 err = util.WriteFile(fs, dummyFile, []byte("dummyContent"), 0644)
1153 s.NoError(err)
1154
1155 r, err := PlainCloneContext(ctx, fs.Join(fs.Root(), repoDir), false, &CloneOptions{
1156 URL: "incorrectOnPurpose",
1157 })
1158 s.NotNil(r)
1159 s.ErrorIs(err, transport.ErrRepositoryNotFound)
1160
1161 _, err = fs.Stat(dummyFile)
1162 s.NoError(err)
1163
1164}
1165
1166func (s *RepositorySuite) TestPlainCloneContextNonExistingOverExistingGitDirectory() {
1167 ctx, cancel := context.WithCancel(context.Background())
1168 defer cancel()
1169
1170 dir, err := os.MkdirTemp("", "")
1171 s.NoError(err)
1172
1173 r, err := PlainInit(dir, false)
1174 s.NotNil(r)
1175 s.NoError(err)
1176
1177 r, err = PlainCloneContext(ctx, dir, false, &CloneOptions{
1178 URL: "incorrectOnPurpose",
1179 })
1180 s.Nil(r)
1181 s.ErrorIs(err, ErrRepositoryAlreadyExists)
1182}
1183
1184func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules() {
1185 if testing.Short() {
1186 s.T().Skip("skipping test in short mode.")
1187 }
1188
1189 dir, err := os.MkdirTemp("", "")
1190 s.NoError(err)
1191
1192 path := fixtures.ByTag("submodule").One().Worktree().Root()
1193 r, err := PlainClone(dir, false, &CloneOptions{
1194 URL: path,
1195 RecurseSubmodules: DefaultSubmoduleRecursionDepth,
1196 })
1197
1198 s.NoError(err)
1199
1200 cfg, err := r.Config()
1201 s.NoError(err)
1202 s.Len(cfg.Remotes, 1)
1203 s.Len(cfg.Branches, 1)
1204 s.Len(cfg.Submodules, 2)
1205}
1206
1207func (s *RepositorySuite) TestPlainCloneWithShallowSubmodules() {
1208 if testing.Short() {
1209 s.T().Skip("skipping test in short mode.")
1210 }
1211
1212 dir, err := os.MkdirTemp("", "")
1213 s.NoError(err)
1214
1215 path := fixtures.ByTag("submodule").One().Worktree().Root()
1216 mainRepo, err := PlainClone(dir, false, &CloneOptions{
1217 URL: path,
1218 RecurseSubmodules: 1,
1219 ShallowSubmodules: true,
1220 })
1221 s.NoError(err)
1222
1223 mainWorktree, err := mainRepo.Worktree()
1224 s.NoError(err)
1225
1226 submodule, err := mainWorktree.Submodule("basic")
1227 s.NoError(err)
1228
1229 subRepo, err := submodule.Repository()
1230 s.NoError(err)
1231
1232 lr, err := subRepo.Log(&LogOptions{})
1233 s.NoError(err)
1234
1235 commitCount := 0
1236 for _, err := lr.Next(); err == nil; _, err = lr.Next() {
1237 commitCount++
1238 }
1239 s.NoError(err)
1240
1241 s.Equal(1, commitCount)
1242}
1243
1244func (s *RepositorySuite) TestPlainCloneNoCheckout() {
1245 dir, err := os.MkdirTemp("", "")
1246 s.NoError(err)
1247
1248 path := fixtures.ByTag("submodule").One().Worktree().Root()
1249 r, err := PlainClone(dir, false, &CloneOptions{
1250 URL: path,
1251 NoCheckout: true,
1252 RecurseSubmodules: DefaultSubmoduleRecursionDepth,
1253 })
1254 s.NoError(err)
1255
1256 h, err := r.Head()
1257 s.NoError(err)
1258 s.Equal("b685400c1f9316f350965a5993d350bc746b0bf4", h.Hash().String())
1259
1260 fi, err := osfs.New(dir).ReadDir("")
1261 s.NoError(err)
1262 s.Len(fi, 1) // .git
1263}
1264
1265func (s *RepositorySuite) TestFetch() {
1266 r, _ := Init(memory.NewStorage(), nil)
1267 _, err := r.CreateRemote(&config.RemoteConfig{
1268 Name: DefaultRemoteName,
1269 URLs: []string{s.GetBasicLocalRepositoryURL()},
1270 })
1271 s.NoError(err)
1272 s.Nil(r.Fetch(&FetchOptions{}))
1273
1274 remotes, err := r.Remotes()
1275 s.NoError(err)
1276 s.Len(remotes, 1)
1277
1278 _, err = r.Head()
1279 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1280
1281 branch, err := r.Reference("refs/remotes/origin/master", false)
1282 s.NoError(err)
1283 s.NotNil(branch)
1284 s.Equal(plumbing.HashReference, branch.Type())
1285 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", branch.Hash().String())
1286}
1287
1288func (s *RepositorySuite) TestFetchContext() {
1289 r, _ := Init(memory.NewStorage(), nil)
1290 _, err := r.CreateRemote(&config.RemoteConfig{
1291 Name: DefaultRemoteName,
1292 URLs: []string{s.GetBasicLocalRepositoryURL()},
1293 })
1294 s.NoError(err)
1295
1296 ctx, cancel := context.WithCancel(context.Background())
1297 cancel()
1298
1299 s.NotNil(r.FetchContext(ctx, &FetchOptions{}))
1300}
1301
1302func (s *RepositorySuite) TestFetchWithFilters() {
1303 r, _ := Init(memory.NewStorage(), nil)
1304 _, err := r.CreateRemote(&config.RemoteConfig{
1305 Name: DefaultRemoteName,
1306 URLs: []string{s.GetBasicLocalRepositoryURL()},
1307 })
1308 s.NoError(err)
1309
1310 err = r.Fetch(&FetchOptions{
1311 Filter: packp.FilterBlobNone(),
1312 })
1313 s.ErrorIs(err, ErrFilterNotSupported)
1314
1315}
1316func (s *RepositorySuite) TestFetchWithFiltersReal() {
1317 r, _ := Init(memory.NewStorage(), nil)
1318 _, err := r.CreateRemote(&config.RemoteConfig{
1319 Name: DefaultRemoteName,
1320 URLs: []string{"https://github.com/git-fixtures/basic.git"},
1321 })
1322 s.NoError(err)
1323 err = r.Fetch(&FetchOptions{
1324 Filter: packp.FilterBlobNone(),
1325 })
1326 s.NoError(err)
1327 blob, err := r.BlobObject(plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492"))
1328 s.NotNil(err)
1329 s.Nil(blob)
1330
1331}
1332func (s *RepositorySuite) TestCloneWithProgress() {
1333 fs := memfs.New()
1334
1335 buf := bytes.NewBuffer(nil)
1336 _, err := Clone(memory.NewStorage(), fs, &CloneOptions{
1337 URL: s.GetBasicLocalRepositoryURL(),
1338 Progress: buf,
1339 })
1340
1341 s.NoError(err)
1342 s.NotEqual(0, buf.Len())
1343}
1344
1345func (s *RepositorySuite) TestCloneDeep() {
1346 fs := memfs.New()
1347 r, _ := Init(memory.NewStorage(), fs)
1348
1349 head, err := r.Head()
1350 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1351 s.Nil(head)
1352
1353 err = r.clone(context.Background(), &CloneOptions{
1354 URL: s.GetBasicLocalRepositoryURL(),
1355 })
1356
1357 s.NoError(err)
1358
1359 remotes, err := r.Remotes()
1360 s.NoError(err)
1361 s.Len(remotes, 1)
1362
1363 head, err = r.Reference(plumbing.HEAD, false)
1364 s.NoError(err)
1365 s.NotNil(head)
1366 s.Equal(plumbing.SymbolicReference, head.Type())
1367 s.Equal("refs/heads/master", head.Target().String())
1368
1369 branch, err := r.Reference(head.Target(), false)
1370 s.NoError(err)
1371 s.NotNil(branch)
1372 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", branch.Hash().String())
1373
1374 branch, err = r.Reference("refs/remotes/origin/master", false)
1375 s.NoError(err)
1376 s.NotNil(branch)
1377 s.Equal(plumbing.HashReference, branch.Type())
1378 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", branch.Hash().String())
1379
1380 fi, err := fs.ReadDir("")
1381 s.NoError(err)
1382 s.Len(fi, 8)
1383}
1384
1385func (s *RepositorySuite) TestCloneConfig() {
1386 r, _ := Init(memory.NewStorage(), nil)
1387
1388 head, err := r.Head()
1389 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1390 s.Nil(head)
1391
1392 err = r.clone(context.Background(), &CloneOptions{
1393 URL: s.GetBasicLocalRepositoryURL(),
1394 })
1395
1396 s.NoError(err)
1397
1398 cfg, err := r.Config()
1399 s.NoError(err)
1400
1401 s.True(cfg.Core.IsBare)
1402 s.Len(cfg.Remotes, 1)
1403 s.Equal("origin", cfg.Remotes["origin"].Name)
1404 s.Len(cfg.Remotes["origin"].URLs, 1)
1405 s.Len(cfg.Branches, 1)
1406 s.Equal("master", cfg.Branches["master"].Name)
1407}
1408
1409func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD() {
1410 s.testCloneSingleBranchAndNonHEADReference("refs/heads/branch")
1411}
1412
1413func (s *RepositorySuite) TestCloneSingleBranchAndNonHEADAndNonFull() {
1414 s.testCloneSingleBranchAndNonHEADReference("branch")
1415}
1416
1417func (s *RepositorySuite) testCloneSingleBranchAndNonHEADReference(ref string) {
1418 r, _ := Init(memory.NewStorage(), nil)
1419
1420 head, err := r.Head()
1421 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1422 s.Nil(head)
1423
1424 err = r.clone(context.Background(), &CloneOptions{
1425 URL: s.GetBasicLocalRepositoryURL(),
1426 ReferenceName: plumbing.ReferenceName(ref),
1427 SingleBranch: true,
1428 })
1429
1430 s.NoError(err)
1431
1432 remotes, err := r.Remotes()
1433 s.NoError(err)
1434 s.Len(remotes, 1)
1435
1436 cfg, err := r.Config()
1437 s.NoError(err)
1438 s.Len(cfg.Branches, 1)
1439 s.Equal("branch", cfg.Branches["branch"].Name)
1440 s.Equal("origin", cfg.Branches["branch"].Remote)
1441 s.Equal(plumbing.ReferenceName("refs/heads/branch"), cfg.Branches["branch"].Merge)
1442
1443 head, err = r.Reference(plumbing.HEAD, false)
1444 s.NoError(err)
1445 s.NotNil(head)
1446 s.Equal(plumbing.SymbolicReference, head.Type())
1447 s.Equal("refs/heads/branch", head.Target().String())
1448
1449 branch, err := r.Reference(head.Target(), false)
1450 s.NoError(err)
1451 s.NotNil(branch)
1452 s.Equal("e8d3ffab552895c19b9fcf7aa264d277cde33881", branch.Hash().String())
1453
1454 branch, err = r.Reference("refs/remotes/origin/branch", false)
1455 s.NoError(err)
1456 s.NotNil(branch)
1457 s.Equal(plumbing.HashReference, branch.Type())
1458 s.Equal("e8d3ffab552895c19b9fcf7aa264d277cde33881", branch.Hash().String())
1459}
1460
1461func (s *RepositorySuite) TestCloneSingleBranchHEADMain() {
1462 r, _ := Init(memory.NewStorage(), nil)
1463
1464 head, err := r.Head()
1465 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1466 s.Nil(head)
1467
1468 err = r.clone(context.Background(), &CloneOptions{
1469 URL: s.GetLocalRepositoryURL(fixtures.ByTag("no-master-head").One()),
1470 SingleBranch: true,
1471 })
1472
1473 s.NoError(err)
1474
1475 remotes, err := r.Remotes()
1476 s.NoError(err)
1477 s.Len(remotes, 1)
1478
1479 cfg, err := r.Config()
1480 s.NoError(err)
1481 s.Len(cfg.Branches, 1)
1482 s.Equal("main", cfg.Branches["main"].Name)
1483 s.Equal("origin", cfg.Branches["main"].Remote)
1484 s.Equal(plumbing.ReferenceName("refs/heads/main"), cfg.Branches["main"].Merge)
1485
1486 head, err = r.Reference(plumbing.HEAD, false)
1487 s.NoError(err)
1488 s.NotNil(head)
1489 s.Equal(plumbing.SymbolicReference, head.Type())
1490 s.Equal("refs/heads/main", head.Target().String())
1491
1492 branch, err := r.Reference(head.Target(), false)
1493 s.NoError(err)
1494 s.NotNil(branch)
1495 s.Equal("786dafbd351e587da1ae97e5fb9fbdf868b4a28f", branch.Hash().String())
1496
1497 branch, err = r.Reference("refs/remotes/origin/HEAD", false)
1498 s.NoError(err)
1499 s.NotNil(branch)
1500 s.Equal(plumbing.HashReference, branch.Type())
1501 s.Equal("786dafbd351e587da1ae97e5fb9fbdf868b4a28f", branch.Hash().String())
1502}
1503
1504func (s *RepositorySuite) TestCloneSingleBranch() {
1505 r, _ := Init(memory.NewStorage(), nil)
1506
1507 head, err := r.Head()
1508 s.ErrorIs(err, plumbing.ErrReferenceNotFound)
1509 s.Nil(head)
1510
1511 err = r.clone(context.Background(), &CloneOptions{
1512 URL: s.GetBasicLocalRepositoryURL(),
1513 SingleBranch: true,
1514 })
1515
1516 s.NoError(err)
1517
1518 remotes, err := r.Remotes()
1519 s.NoError(err)
1520 s.Len(remotes, 1)
1521
1522 cfg, err := r.Config()
1523 s.NoError(err)
1524 s.Len(cfg.Branches, 1)
1525 s.Equal("master", cfg.Branches["master"].Name)
1526 s.Equal("origin", cfg.Branches["master"].Remote)
1527 s.Equal(plumbing.ReferenceName("refs/heads/master"), cfg.Branches["master"].Merge)
1528
1529 head, err = r.Reference(plumbing.HEAD, false)
1530 s.NoError(err)
1531 s.NotNil(head)
1532 s.Equal(plumbing.SymbolicReference, head.Type())
1533 s.Equal("refs/heads/master", head.Target().String())
1534
1535 branch, err := r.Reference(head.Target(), false)
1536 s.NoError(err)
1537 s.NotNil(branch)
1538 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", branch.Hash().String())
1539}
1540
1541func (s *RepositorySuite) TestCloneSingleTag() {
1542 r, _ := Init(memory.NewStorage(), nil)
1543
1544 url := s.GetLocalRepositoryURL(
1545 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
1546 )
1547
1548 err := r.clone(context.Background(), &CloneOptions{
1549 URL: url,
1550 SingleBranch: true,
1551 ReferenceName: plumbing.ReferenceName("refs/tags/commit-tag"),
1552 })
1553 s.NoError(err)
1554
1555 branch, err := r.Reference("refs/tags/commit-tag", false)
1556 s.NoError(err)
1557 s.NotNil(branch)
1558
1559 conf, err := r.Config()
1560 s.NoError(err)
1561 originRemote := conf.Remotes["origin"]
1562 s.NotNil(originRemote)
1563 s.Len(originRemote.Fetch, 1)
1564 s.Equal("+refs/tags/commit-tag:refs/tags/commit-tag", originRemote.Fetch[0].String())
1565}
1566
1567func (s *RepositorySuite) TestCloneDetachedHEAD() {
1568 r, _ := Init(memory.NewStorage(), nil)
1569 err := r.clone(context.Background(), &CloneOptions{
1570 URL: s.GetBasicLocalRepositoryURL(),
1571 ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"),
1572 })
1573 s.NoError(err)
1574
1575 cfg, err := r.Config()
1576 s.NoError(err)
1577 s.Len(cfg.Branches, 0)
1578
1579 head, err := r.Reference(plumbing.HEAD, false)
1580 s.NoError(err)
1581 s.NotNil(head)
1582 s.Equal(plumbing.HashReference, head.Type())
1583 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", head.Hash().String())
1584
1585 count := 0
1586 objects, err := r.Objects()
1587 s.NoError(err)
1588 objects.ForEach(func(object.Object) error { count++; return nil })
1589 s.Equal(28, count)
1590}
1591
1592func (s *RepositorySuite) TestCloneDetachedHEADAndSingle() {
1593 r, _ := Init(memory.NewStorage(), nil)
1594 err := r.clone(context.Background(), &CloneOptions{
1595 URL: s.GetBasicLocalRepositoryURL(),
1596 ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"),
1597 SingleBranch: true,
1598 })
1599 s.NoError(err)
1600
1601 cfg, err := r.Config()
1602 s.NoError(err)
1603 s.Len(cfg.Branches, 0)
1604
1605 head, err := r.Reference(plumbing.HEAD, false)
1606 s.NoError(err)
1607 s.NotNil(head)
1608 s.Equal(plumbing.HashReference, head.Type())
1609 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", head.Hash().String())
1610
1611 count := 0
1612 objects, err := r.Objects()
1613 s.NoError(err)
1614 objects.ForEach(func(object.Object) error { count++; return nil })
1615 s.Equal(28, count)
1616}
1617
1618func (s *RepositorySuite) TestCloneDetachedHEADAndShallow() {
1619 r, _ := Init(memory.NewStorage(), memfs.New())
1620 err := r.clone(context.Background(), &CloneOptions{
1621 URL: s.GetBasicLocalRepositoryURL(),
1622 ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"),
1623 Depth: 1,
1624 })
1625
1626 s.NoError(err)
1627
1628 cfg, err := r.Config()
1629 s.NoError(err)
1630 s.Len(cfg.Branches, 0)
1631
1632 head, err := r.Reference(plumbing.HEAD, false)
1633 s.NoError(err)
1634 s.NotNil(head)
1635 s.Equal(plumbing.HashReference, head.Type())
1636 s.Equal("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", head.Hash().String())
1637
1638 count := 0
1639 objects, err := r.Objects()
1640 s.NoError(err)
1641 objects.ForEach(func(object.Object) error { count++; return nil })
1642 s.Equal(15, count)
1643}
1644
1645func (s *RepositorySuite) TestCloneDetachedHEADAnnotatedTag() {
1646 r, _ := Init(memory.NewStorage(), nil)
1647 err := r.clone(context.Background(), &CloneOptions{
1648 URL: s.GetLocalRepositoryURL(fixtures.ByTag("tags").One()),
1649 ReferenceName: plumbing.ReferenceName("refs/tags/annotated-tag"),
1650 })
1651 s.NoError(err)
1652
1653 cfg, err := r.Config()
1654 s.NoError(err)
1655 s.Len(cfg.Branches, 0)
1656
1657 head, err := r.Reference(plumbing.HEAD, false)
1658 s.NoError(err)
1659 s.NotNil(head)
1660 s.Equal(plumbing.HashReference, head.Type())
1661 s.Equal("f7b877701fbf855b44c0a9e86f3fdce2c298b07f", head.Hash().String())
1662
1663 count := 0
1664 objects, err := r.Objects()
1665 s.NoError(err)
1666 objects.ForEach(func(object.Object) error { count++; return nil })
1667 s.Equal(7, count)
1668}
1669
1670func (s *RepositorySuite) TestCloneWithFilter() {
1671 r, _ := Init(memory.NewStorage(), nil)
1672
1673 err := r.clone(context.Background(), &CloneOptions{
1674 URL: "https://github.com/git-fixtures/basic.git",
1675 Filter: packp.FilterTreeDepth(0),
1676 })
1677 s.NoError(err)
1678 blob, err := r.BlobObject(plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492"))
1679 s.NotNil(err)
1680 s.Nil(blob)
1681}
1682func (s *RepositorySuite) TestPush() {
1683 url, err := os.MkdirTemp("", "")
1684 s.NoError(err)
1685
1686 server, err := PlainInit(url, true)
1687 s.NoError(err)
1688
1689 _, err = s.Repository.CreateRemote(&config.RemoteConfig{
1690 Name: "test",
1691 URLs: []string{url},
1692 })
1693 s.NoError(err)
1694
1695 err = s.Repository.Push(&PushOptions{
1696 RemoteName: "test",
1697 })
1698 s.NoError(err)
1699
1700 AssertReferences(s.T(), server, map[string]string{
1701 "refs/heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
1702 "refs/heads/branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881",
1703 })
1704
1705 AssertReferences(s.T(), s.Repository, map[string]string{
1706 "refs/remotes/test/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
1707 "refs/remotes/test/branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881",
1708 })
1709}
1710
1711func (s *RepositorySuite) TestPushContext() {
1712 url, err := os.MkdirTemp("", "")
1713 s.NoError(err)
1714
1715 _, err = PlainInit(url, true)
1716 s.NoError(err)
1717
1718 _, err = s.Repository.CreateRemote(&config.RemoteConfig{
1719 Name: "foo",
1720 URLs: []string{url},
1721 })
1722 s.NoError(err)
1723
1724 ctx, cancel := context.WithCancel(context.Background())
1725 cancel()
1726
1727 err = s.Repository.PushContext(ctx, &PushOptions{
1728 RemoteName: "foo",
1729 })
1730 s.NotNil(err)
1731}
1732
1733// installPreReceiveHook installs a pre-receive hook in the .git
1734// directory at path which prints message m before exiting
1735// successfully.
1736func installPreReceiveHook(s *RepositorySuite, fs billy.Filesystem, path, m string) {
1737 hooks := fs.Join(path, "hooks")
1738 err := fs.MkdirAll(hooks, 0777)
1739 s.NoError(err)
1740
1741 err = util.WriteFile(fs, fs.Join(hooks, "pre-receive"), preReceiveHook(m), 0777)
1742 s.NoError(err)
1743}
1744
1745func (s *RepositorySuite) TestPushWithProgress() {
1746 fs := s.TemporalFilesystem()
1747
1748 path, err := util.TempDir(fs, "", "")
1749 s.NoError(err)
1750
1751 url := fs.Join(fs.Root(), path)
1752
1753 server, err := PlainInit(url, true)
1754 s.NoError(err)
1755
1756 m := "Receiving..."
1757 installPreReceiveHook(s, fs, path, m)
1758
1759 _, err = s.Repository.CreateRemote(&config.RemoteConfig{
1760 Name: "bar",
1761 URLs: []string{url},
1762 })
1763 s.NoError(err)
1764
1765 var p bytes.Buffer
1766 err = s.Repository.Push(&PushOptions{
1767 RemoteName: "bar",
1768 Progress: &p,
1769 })
1770 s.NoError(err)
1771
1772 AssertReferences(s.T(), server, map[string]string{
1773 "refs/heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
1774 "refs/heads/branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881",
1775 })
1776
1777 s.Equal([]byte(m), (&p).Bytes())
1778}
1779
1780func (s *RepositorySuite) TestPushDepth() {
1781 url, err := os.MkdirTemp("", "")
1782 s.NoError(err)
1783
1784 server, err := PlainClone(url, true, &CloneOptions{
1785 URL: fixtures.Basic().One().DotGit().Root(),
1786 })
1787
1788 s.NoError(err)
1789
1790 r, err := Clone(memory.NewStorage(), memfs.New(), &CloneOptions{
1791 URL: url,
1792 Depth: 1,
1793 })
1794 s.NoError(err)
1795
1796 err = util.WriteFile(r.wt, "foo", nil, 0755)
1797 s.NoError(err)
1798
1799 w, err := r.Worktree()
1800 s.NoError(err)
1801
1802 _, err = w.Add("foo")
1803 s.NoError(err)
1804
1805 hash, err := w.Commit("foo", &CommitOptions{
1806 Author: defaultSignature(),
1807 Committer: defaultSignature(),
1808 })
1809 s.NoError(err)
1810
1811 err = r.Push(&PushOptions{})
1812 s.NoError(err)
1813
1814 AssertReferences(s.T(), server, map[string]string{
1815 "refs/heads/master": hash.String(),
1816 })
1817
1818 AssertReferences(s.T(), r, map[string]string{
1819 "refs/remotes/origin/master": hash.String(),
1820 })
1821}
1822
1823func (s *RepositorySuite) TestPushNonExistentRemote() {
1824 srcFs := fixtures.Basic().One().DotGit()
1825 sto := filesystem.NewStorage(srcFs, cache.NewObjectLRUDefault())
1826
1827 r, err := Open(sto, srcFs)
1828 s.NoError(err)
1829
1830 err = r.Push(&PushOptions{RemoteName: "myremote"})
1831 s.ErrorContains(err, "remote not found")
1832}
1833
1834func (s *RepositorySuite) TestLog() {
1835 r, _ := Init(memory.NewStorage(), nil)
1836 err := r.clone(context.Background(), &CloneOptions{
1837 URL: s.GetBasicLocalRepositoryURL(),
1838 })
1839
1840 s.NoError(err)
1841
1842 cIter, err := r.Log(&LogOptions{
1843 From: plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
1844 })
1845
1846 s.NoError(err)
1847
1848 commitOrder := []plumbing.Hash{
1849 plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
1850 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
1851 }
1852
1853 for _, o := range commitOrder {
1854 commit, err := cIter.Next()
1855 s.NoError(err)
1856 s.Equal(o, commit.Hash)
1857 }
1858 _, err = cIter.Next()
1859 s.ErrorIs(err, io.EOF)
1860}
1861
1862func (s *RepositorySuite) TestLogAll() {
1863 r, _ := Init(memory.NewStorage(), nil)
1864 err := r.clone(context.Background(), &CloneOptions{
1865 URL: s.GetBasicLocalRepositoryURL(),
1866 })
1867 s.NoError(err)
1868
1869 rIter, err := r.Storer.IterReferences()
1870 s.NoError(err)
1871
1872 refCount := 0
1873 err = rIter.ForEach(func(ref *plumbing.Reference) error {
1874 refCount++
1875 return nil
1876 })
1877 s.NoError(err)
1878 s.Equal(5, refCount)
1879
1880 cIter, err := r.Log(&LogOptions{
1881 All: true,
1882 })
1883 s.NoError(err)
1884
1885 commitOrder := []plumbing.Hash{
1886 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
1887 plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"),
1888 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
1889 plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"),
1890 plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"),
1891 plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
1892 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
1893 plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"),
1894 plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
1895 }
1896
1897 for _, o := range commitOrder {
1898 commit, err := cIter.Next()
1899 s.NoError(err)
1900 s.Equal(o, commit.Hash)
1901 }
1902 _, err = cIter.Next()
1903 s.ErrorIs(err, io.EOF)
1904 cIter.Close()
1905}
1906
1907func (s *RepositorySuite) TestLogAllMissingReferences() {
1908 r, _ := Init(memory.NewStorage(), nil)
1909 err := r.clone(context.Background(), &CloneOptions{
1910 URL: s.GetBasicLocalRepositoryURL(),
1911 })
1912 s.NoError(err)
1913 err = r.Storer.RemoveReference(plumbing.HEAD)
1914 s.NoError(err)
1915
1916 rIter, err := r.Storer.IterReferences()
1917 s.NoError(err)
1918
1919 refCount := 0
1920 err = rIter.ForEach(func(ref *plumbing.Reference) error {
1921 refCount++
1922 return nil
1923 })
1924 s.NoError(err)
1925 s.Equal(4, refCount)
1926
1927 err = r.Storer.SetReference(plumbing.NewHashReference(plumbing.ReferenceName("DUMMY"), plumbing.NewHash("DUMMY")))
1928 s.NoError(err)
1929
1930 rIter, err = r.Storer.IterReferences()
1931 s.NoError(err)
1932
1933 refCount = 0
1934 err = rIter.ForEach(func(ref *plumbing.Reference) error {
1935 refCount++
1936 return nil
1937 })
1938 s.NoError(err)
1939 s.Equal(5, refCount)
1940
1941 cIter, err := r.Log(&LogOptions{
1942 All: true,
1943 })
1944 s.NotNil(cIter)
1945 s.NoError(err)
1946
1947 cCount := 0
1948 cIter.ForEach(func(c *object.Commit) error {
1949 cCount++
1950 return nil
1951 })
1952 s.Equal(9, cCount)
1953
1954 _, err = cIter.Next()
1955 s.ErrorIs(err, io.EOF)
1956 cIter.Close()
1957}
1958
1959func (s *RepositorySuite) TestLogAllOrderByTime() {
1960 r, _ := Init(memory.NewStorage(), nil)
1961 err := r.clone(context.Background(), &CloneOptions{
1962 URL: s.GetBasicLocalRepositoryURL(),
1963 })
1964
1965 s.NoError(err)
1966
1967 cIter, err := r.Log(&LogOptions{
1968 Order: LogOrderCommitterTime,
1969 All: true,
1970 })
1971 s.NoError(err)
1972
1973 commitOrder := []plumbing.Hash{
1974 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
1975 plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"),
1976 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
1977 plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"),
1978 plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"),
1979 plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"),
1980 plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
1981 plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
1982 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
1983 }
1984
1985 for _, o := range commitOrder {
1986 commit, err := cIter.Next()
1987 s.NoError(err)
1988 s.Equal(o, commit.Hash)
1989 }
1990 _, err = cIter.Next()
1991 s.ErrorIs(err, io.EOF)
1992 cIter.Close()
1993}
1994
1995func (s *RepositorySuite) TestLogHead() {
1996 r, _ := Init(memory.NewStorage(), nil)
1997 err := r.clone(context.Background(), &CloneOptions{
1998 URL: s.GetBasicLocalRepositoryURL(),
1999 })
2000
2001 s.NoError(err)
2002
2003 cIter, err := r.Log(&LogOptions{})
2004
2005 s.NoError(err)
2006
2007 commitOrder := []plumbing.Hash{
2008 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
2009 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
2010 plumbing.NewHash("af2d6a6954d532f8ffb47615169c8fdf9d383a1a"),
2011 plumbing.NewHash("1669dce138d9b841a518c64b10914d88f5e488ea"),
2012 plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
2013 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
2014 plumbing.NewHash("a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69"),
2015 plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"),
2016 }
2017
2018 for _, o := range commitOrder {
2019 commit, err := cIter.Next()
2020 s.NoError(err)
2021 s.Equal(o, commit.Hash)
2022 }
2023 _, err = cIter.Next()
2024 s.ErrorIs(err, io.EOF)
2025}
2026
2027func (s *RepositorySuite) TestLogError() {
2028 r, _ := Init(memory.NewStorage(), nil)
2029 err := r.clone(context.Background(), &CloneOptions{
2030 URL: s.GetBasicLocalRepositoryURL(),
2031 })
2032
2033 s.NoError(err)
2034
2035 _, err = r.Log(&LogOptions{
2036 From: plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
2037 })
2038 s.NotNil(err)
2039}
2040
2041func (s *RepositorySuite) TestLogFileNext() {
2042 r, _ := Init(memory.NewStorage(), nil)
2043 err := r.clone(context.Background(), &CloneOptions{
2044 URL: s.GetBasicLocalRepositoryURL(),
2045 })
2046
2047 s.NoError(err)
2048
2049 fileName := "vendor/foo.go"
2050 cIter, err := r.Log(&LogOptions{FileName: &fileName})
2051
2052 s.NoError(err)
2053
2054 commitOrder := []plumbing.Hash{
2055 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
2056 }
2057
2058 for _, o := range commitOrder {
2059 commit, err := cIter.Next()
2060 s.NoError(err)
2061 s.Equal(o, commit.Hash)
2062 }
2063 _, err = cIter.Next()
2064 s.ErrorIs(err, io.EOF)
2065}
2066
2067func (s *RepositorySuite) TestLogFileForEach() {
2068 r, _ := Init(memory.NewStorage(), nil)
2069 err := r.clone(context.Background(), &CloneOptions{
2070 URL: s.GetBasicLocalRepositoryURL(),
2071 })
2072
2073 s.NoError(err)
2074
2075 fileName := "php/crappy.php"
2076 cIter, err := r.Log(&LogOptions{FileName: &fileName})
2077 s.NoError(err)
2078 defer cIter.Close()
2079
2080 commitOrder := []plumbing.Hash{
2081 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
2082 }
2083
2084 expectedIndex := 0
2085 err = cIter.ForEach(func(commit *object.Commit) error {
2086 expectedCommitHash := commitOrder[expectedIndex]
2087 s.Equal(expectedCommitHash.String(), commit.Hash.String())
2088 expectedIndex++
2089 return nil
2090 })
2091 s.NoError(err)
2092 s.Equal(1, expectedIndex)
2093}
2094
2095func (s *RepositorySuite) TestLogNonHeadFile() {
2096 r, _ := Init(memory.NewStorage(), nil)
2097 err := r.clone(context.Background(), &CloneOptions{
2098 URL: s.GetBasicLocalRepositoryURL(),
2099 })
2100
2101 s.NoError(err)
2102
2103 fileName := "README"
2104 cIter, err := r.Log(&LogOptions{FileName: &fileName})
2105 s.NoError(err)
2106 defer cIter.Close()
2107
2108 _, err = cIter.Next()
2109 s.ErrorIs(err, io.EOF)
2110}
2111
2112func (s *RepositorySuite) TestLogAllFileForEach() {
2113 r, _ := Init(memory.NewStorage(), nil)
2114 err := r.clone(context.Background(), &CloneOptions{
2115 URL: s.GetBasicLocalRepositoryURL(),
2116 })
2117
2118 s.NoError(err)
2119
2120 fileName := "README"
2121 cIter, err := r.Log(&LogOptions{FileName: &fileName, All: true})
2122 s.NoError(err)
2123 defer cIter.Close()
2124
2125 commitOrder := []plumbing.Hash{
2126 plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"),
2127 }
2128
2129 expectedIndex := 0
2130 err = cIter.ForEach(func(commit *object.Commit) error {
2131 expectedCommitHash := commitOrder[expectedIndex]
2132 s.Equal(expectedCommitHash.String(), commit.Hash.String())
2133 expectedIndex++
2134 return nil
2135 })
2136 s.NoError(err)
2137 s.Equal(1, expectedIndex)
2138}
2139
2140func (s *RepositorySuite) TestLogInvalidFile() {
2141 r, _ := Init(memory.NewStorage(), nil)
2142 err := r.clone(context.Background(), &CloneOptions{
2143 URL: s.GetBasicLocalRepositoryURL(),
2144 })
2145 s.NoError(err)
2146
2147 // Throwing in a file that does not exist
2148 fileName := "vendor/foo12.go"
2149 cIter, err := r.Log(&LogOptions{FileName: &fileName})
2150 // Not raising an error since `git log -- vendor/foo12.go` responds silently
2151 s.NoError(err)
2152 defer cIter.Close()
2153
2154 _, err = cIter.Next()
2155 s.ErrorIs(err, io.EOF)
2156}
2157
2158func (s *RepositorySuite) TestLogFileInitialCommit() {
2159 r, _ := Init(memory.NewStorage(), nil)
2160 err := r.clone(context.Background(), &CloneOptions{
2161 URL: s.GetBasicLocalRepositoryURL(),
2162 })
2163 s.NoError(err)
2164
2165 fileName := "LICENSE"
2166 cIter, err := r.Log(&LogOptions{
2167 Order: LogOrderCommitterTime,
2168 FileName: &fileName,
2169 })
2170 s.NoError(err)
2171 defer cIter.Close()
2172
2173 commitOrder := []plumbing.Hash{
2174 plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d"),
2175 }
2176
2177 expectedIndex := 0
2178 err = cIter.ForEach(func(commit *object.Commit) error {
2179 expectedCommitHash := commitOrder[expectedIndex]
2180 s.Equal(expectedCommitHash.String(), commit.Hash.String())
2181 expectedIndex++
2182 return nil
2183 })
2184 s.NoError(err)
2185 s.Equal(1, expectedIndex)
2186}
2187
2188func (s *RepositorySuite) TestLogFileWithOtherParamsFail() {
2189 r, _ := Init(memory.NewStorage(), nil)
2190 err := r.clone(context.Background(), &CloneOptions{
2191 URL: s.GetBasicLocalRepositoryURL(),
2192 })
2193 s.NoError(err)
2194
2195 fileName := "vendor/foo.go"
2196 cIter, err := r.Log(&LogOptions{
2197 Order: LogOrderCommitterTime,
2198 FileName: &fileName,
2199 From: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
2200 })
2201 s.NoError(err)
2202 defer cIter.Close()
2203
2204 _, iterErr := cIter.Next()
2205 s.Equal(io.EOF, iterErr)
2206}
2207
2208func (s *RepositorySuite) TestLogFileWithOtherParamsPass() {
2209 r, _ := Init(memory.NewStorage(), nil)
2210 err := r.clone(context.Background(), &CloneOptions{
2211 URL: s.GetBasicLocalRepositoryURL(),
2212 })
2213 s.NoError(err)
2214
2215 fileName := "LICENSE"
2216 cIter, err := r.Log(&LogOptions{
2217 Order: LogOrderCommitterTime,
2218 FileName: &fileName,
2219 From: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
2220 })
2221 s.NoError(err)
2222 commitVal, iterErr := cIter.Next()
2223 s.Equal(nil, iterErr)
2224 s.Equal("b029517f6300c2da0f4b651b8642506cd6aaf45d", commitVal.Hash.String())
2225
2226 _, iterErr = cIter.Next()
2227 s.Equal(io.EOF, iterErr)
2228}
2229
2230type mockErrCommitIter struct{}
2231
2232func (m *mockErrCommitIter) Next() (*object.Commit, error) {
2233 return nil, errors.New("mock next error")
2234}
2235
2236func (m *mockErrCommitIter) ForEach(func(*object.Commit) error) error {
2237 return errors.New("mock foreach error")
2238}
2239
2240func (m *mockErrCommitIter) Close() {}
2241
2242func (s *RepositorySuite) TestLogFileWithError() {
2243 fileName := "README"
2244 cIter := object.NewCommitFileIterFromIter(fileName, &mockErrCommitIter{}, false)
2245 defer cIter.Close()
2246
2247 err := cIter.ForEach(func(commit *object.Commit) error {
2248 return nil
2249 })
2250 s.NotNil(err)
2251}
2252
2253func (s *RepositorySuite) TestLogPathWithError() {
2254 fileName := "README"
2255 pathIter := func(path string) bool {
2256 return path == fileName
2257 }
2258 cIter := object.NewCommitPathIterFromIter(pathIter, &mockErrCommitIter{}, false)
2259 defer cIter.Close()
2260
2261 err := cIter.ForEach(func(commit *object.Commit) error {
2262 return nil
2263 })
2264 s.NotNil(err)
2265}
2266
2267func (s *RepositorySuite) TestLogPathRegexpWithError() {
2268 pathRE := regexp.MustCompile("R.*E")
2269 pathIter := func(path string) bool {
2270 return pathRE.MatchString(path)
2271 }
2272 cIter := object.NewCommitPathIterFromIter(pathIter, &mockErrCommitIter{}, false)
2273 defer cIter.Close()
2274
2275 err := cIter.ForEach(func(commit *object.Commit) error {
2276 return nil
2277 })
2278 s.NotNil(err)
2279}
2280
2281func (s *RepositorySuite) TestLogPathFilterRegexp() {
2282 pathRE := regexp.MustCompile(`.*\.go`)
2283 pathIter := func(path string) bool {
2284 return pathRE.MatchString(path)
2285 }
2286
2287 r, _ := Init(memory.NewStorage(), nil)
2288 err := r.clone(context.Background(), &CloneOptions{
2289 URL: s.GetBasicLocalRepositoryURL(),
2290 })
2291 s.NoError(err)
2292
2293 expectedCommitIDs := []string{
2294 "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
2295 "918c48b83bd081e863dbe1b80f8998f058cd8294",
2296 }
2297 commitIDs := []string{}
2298
2299 cIter, err := r.Log(&LogOptions{
2300 PathFilter: pathIter,
2301 From: plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
2302 })
2303 s.NoError(err)
2304 defer cIter.Close()
2305
2306 cIter.ForEach(func(commit *object.Commit) error {
2307 commitIDs = append(commitIDs, commit.ID().String())
2308 return nil
2309 })
2310 s.Equal(
2311 strings.Join(expectedCommitIDs, ", "),
2312 strings.Join(commitIDs, ", "),
2313 )
2314}
2315
2316func (s *RepositorySuite) TestLogLimitNext() {
2317 r, _ := Init(memory.NewStorage(), nil)
2318 err := r.clone(context.Background(), &CloneOptions{
2319 URL: s.GetBasicLocalRepositoryURL(),
2320 })
2321
2322 s.NoError(err)
2323
2324 since := time.Date(2015, 4, 1, 0, 0, 0, 0, time.UTC)
2325 cIter, err := r.Log(&LogOptions{Since: &since})
2326
2327 s.NoError(err)
2328
2329 commitOrder := []plumbing.Hash{
2330 plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
2331 }
2332
2333 for _, o := range commitOrder {
2334 commit, err := cIter.Next()
2335 s.NoError(err)
2336 s.Equal(o, commit.Hash)
2337 }
2338 _, err = cIter.Next()
2339 s.ErrorIs(err, io.EOF)
2340}
2341
2342func (s *RepositorySuite) TestLogLimitForEach() {
2343 r, _ := Init(memory.NewStorage(), nil)
2344 err := r.clone(context.Background(), &CloneOptions{
2345 URL: s.GetBasicLocalRepositoryURL(),
2346 })
2347
2348 s.NoError(err)
2349
2350 since := time.Date(2015, 3, 31, 11, 54, 0, 0, time.UTC)
2351 until := time.Date(2015, 4, 1, 0, 0, 0, 0, time.UTC)
2352 cIter, err := r.Log(&LogOptions{Since: &since, Until: &until})
2353 s.NoError(err)
2354 defer cIter.Close()
2355
2356 commitOrder := []plumbing.Hash{
2357 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
2358 }
2359
2360 expectedIndex := 0
2361 err = cIter.ForEach(func(commit *object.Commit) error {
2362 expectedCommitHash := commitOrder[expectedIndex]
2363 s.Equal(expectedCommitHash.String(), commit.Hash.String())
2364 expectedIndex++
2365 return nil
2366 })
2367 s.NoError(err)
2368 s.Equal(1, expectedIndex)
2369}
2370
2371func (s *RepositorySuite) TestLogAllLimitForEach() {
2372 r, _ := Init(memory.NewStorage(), nil)
2373 err := r.clone(context.Background(), &CloneOptions{
2374 URL: s.GetBasicLocalRepositoryURL(),
2375 })
2376
2377 s.NoError(err)
2378
2379 since := time.Date(2015, 3, 31, 11, 54, 0, 0, time.UTC)
2380 until := time.Date(2015, 4, 1, 0, 0, 0, 0, time.UTC)
2381 cIter, err := r.Log(&LogOptions{Since: &since, Until: &until, All: true})
2382 s.NoError(err)
2383 defer cIter.Close()
2384
2385 commitOrder := []plumbing.Hash{
2386 plumbing.NewHash("e8d3ffab552895c19b9fcf7aa264d277cde33881"),
2387 plumbing.NewHash("918c48b83bd081e863dbe1b80f8998f058cd8294"),
2388 }
2389
2390 expectedIndex := 0
2391 err = cIter.ForEach(func(commit *object.Commit) error {
2392 expectedCommitHash := commitOrder[expectedIndex]
2393 s.Equal(expectedCommitHash.String(), commit.Hash.String())
2394 expectedIndex++
2395 return nil
2396 })
2397 s.NoError(err)
2398 s.Equal(2, expectedIndex)
2399}
2400
2401func (s *RepositorySuite) TestLogLimitWithOtherParamsFail() {
2402 r, _ := Init(memory.NewStorage(), nil)
2403 err := r.clone(context.Background(), &CloneOptions{
2404 URL: s.GetBasicLocalRepositoryURL(),
2405 })
2406 s.NoError(err)
2407
2408 since := time.Date(2015, 3, 31, 11, 54, 0, 0, time.UTC)
2409 cIter, err := r.Log(&LogOptions{
2410 Order: LogOrderCommitterTime,
2411 Since: &since,
2412 From: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
2413 })
2414 s.NoError(err)
2415 defer cIter.Close()
2416
2417 _, iterErr := cIter.Next()
2418 s.Equal(io.EOF, iterErr)
2419}
2420
2421func (s *RepositorySuite) TestLogLimitWithOtherParamsPass() {
2422 r, _ := Init(memory.NewStorage(), nil)
2423 err := r.clone(context.Background(), &CloneOptions{
2424 URL: s.GetBasicLocalRepositoryURL(),
2425 })
2426 s.NoError(err)
2427
2428 until := time.Date(2015, 3, 31, 11, 43, 0, 0, time.UTC)
2429 cIter, err := r.Log(&LogOptions{
2430 Order: LogOrderCommitterTime,
2431 Until: &until,
2432 From: plumbing.NewHash("35e85108805c84807bc66a02d91535e1e24b38b9"),
2433 })
2434 s.NoError(err)
2435 defer cIter.Close()
2436
2437 commitVal, iterErr := cIter.Next()
2438 s.Equal(nil, iterErr)
2439 s.Equal("b029517f6300c2da0f4b651b8642506cd6aaf45d", commitVal.Hash.String())
2440
2441 _, iterErr = cIter.Next()
2442 s.Equal(io.EOF, iterErr)
2443}
2444
2445func (s *RepositorySuite) TestConfigScoped() {
2446 r, _ := Init(memory.NewStorage(), nil)
2447 err := r.clone(context.Background(), &CloneOptions{
2448 URL: s.GetBasicLocalRepositoryURL(),
2449 })
2450 s.NoError(err)
2451
2452 cfg, err := r.ConfigScoped(config.LocalScope)
2453 s.NoError(err)
2454 s.Equal("", cfg.User.Email)
2455
2456 cfg, err = r.ConfigScoped(config.SystemScope)
2457 s.NoError(err)
2458 s.NotEqual("", cfg.User.Email)
2459}
2460
2461func (s *RepositorySuite) TestCommit() {
2462 r, _ := Init(memory.NewStorage(), nil)
2463 err := r.clone(context.Background(), &CloneOptions{
2464 URL: s.GetBasicLocalRepositoryURL(),
2465 })
2466
2467 s.NoError(err)
2468
2469 hash := plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47")
2470 commit, err := r.CommitObject(hash)
2471 s.NoError(err)
2472
2473 s.False(commit.Hash.IsZero())
2474 s.Equal(commit.ID(), commit.Hash)
2475 s.Equal(hash, commit.Hash)
2476 s.Equal(plumbing.CommitObject, commit.Type())
2477
2478 tree, err := commit.Tree()
2479 s.NoError(err)
2480 s.False(tree.Hash.IsZero())
2481
2482 s.Equal("daniel@lordran.local", commit.Author.Email)
2483}
2484
2485func (s *RepositorySuite) TestCommits() {
2486 r, _ := Init(memory.NewStorage(), nil)
2487 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
2488 s.NoError(err)
2489
2490 count := 0
2491 commits, err := r.CommitObjects()
2492 s.NoError(err)
2493 for {
2494 commit, err := commits.Next()
2495 if err != nil {
2496 break
2497 }
2498
2499 count++
2500 s.False(commit.Hash.IsZero())
2501 s.Equal(commit.ID(), commit.Hash)
2502 s.Equal(plumbing.CommitObject, commit.Type())
2503 }
2504
2505 s.Equal(9, count)
2506}
2507
2508func (s *RepositorySuite) TestBlob() {
2509 r, _ := Init(memory.NewStorage(), nil)
2510 err := r.clone(context.Background(), &CloneOptions{
2511 URL: s.GetBasicLocalRepositoryURL(),
2512 })
2513
2514 s.NoError(err)
2515
2516 blob, err := r.BlobObject(plumbing.NewHash("b8e471f58bcbca63b07bda20e428190409c2db47"))
2517 s.NotNil(err)
2518 s.Nil(blob)
2519
2520 blobHash := plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
2521 blob, err = r.BlobObject(blobHash)
2522 s.NoError(err)
2523
2524 s.False(blob.Hash.IsZero())
2525 s.Equal(blob.ID(), blob.Hash)
2526 s.Equal(blobHash, blob.Hash)
2527 s.Equal(plumbing.BlobObject, blob.Type())
2528}
2529
2530func (s *RepositorySuite) TestBlobs() {
2531 r, _ := Init(memory.NewStorage(), nil)
2532 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
2533 s.NoError(err)
2534
2535 count := 0
2536 blobs, err := r.BlobObjects()
2537 s.NoError(err)
2538 for {
2539 blob, err := blobs.Next()
2540 if err != nil {
2541 break
2542 }
2543
2544 count++
2545 s.False(blob.Hash.IsZero())
2546 s.Equal(blob.ID(), blob.Hash)
2547 s.Equal(plumbing.BlobObject, blob.Type())
2548 }
2549
2550 s.Equal(10, count)
2551}
2552
2553func (s *RepositorySuite) TestTagObject() {
2554 url := s.GetLocalRepositoryURL(
2555 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2556 )
2557
2558 r, _ := Init(memory.NewStorage(), nil)
2559 err := r.clone(context.Background(), &CloneOptions{URL: url})
2560 s.NoError(err)
2561
2562 hash := plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc")
2563 tag, err := r.TagObject(hash)
2564 s.NoError(err)
2565
2566 s.False(tag.Hash.IsZero())
2567 s.Equal(hash, tag.Hash)
2568 s.Equal(plumbing.TagObject, tag.Type())
2569}
2570
2571func (s *RepositorySuite) TestTags() {
2572 url := s.GetLocalRepositoryURL(
2573 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2574 )
2575
2576 r, _ := Init(memory.NewStorage(), nil)
2577 err := r.clone(context.Background(), &CloneOptions{URL: url})
2578 s.NoError(err)
2579
2580 count := 0
2581 tags, err := r.Tags()
2582 s.NoError(err)
2583
2584 tags.ForEach(func(tag *plumbing.Reference) error {
2585 count++
2586 s.False(tag.Hash().IsZero())
2587 s.True(tag.Name().IsTag())
2588 return nil
2589 })
2590
2591 s.Equal(5, count)
2592}
2593
2594func (s *RepositorySuite) TestCreateTagLightweight() {
2595 url := s.GetLocalRepositoryURL(
2596 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2597 )
2598
2599 r, _ := Init(memory.NewStorage(), nil)
2600 err := r.clone(context.Background(), &CloneOptions{URL: url})
2601 s.NoError(err)
2602
2603 expected, err := r.Head()
2604 s.NoError(err)
2605
2606 ref, err := r.CreateTag("foobar", expected.Hash(), nil)
2607 s.NoError(err)
2608 s.NotNil(ref)
2609
2610 actual, err := r.Tag("foobar")
2611 s.NoError(err)
2612
2613 s.Equal(actual.Hash(), expected.Hash())
2614}
2615
2616func (s *RepositorySuite) TestCreateTagLightweightExists() {
2617 url := s.GetLocalRepositoryURL(
2618 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2619 )
2620
2621 r, _ := Init(memory.NewStorage(), nil)
2622 err := r.clone(context.Background(), &CloneOptions{URL: url})
2623 s.NoError(err)
2624
2625 expected, err := r.Head()
2626 s.NoError(err)
2627
2628 ref, err := r.CreateTag("lightweight-tag", expected.Hash(), nil)
2629 s.Nil(ref)
2630 s.ErrorIs(err, ErrTagExists)
2631}
2632
2633func (s *RepositorySuite) TestCreateTagAnnotated() {
2634 url := s.GetLocalRepositoryURL(
2635 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2636 )
2637
2638 r, _ := Init(memory.NewStorage(), nil)
2639 err := r.clone(context.Background(), &CloneOptions{URL: url})
2640 s.NoError(err)
2641
2642 h, err := r.Head()
2643 s.NoError(err)
2644
2645 expectedHash := h.Hash()
2646
2647 ref, err := r.CreateTag("foobar", expectedHash, &CreateTagOptions{
2648 Tagger: defaultSignature(),
2649 Message: "foo bar baz qux",
2650 })
2651 s.NoError(err)
2652
2653 tag, err := r.Tag("foobar")
2654 s.NoError(err)
2655
2656 obj, err := r.TagObject(tag.Hash())
2657 s.NoError(err)
2658
2659 s.Equal(tag, ref)
2660 s.Equal(ref.Hash(), obj.Hash)
2661 s.Equal(plumbing.TagObject, obj.Type())
2662 s.Equal(expectedHash, obj.Target)
2663}
2664
2665func (s *RepositorySuite) TestCreateTagAnnotatedBadOpts() {
2666 url := s.GetLocalRepositoryURL(
2667 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2668 )
2669
2670 r, _ := Init(memory.NewStorage(), nil)
2671 err := r.clone(context.Background(), &CloneOptions{URL: url})
2672 s.NoError(err)
2673
2674 h, err := r.Head()
2675 s.NoError(err)
2676
2677 expectedHash := h.Hash()
2678
2679 ref, err := r.CreateTag("foobar", expectedHash, &CreateTagOptions{})
2680 s.Nil(ref)
2681 s.ErrorIs(err, ErrMissingMessage)
2682}
2683
2684func (s *RepositorySuite) TestCreateTagAnnotatedBadHash() {
2685 url := s.GetLocalRepositoryURL(
2686 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2687 )
2688
2689 r, _ := Init(memory.NewStorage(), nil)
2690 err := r.clone(context.Background(), &CloneOptions{URL: url})
2691 s.NoError(err)
2692
2693 ref, err := r.CreateTag("foobar", plumbing.ZeroHash, &CreateTagOptions{
2694 Tagger: defaultSignature(),
2695 Message: "foo bar baz qux",
2696 })
2697 s.Nil(ref)
2698 s.ErrorIs(err, plumbing.ErrObjectNotFound)
2699}
2700
2701func (s *RepositorySuite) TestCreateTagSigned() {
2702 url := s.GetLocalRepositoryURL(
2703 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2704 )
2705
2706 r, _ := Init(memory.NewStorage(), nil)
2707 err := r.clone(context.Background(), &CloneOptions{URL: url})
2708 s.NoError(err)
2709
2710 h, err := r.Head()
2711 s.NoError(err)
2712
2713 key := commitSignKey(s.T(), true)
2714 _, err = r.CreateTag("foobar", h.Hash(), &CreateTagOptions{
2715 Tagger: defaultSignature(),
2716 Message: "foo bar baz qux",
2717 SignKey: key,
2718 })
2719 s.NoError(err)
2720
2721 tag, err := r.Tag("foobar")
2722 s.NoError(err)
2723
2724 obj, err := r.TagObject(tag.Hash())
2725 s.NoError(err)
2726
2727 // Verify the tag.
2728 pks := new(bytes.Buffer)
2729 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil)
2730 s.NoError(err)
2731
2732 err = key.Serialize(pkw)
2733 s.NoError(err)
2734 err = pkw.Close()
2735 s.NoError(err)
2736
2737 actual, err := obj.Verify(pks.String())
2738 s.NoError(err)
2739 s.Equal(key.PrimaryKey, actual.PrimaryKey)
2740}
2741
2742func (s *RepositorySuite) TestCreateTagSignedBadKey() {
2743 url := s.GetLocalRepositoryURL(
2744 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2745 )
2746
2747 r, _ := Init(memory.NewStorage(), nil)
2748 err := r.clone(context.Background(), &CloneOptions{URL: url})
2749 s.NoError(err)
2750
2751 h, err := r.Head()
2752 s.NoError(err)
2753
2754 key := commitSignKey(s.T(), false)
2755 _, err = r.CreateTag("foobar", h.Hash(), &CreateTagOptions{
2756 Tagger: defaultSignature(),
2757 Message: "foo bar baz qux",
2758 SignKey: key,
2759 })
2760 s.ErrorIs(err, openpgperr.InvalidArgumentError("signing key is encrypted"))
2761}
2762
2763func (s *RepositorySuite) TestCreateTagCanonicalize() {
2764 url := s.GetLocalRepositoryURL(
2765 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2766 )
2767
2768 r, _ := Init(memory.NewStorage(), nil)
2769 err := r.clone(context.Background(), &CloneOptions{URL: url})
2770 s.NoError(err)
2771
2772 h, err := r.Head()
2773 s.NoError(err)
2774
2775 key := commitSignKey(s.T(), true)
2776 _, err = r.CreateTag("foobar", h.Hash(), &CreateTagOptions{
2777 Tagger: defaultSignature(),
2778 Message: "\n\nfoo bar baz qux\n\nsome message here",
2779 SignKey: key,
2780 })
2781 s.NoError(err)
2782
2783 tag, err := r.Tag("foobar")
2784 s.NoError(err)
2785
2786 obj, err := r.TagObject(tag.Hash())
2787 s.NoError(err)
2788
2789 // Assert the new canonicalized message.
2790 s.Equal("foo bar baz qux\n\nsome message here\n", obj.Message)
2791
2792 // Verify the tag.
2793 pks := new(bytes.Buffer)
2794 pkw, err := armor.Encode(pks, openpgp.PublicKeyType, nil)
2795 s.NoError(err)
2796
2797 err = key.Serialize(pkw)
2798 s.NoError(err)
2799 err = pkw.Close()
2800 s.NoError(err)
2801
2802 actual, err := obj.Verify(pks.String())
2803 s.NoError(err)
2804 s.Equal(key.PrimaryKey, actual.PrimaryKey)
2805}
2806
2807func (s *RepositorySuite) TestTagLightweight() {
2808 url := s.GetLocalRepositoryURL(
2809 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2810 )
2811
2812 r, _ := Init(memory.NewStorage(), nil)
2813 err := r.clone(context.Background(), &CloneOptions{URL: url})
2814 s.NoError(err)
2815
2816 expected := plumbing.NewHash("f7b877701fbf855b44c0a9e86f3fdce2c298b07f")
2817
2818 tag, err := r.Tag("lightweight-tag")
2819 s.NoError(err)
2820
2821 actual := tag.Hash()
2822 s.Equal(actual, expected)
2823}
2824
2825func (s *RepositorySuite) TestTagLightweightMissingTag() {
2826 url := s.GetLocalRepositoryURL(
2827 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2828 )
2829
2830 r, _ := Init(memory.NewStorage(), nil)
2831 err := r.clone(context.Background(), &CloneOptions{URL: url})
2832 s.NoError(err)
2833
2834 tag, err := r.Tag("lightweight-tag-tag")
2835 s.Nil(tag)
2836 s.ErrorIs(err, ErrTagNotFound)
2837}
2838
2839func (s *RepositorySuite) TestDeleteTag() {
2840 url := s.GetLocalRepositoryURL(
2841 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2842 )
2843
2844 r, _ := Init(memory.NewStorage(), nil)
2845 err := r.clone(context.Background(), &CloneOptions{URL: url})
2846 s.NoError(err)
2847
2848 err = r.DeleteTag("lightweight-tag")
2849 s.NoError(err)
2850
2851 _, err = r.Tag("lightweight-tag")
2852 s.ErrorIs(err, ErrTagNotFound)
2853}
2854
2855func (s *RepositorySuite) TestDeleteTagMissingTag() {
2856 url := s.GetLocalRepositoryURL(
2857 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2858 )
2859
2860 r, _ := Init(memory.NewStorage(), nil)
2861 err := r.clone(context.Background(), &CloneOptions{URL: url})
2862 s.NoError(err)
2863
2864 err = r.DeleteTag("lightweight-tag-tag")
2865 s.ErrorIs(err, ErrTagNotFound)
2866}
2867
2868func (s *RepositorySuite) TestDeleteTagAnnotated() {
2869 url := s.GetLocalRepositoryURL(
2870 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2871 )
2872
2873 fs := s.TemporalFilesystem()
2874
2875 fss := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
2876
2877 r, _ := Init(fss, nil)
2878 err := r.clone(context.Background(), &CloneOptions{URL: url})
2879 s.NoError(err)
2880
2881 ref, err := r.Tag("annotated-tag")
2882 s.NotNil(ref)
2883 s.NoError(err)
2884
2885 obj, err := r.TagObject(ref.Hash())
2886 s.NotNil(obj)
2887 s.NoError(err)
2888
2889 err = r.DeleteTag("annotated-tag")
2890 s.NoError(err)
2891
2892 _, err = r.Tag("annotated-tag")
2893 s.ErrorIs(err, ErrTagNotFound)
2894
2895 // Run a prune (and repack, to ensure that we are GCing everything regardless
2896 // of the fixture in use) and try to get the tag object again.
2897 //
2898 // The repo needs to be re-opened after the repack.
2899 err = r.Prune(PruneOptions{Handler: r.DeleteObject})
2900 s.NoError(err)
2901
2902 err = r.RepackObjects(&RepackConfig{})
2903 s.NoError(err)
2904
2905 r, err = PlainOpen(fs.Root())
2906 s.NotNil(r)
2907 s.NoError(err)
2908
2909 // Now check to see if the GC was effective in removing the tag object.
2910 obj, err = r.TagObject(ref.Hash())
2911 s.Nil(obj)
2912 s.ErrorIs(err, plumbing.ErrObjectNotFound)
2913}
2914
2915func (s *RepositorySuite) TestDeleteTagAnnotatedUnpacked() {
2916 url := s.GetLocalRepositoryURL(
2917 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
2918 )
2919
2920 fs := s.TemporalFilesystem()
2921
2922 fss := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
2923
2924 r, _ := Init(fss, nil)
2925 err := r.clone(context.Background(), &CloneOptions{URL: url})
2926 s.NoError(err)
2927
2928 // Create a tag for the deletion test. This ensures that the ultimate loose
2929 // object will be unpacked (as we aren't doing anything that should pack it),
2930 // so that we can effectively test that a prune deletes it, without having to
2931 // resort to a repack.
2932 h, err := r.Head()
2933 s.NoError(err)
2934
2935 expectedHash := h.Hash()
2936
2937 ref, err := r.CreateTag("foobar", expectedHash, &CreateTagOptions{
2938 Tagger: defaultSignature(),
2939 Message: "foo bar baz qux",
2940 })
2941 s.NoError(err)
2942
2943 tag, err := r.Tag("foobar")
2944 s.NoError(err)
2945
2946 obj, err := r.TagObject(tag.Hash())
2947 s.NotNil(obj)
2948 s.NoError(err)
2949
2950 err = r.DeleteTag("foobar")
2951 s.NoError(err)
2952
2953 _, err = r.Tag("foobar")
2954 s.ErrorIs(err, ErrTagNotFound)
2955
2956 // As mentioned, only run a prune. We are not testing for packed objects
2957 // here.
2958 err = r.Prune(PruneOptions{Handler: r.DeleteObject})
2959 s.NoError(err)
2960
2961 // Now check to see if the GC was effective in removing the tag object.
2962 obj, err = r.TagObject(ref.Hash())
2963 s.Nil(obj)
2964 s.ErrorIs(err, plumbing.ErrObjectNotFound)
2965}
2966
2967func (s *RepositorySuite) TestInvalidTagName() {
2968 r, err := Init(memory.NewStorage(), nil)
2969 s.NoError(err)
2970 for i, name := range []string{
2971 "",
2972 "foo bar",
2973 "foo\tbar",
2974 "foo\nbar",
2975 } {
2976 _, err = r.CreateTag(name, plumbing.ZeroHash, nil)
2977 s.Error(err, fmt.Sprintf("case %d %q", i, name))
2978 }
2979}
2980
2981func (s *RepositorySuite) TestBranches() {
2982 f := fixtures.ByURL("https://github.com/git-fixtures/root-references.git").One()
2983 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
2984 r, err := Open(sto, f.DotGit())
2985 s.NoError(err)
2986
2987 count := 0
2988 branches, err := r.Branches()
2989 s.NoError(err)
2990
2991 branches.ForEach(func(branch *plumbing.Reference) error {
2992 count++
2993 s.False(branch.Hash().IsZero())
2994 s.True(branch.Name().IsBranch())
2995 return nil
2996 })
2997
2998 s.Equal(8, count)
2999}
3000
3001func (s *RepositorySuite) TestNotes() {
3002 // TODO add fixture with Notes
3003 url := s.GetLocalRepositoryURL(
3004 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
3005 )
3006
3007 r, _ := Init(memory.NewStorage(), nil)
3008 err := r.clone(context.Background(), &CloneOptions{URL: url})
3009 s.NoError(err)
3010
3011 count := 0
3012 notes, err := r.Notes()
3013 s.NoError(err)
3014
3015 notes.ForEach(func(note *plumbing.Reference) error {
3016 count++
3017 s.False(note.Hash().IsZero())
3018 s.True(note.Name().IsNote())
3019 return nil
3020 })
3021
3022 s.Equal(0, count)
3023}
3024
3025func (s *RepositorySuite) TestTree() {
3026 r, _ := Init(memory.NewStorage(), nil)
3027 err := r.clone(context.Background(), &CloneOptions{
3028 URL: s.GetBasicLocalRepositoryURL(),
3029 })
3030 s.NoError(err)
3031
3032 invalidHash := plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
3033 tree, err := r.TreeObject(invalidHash)
3034 s.Nil(tree)
3035 s.NotNil(err)
3036
3037 hash := plumbing.NewHash("dbd3641b371024f44d0e469a9c8f5457b0660de1")
3038 tree, err = r.TreeObject(hash)
3039 s.NoError(err)
3040
3041 s.False(tree.Hash.IsZero())
3042 s.Equal(tree.ID(), tree.Hash)
3043 s.Equal(hash, tree.Hash)
3044 s.Equal(plumbing.TreeObject, tree.Type())
3045 s.NotEqual(0, len(tree.Entries))
3046}
3047
3048func (s *RepositorySuite) TestTrees() {
3049 r, _ := Init(memory.NewStorage(), nil)
3050 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3051 s.NoError(err)
3052
3053 count := 0
3054 trees, err := r.TreeObjects()
3055 s.NoError(err)
3056 for {
3057 tree, err := trees.Next()
3058 if err != nil {
3059 break
3060 }
3061
3062 count++
3063 s.False(tree.Hash.IsZero())
3064 s.Equal(tree.ID(), tree.Hash)
3065 s.Equal(plumbing.TreeObject, tree.Type())
3066 s.NotEqual(0, len(tree.Entries))
3067 }
3068
3069 s.Equal(12, count)
3070}
3071
3072func (s *RepositorySuite) TestTagObjects() {
3073 url := s.GetLocalRepositoryURL(
3074 fixtures.ByURL("https://github.com/git-fixtures/tags.git").One(),
3075 )
3076
3077 r, _ := Init(memory.NewStorage(), nil)
3078 err := r.clone(context.Background(), &CloneOptions{URL: url})
3079 s.NoError(err)
3080
3081 count := 0
3082 tags, err := r.TagObjects()
3083 s.NoError(err)
3084
3085 tags.ForEach(func(tag *object.Tag) error {
3086 count++
3087
3088 s.False(tag.Hash.IsZero())
3089 s.Equal(plumbing.TagObject, tag.Type())
3090 return nil
3091 })
3092
3093 refs, _ := r.References()
3094 refs.ForEach(func(ref *plumbing.Reference) error {
3095 return nil
3096 })
3097
3098 s.Equal(4, count)
3099}
3100
3101func (s *RepositorySuite) TestCommitIterClosePanic() {
3102 r, _ := Init(memory.NewStorage(), nil)
3103 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3104 s.NoError(err)
3105
3106 commits, err := r.CommitObjects()
3107 s.NoError(err)
3108 commits.Close()
3109}
3110
3111func (s *RepositorySuite) TestRef() {
3112 r, _ := Init(memory.NewStorage(), nil)
3113 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3114 s.NoError(err)
3115
3116 ref, err := r.Reference(plumbing.HEAD, false)
3117 s.NoError(err)
3118 s.Equal(plumbing.HEAD, ref.Name())
3119
3120 ref, err = r.Reference(plumbing.HEAD, true)
3121 s.NoError(err)
3122 s.Equal(plumbing.ReferenceName("refs/heads/master"), ref.Name())
3123}
3124
3125func (s *RepositorySuite) TestRefs() {
3126 r, _ := Init(memory.NewStorage(), nil)
3127 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3128 s.NoError(err)
3129
3130 s.NoError(err)
3131
3132 iter, err := r.References()
3133 s.NoError(err)
3134 s.NotNil(iter)
3135}
3136
3137func (s *RepositorySuite) TestObject() {
3138 r, _ := Init(memory.NewStorage(), nil)
3139 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3140 s.NoError(err)
3141
3142 hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
3143 o, err := r.Object(plumbing.CommitObject, hash)
3144 s.NoError(err)
3145
3146 s.False(o.ID().IsZero())
3147 s.Equal(plumbing.CommitObject, o.Type())
3148}
3149
3150func (s *RepositorySuite) TestObjects() {
3151 r, _ := Init(memory.NewStorage(), nil)
3152 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3153 s.NoError(err)
3154
3155 count := 0
3156 objects, err := r.Objects()
3157 s.NoError(err)
3158 for {
3159 o, err := objects.Next()
3160 if err != nil {
3161 break
3162 }
3163
3164 count++
3165 s.False(o.ID().IsZero())
3166 s.NotEqual(plumbing.AnyObject, o.Type())
3167 }
3168
3169 s.Equal(31, count)
3170}
3171
3172func (s *RepositorySuite) TestObjectNotFound() {
3173 r, _ := Init(memory.NewStorage(), nil)
3174 err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()})
3175 s.NoError(err)
3176
3177 hash := plumbing.NewHash("0a3fb06ff80156fb153bcdcc58b5e16c2d27625c")
3178 tag, err := r.Object(plumbing.TagObject, hash)
3179 s.ErrorIs(err, plumbing.ErrObjectNotFound)
3180 s.Nil(tag)
3181}
3182
3183func (s *RepositorySuite) TestWorktree() {
3184 def := memfs.New()
3185 r, _ := Init(memory.NewStorage(), def)
3186 w, err := r.Worktree()
3187 s.NoError(err)
3188 s.Equal(def, w.Filesystem)
3189}
3190
3191func (s *RepositorySuite) TestWorktreeBare() {
3192 r, _ := Init(memory.NewStorage(), nil)
3193 w, err := r.Worktree()
3194 s.ErrorIs(err, ErrIsBareRepository)
3195 s.Nil(w)
3196}
3197
3198func (s *RepositorySuite) TestResolveRevision() {
3199 f := fixtures.ByURL("https://github.com/git-fixtures/basic.git").One()
3200 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
3201 r, err := Open(sto, f.DotGit())
3202 s.NoError(err)
3203
3204 datas := map[string]string{
3205 "HEAD": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3206 "heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3207 "heads/master~1": "918c48b83bd081e863dbe1b80f8998f058cd8294",
3208 "refs/heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3209 "refs/heads/master~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d",
3210 "refs/tags/v1.0.0": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3211 "refs/remotes/origin/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3212 "refs/remotes/origin/HEAD": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3213 "HEAD~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d",
3214 "HEAD~3^2": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69",
3215 "HEAD~3^2^0": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69",
3216 "HEAD~2^{/binary file}": "35e85108805c84807bc66a02d91535e1e24b38b9",
3217 "HEAD~^{/!-some}": "1669dce138d9b841a518c64b10914d88f5e488ea",
3218 "master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3219 "branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881",
3220 "v1.0.0": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
3221 "branch~1": "918c48b83bd081e863dbe1b80f8998f058cd8294",
3222 "v1.0.0~1": "918c48b83bd081e863dbe1b80f8998f058cd8294",
3223 "master~1": "918c48b83bd081e863dbe1b80f8998f058cd8294",
3224 "918c48b83bd081e863dbe1b80f8998f058cd8294": "918c48b83bd081e863dbe1b80f8998f058cd8294",
3225 "918c48b": "918c48b83bd081e863dbe1b80f8998f058cd8294", // odd number of hex digits
3226 }
3227
3228 for rev, hash := range datas {
3229 h, err := r.ResolveRevision(plumbing.Revision(rev))
3230
3231 s.NoError(err, fmt.Sprintf("while checking %s", rev))
3232 s.Equal(hash, h.String(), fmt.Sprintf("while checking %s", rev))
3233 }
3234}
3235
3236func (s *RepositorySuite) TestResolveRevisionAnnotated() {
3237 f := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One()
3238 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
3239 r, err := Open(sto, f.DotGit())
3240 s.NoError(err)
3241
3242 datas := map[string]string{
3243 "refs/tags/annotated-tag": "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
3244 "b742a2a9fa0afcfa9a6fad080980fbc26b007c69": "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
3245 }
3246
3247 for rev, hash := range datas {
3248 h, err := r.ResolveRevision(plumbing.Revision(rev))
3249
3250 s.NoError(err, fmt.Sprintf("while checking %s", rev))
3251 s.Equal(hash, h.String(), fmt.Sprintf("while checking %s", rev))
3252 }
3253}
3254
3255func (s *RepositorySuite) TestResolveRevisionWithErrors() {
3256 url := s.GetLocalRepositoryURL(
3257 fixtures.ByURL("https://github.com/git-fixtures/basic.git").One(),
3258 )
3259
3260 r, _ := Init(memory.NewStorage(), nil)
3261 err := r.clone(context.Background(), &CloneOptions{URL: url})
3262 s.NoError(err)
3263
3264 headRef, err := r.Head()
3265 s.NoError(err)
3266
3267 ref := plumbing.NewHashReference("refs/heads/918c48b83bd081e863dbe1b80f8998f058cd8294", headRef.Hash())
3268 err = r.Storer.SetReference(ref)
3269 s.NoError(err)
3270
3271 datas := map[string]string{
3272 "efs/heads/master~": "reference not found",
3273 "HEAD^3": `Revision invalid : "3" found must be 0, 1 or 2 after "^"`,
3274 "HEAD^{/whatever}": `no commit message match regexp: "whatever"`,
3275 "4e1243bd22c66e76c2ba9eddc1f91394e57f9f83": "reference not found",
3276 }
3277
3278 for rev, rerr := range datas {
3279 _, err := r.ResolveRevision(plumbing.Revision(rev))
3280 s.NotNil(err)
3281 s.Equal(rerr, err.Error())
3282 }
3283}
3284
3285func (s *RepositorySuite) testRepackObjects(deleteTime time.Time, expectedPacks int) {
3286 srcFs := fixtures.ByTag("unpacked").One().DotGit()
3287 var sto storage.Storer
3288 var err error
3289 sto = filesystem.NewStorage(srcFs, cache.NewObjectLRUDefault())
3290
3291 los := sto.(storer.LooseObjectStorer)
3292 s.NotNil(los)
3293
3294 numLooseStart := 0
3295 err = los.ForEachObjectHash(func(_ plumbing.Hash) error {
3296 numLooseStart++
3297 return nil
3298 })
3299 s.NoError(err)
3300 s.True(numLooseStart > 0)
3301
3302 pos := sto.(storer.PackedObjectStorer)
3303 s.NotNil(los)
3304
3305 packs, err := pos.ObjectPacks()
3306 s.NoError(err)
3307 numPacksStart := len(packs)
3308 s.True(numPacksStart > 1)
3309
3310 r, err := Open(sto, srcFs)
3311 s.NoError(err)
3312 s.NotNil(r)
3313
3314 err = r.RepackObjects(&RepackConfig{
3315 OnlyDeletePacksOlderThan: deleteTime,
3316 })
3317 s.NoError(err)
3318
3319 numLooseEnd := 0
3320 err = los.ForEachObjectHash(func(_ plumbing.Hash) error {
3321 numLooseEnd++
3322 return nil
3323 })
3324 s.NoError(err)
3325 s.Equal(0, numLooseEnd)
3326
3327 packs, err = pos.ObjectPacks()
3328 s.NoError(err)
3329 numPacksEnd := len(packs)
3330 s.Equal(expectedPacks, numPacksEnd)
3331}
3332
3333func (s *RepositorySuite) TestRepackObjects() {
3334 if testing.Short() {
3335 s.T().Skip("skipping test in short mode.")
3336 }
3337
3338 s.testRepackObjects(time.Time{}, 1)
3339}
3340
3341func (s *RepositorySuite) TestRepackObjectsWithNoDelete() {
3342 if testing.Short() {
3343 s.T().Skip("skipping test in short mode.")
3344 }
3345
3346 s.testRepackObjects(time.Unix(0, 1), 3)
3347}
3348
3349func ExecuteOnPath(t *testing.T, path string, cmds ...string) error {
3350 for _, cmd := range cmds {
3351 err := executeOnPath(path, cmd)
3352 assert.NoError(t, err)
3353 }
3354
3355 return nil
3356}
3357
3358func executeOnPath(path, cmd string) error {
3359 args := strings.Split(cmd, " ")
3360 c := exec.Command(args[0], args[1:]...)
3361 c.Dir = path
3362 c.Env = os.Environ()
3363
3364 buf := bytes.NewBuffer(nil)
3365 c.Stderr = buf
3366 c.Stdout = buf
3367
3368 return c.Run()
3369}
3370
3371func (s *RepositorySuite) TestBrokenMultipleShallowFetch() {
3372 r, _ := Init(memory.NewStorage(), nil)
3373 _, err := r.CreateRemote(&config.RemoteConfig{
3374 Name: DefaultRemoteName,
3375 URLs: []string{s.GetBasicLocalRepositoryURL()},
3376 })
3377 s.NoError(err)
3378
3379 s.NoError(r.Fetch(&FetchOptions{
3380 Depth: 2,
3381 RefSpecs: []config.RefSpec{config.RefSpec("refs/heads/master:refs/heads/master")},
3382 }))
3383
3384 shallows, err := r.Storer.Shallow()
3385 s.NoError(err)
3386 s.Len(shallows, 1)
3387
3388 ref, err := r.Reference("refs/heads/master", true)
3389 s.NoError(err)
3390 cobj, err := r.CommitObject(ref.Hash())
3391 s.NoError(err)
3392 s.NotNil(cobj)
3393 err = object.NewCommitPreorderIter(cobj, nil, nil).ForEach(func(c *object.Commit) error {
3394 for _, ph := range c.ParentHashes {
3395 for _, h := range shallows {
3396 if ph == h {
3397 return storer.ErrStop
3398 }
3399 }
3400 }
3401
3402 return nil
3403 })
3404 s.NoError(err)
3405
3406 s.NoError(r.Fetch(&FetchOptions{
3407 Depth: 5,
3408 RefSpecs: []config.RefSpec{config.RefSpec("refs/heads/*:refs/heads/*")},
3409 }))
3410
3411 shallows, err = r.Storer.Shallow()
3412 s.NoError(err)
3413 s.Len(shallows, 3)
3414
3415 ref, err = r.Reference("refs/heads/master", true)
3416 s.NoError(err)
3417 cobj, err = r.CommitObject(ref.Hash())
3418 s.NoError(err)
3419 s.NotNil(cobj)
3420 err = object.NewCommitPreorderIter(cobj, nil, nil).ForEach(func(c *object.Commit) error {
3421 for _, ph := range c.ParentHashes {
3422 for _, h := range shallows {
3423 if ph == h {
3424 return storer.ErrStop
3425 }
3426 }
3427 }
3428
3429 return nil
3430 })
3431 s.NoError(err)
3432}
3433
3434func (s *RepositorySuite) TestDotGitToOSFilesystemsInvalidPath() {
3435 _, _, err := dotGitToOSFilesystems("\000", false)
3436 s.NotNil(err)
3437}
3438
3439func (s *RepositorySuite) TestIssue674() {
3440 r, _ := Init(memory.NewStorage(), nil)
3441 h, err := r.ResolveRevision(plumbing.Revision(""))
3442
3443 s.NotNil(err)
3444 s.NotNil(h)
3445 s.True(h.IsZero())
3446}
3447
3448func BenchmarkObjects(b *testing.B) {
3449 defer fixtures.Clean()
3450
3451 for _, f := range fixtures.ByTag("packfile") {
3452 if f.DotGitHash == "" {
3453 continue
3454 }
3455
3456 b.Run(f.URL, func(b *testing.B) {
3457 fs := f.DotGit()
3458 st := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
3459
3460 worktree, err := fs.Chroot(filepath.Dir(fs.Root()))
3461 if err != nil {
3462 b.Fatal(err)
3463 }
3464
3465 repo, err := Open(st, worktree)
3466 if err != nil {
3467 b.Fatal(err)
3468 }
3469
3470 for i := 0; i < b.N; i++ {
3471 iter, err := repo.Objects()
3472 if err != nil {
3473 b.Fatal(err)
3474 }
3475
3476 for {
3477 _, err := iter.Next()
3478 if err == io.EOF {
3479 break
3480 }
3481
3482 if err != nil {
3483 b.Fatal(err)
3484 }
3485 }
3486
3487 iter.Close()
3488 }
3489 })
3490 }
3491}
3492
3493func BenchmarkPlainClone(b *testing.B) {
3494 b.StopTimer()
3495 clone := func(b *testing.B) {
3496 _, err := PlainClone(b.TempDir(), true, &CloneOptions{
3497 URL: "https://github.com/go-git/go-git.git",
3498 Depth: 1,
3499 Tags: plumbing.NoTags,
3500 SingleBranch: true,
3501 })
3502 if err != nil {
3503 b.Error(err)
3504 }
3505 }
3506
3507 // Warm-up as the initial clone could have a higher cost which
3508 // may skew results.
3509 clone(b)
3510
3511 b.StartTimer()
3512 for i := 0; i < b.N; i++ {
3513 clone(b)
3514 }
3515}